static void testStringSet() { enum { numStrings = 1000 }; HashSet<std::string> set; std::vector<std::string> strings; srand(0); for(Uptr i = 0;i < numStrings;++i) { while(true) { std::string randomString = generateRandomString(); bool alreadySawString = false; for(const std::string& string : strings) { if(string == randomString) { alreadySawString = true; break; } } if(!alreadySawString) { strings.push_back(std::move(randomString)); break; } }; } for(Uptr i = 0;i < strings.size();++i) { errorUnless(set.add(strings[i])); errorUnless(!set.add(strings[i])); for(Uptr j = 0;j < strings.size();++j) { const bool expectedContains = j <= i; errorUnless(set.contains(strings[j]) == expectedContains); } } for(Uptr i = 0;i < strings.size();++i) { errorUnless(set.remove(strings[i])); errorUnless(!set.remove(strings[i])); for(Uptr j = 0;j < strings.size();++j) { const bool expectedContains = j > i; errorUnless(set.contains(strings[j]) == expectedContains); } } }
void RadioButtonGroup::remove(HTMLInputElement* button) { ASSERT(button->isRadioButton()); HashSet<HTMLInputElement*>::iterator it = m_members.find(button); if (it == m_members.end()) return; bool wasValid = isValid(); m_members.remove(it); if (button->isRequired()) { ASSERT(m_requiredCount); --m_requiredCount; } if (m_checkedButton) { button->setNeedsStyleRecalc(); if (m_checkedButton == button) { m_checkedButton = nullptr; setNeedsStyleRecalcForAllButtons(); } } if (m_members.isEmpty()) { ASSERT(!m_requiredCount); ASSERT(!m_checkedButton); } else if (wasValid != isValid()) updateValidityForAllButtons(); if (!wasValid) { // A radio button not in a group is always valid. We need to make it // valid only if the group was invalid. button->updateValidity(); } }
void WebProcessTest::assertObjectIsDeletedWhenTestFinishes(GObject* object) { s_watchedObjects.add(object); g_object_weak_ref(object, [](gpointer, GObject* finalizedObject) { s_watchedObjects.remove(finalizedObject); }, nullptr); }
TEST(FileSystemIterator, multipleFiles) { TestDirectory rootDir(File("foobarfolder")); rootDir.addFile("foobar1.txt"); rootDir.addFile("foobar2.txt"); rootDir.addFile("foobar3.txt"); rootDir.addFile("foobar4.txt"); rootDir.addFile("foobar5.txt"); HashSet<String> filenames; filenames.put("foobar1.txt"); filenames.put("foobar2.txt"); filenames.put("foobar3.txt"); filenames.put("foobar4.txt"); filenames.put("foobar5.txt"); FileSystemIterator iter(rootDir.getFile()); while (iter.isValid()) { EXPECT_TRUE(filenames.hasElement(iter->getFileName())); filenames.remove(iter->getFileName()); iter.next(); } EXPECT_EQ(0u, filenames.count()); }
static void testSetIterator() { // Add 1..9 to a HashSet. HashSet<Uptr> a; for(Uptr i = 1;i < 10;++i) { a.add(i); } // 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 = 45 { Uptr sum = 0; for(Uptr i : a) { sum += i; } errorUnless(sum == 45); } // Remove 5. a.remove(5); // 1 + 2 + 3 + 4 + 6 + 7 + 8 + 9 = 40 { Uptr sum = 0; for(Uptr i : a) { sum += i; } errorUnless(sum == 40); } }
static void testU32Set() { HashSet<U32> set; enum { maxI = 1024 * 1024 }; for(Uptr i = 0;i < maxI;++i) { errorUnless(!set.contains(U32(i))); } errorUnless(set.size() == 0); for(Uptr i = 0;i < maxI;++i) { errorUnless(!set.contains(U32(i))); errorUnless(!set.get(U32(i))); errorUnless(set.add(U32(i))); errorUnless(set.contains(U32(i))); errorUnless(set.get(U32(i))); errorUnless(set.size() == i + 1); } for(Uptr i = 0;i < maxI;++i) { errorUnless(set.contains(U32(i))); errorUnless(set.remove(U32(i))); errorUnless(!set.contains(U32(i))); errorUnless(set.size() == maxI - i - 1); } for(Uptr i = 0;i < maxI;++i) { errorUnless(!set.contains(U32(i))); } }
void js::gc::TraceTenuredFinalize(Cell *thing) { if (!gcTraceFile) return; if (thing->tenuredGetAllocKind() == FINALIZE_OBJECT_GROUP) tracedGroups.remove(static_cast<const ObjectGroup *>(thing)); TraceEvent(TraceEventTenuredFinalize, uint64_t(thing)); }
static void webkit_web_view_container_remove(GtkContainer* container, GtkWidget* widget) { //WebKitWebView* webView = WEBKIT_WEB_VIEW(container); ////WebKitWebViewPrivate* priv = webView->priv; if (children.contains(widget)) { gtk_widget_unparent(widget); children.remove(widget); } }
void SharedWorkerProxy::documentDetached(Document* document) { if (isClosing()) return; // Remove the document from our set (if it's there) and if that was the last document in the set, mark the proxy as closed. MutexLocker lock(m_workerDocumentsLock); m_workerDocuments.remove(document); if (!m_workerDocuments.size()) close(); }
void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const { // FIXME: This should be a case insensitive set. HashSet<String> uniqueFilenames; HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATAW findFileData; String oldWMPPluginPath; String newWMPPluginPath; Vector<String>::const_iterator end = m_pluginDirectories.end(); for (Vector<String>::const_iterator it = m_pluginDirectories.begin(); it != end; ++it) { String pattern = *it + "\\*"; hFind = FindFirstFileW(pattern.charactersWithNullTermination(), &findFileData); if (hFind == INVALID_HANDLE_VALUE) continue; do { if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; String filename = String(findFileData.cFileName, wcslen(findFileData.cFileName)); if ((!filename.startsWith("np", false) || !filename.endsWith("dll", false)) && (!equalIgnoringCase(filename, "Plugin.dll") || !it->endsWith("Shockwave 10", false))) continue; String fullPath = *it + "\\" + filename; if (!uniqueFilenames.add(fullPath).second) continue; paths.add(fullPath); if (equalIgnoringCase(filename, "npdsplay.dll")) oldWMPPluginPath = fullPath; else if (equalIgnoringCase(filename, "np-mswmp.dll")) newWMPPluginPath = fullPath; } while (FindNextFileW(hFind, &findFileData) != 0); FindClose(hFind); } addPluginPathsFromRegistry(HKEY_LOCAL_MACHINE, paths); addPluginPathsFromRegistry(HKEY_CURRENT_USER, paths); // If both the old and new WMP plugin are present in the plugins set, // we remove the old one so we don't end up choosing the old one. if (!oldWMPPluginPath.isEmpty() && !newWMPPluginPath.isEmpty()) paths.remove(oldWMPPluginPath); }
void JavaScriptDebugServer::removeBreakpoint(int sourceID, unsigned lineNumber) { HashSet<unsigned>* lines = m_breakpoints.get(sourceID); if (!lines) return; lines->remove(lineNumber); if (!lines->isEmpty()) return; m_breakpoints.remove(sourceID); delete lines; }
TEST(FileSystemIterator, defaultAcceptTest) { // setup TestDirectory rootDir(File("foobarfolder")); rootDir.addFile("foobar1.txt"); rootDir.addDirectory("foobar")->addFile("foobar2.txt"); HashSet<String> filenames; filenames.put("foobar1.txt"); filenames.put("foobar"); filenames.put("foobar2.txt"); FileSystemIterator iter(rootDir.getFile()); EXPECT_TRUE(iter.isValid()); EXPECT_TRUE(filenames.hasElement(iter->getFileName())); filenames.remove(iter->getFileName()); EXPECT_TRUE(iter.next()); EXPECT_TRUE(filenames.hasElement(iter->getFileName())); filenames.remove(iter->getFileName()); EXPECT_TRUE(iter.next()); EXPECT_TRUE(filenames.hasElement(iter->getFileName())); filenames.remove(iter->getFileName()); EXPECT_EQ(0u, filenames.count()); EXPECT_FALSE(iter.next()); EXPECT_FALSE(iter.isValid()); // iterate again, but don't finish FileSystemIterator iter2(rootDir.getFile()); }
int _tmain(int argc, _TCHAR* argv[]) { HashSet myHashSet; for (int i=0; i<30; i++) { myHashSet.insert(i); } cout<<"The HashSet:"<<endl; myHashSet.printHashSet(cout); cout<<"The number 0 exists?"<<"\t"<<myHashSet.exists(0)<<endl; cout<<"The number 30 exists?"<<"\t"<<myHashSet.exists(30)<<endl; cout<<"insert 40:"<<myHashSet.insert(40)<<endl; myHashSet.printHashSet(cout); cout<<"insert 10:"<<myHashSet.insert(10)<<endl; myHashSet.printHashSet(cout); cout<<"delete 40:"<<myHashSet.remove(40)<<endl; myHashSet.printHashSet(cout); cout<<"delete 50:"<<myHashSet.remove(50)<<endl; myHashSet.printHashSet(cout); return 0; }
void DisplayRefreshMonitor::displayDidRefresh() { double monotonicAnimationStartTime; { MutexLocker lock(m_mutex); if (!m_scheduled) ++m_unscheduledFireCount; else m_unscheduledFireCount = 0; m_scheduled = false; monotonicAnimationStartTime = m_monotonicAnimationStartTime; } // The call back can cause all our clients to be unregistered, so we need to protect // against deletion until the end of the method. Ref<DisplayRefreshMonitor> protect(*this); // Copy the hash table and remove clients from it one by one so we don't notify // any client twice, but can respond to removal of clients during the delivery process. HashSet<DisplayRefreshMonitorClient*> clientsToBeNotified = m_clients; m_clientsToBeNotified = &clientsToBeNotified; while (!clientsToBeNotified.isEmpty()) { // Take a random client out of the set. Ordering doesn't matter. // FIXME: Would read more cleanly if HashSet had a take function. auto it = clientsToBeNotified.begin(); DisplayRefreshMonitorClient* client = *it; clientsToBeNotified.remove(it); client->fireDisplayRefreshIfNeeded(monotonicAnimationStartTime); // This checks if this function was reentered. In that case, stop iterating // since it's not safe to use the set any more. if (m_clientsToBeNotified != &clientsToBeNotified) break; } if (m_clientsToBeNotified == &clientsToBeNotified) m_clientsToBeNotified = nullptr; { MutexLocker lock(m_mutex); m_previousFrameDone = true; } DisplayRefreshMonitorManager::sharedManager()->displayDidRefresh(this); }
void SVGDocumentExtensions::removeAnimationElementFromTarget(SVGSMILElement* animationElement, SVGElement* targetElement) { ASSERT(targetElement); ASSERT(animationElement); HashMap<SVGElement*, HashSet<SVGSMILElement*>* >::iterator it = m_animatedElements.find(targetElement); ASSERT(it != m_animatedElements.end()); HashSet<SVGSMILElement*>* animationElementsForTarget = it->value; ASSERT(!animationElementsForTarget->isEmpty()); animationElementsForTarget->remove(animationElement); if (animationElementsForTarget->isEmpty()) { m_animatedElements.remove(it); delete animationElementsForTarget; } }
void SVGDocumentExtensions::removeAllTargetReferencesForElement(SVGElement* referencingElement) { Vector<SVGElement*> toBeRemoved; HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator end = m_elementDependencies.end(); for (HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator it = m_elementDependencies.begin(); it != end; ++it) { SVGElement* referencedElement = it->key; HashSet<SVGElement*>* referencingElements = it->value.get(); HashSet<SVGElement*>::iterator setIt = referencingElements->find(referencingElement); if (setIt == referencingElements->end()) continue; referencingElements->remove(setIt); if (referencingElements->isEmpty()) toBeRemoved.append(referencedElement); } m_elementDependencies.removeAll(toBeRemoved); }
TEST(WTF_HashSet, UniquePtrKey_RemoveUsingRawPointer) { ConstructorDestructorCounter::TestingScope scope; HashSet<std::unique_ptr<ConstructorDestructorCounter>> set; auto uniquePtr = std::make_unique<ConstructorDestructorCounter>(); ConstructorDestructorCounter* ptr = uniquePtr.get(); set.add(WTF::move(uniquePtr)); EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); bool result = set.remove(ptr); EXPECT_EQ(true, result); EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); }
void AbstractDatabase::closeDatabase() { if (!m_opened) return; m_sqliteDatabase.close(); m_opened = false; { MutexLocker locker(guidMutex()); HashSet<AbstractDatabase*>* hashSet = guidToDatabaseMap().get(m_guid); ASSERT(hashSet); ASSERT(hashSet->contains(this)); hashSet->remove(this); if (hashSet->isEmpty()) { guidToDatabaseMap().remove(m_guid); delete hashSet; guidToVersionMap().remove(m_guid); } } }
static void testSetCopy() { // Add 1000..1999 to a HashSet. HashSet<Uptr> a; for(Uptr i = 0;i < 1000;++i) { a.add(i + 1000); } // Copy the set to a new HashSet. HashSet<Uptr> b {a}; // Test that both the new and old HashSet contain the expected numbers. for(Uptr i = 0;i < 1000;++i) { errorUnless(!a.contains(i)); errorUnless(a.contains(i + 1000)); errorUnless(!a.contains(i + 2000)); errorUnless(!b.contains(i)); errorUnless(b.contains(i + 1000)); errorUnless(!b.contains(i + 2000)); } // Test copying a set from itself. b = b; // Test that the set wasn't changed by the copy-to-self. for(Uptr i = 0;i < 1000;++i) { errorUnless(!b.contains(i)); errorUnless(b.contains(i + 1000)); errorUnless(!b.contains(i + 2000)); } // Test removing an element from the set. b.remove(1000); errorUnless(a.contains(1000)); errorUnless(!b.contains(1000)); }
TEST(WTF_HashSet, MoveOnly) { HashSet<MoveOnly> hashSet; for (size_t i = 0; i < 100; ++i) { MoveOnly moveOnly(i + 1); hashSet.add(WTF::move(moveOnly)); } for (size_t i = 0; i < 100; ++i) EXPECT_TRUE(hashSet.contains(MoveOnly(i + 1))); for (size_t i = 0; i < 100; ++i) EXPECT_TRUE(hashSet.remove(MoveOnly(i + 1))); EXPECT_TRUE(hashSet.isEmpty()); for (size_t i = 0; i < 100; ++i) hashSet.add(MoveOnly(i + 1)); for (size_t i = 0; i < 100; ++i) EXPECT_TRUE(hashSet.take(MoveOnly(i + 1)) == MoveOnly(i + 1)); EXPECT_TRUE(hashSet.isEmpty()); for (size_t i = 0; i < 100; ++i) hashSet.add(MoveOnly(i + 1)); HashSet<MoveOnly> secondSet; for (size_t i = 0; i < 100; ++i) secondSet.add(hashSet.takeAny()); EXPECT_TRUE(hashSet.isEmpty()); for (size_t i = 0; i < 100; ++i) EXPECT_TRUE(secondSet.contains(MoveOnly(i + 1))); }
void DatabaseBackendBase::closeDatabase() { if (!m_opened) return; m_sqliteDatabase.close(); m_opened = false; // See comment at the top this file regarding calling removeOpenDatabase(). DatabaseTracker::tracker().removeOpenDatabase(this); { MutexLocker locker(guidMutex()); HashSet<DatabaseBackendBase*>* hashSet = guidToDatabaseMap().get(m_guid); ASSERT(hashSet); ASSERT(hashSet->contains(this)); hashSet->remove(this); if (hashSet->isEmpty()) { guidToDatabaseMap().remove(m_guid); delete hashSet; guidToVersionMap().remove(m_guid); } } }
static void compileStub( unsigned exitID, JITCode* jitCode, OSRExit& exit, VM* vm, CodeBlock* codeBlock) { StackMaps::Record* record = nullptr; for (unsigned i = jitCode->stackmaps.records.size(); i--;) { record = &jitCode->stackmaps.records[i]; if (record->patchpointID == exit.m_stackmapID) break; } RELEASE_ASSERT(record->patchpointID == exit.m_stackmapID); // This code requires framePointerRegister is the same as callFrameRegister static_assert(MacroAssembler::framePointerRegister == GPRInfo::callFrameRegister, "MacroAssembler::framePointerRegister and GPRInfo::callFrameRegister must be the same"); CCallHelpers jit(vm, codeBlock); // We need scratch space to save all registers, to build up the JS stack, to deal with unwind // fixup, pointers to all of the objects we materialize, and the elements inside those objects // that we materialize. // Figure out how much space we need for those object allocations. unsigned numMaterializations = 0; size_t maxMaterializationNumArguments = 0; for (ExitTimeObjectMaterialization* materialization : exit.m_materializations) { numMaterializations++; maxMaterializationNumArguments = std::max( maxMaterializationNumArguments, materialization->properties().size()); } ScratchBuffer* scratchBuffer = vm->scratchBufferForSize( sizeof(EncodedJSValue) * ( exit.m_values.size() + numMaterializations + maxMaterializationNumArguments) + requiredScratchMemorySizeInBytes() + codeBlock->calleeSaveRegisters()->size() * sizeof(uint64_t)); EncodedJSValue* scratch = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0; EncodedJSValue* materializationPointers = scratch + exit.m_values.size(); EncodedJSValue* materializationArguments = materializationPointers + numMaterializations; char* registerScratch = bitwise_cast<char*>(materializationArguments + maxMaterializationNumArguments); uint64_t* unwindScratch = bitwise_cast<uint64_t*>(registerScratch + requiredScratchMemorySizeInBytes()); HashMap<ExitTimeObjectMaterialization*, EncodedJSValue*> materializationToPointer; unsigned materializationCount = 0; for (ExitTimeObjectMaterialization* materialization : exit.m_materializations) { materializationToPointer.add( materialization, materializationPointers + materializationCount++); } // Note that we come in here, the stack used to be as LLVM left it except that someone called pushToSave(). // We don't care about the value they saved. But, we do appreciate the fact that they did it, because we use // that slot for saveAllRegisters(). saveAllRegisters(jit, registerScratch); // Bring the stack back into a sane form and assert that it's sane. jit.popToRestore(GPRInfo::regT0); jit.checkStackPointerAlignment(); if (vm->m_perBytecodeProfiler && codeBlock->jitCode()->dfgCommon()->compilation) { Profiler::Database& database = *vm->m_perBytecodeProfiler; Profiler::Compilation* compilation = codeBlock->jitCode()->dfgCommon()->compilation.get(); Profiler::OSRExit* profilerExit = compilation->addOSRExit( exitID, Profiler::OriginStack(database, codeBlock, exit.m_codeOrigin), exit.m_kind, exit.m_kind == UncountableInvalidation); jit.add64(CCallHelpers::TrustedImm32(1), CCallHelpers::AbsoluteAddress(profilerExit->counterAddress())); } // The remaining code assumes that SP/FP are in the same state that they were in the FTL's // call frame. // Get the call frame and tag thingies. // Restore the exiting function's callFrame value into a regT4 jit.move(MacroAssembler::TrustedImm64(TagTypeNumber), GPRInfo::tagTypeNumberRegister); jit.move(MacroAssembler::TrustedImm64(TagMask), GPRInfo::tagMaskRegister); // Do some value profiling. if (exit.m_profileDataFormat != DataFormatNone) { record->locations[0].restoreInto(jit, jitCode->stackmaps, registerScratch, GPRInfo::regT0); reboxAccordingToFormat( exit.m_profileDataFormat, jit, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT2); if (exit.m_kind == BadCache || exit.m_kind == BadIndexingType) { CodeOrigin codeOrigin = exit.m_codeOriginForExitProfile; if (ArrayProfile* arrayProfile = jit.baselineCodeBlockFor(codeOrigin)->getArrayProfile(codeOrigin.bytecodeIndex)) { jit.load32(MacroAssembler::Address(GPRInfo::regT0, JSCell::structureIDOffset()), GPRInfo::regT1); jit.store32(GPRInfo::regT1, arrayProfile->addressOfLastSeenStructureID()); jit.load8(MacroAssembler::Address(GPRInfo::regT0, JSCell::indexingTypeOffset()), GPRInfo::regT1); jit.move(MacroAssembler::TrustedImm32(1), GPRInfo::regT2); jit.lshift32(GPRInfo::regT1, GPRInfo::regT2); jit.or32(GPRInfo::regT2, MacroAssembler::AbsoluteAddress(arrayProfile->addressOfArrayModes())); } } if (!!exit.m_valueProfile) jit.store64(GPRInfo::regT0, exit.m_valueProfile.getSpecFailBucket(0)); } // Materialize all objects. Don't materialize an object until all // of the objects it needs have been materialized. We break cycles // by populating objects late - we only consider an object as // needing another object if the later is needed for the // allocation of the former. HashSet<ExitTimeObjectMaterialization*> toMaterialize; for (ExitTimeObjectMaterialization* materialization : exit.m_materializations) toMaterialize.add(materialization); while (!toMaterialize.isEmpty()) { unsigned previousToMaterializeSize = toMaterialize.size(); Vector<ExitTimeObjectMaterialization*> worklist; worklist.appendRange(toMaterialize.begin(), toMaterialize.end()); for (ExitTimeObjectMaterialization* materialization : worklist) { // Check if we can do anything about this right now. bool allGood = true; for (ExitPropertyValue value : materialization->properties()) { if (!value.value().isObjectMaterialization()) continue; if (!value.location().neededForMaterialization()) continue; if (toMaterialize.contains(value.value().objectMaterialization())) { // Gotta skip this one, since it needs a // materialization that hasn't been materialized. allGood = false; break; } } if (!allGood) continue; // All systems go for materializing the object. First we // recover the values of all of its fields and then we // call a function to actually allocate the beast. // We only recover the fields that are needed for the allocation. for (unsigned propertyIndex = materialization->properties().size(); propertyIndex--;) { const ExitPropertyValue& property = materialization->properties()[propertyIndex]; const ExitValue& value = property.value(); if (!property.location().neededForMaterialization()) continue; compileRecovery( jit, value, record, jitCode->stackmaps, registerScratch, materializationToPointer); jit.storePtr(GPRInfo::regT0, materializationArguments + propertyIndex); } // This call assumes that we don't pass arguments on the stack. jit.setupArgumentsWithExecState( CCallHelpers::TrustedImmPtr(materialization), CCallHelpers::TrustedImmPtr(materializationArguments)); jit.move(CCallHelpers::TrustedImmPtr(bitwise_cast<void*>(operationMaterializeObjectInOSR)), GPRInfo::nonArgGPR0); jit.call(GPRInfo::nonArgGPR0); jit.storePtr(GPRInfo::returnValueGPR, materializationToPointer.get(materialization)); // Let everyone know that we're done. toMaterialize.remove(materialization); } // We expect progress! This ensures that we crash rather than looping infinitely if there // is something broken about this fixpoint. Or, this could happen if we ever violate the // "materializations form a DAG" rule. RELEASE_ASSERT(toMaterialize.size() < previousToMaterializeSize); } // Now that all the objects have been allocated, we populate them // with the correct values. This time we can recover all the // fields, including those that are only needed for the allocation. for (ExitTimeObjectMaterialization* materialization : exit.m_materializations) { for (unsigned propertyIndex = materialization->properties().size(); propertyIndex--;) { const ExitValue& value = materialization->properties()[propertyIndex].value(); compileRecovery( jit, value, record, jitCode->stackmaps, registerScratch, materializationToPointer); jit.storePtr(GPRInfo::regT0, materializationArguments + propertyIndex); } // This call assumes that we don't pass arguments on the stack jit.setupArgumentsWithExecState( CCallHelpers::TrustedImmPtr(materialization), CCallHelpers::TrustedImmPtr(materializationToPointer.get(materialization)), CCallHelpers::TrustedImmPtr(materializationArguments)); jit.move(CCallHelpers::TrustedImmPtr(bitwise_cast<void*>(operationPopulateObjectInOSR)), GPRInfo::nonArgGPR0); jit.call(GPRInfo::nonArgGPR0); } // Save all state from wherever the exit data tells us it was, into the appropriate place in // the scratch buffer. This also does the reboxing. for (unsigned index = exit.m_values.size(); index--;) { compileRecovery( jit, exit.m_values[index], record, jitCode->stackmaps, registerScratch, materializationToPointer); jit.store64(GPRInfo::regT0, scratch + index); } // Henceforth we make it look like the exiting function was called through a register // preservation wrapper. This implies that FP must be nudged down by a certain amount. Then // we restore the various things according to either exit.m_values or by copying from the // old frame, and finally we save the various callee-save registers into where the // restoration thunk would restore them from. // Before we start messing with the frame, we need to set aside any registers that the // FTL code was preserving. for (unsigned i = codeBlock->calleeSaveRegisters()->size(); i--;) { RegisterAtOffset entry = codeBlock->calleeSaveRegisters()->at(i); jit.load64( MacroAssembler::Address(MacroAssembler::framePointerRegister, entry.offset()), GPRInfo::regT0); jit.store64(GPRInfo::regT0, unwindScratch + i); } jit.load32(CCallHelpers::payloadFor(JSStack::ArgumentCount), GPRInfo::regT2); // Let's say that the FTL function had failed its arity check. In that case, the stack will // contain some extra stuff. // // We compute the padded stack space: // // paddedStackSpace = roundUp(codeBlock->numParameters - regT2 + 1) // // The stack will have regT2 + CallFrameHeaderSize stuff. // We want to make the stack look like this, from higher addresses down: // // - argument padding // - actual arguments // - call frame header // This code assumes that we're dealing with FunctionCode. RELEASE_ASSERT(codeBlock->codeType() == FunctionCode); jit.add32( MacroAssembler::TrustedImm32(-codeBlock->numParameters()), GPRInfo::regT2, GPRInfo::regT3); MacroAssembler::Jump arityIntact = jit.branch32( MacroAssembler::GreaterThanOrEqual, GPRInfo::regT3, MacroAssembler::TrustedImm32(0)); jit.neg32(GPRInfo::regT3); jit.add32(MacroAssembler::TrustedImm32(1 + stackAlignmentRegisters() - 1), GPRInfo::regT3); jit.and32(MacroAssembler::TrustedImm32(-stackAlignmentRegisters()), GPRInfo::regT3); jit.add32(GPRInfo::regT3, GPRInfo::regT2); arityIntact.link(&jit); CodeBlock* baselineCodeBlock = jit.baselineCodeBlockFor(exit.m_codeOrigin); // First set up SP so that our data doesn't get clobbered by signals. unsigned conservativeStackDelta = (exit.m_values.numberOfLocals() + baselineCodeBlock->calleeSaveSpaceAsVirtualRegisters()) * sizeof(Register) + maxFrameExtentForSlowPathCall; conservativeStackDelta = WTF::roundUpToMultipleOf( stackAlignmentBytes(), conservativeStackDelta); jit.addPtr( MacroAssembler::TrustedImm32(-conservativeStackDelta), MacroAssembler::framePointerRegister, MacroAssembler::stackPointerRegister); jit.checkStackPointerAlignment(); RegisterSet allFTLCalleeSaves = RegisterSet::ftlCalleeSaveRegisters(); RegisterAtOffsetList* baselineCalleeSaves = baselineCodeBlock->calleeSaveRegisters(); for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) { if (!allFTLCalleeSaves.get(reg)) continue; unsigned unwindIndex = codeBlock->calleeSaveRegisters()->indexOf(reg); RegisterAtOffset* baselineRegisterOffset = baselineCalleeSaves->find(reg); if (reg.isGPR()) { GPRReg regToLoad = baselineRegisterOffset ? GPRInfo::regT0 : reg.gpr(); if (unwindIndex == UINT_MAX) { // The FTL compilation didn't preserve this register. This means that it also // didn't use the register. So its value at the beginning of OSR exit should be // preserved by the thunk. Luckily, we saved all registers into the register // scratch buffer, so we can restore them from there. jit.load64(registerScratch + offsetOfReg(reg), regToLoad); } else { // The FTL compilation preserved the register. Its new value is therefore // irrelevant, but we can get the value that was preserved by using the unwind // data. We've already copied all unwind-able preserved registers into the unwind // scratch buffer, so we can get it from there. jit.load64(unwindScratch + unwindIndex, regToLoad); } if (baselineRegisterOffset) jit.store64(regToLoad, MacroAssembler::Address(MacroAssembler::framePointerRegister, baselineRegisterOffset->offset())); } else { FPRReg fpRegToLoad = baselineRegisterOffset ? FPRInfo::fpRegT0 : reg.fpr(); if (unwindIndex == UINT_MAX) jit.loadDouble(MacroAssembler::TrustedImmPtr(registerScratch + offsetOfReg(reg)), fpRegToLoad); else jit.loadDouble(MacroAssembler::TrustedImmPtr(unwindScratch + unwindIndex), fpRegToLoad); if (baselineRegisterOffset) jit.storeDouble(fpRegToLoad, MacroAssembler::Address(MacroAssembler::framePointerRegister, baselineRegisterOffset->offset())); } } size_t baselineVirtualRegistersForCalleeSaves = baselineCodeBlock->calleeSaveSpaceAsVirtualRegisters(); // Now get state out of the scratch buffer and place it back into the stack. The values are // already reboxed so we just move them. for (unsigned index = exit.m_values.size(); index--;) { VirtualRegister reg = exit.m_values.virtualRegisterForIndex(index); if (reg.isLocal() && reg.toLocal() < static_cast<int>(baselineVirtualRegistersForCalleeSaves)) continue; jit.load64(scratch + index, GPRInfo::regT0); jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(reg)); } handleExitCounts(jit, exit); reifyInlinedCallFrames(jit, exit); adjustAndJumpToTarget(jit, exit, false); LinkBuffer patchBuffer(*vm, jit, codeBlock); exit.m_code = FINALIZE_CODE_IF( shouldDumpDisassembly() || Options::verboseOSR() || Options::verboseFTLOSRExit(), patchBuffer, ("FTL OSR exit #%u (%s, %s) from %s, with operands = %s, and record = %s", exitID, toCString(exit.m_codeOrigin).data(), exitKindToString(exit.m_kind), toCString(*codeBlock).data(), toCString(ignoringContext<DumpContext>(exit.m_values)).data(), toCString(*record).data())); }
int hashset_testmain(int argc, const char *argv[]) { /* 0: Test Data/Result Initialization; Test Environment peekup */ testlog = fopen("hashsettest_log.txt", "a"); testresult = fopen("hashsettest_result.txt", "a"); char *string0 = "tester0"; char *string1 = "tester1"; char *string2 = "tester2"; char *string3 = "tester3"; char *string4 = "tester4"; char *string5 = "tester5"; char *string6 = "tester6"; char *string7 = "tester7"; char *string8 = "tester8"; char *string9 = "tester9"; /* 1: No Data Structure Memory Test */ stage_log(1, "START"); HashSet *set = hashset_create(DATATYPE_STRING, POOLTYPE_LOCAL, 10); if(set != NULL) stage_result(1, "create", true, set); else stage_result(1, "create", false, set); stage_log(1, "END"); /* 2: No Node Memory Test */ stage_log(2, "START"); /* Test Data peekup */ set = hashset_create(DATATYPE_STRING, POOLTYPE_LOCAL, 10); bool stage2_result = set->add(set, string0); if(stage2_result == true) stage_result(2, "get", true, set); else stage_result(2, "get", false, set); hashset_destroy(set); stage_log(2, "END"); /* 3: Between Node Memory: NULL Data */ stage_log(3, "START"); /* Test Data peekup */ set = hashset_create(DATATYPE_STRING, POOLTYPE_LOCAL, 10); /* Test Procedure */ void* stage31_result = set->get(set, (void *)NULL); if(stage31_result != NULL) stage_result(3, "get", true, set); else stage_result(3, "get", false, set); bool stage32_result = set->add(set, (void *)NULL); if(stage32_result == true) stage_result(3, "add", true, set); else stage_result(3, "add", false, set); bool stage33_result = set->remove(set, (void*)NULL); if(stage33_result == true) stage_result(3, "remove", true, set); else stage_result(3, "remove", false, set); bool stage34_result = set->contains(set, (void *)NULL); if(stage34_result == true) stage_result(3, "contains", true, set); else stage_result(3, "contains", false, set); hashset_destroy(set); stage_log(3, "END"); /* 4: Between Node Memory: No Duplicated Data */ stage_log(4, "START"); /* Test Data removeup */ set = hashset_create(DATATYPE_STRING, POOLTYPE_LOCAL, 10); set->add(set, string0); set->add(set, string1); set->add(set, string2); set->add(set, string3); set->add(set, string4); /* Test Procedure */ void* stage41_result = set->get(set, string5); if(stage41_result != NULL) stage_result(4, "get", true, set); else stage_result(4, "get", false, set); bool stage42_result = set->add(set, string5 ); if(stage42_result == true) stage_result(4, "add", true, set); else stage_result(4, "add", false, set); bool stage43_result = set->remove(set, string5); if(stage43_result == true) stage_result(4, "remove", true, set); else stage_result(4, "remove", false, set); bool stage44_result = set->contains(set, string1); if(stage44_result == true) stage_result(4, "contains", true, set); else stage_result(4, "contains", false, set); /* abnormal case */ void* stage45_result = set->get(set, string9); if(stage45_result != NULL) stage_result(4, "abnormal get", true, set); else stage_result(4, "abnormal get", false, set); bool stage46_result = set->remove(set, string8); if(stage46_result == true) stage_result(4, "abnormal remove", true, set); else stage_result(4, "abnormal remove", false, set); bool stage47_result = set->contains(set, string8); if(stage47_result == true) stage_result(4, "abnormal contains", true, set); else stage_result(4, "abnormal contains", false, set); hashset_destroy(set); stage_log(4, "END"); /* 5: Between Node Memory: Duplicated Data */ stage_log(5, "START"); /* Test Data removeup */ set = hashset_create(DATATYPE_STRING, POOLTYPE_LOCAL, 10); set->add(set, string0); set->add(set, string1); set->add(set, string2); set->add(set, string3); set->add(set, string4); /* Test Procedure */ void* stage51_result = set->get(set, string0); if(stage51_result != NULL) stage_result(5, "get", true, set); else stage_result(5, "get", false, set); bool stage52_result = set->add(set, string1); if(stage52_result == true) stage_result(5, "add", true, set); else stage_result(5, "add", false, set); bool stage53_result = set->remove(set, string1); if(stage53_result == true) stage_result(5, "remove", true, set); else stage_result(5, "remove", false, set); bool stage54_result = set->contains(set, string1); if(stage54_result == true) stage_result(5, "contains", true, set); else stage_result(5, "contains", false, set); hashset_destroy(set); stage_log(5, "END"); /* 6: Max Node Memory: NULL Data */ stage_log(6, "START"); /* Test Data removeup */ set = hashset_create(DATATYPE_STRING, POOLTYPE_LOCAL, 10); set->add(set, string0); set->add(set, string1); set->add(set, string2); set->add(set, string3); set->add(set, string4); set->add(set, string5); set->add(set, string6); set->add(set, string7); set->add(set, string8); set->add(set, string9); /* Test Procedure */ void* stage61_result = set->get(set, (void *)NULL); if(stage61_result != NULL) stage_result(6, "get", true, set); else stage_result(6, "get", false, set); bool stage62_result = set->add(set, (void *)NULL); if(stage62_result == true) stage_result(6, "add", true, set); else stage_result(6, "add", false, set); bool stage63_result = set->remove(set, (void *)NULL); if(stage63_result == true) stage_result(6, "remove", true, set); else stage_result(6, "remove", false, set); bool stage64_result = set->contains(set, (void *)NULL); if(stage64_result == true) stage_result(6, "contains", true, set); else stage_result(6, "contains", false, set); hashset_destroy(set); stage_log(6, "END"); /* 7: Max Node Memory: No Duplicated Data */ stage_log(7, "START"); /* Test Data removeup */ set = hashset_create(DATATYPE_STRING, POOLTYPE_LOCAL, 10); set->add(set, string0); set->add(set, string1); set->add(set, string2); set->add(set, string3); set->add(set, string4); set->add(set, string5); set->add(set, string6); set->add(set, string7); set->add(set, string8); set->add(set, string9); /* Test Procedure */ void* stage71_result = set->get(set, string1); if(stage71_result != NULL) stage_result(7, "get", true, set); else stage_result(7, "get", false, set); bool stage72_result = set->add(set, string1); if(stage72_result == true) stage_result(7, "add", true, set); else stage_result(7, "add", false, set); bool stage73_result = set->remove(set, string1); if(stage73_result == true) stage_result(7, "remove", true, set); else stage_result(7, "remove", false, set); bool stage74_result = set->contains(set, string1); if(stage74_result == true) stage_result(7, "contains", true, set); else stage_result(7, "contains", false, set); hashset_destroy(set); stage_log(7, "END"); /* 8: Max Node Memory: Duplicated Data */ stage_log(8, "START"); /* Test Data removeup */ set = hashset_create(DATATYPE_STRING, POOLTYPE_LOCAL, 10); set->add(set, string0); set->add(set, string1); set->add(set, string2); set->add(set, string3); set->add(set, string4); set->add(set, string0); set->add(set, string1); set->add(set, string2); set->add(set, string3); set->add(set, string4); /* Test Procedure */ void* stage81_result = set->get(set, string1); if(stage81_result != NULL) stage_result(8, "get", true, set); else stage_result(8, "get", false, set); bool stage82_result = set->add(set, string1); if(stage82_result == true) stage_result(8, "add", true, set); else stage_result(8, "add", false, set); bool stage83_result = set->remove(set, string1); if(stage83_result == true) stage_result(8, "remove", true, set); else stage_result(8, "remove", false, set); bool stage84_result = set->contains(set, string1); if(stage84_result == true) stage_result(8, "contains", true, set); else stage_result(8, "contains", false, set); hashset_destroy(set); stage_log(8, "END"); /* 9: Test Result Store */ fclose(testlog); fclose(testresult); return 0; }
// ECMA 15.4.4 JSValue* ArrayProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args) { unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); JSValue *result = 0; // work around gcc 4.0 bug in uninitialized variable warning switch (id) { case ToLocaleString: case ToString: if (!thisObj->inherits(&ArrayInstance::info)) return throwError(exec, TypeError); // fall through case Join: { static HashSet<JSObject*> visitedElems; if (visitedElems.contains(thisObj)) return jsString(""); UString separator = ","; UString str = ""; visitedElems.add(thisObj); if (id == Join && !args[0]->isUndefined()) separator = args[0]->toString(exec); for (unsigned int k = 0; k < length; k++) { if (k >= 1) str += separator; if (str.isNull()) { JSObject *error = Error::create(exec, GeneralError, "Out of memory"); exec->setException(error); break; } JSValue* element = thisObj->get(exec, k); if (element->isUndefinedOrNull()) continue; bool fallback = false; if (id == ToLocaleString) { JSObject* o = element->toObject(exec); JSValue* conversionFunction = o->get(exec, exec->propertyNames().toLocaleString); if (conversionFunction->isObject() && static_cast<JSObject*>(conversionFunction)->implementsCall()) str += static_cast<JSObject*>(conversionFunction)->call(exec, o, List())->toString(exec); else // try toString() fallback fallback = true; } if (id == ToString || id == Join || fallback) str += element->toString(exec); if (str.isNull()) { JSObject *error = Error::create(exec, GeneralError, "Out of memory"); exec->setException(error); } if (exec->hadException()) break; } visitedElems.remove(thisObj); result = jsString(str); break; } case Concat: { JSObject *arr = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec,List::empty())); int n = 0; JSValue *curArg = thisObj; JSObject *curObj = static_cast<JSObject *>(thisObj); ListIterator it = args.begin(); for (;;) { if (curArg->isObject() && curObj->inherits(&ArrayInstance::info)) { unsigned int k = 0; // Older versions tried to optimize out getting the length of thisObj // by checking for n != 0, but that doesn't work if thisObj is an empty array. length = curObj->get(exec, exec->propertyNames().length)->toUInt32(exec); while (k < length) { if (JSValue *v = getProperty(exec, curObj, k)) arr->put(exec, n, v); n++; k++; } } else { arr->put(exec, n, curArg); n++; } if (it == args.end()) break; curArg = *it; curObj = static_cast<JSObject *>(it++); // may be 0 } arr->put(exec, exec->propertyNames().length, jsNumber(n), DontEnum | DontDelete); result = arr; break; } case Pop:{ if (length == 0) { thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete); result = jsUndefined(); } else { result = thisObj->get(exec, length - 1); thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1), DontEnum | DontDelete); } break; } case Push: { for (int n = 0; n < args.size(); n++) thisObj->put(exec, length + n, args[n]); length += args.size(); thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete); result = jsNumber(length); break; } case Reverse: { unsigned int middle = length / 2; for (unsigned int k = 0; k < middle; k++) { unsigned lk1 = length - k - 1; JSValue *obj2 = getProperty(exec, thisObj, lk1); JSValue *obj = getProperty(exec, thisObj, k); if (obj2) thisObj->put(exec, k, obj2); else thisObj->deleteProperty(exec, k); if (obj) thisObj->put(exec, lk1, obj); else thisObj->deleteProperty(exec, lk1); } result = thisObj; break; } case Shift: { if (length == 0) { thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete); result = jsUndefined(); } else { result = thisObj->get(exec, 0); for(unsigned int k = 1; k < length; k++) { if (JSValue *obj = getProperty(exec, thisObj, k)) thisObj->put(exec, k-1, obj); else thisObj->deleteProperty(exec, k-1); } thisObj->deleteProperty(exec, length - 1); thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1), DontEnum | DontDelete); } break; } case Slice: { // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10 // We return a new array JSObject *resObj = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec,List::empty())); result = resObj; double begin = 0; if (!args[0]->isUndefined()) { begin = args[0]->toInteger(exec); if (begin >= 0) { // false for NaN if (begin > length) begin = length; } else { begin += length; if (!(begin >= 0)) // true for NaN begin = 0; } } double end = length; if (!args[1]->isUndefined()) { end = args[1]->toInteger(exec); if (end < 0) { // false for NaN end += length; if (end < 0) end = 0; } else { if (!(end <= length)) // true for NaN end = length; } } //printf( "Slicing from %d to %d \n", begin, end ); int n = 0; int b = static_cast<int>(begin); int e = static_cast<int>(end); for(int k = b; k < e; k++, n++) { if (JSValue *v = getProperty(exec, thisObj, k)) resObj->put(exec, n, v); } resObj->put(exec, exec->propertyNames().length, jsNumber(n), DontEnum | DontDelete); break; } case Sort:{ #if 0 printf("KJS Array::Sort length=%d\n", length); for ( unsigned int i = 0 ; i<length ; ++i ) printf("KJS Array::Sort: %d: %s\n", i, thisObj->get(exec, i)->toString(exec).ascii() ); #endif JSObject *sortFunction = NULL; if (!args[0]->isUndefined()) { sortFunction = args[0]->toObject(exec); if (!sortFunction->implementsCall()) sortFunction = NULL; } if (thisObj->classInfo() == &ArrayInstance::info) { if (sortFunction) ((ArrayInstance *)thisObj)->sort(exec, sortFunction); else ((ArrayInstance *)thisObj)->sort(exec); result = thisObj; break; } if (length == 0) { thisObj->put(exec, exec->propertyNames().length, jsNumber(0), DontEnum | DontDelete); result = thisObj; break; } // "Min" sort. Not the fastest, but definitely less code than heapsort // or quicksort, and much less swapping than bubblesort/insertionsort. for ( unsigned int i = 0 ; i<length-1 ; ++i ) { JSValue *iObj = thisObj->get(exec,i); unsigned int themin = i; JSValue *minObj = iObj; for ( unsigned int j = i+1 ; j<length ; ++j ) { JSValue *jObj = thisObj->get(exec,j); double cmp; if (jObj->isUndefined()) { cmp = 1; // don't check minObj because there's no need to differentiate == (0) from > (1) } else if (minObj->isUndefined()) { cmp = -1; } else if (sortFunction) { List l; l.append(jObj); l.append(minObj); cmp = sortFunction->call(exec, exec->dynamicInterpreter()->globalObject(), l)->toNumber(exec); } else { cmp = (jObj->toString(exec) < minObj->toString(exec)) ? -1 : 1; } if ( cmp < 0 ) { themin = j; minObj = jObj; } } // Swap themin and i if ( themin > i ) { //printf("KJS Array::Sort: swapping %d and %d\n", i, themin ); thisObj->put( exec, i, minObj ); thisObj->put( exec, themin, iObj ); } } #if 0 printf("KJS Array::Sort -- Resulting array:\n"); for ( unsigned int i = 0 ; i<length ; ++i ) printf("KJS Array::Sort: %d: %s\n", i, thisObj->get(exec, i)->toString(exec).ascii() ); #endif result = thisObj; break; } case Splice: { // 15.4.4.12 - oh boy this is huge JSObject *resObj = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec,List::empty())); result = resObj; int begin = args[0]->toUInt32(exec); if ( begin < 0 ) begin = maxInt( begin + length, 0 ); else begin = minInt( begin, length ); unsigned int deleteCount = minInt( maxInt( args[1]->toUInt32(exec), 0 ), length - begin ); //printf( "Splicing from %d, deleteCount=%d \n", begin, deleteCount ); for(unsigned int k = 0; k < deleteCount; k++) { if (JSValue *v = getProperty(exec, thisObj, k+begin)) resObj->put(exec, k, v); } resObj->put(exec, exec->propertyNames().length, jsNumber(deleteCount), DontEnum | DontDelete); unsigned int additionalArgs = maxInt( args.size() - 2, 0 ); if ( additionalArgs != deleteCount ) { if ( additionalArgs < deleteCount ) { for ( unsigned int k = begin; k < length - deleteCount; ++k ) { if (JSValue *v = getProperty(exec, thisObj, k+deleteCount)) thisObj->put(exec, k+additionalArgs, v); else thisObj->deleteProperty(exec, k+additionalArgs); } for ( unsigned int k = length ; k > length - deleteCount + additionalArgs; --k ) thisObj->deleteProperty(exec, k-1); } else { for ( unsigned int k = length - deleteCount; (int)k > begin; --k ) { if (JSValue *obj = getProperty(exec, thisObj, k + deleteCount - 1)) thisObj->put(exec, k + additionalArgs - 1, obj); else thisObj->deleteProperty(exec, k+additionalArgs-1); } } } for ( unsigned int k = 0; k < additionalArgs; ++k ) { thisObj->put(exec, k+begin, args[k+2]); } thisObj->put(exec, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs), DontEnum | DontDelete); break; } case UnShift: { // 15.4.4.13 unsigned int nrArgs = args.size(); for ( unsigned int k = length; k > 0; --k ) { if (JSValue *v = getProperty(exec, thisObj, k - 1)) thisObj->put(exec, k+nrArgs-1, v); else thisObj->deleteProperty(exec, k+nrArgs-1); } for ( unsigned int k = 0; k < nrArgs; ++k ) thisObj->put(exec, k, args[k]); result = jsNumber(length + nrArgs); thisObj->put(exec, exec->propertyNames().length, result, DontEnum | DontDelete); break; } case Filter: case Map: { JSObject *eachFunction = args[0]->toObject(exec); if (!eachFunction->implementsCall()) return throwError(exec, TypeError); JSObject *applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicInterpreter()->globalObject() : args[1]->toObject(exec); JSObject *resultArray; if (id == Filter) resultArray = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, List::empty())); else { List args; args.append(jsNumber(length)); resultArray = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, args)); } unsigned filterIndex = 0; for (unsigned k = 0; k < length && !exec->hadException(); ++k) { PropertySlot slot; if (!thisObj->getPropertySlot(exec, k, slot)) continue; JSValue *v = slot.getValue(exec, thisObj, k); List eachArguments; eachArguments.append(v); eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); JSValue *result = eachFunction->call(exec, applyThis, eachArguments); if (id == Map) resultArray->put(exec, k, result); else if (result->toBoolean(exec)) resultArray->put(exec, filterIndex++, v); } return resultArray; } case Every: case ForEach: case Some: { //Documentation for these three is available at: //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some JSObject *eachFunction = args[0]->toObject(exec); if (!eachFunction->implementsCall()) return throwError(exec, TypeError); JSObject *applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicInterpreter()->globalObject() : args[1]->toObject(exec); if (id == Some || id == Every) result = jsBoolean(id == Every); else result = jsUndefined(); for (unsigned k = 0; k < length && !exec->hadException(); ++k) { PropertySlot slot; if (!thisObj->getPropertySlot(exec, k, slot)) continue; List eachArguments; eachArguments.append(slot.getValue(exec, thisObj, k)); eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); bool predicateResult = eachFunction->call(exec, applyThis, eachArguments)->toBoolean(exec); if (id == Every && !predicateResult) { result = jsBoolean(false); break; } if (id == Some && predicateResult) { result = jsBoolean(true); break; } } break; } case IndexOf: { // JavaScript 1.5 Extension by Mozilla // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf unsigned index = 0; double d = args[1]->toInteger(exec); if (d < 0) d += length; if (d > 0) { if (d > length) index = length; else index = static_cast<unsigned>(d); } JSValue* searchElement = args[0]; for (; index < length; ++index) { JSValue* e = getProperty(exec, thisObj, index); if (!e) continue; if (strictEqual(exec, searchElement, e)) return jsNumber(index); } return jsNumber(-1); } case LastIndexOf: { // JavaScript 1.6 Extension by Mozilla // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf int index = length - 1; double d = args[1]->toInteger(exec); if (d < 0) { d += length; if (d < 0) return jsNumber(-1); } if (d < length) index = static_cast<int>(d); JSValue* searchElement = args[0]; for (; index >= 0; --index) { JSValue* e = getProperty(exec, thisObj, index); if (!e) continue; if (strictEqual(exec, searchElement, e)) return jsNumber(index); } return jsNumber(-1); } default: assert(0); result = 0; break; } return result; }
void AXObjectCacheImpl::updateAriaOwns(const AXObject* owner, const Vector<String>& idVector, HeapVector<Member<AXObject>>& ownedChildren) { // // Update the map from the AXID of this element to the ids of the owned children, // and the reverse map from ids to possible AXID owners. // HashSet<String> currentIds = m_ariaOwnerToIdsMapping.get(owner->axObjectID()); HashSet<String> newIds; bool idsChanged = false; for (const String& id : idVector) { newIds.add(id); if (!currentIds.contains(id)) { idsChanged = true; HashSet<AXID>* owners = m_idToAriaOwnersMapping.get(id); if (!owners) { owners = new HashSet<AXID>(); m_idToAriaOwnersMapping.set(id, adoptPtr(owners)); } owners->add(owner->axObjectID()); } } for (const String& id : currentIds) { if (!newIds.contains(id)) { idsChanged = true; HashSet<AXID>* owners = m_idToAriaOwnersMapping.get(id); if (owners) { owners->remove(owner->axObjectID()); if (owners->isEmpty()) m_idToAriaOwnersMapping.remove(id); } } } if (idsChanged) m_ariaOwnerToIdsMapping.set(owner->axObjectID(), newIds); // // Now figure out the ids that actually correspond to children that exist and // that we can legally own (not cyclical, not already owned, etc.) and update // the maps and |ownedChildren| based on that. // // Figure out the children that are owned by this object and are in the tree. TreeScope& scope = owner->node()->treeScope(); Vector<AXID> newChildAXIDs; for (const String& idName : idVector) { Element* element = scope.getElementById(AtomicString(idName)); if (!element) continue; AXObject* child = getOrCreate(element); if (!child) continue; // If this child is already aria-owned by a different owner, continue. // It's an author error if this happens and we don't worry about which of the // two owners wins ownership of the child, as long as only one of them does. if (isAriaOwned(child) && getAriaOwnedParent(child) != owner) continue; // You can't own yourself! if (child == owner) continue; // Walk up the parents of the owner object, make sure that this child doesn't appear // there, as that would create a cycle. bool foundCycle = false; for (AXObject* parent = owner->parentObject(); parent && !foundCycle; parent = parent->parentObject()) { if (parent == child) foundCycle = true; } if (foundCycle) continue; newChildAXIDs.append(child->axObjectID()); ownedChildren.append(child); } // Compare this to the current list of owned children, and exit early if there are no changes. Vector<AXID> currentChildAXIDs = m_ariaOwnerToChildrenMapping.get(owner->axObjectID()); bool same = true; if (currentChildAXIDs.size() != newChildAXIDs.size()) { same = false; } else { for (size_t i = 0; i < currentChildAXIDs.size() && same; ++i) { if (currentChildAXIDs[i] != newChildAXIDs[i]) same = false; } } if (same) return; // The list of owned children has changed. Even if they were just reordered, to be safe // and handle all cases we remove all of the current owned children and add the new list // of owned children. for (size_t i = 0; i < currentChildAXIDs.size(); ++i) { // Find the AXObject for the child that this owner no longer owns. AXID removedChildID = currentChildAXIDs[i]; AXObject* removedChild = objectFromAXID(removedChildID); // It's possible that this child has already been owned by some other owner, // in which case we don't need to do anything. if (removedChild && getAriaOwnedParent(removedChild) != owner) continue; // Remove it from the child -> owner mapping so it's not owned by this owner anymore. m_ariaOwnedChildToOwnerMapping.remove(removedChildID); if (removedChild) { // If the child still exists, find its "real" parent, and reparent it back to // its real parent in the tree by detaching it from its current parent and // calling childrenChanged on its real parent. removedChild->detachFromParent(); AXID realParentID = m_ariaOwnedChildToRealParentMapping.get(removedChildID); AXObject* realParent = objectFromAXID(realParentID); childrenChanged(realParent); } // Remove the child -> original parent mapping too since this object has now been // reparented back to its original parent. m_ariaOwnedChildToRealParentMapping.remove(removedChildID); } for (size_t i = 0; i < newChildAXIDs.size(); ++i) { // Find the AXObject for the child that will now be a child of this owner. AXID addedChildID = newChildAXIDs[i]; AXObject* addedChild = objectFromAXID(addedChildID); // Add this child to the mapping from child to owner. m_ariaOwnedChildToOwnerMapping.set(addedChildID, owner->axObjectID()); // Add its parent object to a mapping from child to real parent. If later this owner // doesn't own this child anymore, we need to return it to its original parent. AXObject* originalParent = addedChild->parentObject(); m_ariaOwnedChildToRealParentMapping.set(addedChildID, originalParent->axObjectID()); // Now detach the object from its original parent and call childrenChanged on the // original parent so that it can recompute its list of children. addedChild->detachFromParent(); childrenChanged(originalParent); } // Finally, update the mapping from the owner to the list of child IDs. m_ariaOwnerToChildrenMapping.set(owner->axObjectID(), newChildAXIDs); }
TEST(FileSystemIterator, subSubDirs) { TestDirectory rootDir(File("foobarfolder")); TestDirectoryPtr subDir1 = rootDir.addDirectory("subdir1"); TestDirectoryPtr subDir2 = rootDir.addDirectory("subdir2"); TestDirectoryPtr subDir3 = rootDir.addDirectory("subdir3"); subDir1->addDirectory("sub1subdir1")->addFile("sub1subdir1file1")->addFile("sub1subdir1file2"); subDir1->addDirectory("sub1subdir2")->addFile("sub1subdir2file1")->addFile("sub1subdir2file2"); subDir1->addDirectory("sub1subdir3")->addFile("sub1subdir3file1")->addFile("sub1subdir3file2"); subDir2->addDirectory("sub2subdir1")->addFile("sub2subdir1file1")->addFile("sub2subdir1file2"); subDir2->addDirectory("sub2subdir2")->addFile("sub2subdir2file1")->addFile("sub2subdir2file2"); subDir2->addDirectory("sub2subdir3")->addFile("sub2subdir3file1")->addFile("sub2subdir3file2"); subDir3->addDirectory("sub3subdir1")->addFile("sub3subdir1file1")->addFile("sub3subdir1file2"); subDir3->addDirectory("sub3subdir2")->addFile("sub3subdir2file1")->addFile("sub3subdir2file2"); subDir3->addDirectory("sub3subdir3")->addFile("sub3subdir3file1")->addFile("sub3subdir3file2"); HashSet<String> filenames; filenames.put("subdir1"); filenames.put("subdir2"); filenames.put("subdir3"); filenames.put("sub1subdir1"); filenames.put("sub1subdir2"); filenames.put("sub1subdir3"); filenames.put("sub2subdir1"); filenames.put("sub2subdir2"); filenames.put("sub2subdir3"); filenames.put("sub3subdir1"); filenames.put("sub3subdir2"); filenames.put("sub3subdir3"); filenames.put("sub1subdir1file1"); filenames.put("sub1subdir2file1"); filenames.put("sub1subdir3file1"); filenames.put("sub2subdir1file1"); filenames.put("sub2subdir2file1"); filenames.put("sub2subdir3file1"); filenames.put("sub3subdir1file1"); filenames.put("sub3subdir2file1"); filenames.put("sub3subdir3file1"); filenames.put("sub1subdir1file2"); filenames.put("sub1subdir2file2"); filenames.put("sub1subdir3file2"); filenames.put("sub2subdir1file2"); filenames.put("sub2subdir2file2"); filenames.put("sub2subdir3file2"); filenames.put("sub3subdir1file2"); filenames.put("sub3subdir2file2"); filenames.put("sub3subdir3file2"); FileSystemIterator iter(rootDir.getFile()); while (iter.isValid()) { EXPECT_TRUE(filenames.hasElement(iter->getFileName())); EXPECT_EQ(CAPU_OK, filenames.remove(iter->getFileName())); iter.next(); } EXPECT_EQ(0u, filenames.count()); }
static void sortBlock(unsigned from, unsigned to, Vector<Vector<Node*> >& parentMatrix, bool mayContainAttributeNodes) { ASSERT(from + 1 < to); // Should not call this function with less that two nodes to sort. unsigned minDepth = UINT_MAX; for (unsigned i = from; i < to; ++i) { unsigned depth = parentMatrix[i].size() - 1; if (minDepth > depth) minDepth = depth; } // Find the common ancestor. unsigned commonAncestorDepth = minDepth; Node* commonAncestor; while (true) { commonAncestor = parentWithDepth(commonAncestorDepth, parentMatrix[from]); if (commonAncestorDepth == 0) break; bool allEqual = true; for (unsigned i = from + 1; i < to; ++i) { if (commonAncestor != parentWithDepth(commonAncestorDepth, parentMatrix[i])) { allEqual = false; break; } } if (allEqual) break; --commonAncestorDepth; } if (commonAncestorDepth == minDepth) { // One of the nodes is the common ancestor => it is the first in document order. // Find it and move it to the beginning. for (unsigned i = from; i < to; ++i) if (commonAncestor == parentMatrix[i][0]) { parentMatrix[i].swap(parentMatrix[from]); if (from + 2 < to) sortBlock(from + 1, to, parentMatrix, mayContainAttributeNodes); return; } } if (mayContainAttributeNodes && commonAncestor->isElementNode()) { // The attribute nodes and namespace nodes of an element occur before the children of the element. // The namespace nodes are defined to occur before the attribute nodes. // The relative order of namespace nodes is implementation-dependent. // The relative order of attribute nodes is implementation-dependent. unsigned sortedEnd = from; // FIXME: namespace nodes are not implemented. for (unsigned i = sortedEnd; i < to; ++i) { Node* n = parentMatrix[i][0]; if (n->isAttributeNode() && static_cast<Attr*>(n)->ownerElement() == commonAncestor) parentMatrix[i].swap(parentMatrix[sortedEnd++]); } if (sortedEnd != from) { if (to - sortedEnd > 1) sortBlock(sortedEnd, to, parentMatrix, mayContainAttributeNodes); return; } } // Children nodes of the common ancestor induce a subdivision of our node-set. // Sort it according to this subdivision, and recursively sort each group. HashSet<Node*> parentNodes; for (unsigned i = from; i < to; ++i) parentNodes.add(parentWithDepth(commonAncestorDepth + 1, parentMatrix[i])); unsigned previousGroupEnd = from; unsigned groupEnd = from; for (Node* n = commonAncestor->firstChild(); n; n = n->nextSibling()) { // If parentNodes contains the node, perform a linear search to move its children in the node-set to the beginning. if (parentNodes.contains(n)) { for (unsigned i = groupEnd; i < to; ++i) if (parentWithDepth(commonAncestorDepth + 1, parentMatrix[i]) == n) parentMatrix[i].swap(parentMatrix[groupEnd++]); if (groupEnd - previousGroupEnd > 1) sortBlock(previousGroupEnd, groupEnd, parentMatrix, mayContainAttributeNodes); ASSERT(previousGroupEnd != groupEnd); previousGroupEnd = groupEnd; #ifndef NDEBUG parentNodes.remove(n); #endif } } ASSERT(parentNodes.isEmpty()); }
void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate& update, const Element* animatingElement, Element& element, const ComputedStyle& style, ComputedStyle* parentStyle, StyleResolver* resolver) { const ElementAnimations* elementAnimations = animatingElement ? animatingElement->elementAnimations() : nullptr; bool isAnimationStyleChange = elementAnimations && elementAnimations->isAnimationStyleChange(); #if !ENABLE(ASSERT) // If we're in an animation style change, no animations can have started, been cancelled or changed play state. // When ASSERT is enabled, we verify this optimization. if (isAnimationStyleChange) return; #endif const CSSAnimationData* animationData = style.animations(); const CSSAnimations* cssAnimations = elementAnimations ? &elementAnimations->cssAnimations() : nullptr; const Element* elementForScoping = animatingElement ? animatingElement : &element; HashSet<AtomicString> inactive; if (cssAnimations) { for (const auto& entry : cssAnimations->m_animations) inactive.add(entry.key); } if (style.display() != NONE) { for (size_t i = 0; animationData && i < animationData->nameList().size(); ++i) { AtomicString animationName(animationData->nameList()[i]); if (animationName == CSSAnimationData::initialName()) continue; const bool isPaused = CSSTimingData::getRepeated(animationData->playStateList(), i) == AnimPlayStatePaused; Timing timing = animationData->convertToTiming(i); Timing specifiedTiming = timing; RefPtr<TimingFunction> keyframeTimingFunction = timing.timingFunction; timing.timingFunction = Timing::defaults().timingFunction; RefPtrWillBeRawPtr<StyleRuleKeyframes> keyframesRule = resolver->findKeyframesRule(elementForScoping, animationName); if (!keyframesRule) continue; // Cancel the animation if there's no style rule for it. if (cssAnimations) { AnimationMap::const_iterator existing(cssAnimations->m_animations.find(animationName)); if (existing != cssAnimations->m_animations.end()) { inactive.remove(animationName); const RunningAnimation* runningAnimation = existing->value.get(); Animation* animation = runningAnimation->animation.get(); if (keyframesRule != runningAnimation->styleRule || keyframesRule->version() != runningAnimation->styleRuleVersion || runningAnimation->specifiedTiming != specifiedTiming) { ASSERT(!isAnimationStyleChange); update.updateAnimation(animationName, animation, InertEffect::create( createKeyframeEffectModel(resolver, animatingElement, element, &style, parentStyle, animationName, keyframeTimingFunction.get(), i), timing, isPaused, animation->unlimitedCurrentTimeInternal()), specifiedTiming, keyframesRule); } if (isPaused != animation->paused()) { ASSERT(!isAnimationStyleChange); update.toggleAnimationPaused(animationName); } continue; } } ASSERT(!isAnimationStyleChange); update.startAnimation(animationName, InertEffect::create( createKeyframeEffectModel(resolver, animatingElement, element, &style, parentStyle, animationName, keyframeTimingFunction.get(), i), timing, isPaused, 0), specifiedTiming, keyframesRule); } } ASSERT(inactive.isEmpty() || cssAnimations); for (const AtomicString& animationName : inactive) { ASSERT(!isAnimationStyleChange); update.cancelAnimation(animationName, *cssAnimations->m_animations.get(animationName)->animation); } }
void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, const Element* animatingElement, Element& element, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver) { const ActiveAnimations* activeAnimations = animatingElement ? animatingElement->activeAnimations() : nullptr; #if !ENABLE(ASSERT) // If we're in an animation style change, no animations can have started, been cancelled or changed play state. // When ASSERT is enabled, we verify this optimization. if (activeAnimations && activeAnimations->isAnimationStyleChange()) return; #endif const CSSAnimationData* animationData = style.animations(); const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->cssAnimations() : nullptr; HashSet<AtomicString> inactive; if (cssAnimations) { for (const auto& entry : cssAnimations->m_animations) inactive.add(entry.key); } if (style.display() != NONE) { for (size_t i = 0; animationData && i < animationData->nameList().size(); ++i) { AtomicString animationName(animationData->nameList()[i]); if (animationName == CSSAnimationData::initialName()) continue; bool isPaused = CSSTimingData::getRepeated(animationData->playStateList(), i) == AnimPlayStatePaused; // Keyframes and animation properties are snapshotted when the // animation starts, so we don't need to track changes to these, // with the exception of play-state. if (cssAnimations) { AnimationMap::const_iterator existing(cssAnimations->m_animations.find(animationName)); if (existing != cssAnimations->m_animations.end()) { inactive.remove(animationName); AnimationPlayer* player = existing->value.get(); if (isPaused != player->paused()) { ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()); update->toggleAnimationPaused(animationName); } continue; } } Timing timing = animationData->convertToTiming(i); RefPtr<TimingFunction> keyframeTimingFunction = timing.timingFunction; timing.timingFunction = Timing::defaults().timingFunction; AnimatableValueKeyframeVector resolvedKeyframes; resolveKeyframes(resolver, animatingElement, element, style, parentStyle, animationName, keyframeTimingFunction.get(), resolvedKeyframes); if (!resolvedKeyframes.isEmpty()) { ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()); update->startAnimation(animationName, InertAnimation::create(AnimatableValueKeyframeEffectModel::create(resolvedKeyframes), timing, isPaused)); } } } ASSERT(inactive.isEmpty() || cssAnimations); for (const AtomicString& animationName : inactive) { ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()); update->cancelAnimation(animationName, *cssAnimations->m_animations.get(animationName)); } }