void CodeGenWorkItem::OnAddToJitQueue() { Assert(!this->isInJitQueue); this->isInJitQueue = true; VerifyJitMode(); this->entryPointInfo->SetCodeGenQueued(); if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_QUEUED())) { WCHAR displayNameBuffer[256]; WCHAR* displayName = displayNameBuffer; size_t sizeInChars = this->GetDisplayName(displayName, 256); if(sizeInChars > 256) { displayName = HeapNewArray(WCHAR, sizeInChars); this->GetDisplayName(displayName, 256); } JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_QUEUED( this->GetFunctionNumber(), displayName, this->GetScriptContext(), this->GetInterpretedCount())); if(displayName != displayNameBuffer) { HeapDeleteArray(sizeInChars, displayName); } } }
SourceDynamicProfileManager * SourceDynamicProfileManager::LoadFromDynamicProfileStorage(SourceContextInfo* info, ScriptContext* scriptContext, IActiveScriptDataCache* profileDataCache) { SourceDynamicProfileManager* manager = nullptr; Recycler* recycler = scriptContext->GetRecycler(); #ifdef DYNAMIC_PROFILE_STORAGE if(DynamicProfileStorage::IsEnabled() && info->url != nullptr) { manager = DynamicProfileStorage::Load(info->url, [recycler](char const * buffer, uint length) -> SourceDynamicProfileManager * { BufferReader reader(buffer, length); return SourceDynamicProfileManager::Deserialize(&reader, recycler); }); } #endif if(manager == nullptr) { manager = RecyclerNew(recycler, SourceDynamicProfileManager, recycler); } if(profileDataCache != nullptr) { bool profileLoaded = manager->LoadFromProfileCache(profileDataCache, info->url); if(profileLoaded) { JS_ETW(EventWriteJSCRIPT_PROFILE_LOAD(info->dwHostSourceContext, scriptContext)); } } return manager; }
void CodeGenWorkItem::OnRemoveFromJitQueue(NativeCodeGenerator* generator) { // This is called from within the lock this->isInJitQueue = false; this->entryPointInfo->SetCodeGenPending(); functionBody->GetScriptContext()->GetThreadContext()->UnregisterCodeGenRecyclableData(this->recyclableData); this->recyclableData = nullptr; if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_DEQUEUED())) { WCHAR displayNameBuffer[256]; WCHAR* displayName = displayNameBuffer; size_t sizeInChars = this->GetDisplayName(displayName, 256); if(sizeInChars > 256) { displayName = HeapNewArray(WCHAR, sizeInChars); this->GetDisplayName(displayName, 256); } JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_DEQUEUED( this->GetFunctionNumber(), displayName, this->GetScriptContext(), this->GetInterpretedCount())); if(displayName != displayNameBuffer) { HeapDeleteArray(sizeInChars, displayName); } } if(this->Type() == JsLoopBodyWorkItemType) { // Go ahead and delete it and let it re-queue if more interpreting of the loop happens auto loopBodyWorkItem = static_cast<JsLoopBodyCodeGen*>(this); loopBodyWorkItem->loopHeader->ResetInterpreterCount(); loopBodyWorkItem->GetEntryPoint()->Reset(); HeapDelete(loopBodyWorkItem); } else { Assert(GetJitMode() == ExecutionMode::FullJit); // simple JIT work items are not removed from the queue GetFunctionBody()->OnFullJitDequeued(static_cast<Js::FunctionEntryPointInfo *>(GetEntryPoint())); // Add it back to the list of available functions to be jitted generator->AddWorkItem(this); } }
Var JavascriptExternalFunction::FinishExternalCall(Var result) { ScriptContext * scriptContext = this->type->GetScriptContext(); if ( NULL == result ) { result = scriptContext->GetLibrary()->GetUndefined(); } else { result = CrossSite::MarshalVar(scriptContext, result); } if (IS_JS_ETW(EventEnabledJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_STOP())) { JS_ETW(EventWriteJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_STOP(scriptContext, this, 0)); } return result; }
JavascriptGeneratorFunction* JavascriptGeneratorFunction::OP_NewScGenFunc(FrameDisplay *environment, FunctionProxy** proxyRef) { FunctionProxy* functionProxy = *proxyRef; ScriptContext* scriptContext = functionProxy->GetScriptContext(); bool hasSuperReference = functionProxy->HasSuperReference(); GeneratorVirtualScriptFunction* scriptFunction = scriptContext->GetLibrary()->CreateGeneratorVirtualScriptFunction(functionProxy); scriptFunction->SetEnvironment(environment); scriptFunction->SetHasSuperReference(hasSuperReference); JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_FUNCTION(scriptFunction, EtwTrace::GetFunctionId(functionProxy))); JavascriptGeneratorFunction* genFunc = scriptContext->GetLibrary()->CreateGeneratorFunction(functionInfo.GetOriginalEntryPoint(), scriptFunction); scriptFunction->SetRealGeneratorFunction(genFunc); return genFunc; }
void JavascriptExternalFunction::PrepareExternalCall(Js::Arguments * args) { ScriptContext * scriptContext = this->type->GetScriptContext(); Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException()); scriptContext->VerifyAlive(); Assert(scriptContext->GetThreadContext()->IsScriptActive()); if (args->Info.Count == 0) { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined); } Var &thisVar = args->Values[0]; Js::TypeId typeId = Js::JavascriptOperators::GetTypeId(thisVar); this->callCount++; if (IS_JS_ETW(EventEnabledJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_START())) { JavascriptFunction* caller = nullptr; // Lot the caller function if the call count of the external function pass certain threshold (randomly pick 256) // we don't want to call stackwalk too often. The threshold can be adjusted as needed. if (callCount >= ETW_MIN_COUNT_FOR_CALLER && ((callCount % ETW_MIN_COUNT_FOR_CALLER) == 0)) { Js::JavascriptStackWalker stackWalker(scriptContext); bool foundScriptCaller = false; while(stackWalker.GetCaller(&caller)) { if(caller != nullptr && Js::ScriptFunction::Is(caller)) { foundScriptCaller = true; break; } } if(foundScriptCaller) { Var sourceString = caller->EnsureSourceString(); Assert(JavascriptString::Is(sourceString)); const char16* callerString = Js::JavascriptString::FromVar(sourceString)->GetSz(); char16* outString = (char16*)callerString; int length = 0; if (wcschr(callerString, _u('\n')) != NULL || wcschr(callerString, _u('\n')) != NULL) { length = Js::JavascriptString::FromVar(sourceString)->GetLength(); outString = HeapNewArray(char16, length+1); int j = 0; for (int i = 0; i < length; i++) { if (callerString[i] != _u('\n') && callerString[i] != L'\r') { outString[j++] = callerString[i]; } } outString[j] = L'\0'; } JS_ETW(EventWriteJSCRIPT_HOSTING_CALLER_TO_EXTERNAL(scriptContext, this, typeId, outString, callCount)); if (outString != callerString) { HeapDeleteArray(length+1, outString); } #if DBG_DUMP if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::HostPhase)) { Output::Print(_u("Large number of Call to trampoline: methodAddr= %p, Object typeid= %d, caller method= %s, callcount= %d\n"), this, typeId, callerString, callCount); } #endif } } JS_ETW(EventWriteJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_START(scriptContext, this, typeId)); #if DBG_DUMP if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::HostPhase)) { Output::Print(_u("Call to trampoline: methodAddr= %p, Object typeid= %d\n"), this, typeId); } #endif } Js::RecyclableObject* directHostObject = nullptr; switch(typeId) { case TypeIds_Integer: #if FLOATVAR case TypeIds_Number: #endif // FLOATVAR Assert(!Js::RecyclableObject::Is(thisVar)); break; default: { Assert(Js::RecyclableObject::Is(thisVar)); ScriptContext* scriptContextThisVar = Js::RecyclableObject::FromVar(thisVar)->GetScriptContext(); // We need to verify "this" pointer is active as well. The problem is that DOM prototype functions are // the same across multiple frames, and caller can do function.call(closedthis) Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException()); scriptContextThisVar->VerifyAlive(); // translate direct host for fastDOM. switch(typeId) { case Js::TypeIds_GlobalObject: { Js::GlobalObject* srcGlobalObject = static_cast<Js::GlobalObject*>(thisVar); directHostObject = srcGlobalObject->GetDirectHostObject(); // For jsrt, direct host object can be null. If thats the case don't change it. if (directHostObject != nullptr) { thisVar = directHostObject; } } break; case Js::TypeIds_Undefined: case Js::TypeIds_Null: { // Call to DOM function with this as "undefined" or "null" // This should be converted to Global object Js::GlobalObject* srcGlobalObject = scriptContextThisVar->GetGlobalObject() ; directHostObject = srcGlobalObject->GetDirectHostObject(); // For jsrt, direct host object can be null. If thats the case don't change it. if (directHostObject != nullptr) { thisVar = directHostObject; } } break; } } break; } }
void* AsmJsEncoder::Encode( FunctionBody* functionBody ) { Assert( functionBody ); mFunctionBody = functionBody; #if DBG_DUMP AsmJsJitTemplate::Globals::CurrentEncodingFunction = mFunctionBody; #endif AsmJsFunctionInfo* asmInfo = functionBody->GetAsmJsFunctionInfo(); FunctionEntryPointInfo* entryPointInfo = ((FunctionEntryPointInfo*)(functionBody->GetDefaultEntryPointInfo())); // number of var on the stack + ebp + eip mIntOffset = asmInfo->GetIntByteOffset() + GetOffset<Var>(); mDoubleOffset = asmInfo->GetDoubleByteOffset() + GetOffset<Var>(); mFloatOffset = asmInfo->GetFloatByteOffset() + GetOffset<Var>(); mSimdOffset = asmInfo->GetSimdByteOffset() + GetOffset<Var>(); NoRecoverMemoryArenaAllocator localAlloc(_u("BE-AsmJsEncoder"), GetPageAllocator(), Js::Throw::OutOfMemory); mLocalAlloc = &localAlloc; mRelocLabelMap = Anew( mLocalAlloc, RelocLabelMap, mLocalAlloc ); mTemplateData = AsmJsJitTemplate::InitTemplateData(); mEncodeBufferSize = GetEncodeBufferSize(functionBody); mEncodeBuffer = AnewArray((&localAlloc), BYTE, mEncodeBufferSize); mPc = mEncodeBuffer; mReader.Create( functionBody ); ip = mReader.GetIP(); #ifdef ENABLE_DEBUG_CONFIG_OPTIONS if( PHASE_TRACE( Js::AsmjsEncoderPhase, mFunctionBody ) ) { Output::Print( _u("\n\n") ); functionBody->DumpFullFunctionName(); Output::Print( _u("\n StackSize = %d , Offsets: Var = %d, Int = %d, Double = %d\n"), mFunctionBody->GetAsmJsFunctionInfo()->GetTotalSizeinBytes(), GetOffset<Var>(), GetOffset<int>(), GetOffset<double>() ); } #endif AsmJsJitTemplate::FunctionEntry::ApplyTemplate( this, mPc ); while( ReadOp() ){} AsmJsJitTemplate::FunctionExit::ApplyTemplate( this, mPc ); AsmJsJitTemplate::FreeTemplateData( mTemplateData ); #if DBG_DUMP AsmJsJitTemplate::Globals::CurrentEncodingFunction = nullptr; #endif ApplyRelocs(); ptrdiff_t codeSize = mPc - mEncodeBuffer; if( codeSize > 0 ) { Assert( ::Math::FitsInDWord( codeSize ) ); BYTE *buffer; EmitBufferAllocation *allocation = GetCodeGenAllocator()->emitBufferManager.AllocateBuffer( codeSize, &buffer, 0, 0 ); functionBody->GetAsmJsFunctionInfo()->mTJBeginAddress = buffer; if (buffer == nullptr) { Js::Throw::OutOfMemory(); } if (!GetCodeGenAllocator()->emitBufferManager.CommitBuffer(allocation, buffer, codeSize, mEncodeBuffer)) { Js::Throw::OutOfMemory(); } functionBody->GetScriptContext()->GetThreadContext()->SetValidCallTargetForCFG(buffer); // TODO: improve this once EntryPoint cleanup work is complete! #if 0 const char16 *const functionName = functionBody->GetDisplayName(); const char16 *const suffix = _u("TJ"); char16 functionNameArray[256]; const size_t functionNameCharLength = functionBody->GetDisplayNameLength(); wcscpy_s(functionNameArray, 256, functionName); wcscpy_s(&functionNameArray[functionNameCharLength], 256 - functionNameCharLength, suffix); #endif JS_ETW(EventWriteMethodLoad(functionBody->GetScriptContext(), (void *)buffer, codeSize, EtwTrace::GetFunctionId(functionBody), 0 /* methodFlags - for future use*/, MethodType_Jit, EtwTrace::GetSourceId(functionBody), functionBody->GetLineNumber(), functionBody->GetColumnNumber(), functionBody->GetDisplayName())); entryPointInfo->SetTJCodeGenDone(); // set the codegen to done state for TJ entryPointInfo->SetCodeSize(codeSize); return buffer; } return nullptr; }
void BackgroundJobProcessor::Run(ParallelThreadData* threadData) { JS_ETW(EventWriteJSCRIPT_NATIVECODEGEN_START(this, 0)); ArenaAllocator threadArena(_u("ThreadArena"), threadData->GetPageAllocator(), Js::Throw::OutOfMemory); threadData->threadArena = &threadArena; { // Make sure we take decommit action before the threadArena is torn down, in case the // thread context goes away and the loop exits. struct AutoDecommit { AutoDecommit(JobProcessor *proc, ParallelThreadData *data) : processor(proc), threadData(data) {} ~AutoDecommit() { processor->ForEachManager([this](JobManager *manager){ manager->OnDecommit(this->threadData); }); } ParallelThreadData *threadData; JobProcessor *processor; } autoDecommit(this, threadData); criticalSection.Enter(); while (!IsClosed() || (jobs.Head() && jobs.Head()->IsCritical())) { Job *job = jobs.UnlinkFromBeginning(); if(!job) { // No jobs in queue, wait for one Assert(!IsClosed()); Assert(!threadData->isWaitingForJobs); threadData->isWaitingForJobs = true; criticalSection.Leave(); JS_ETW(EventWriteJSCRIPT_NATIVECODEGEN_STOP(this, 0)); if (threadService->HasCallback()) { // We have a thread service, so simply return the thread back now. // When new jobs are submitted, we will be called to process again. return; } WaitForJobReadyOrShutdown(threadData); JS_ETW(EventWriteJSCRIPT_NATIVECODEGEN_START(this, 0)); criticalSection.Enter(); threadData->isWaitingForJobs = false; continue; } Assert(numJobs != 0); --numJobs; threadData->currentJob = job; criticalSection.Leave(); const bool succeeded = Process(job, threadData); criticalSection.Enter(); threadData->currentJob = 0; JobManager *const manager = job->Manager(); JobProcessed(manager, job, succeeded); // the job may be deleted during this and should not be used afterwards Assert(manager->numJobsAddedToProcessor != 0); --manager->numJobsAddedToProcessor; if(manager->isWaitable) { WaitableJobManager *const waitableManager = static_cast<WaitableJobManager *>(manager); Assert(!(waitableManager->jobBeingWaitedUpon && waitableManager->isWaitingForQueuedJobs)); if(waitableManager->jobBeingWaitedUpon == job) { waitableManager->jobBeingWaitedUpon = 0; waitableManager->jobBeingWaitedUponProcessed.Set(); } else if(waitableManager->isWaitingForQueuedJobs && manager->numJobsAddedToProcessor == 0) { waitableManager->isWaitingForQueuedJobs = false; waitableManager->queuedJobsProcessed.Set(); } } if(manager->numJobsAddedToProcessor == 0) LastJobProcessed(manager); // the manager may be deleted during this and should not be used afterwards } criticalSection.Leave(); JS_ETW(EventWriteJSCRIPT_NATIVECODEGEN_STOP(this, 0)); } }
Js::Var JSONParser::ParseObject() { PROBE_STACK(scriptContext, Js::Constants::MinStackDefault); Js::Var retVal; switch (m_token.tk) { case tkFltCon: retVal = Js::JavascriptNumber::ToVarIntCheck(m_token.GetDouble(), scriptContext); Scan(); return retVal; case tkStrCon: { // will auto-null-terminate the string (as length=len+1) uint len = m_scanner.GetCurrentStringLen(); retVal = Js::JavascriptString::NewCopyBuffer(m_scanner.GetCurrentString(), len, scriptContext); Scan(); return retVal; } case tkTRUE: retVal = scriptContext->GetLibrary()->GetTrue(); Scan(); return retVal; case tkFALSE: retVal = scriptContext->GetLibrary()->GetFalse(); Scan(); return retVal; case tkNULL: retVal = scriptContext->GetLibrary()->GetNull(); Scan(); return retVal; case tkSub: // unary minus if (Scan() == tkFltCon) { retVal = Js::JavascriptNumber::ToVarIntCheck(-m_token.GetDouble(), scriptContext); Scan(); return retVal; } else { m_scanner.ThrowSyntaxError(JSERR_JsonBadNumber); } case tkLBrack: { Js::JavascriptArray* arrayObj = scriptContext->GetLibrary()->CreateArray(0); //skip '[' Scan(); //iterate over the array members, get JSON objects and add them in the pArrayMemberList uint k = 0; while (true) { if(tkRBrack == m_token.tk) { break; } Js::Var value = ParseObject(); arrayObj->SetItem(k++, value, Js::PropertyOperation_None); // if next token is not a comma consider the end of the array member list. if (tkComma != m_token.tk) break; Scan(); if(tkRBrack == m_token.tk) { m_scanner.ThrowSyntaxError(JSERR_JsonIllegalChar); } } //check and consume the ending ']' CheckCurrentToken(tkRBrack, JSERR_JsonNoRbrack); return arrayObj; } case tkLCurly: { // Parse an object, "{"name1" : ObjMember1, "name2" : ObjMember2, ...} " if(IsCaching()) { if(!typeCacheList) { typeCacheList = Anew(this->arenaAllocator, JsonTypeCacheList, this->arenaAllocator, 8); } } // first, create the object Js::DynamicObject* object = scriptContext->GetLibrary()->CreateObject(); JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(object)); #if ENABLE_DEBUG_CONFIG_OPTIONS if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag)) { object = DynamicObject::FromVar(JavascriptProxy::AutoProxyWrapper(object)); } #endif //next token after '{' Scan(); //if empty object "{}" return; if(tkRCurly == m_token.tk) { Scan(); return object; } JsonTypeCache* previousCache = nullptr; JsonTypeCache* currentCache = nullptr; //parse the list of members while(true) { // parse a list member: "name" : ObjMember // and add it to the object. //pick "name" if(tkStrCon != m_token.tk) { m_scanner.ThrowSyntaxError(JSERR_JsonIllegalChar); } // currentStrLength = length w/o null-termination WCHAR* currentStr = m_scanner.GetCurrentString(); uint currentStrLength = m_scanner.GetCurrentStringLen(); DynamicType* typeWithoutProperty = object->GetDynamicType(); if(IsCaching()) { if(!previousCache) { // This is the first property in the list - see if we have an existing cache for it. currentCache = typeCacheList->LookupWithKey(Js::HashedCharacterBuffer<WCHAR>(currentStr, currentStrLength), nullptr); } if(currentCache && currentCache->typeWithoutProperty == typeWithoutProperty && currentCache->propertyRecord->Equals(JsUtil::CharacterBuffer<WCHAR>(currentStr, currentStrLength))) { //check and consume ":" if(Scan() != tkColon ) { m_scanner.ThrowSyntaxError(JSERR_JsonNoColon); } Scan(); // Cache all values from currentCache as there is a chance that ParseObject might change the cache DynamicType* typeWithProperty = currentCache->typeWithProperty; PropertyId propertyId = currentCache->propertyRecord->GetPropertyId(); PropertyIndex propertyIndex = currentCache->propertyIndex; previousCache = currentCache; currentCache = currentCache->next; // fast path for type transition and property set object->EnsureSlots(typeWithoutProperty->GetTypeHandler()->GetSlotCapacity(), typeWithProperty->GetTypeHandler()->GetSlotCapacity(), scriptContext, typeWithProperty->GetTypeHandler()); object->ReplaceType(typeWithProperty); Js::Var value = ParseObject(); object->SetSlot(SetSlotArguments(propertyId, propertyIndex, value)); // if the next token is not a comma consider the list of members done. if (tkComma != m_token.tk) break; Scan(); continue; } } // slow path Js::PropertyRecord const * propertyRecord; scriptContext->GetOrAddPropertyRecord(currentStr, currentStrLength, &propertyRecord); //check and consume ":" if(Scan() != tkColon ) { m_scanner.ThrowSyntaxError(JSERR_JsonNoColon); } Scan(); Js::Var value = ParseObject(); PropertyValueInfo info; object->SetProperty(propertyRecord->GetPropertyId(), value, PropertyOperation_None, &info); DynamicType* typeWithProperty = object->GetDynamicType(); if(IsCaching() && !propertyRecord->IsNumeric() && !info.IsNoCache() && typeWithProperty->GetIsShared() && typeWithProperty->GetTypeHandler()->IsPathTypeHandler()) { PropertyIndex propertyIndex = info.GetPropertyIndex(); if(!previousCache) { // This is the first property in the set add it to the dictionary. currentCache = JsonTypeCache::New(this->arenaAllocator, propertyRecord, typeWithoutProperty, typeWithProperty, propertyIndex); typeCacheList->AddNew(propertyRecord, currentCache); } else if(!currentCache) { currentCache = JsonTypeCache::New(this->arenaAllocator, propertyRecord, typeWithoutProperty, typeWithProperty, propertyIndex); previousCache->next = currentCache; } else { // cache miss!! currentCache->Update(propertyRecord, typeWithoutProperty, typeWithProperty, propertyIndex); } previousCache = currentCache; currentCache = currentCache->next; } // if the next token is not a comma consider the list of members done. if (tkComma != m_token.tk) break; Scan(); } // check and consume the ending '}" CheckCurrentToken(tkRCurly, JSERR_JsonNoRcurly); return object; } default: m_scanner.ThrowSyntaxError(JSERR_JsonSyntax); } }