void ion::HandleException(ResumeFromException *rfe) { AssertCanGC(); JSContext *cx = GetIonContext()->cx; IonSpew(IonSpew_Invalidate, "handling exception"); // Immediately remove any bailout frame guard that might be left over from // an error in between ConvertFrames and ThunkToInterpreter. js_delete(cx->runtime->ionActivation->maybeTakeBailout()); IonFrameIterator iter(cx->runtime->ionTop); while (!iter.isEntry()) { if (iter.isScripted()) { // Search each inlined frame for live iterator objects, and close // them. InlineFrameIterator frames(&iter); for (;;) { CloseLiveIterators(cx, frames); // When profiling, each frame popped needs a notification that // the function has exited, so invoke the probe that a function // is exiting. AutoAssertNoGC nogc; RawScript script = frames.script(); Probes::exitScript(cx, script, script->function(), NULL); if (!frames.more()) break; ++frames; } IonScript *ionScript; if (iter.checkInvalidation(&ionScript)) ionScript->decref(cx->runtime->defaultFreeOp()); } ++iter; } // Clear any Ion return override that's been set. // This may happen if a callVM function causes an invalidation (setting the // override), and then fails, bypassing the bailout handlers that would // otherwise clear the return override. if (cx->runtime->hasIonReturnOverride()) cx->runtime->takeIonReturnOverride(); rfe->stackPointer = iter.fp(); }
bool TypeInferenceOracle::elementWriteNeedsBarrier(RawScript script, jsbytecode *pc) { // Return true if SETELEM-like instructions need a write barrier before modifying // a property. The object is the third value popped by SETELEM. return elementWriteNeedsBarrier(script->analysis()->poppedTypes(pc, 2)); }
bool TypeInferenceOracle::arrayResultShouldHaveDoubleConversion(RawScript script, jsbytecode *pc) { types::StackTypeSet::DoubleConversion conversion = script->analysis()->pushedTypes(pc, 0)->convertDoubleElements(cx); return conversion == types::StackTypeSet::AlwaysConvertToDoubles; }
bool TypeInferenceOracle::elementReadShouldAlwaysLoadDoubles(RawScript script, jsbytecode *pc) { StackTypeSet *types = script->analysis()->poppedTypes(pc, 1); types::StackTypeSet::DoubleConversion conversion = types->convertDoubleElements(cx); return conversion == StackTypeSet::AlwaysConvertToDoubles; }
bool TypeInferenceOracle::elementReadIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType) { // Check whether the object is a typed array and index is int32 or double. StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1); StackTypeSet *id = DropUnrooted(script)->analysis()->poppedTypes(pc, 0); JSValueType idType = id->getKnownTypeTag(); if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE) return false; if (obj->hasType(types::Type::StringType())) return false; *arrayType = obj->getTypedArrayType(); if (*arrayType == TypedArray::TYPE_MAX) return false; JS_ASSERT(*arrayType >= 0 && *arrayType < TypedArray::TYPE_MAX); // Unlike dense arrays, the types of elements in typed arrays are not // guaranteed to be present in the object's type, and we need to use // knowledge about the possible contents of the array vs. the types // that have been read out of it to figure out how to do the load. types::TypeSet *result = propertyRead(script, pc); if (*arrayType == TypedArray::TYPE_FLOAT32 || *arrayType == TypedArray::TYPE_FLOAT64) { if (!result->hasType(types::Type::DoubleType())) return false; } else { if (!result->hasType(types::Type::Int32Type())) return false; } return true; }
MIRType TypeInferenceOracle::elementWrite(RawScript script, jsbytecode *pc) { StackTypeSet *objTypes = script->analysis()->poppedTypes(pc, 2); MIRType elementType = MIRType_None; unsigned count = objTypes->getObjectCount(); for (unsigned i = 0; i < count; i++) { if (objTypes->getSingleObject(i)) return MIRType_None; if (TypeObject *object = objTypes->getTypeObject(i)) { if (object->unknownProperties()) return MIRType_None; types::HeapTypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false); if (!elementTypes) return MIRType_None; MIRType type = getMIRType(elementTypes); if (type == MIRType_None) return MIRType_None; if (elementType == MIRType_None) elementType = type; else if (elementType != type) return MIRType_None; } } return elementType; }
bool TypeInferenceOracle::elementWriteIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType) { return elementWriteIsTypedArray(script->analysis()->poppedTypes(pc, 2), script->analysis()->poppedTypes(pc, 1), arrayType); }
bool TypeInferenceOracle::elementWriteIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType) { // Check whether the object is a dense array and index is int32 or double. StackTypeSet *obj = script->analysis()->poppedTypes(pc, 2); StackTypeSet *id = script->analysis()->poppedTypes(pc, 1); JSValueType idType = id->getKnownTypeTag(); if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE) return false; *arrayType = obj->getTypedArrayType(); if (*arrayType == TypedArray::TYPE_MAX) return false; return true; }
bool TypeInferenceOracle::elementReadIsDenseNative(RawScript script, jsbytecode *pc) { // Check whether the object is a dense array and index is int32 or double. StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1); StackTypeSet *id = script->analysis()->poppedTypes(pc, 0); JSValueType idType = id->getKnownTypeTag(); if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE) return false; if (obj->hasType(types::Type::StringType())) return false; Class *clasp = obj->getKnownClass(); return clasp && clasp->isNative(); }
bool TypeInferenceOracle::elementWriteNeedsDoubleConversion(RawScript script, jsbytecode *pc) { StackTypeSet *types = script->analysis()->poppedTypes(pc, 2); types::StackTypeSet::DoubleConversion conversion = types->convertDoubleElements(cx); return conversion == StackTypeSet::AlwaysConvertToDoubles || conversion == StackTypeSet::MaybeConvertToDoubles; }
static bool SetSourceMap(JSContext *cx, TokenStream &tokenStream, ScriptSource *ss, RawScript script) { if (tokenStream.hasSourceMap()) { if (!ss->setSourceMap(cx, tokenStream.releaseSourceMap(), script->filename())) return false; } return true; }
uint32 ion::RecompileForInlining() { JSContext *cx = GetIonContext()->cx; RawScript script = cx->fp()->script().unsafeGet(); IonSpew(IonSpew_Inlining, "Recompiling script to inline calls %s:%d", script->filename, script->lineno); // Invalidate the script to force a recompile. if (!Invalidate(cx, script, /* resetUses */ false)) return BAILOUT_RETURN_FATAL_ERROR; // Invalidation should not reset the use count. JS_ASSERT(script->getUseCount() >= js_IonOptions.usesBeforeInlining); return true; }
TypeOracle::Binary TypeInferenceOracle::binaryOp(RawScript script, jsbytecode *pc) { JS_ASSERT(script == this->script()); JSOp op = (JSOp)*pc; Binary res; if (op == JSOP_NEG || op == JSOP_POS) { res.lhs = getMIRType(script->analysis()->poppedTypes(pc, 0)); res.rhs = MIRType_Int32; res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0)); } else { res.lhs = getMIRType(script->analysis()->poppedTypes(pc, 1)); res.rhs = getMIRType(script->analysis()->poppedTypes(pc, 0)); res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0)); } return res; }
TypeOracle::Unary TypeInferenceOracle::unaryOp(RawScript script, jsbytecode *pc) { JS_ASSERT(script == this->script()); Unary res; res.ival = getMIRType(script->analysis()->poppedTypes(pc, 0)); res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0)); return res; }
uint32_t ion::CachedShapeGuardFailure() { JSContext *cx = GetIonContext()->cx; RawScript script = GetBailedJSScript(cx); JS_ASSERT(!script->ionScript()->invalidated()); script->failedShapeGuard = true; // Purge JM caches in the script and all inlined script, to avoid baking in // the same shape guard next time. for (size_t i = 0; i < script->ionScript()->scriptEntries(); i++) mjit::PurgeCaches(script->ionScript()->getScript(i)); IonSpew(IonSpew_Invalidate, "Invalidating due to shape guard failure"); return Invalidate(cx, script); }
TypeOracle::BinaryTypes TypeInferenceOracle::binaryTypes(RawScript script, jsbytecode *pc) { JS_ASSERT(script == this->script()); JSOp op = (JSOp)*pc; BinaryTypes res; if (op == JSOP_NEG || op == JSOP_POS) { res.lhsTypes = script->analysis()->poppedTypes(pc, 0); res.rhsTypes = NULL; res.outTypes = script->analysis()->pushedTypes(pc, 0); } else { res.lhsTypes = script->analysis()->poppedTypes(pc, 1); res.rhsTypes = script->analysis()->poppedTypes(pc, 0); res.outTypes = script->analysis()->pushedTypes(pc, 0); } return res; }
bool TypeInferenceOracle::elementWriteHasExtraIndexedProperty(RawScript script, jsbytecode *pc) { StackTypeSet *obj = script->analysis()->poppedTypes(pc, 2); if (obj->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW)) return true; return types::TypeCanHaveExtraIndexedProperties(cx, obj); }
uint32_t ion::BoundsCheckFailure() { JSContext *cx = GetIonContext()->cx; RawScript script = GetBailedJSScript(cx); IonSpew(IonSpew_Bailouts, "Bounds check failure %s:%d", script->filename(), script->lineno); if (!script->failedBoundsCheck) { script->failedBoundsCheck = true; // Invalidate the script to force a recompile. IonSpew(IonSpew_Invalidate, "Invalidating due to bounds check failure"); return Invalidate(cx, script); } return true; }
bool JSRuntime::cloneSelfHostedFunctionScript(JSContext *cx, Handle<PropertyName*> name, Handle<JSFunction*> targetFun) { RootedValue funVal(cx); if (!getUnclonedSelfHostedValue(cx, name, &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; targetFun->setScript(cscript); cscript->setFunction(targetFun); JS_ASSERT(sourceFun->nargs == targetFun->nargs); targetFun->flags = sourceFun->flags | JSFunction::EXTENDED; return true; }
bool IonFrameIterator::checkInvalidation(IonScript **ionScriptOut) const { AutoAssertNoGC nogc; uint8_t *returnAddr = returnAddressToFp(); RawScript script = this->script(); // N.B. the current IonScript is not the same as the frame's // IonScript if the frame has since been invalidated. IonScript *currentIonScript = script->ion; bool invalidated = !script->hasIonScript() || !currentIonScript->containsReturnAddress(returnAddr); if (!invalidated) return false; int32_t invalidationDataOffset = ((int32_t *) returnAddr)[-1]; uint8_t *ionScriptDataOffset = returnAddr + invalidationDataOffset; IonScript *ionScript = (IonScript *) Assembler::getPointer(ionScriptDataOffset); JS_ASSERT(ionScript->containsReturnAddress(returnAddr)); *ionScriptOut = ionScript; return true; }
void TypeInferenceOracle::elementReadGeneric(RawScript script, jsbytecode *pc, bool *cacheable, bool *monitorResult, bool *intIndex) { MIRType obj = getMIRType(script->analysis()->poppedTypes(pc, 1)); MIRType id = getMIRType(script->analysis()->poppedTypes(pc, 0)); *cacheable = (obj == MIRType_Object && (id == MIRType_Value || id == MIRType_Int32 || id == MIRType_String)); *intIndex = id == MIRType_Int32; // Turn off cacheing if the element is int32 and we've seen non-native objects as the target // of this getelem. if (*cacheable && id == MIRType_Int32 && script->analysis()->getCode(pc).nonNativeGetElement) *cacheable = false; if (*cacheable) *monitorResult = (id == MIRType_String || script->analysis()->getCode(pc).getStringElement); else *monitorResult = true; }
JS_SetTopFrameAnnotation(JSContext *cx, void *annotation) { AutoAssertNoGC nogc; StackFrame *fp = cx->fp(); JS_ASSERT_IF(fp->beginsIonActivation(), !fp->annotation()); // Note that if this frame is running in Ion, the actual calling frame // could be inlined or a callee and thus we won't have a correct |fp|. // To account for this, ion::InvalidationBailout will transfer an // annotation from the old cx->fp() to the new top frame. This works // because we will never EnterIon on a frame with an annotation. fp->setAnnotation(annotation); RawScript script = fp->script(); ReleaseAllJITCode(cx->runtime->defaultFreeOp()); // Ensure that we'll never try to compile this again. JS_ASSERT(!script->hasAnyIonScript()); for (EACH_COMPILE_MODE(cmode)) script->ions[cmode] = ION_DISABLED_SCRIPT; }
bool TypeInferenceOracle::elementReadIsString(RawScript script, jsbytecode *pc) { // Check for string[index]. StackTypeSet *value = script->analysis()->poppedTypes(pc, 1); StackTypeSet *id = script->analysis()->poppedTypes(pc, 0); if (value->getKnownTypeTag() != JSVAL_TYPE_STRING) return false; JSValueType idType = id->getKnownTypeTag(); if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE) return false; // This function is used for jsop_getelem_string which should return // undefined if this is out-side the string bounds. Currently we just // fallback to a CallGetElement. StackTypeSet *pushed = script->analysis()->pushedTypes(pc, 0); if (pushed->getKnownTypeTag() != JSVAL_TYPE_STRING) return false; return true; }
LazyArgumentsType TypeInferenceOracle::elementWriteMagicArguments(RawScript script, jsbytecode *pc) { StackTypeSet *obj = script->analysis()->poppedTypes(pc, 2); return isArgumentObject(obj); }
bool TypeInferenceOracle::propertyReadAccessGetter(RawScript script, jsbytecode *pc) { return script->analysis()->getCode(pc).accessGetter; }
bool TypeInferenceOracle::propertyWriteNeedsBarrier(RawScript script, jsbytecode *pc, RawId id) { StackTypeSet *types = script->analysis()->poppedTypes(pc, 1); return types->propertyNeedsBarrier(cx, id); }
bool TypeInferenceOracle::propertyWriteCanSpecialize(RawScript script, jsbytecode *pc) { return !script->analysis()->getCode(pc).monitoredTypes; }
bool TypeInferenceOracle::elementReadHasExtraIndexedProperty(RawScript script, jsbytecode *pc) { StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1); return types::TypeCanHaveExtraIndexedProperties(cx, obj); }
bool TypeInferenceOracle::setElementHasWrittenHoles(RawScript script, jsbytecode *pc) { return script->analysis()->getCode(pc).arrayWriteHole; }
bool TypeInferenceOracle::elementWriteIsPacked(RawScript script, jsbytecode *pc) { StackTypeSet *types = script->analysis()->poppedTypes(pc, 2); return !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED); }