void addAccessibilityNotificationHandler(AccessibilityNotificationHandler* notificationHandler) { if (!notificationHandler) return; #if PLATFORM(GTK) JSGlobalContextRef jsContext = webkit_web_frame_get_global_context(mainFrame); #else JSContextRef jsContext = 0; #endif if (!jsContext) return; JSValueProtect(jsContext, notificationHandler->notificationFunctionCallback()); // Check if this notification handler is related to a specific element. if (notificationHandler->platformElement()) { if (notificationHandlers.contains(notificationHandler->platformElement())) { JSValueUnprotect(jsContext, notificationHandlers.find(notificationHandler->platformElement())->value->notificationFunctionCallback()); notificationHandlers.remove(notificationHandler->platformElement()); } notificationHandlers.add(notificationHandler->platformElement(), notificationHandler); } else { if (notificationHandlers.contains(GlobalNotificationKey)) { JSValueUnprotect(jsContext, notificationHandlers.find(GlobalNotificationKey)->value->notificationFunctionCallback()); notificationHandlers.remove(GlobalNotificationKey); } notificationHandlers.add(GlobalNotificationKey, notificationHandler); } connectAccessibilityCallbacks(); }
TEST(TestHashMapCopyConstructor, copyHashMapHasSameData) { HashMap first; first.add("Ford", "Tang"); HashMap second = first; ASSERT_EQ(first.size(), second.size()); ASSERT_EQ(first.contains("Ford"), second.contains("Ford")); ASSERT_EQ(first.value("Ford"), second.value("Ford")); }
TEST(TestHashMapAssignmentOperator, assignmentShouldHaveSameData) { HashMap first; first.add("Ford", "Tang"); HashMap second; second.add("Diana", "Chang"); second.add("Bronx", "Alora"); second = first; ASSERT_EQ(first.size(), second.size()); ASSERT_EQ(first.contains("Ford"), second.contains("Ford")); ASSERT_EQ(first.value("Ford"), second.value("Ford")); }
static std::string testContains() { HashMap<std::size_t,double> map; for(std::size_t ii = 1; ii != 100; ++ii) { map[ii] = 0; for(std::size_t jj = ii; jj; --jj) { if( !map.contains(jj) ) { return "Contains failed on inserted item."; } } for(std::size_t jj = 101; jj != 150; ++jj) { if( map.contains(jj) ) { return "Contains failed on uninserted item."; } } } return ""; }
ObjectReference BinaryPropertyListPlan::integerObjectReference(int integer) const { ASSERT(integer >= 0); if (!integer) { ASSERT(m_integerZeroObjectReference != invalidObjectReference()); return m_integerZeroObjectReference; } ASSERT(m_integers.contains(integer)); return m_integers.get(integer); }
TEST(WebKit2, WKRetainPtr) { WKRetainPtr<WKStringRef> string1 = adoptWK(WKStringCreateWithUTF8CString("a")); WKRetainPtr<WKStringRef> string2 = adoptWK(WKStringCreateWithUTF8CString("a")); WKRetainPtr<WKStringRef> string3 = adoptWK(WKStringCreateWithUTF8CString("a")); WKRetainPtr<WKStringRef> string4 = adoptWK(WKStringCreateWithUTF8CString("a")); HashMap<WKRetainPtr<WKStringRef>, int> map; map.set(string2, 2); map.set(string1, 1); EXPECT_TRUE(map.contains(string1)); EXPECT_TRUE(map.contains(string2)); EXPECT_FALSE(map.contains(string3)); EXPECT_FALSE(map.contains(string4)); EXPECT_EQ(1, map.get(string1)); EXPECT_EQ(2, map.get(string2)); }
DataObjectGtk* DataObjectGtk::forClipboard(GtkClipboard* clipboard) { static HashMap<GtkClipboard*, RefPtr<DataObjectGtk> > objectMap; if (!objectMap.contains(clipboard)) { RefPtr<DataObjectGtk> dataObject = DataObjectGtk::create(); objectMap.set(clipboard, dataObject); return dataObject.get(); } HashMap<GtkClipboard*, RefPtr<DataObjectGtk> >::iterator it = objectMap.find(clipboard); return it->value.get(); }
// Test string version. TEST(HashTest, STRINGHash) { using namespace eoaix; HashMap<std::string, TestKey> htable; TestKey t1(1, "1str"); TestKey t2(2, "2str"); TestKey t4(4, "4str"); htable.insert(std::string("2"), t2); htable.insert(std::string("1"), t1); TestKey* t3 = htable.find_val(std::string("1")); EXPECT_EQ(t3->toString(), t1.toString()); EXPECT_EQ(htable.contains(std::string("1")), true); EXPECT_EQ(htable.contains(std::string("4")), false) << "htable.contains(t4) != true\n"; }
void Keymap::loadMappings(const HardwareInputSet *hwKeys) { if (!_configDomain) return; if (_actions.empty()) return; Common::KeymapperDefaultBindings *defaults = g_system->getKeymapperDefaultBindings(); HashMap<String, const HardwareInput *> mappedInputs; List<Action*>::iterator it; String prefix = KEYMAP_KEY_PREFIX + _name + "_"; for (it = _actions.begin(); it != _actions.end(); ++it) { Action* ua = *it; String actionId(ua->id); String confKey = prefix + actionId; String hwInputId = _configDomain->getVal(confKey); bool defaulted = false; // fall back to the platform-specific defaults if (hwInputId.empty() && defaults) { hwInputId = defaults->getDefaultBinding(_name, actionId); if (!hwInputId.empty()) defaulted = true; } // there's no mapping if (hwInputId.empty()) continue; const HardwareInput *hwInput = hwKeys->findHardwareInput(hwInputId.c_str()); if (!hwInput) { warning("HardwareInput with ID '%s' not known", hwInputId.c_str()); continue; } if (defaulted) { if (mappedInputs.contains(hwInputId)) { debug(1, "Action [%s] not falling back to hardcoded default value [%s] because the hardware input is in use", confKey.c_str(), hwInputId.c_str()); continue; } warning("Action [%s] fell back to hardcoded default value [%s]", confKey.c_str(), hwInputId.c_str()); } mappedInputs.setVal(hwInputId, hwInput); // map the key ua->mapInput(hwInput); } }
inline void CalculationValueMap::deref(unsigned handle) { ASSERT(m_map.contains(handle)); auto it = m_map.find(handle); if (it->value.referenceCountMinusOne) { --it->value.referenceCountMinusOne; return; } // The adoptRef here is balanced by the leakRef in the insert member function. Ref<CalculationValue> value { adoptRef(*it->value.value) }; m_map.remove(it); }
String CSSPrimitiveValue::customSerializeResolvingVariables(const HashMap<AtomicString, String>& variables) const { if (isVariableName() && variables.contains(m_value.string)) return variables.get(m_value.string); if (CSSCalcValue* calcValue = cssCalcValue()) return calcValue->customSerializeResolvingVariables(variables); if (Pair* pairValue = getPairValue()) return pairValue->serializeResolvingVariables(variables); if (Rect* rectVal = getRectValue()) return rectVal->serializeResolvingVariables(variables); if (Quad* quadVal = getQuadValue()) return quadVal->serializeResolvingVariables(variables); if (CSSBasicShape* shapeValue = getShapeValue()) return shapeValue->serializeResolvingVariables(variables); return customCssText(); }
void WebProcessPool::setPluginLoadClientPolicy(WebCore::PluginLoadClientPolicy policy, const String& host, const String& bundleIdentifier, const String& versionString) { #if ENABLE(NETSCAPE_PLUGIN_API) HashMap<String, HashMap<String, uint8_t>> policiesByIdentifier; if (m_pluginLoadClientPolicies.contains(host)) policiesByIdentifier = m_pluginLoadClientPolicies.get(host); HashMap<String, uint8_t> versionsToPolicies; if (policiesByIdentifier.contains(bundleIdentifier)) versionsToPolicies = policiesByIdentifier.get(bundleIdentifier); versionsToPolicies.set(versionString, policy); policiesByIdentifier.set(bundleIdentifier, versionsToPolicies); m_pluginLoadClientPolicies.set(host, policiesByIdentifier); #endif sendToAllProcesses(Messages::WebProcess::SetPluginLoadClientPolicy(policy, host, bundleIdentifier, versionString)); }
TEST(HashTest, ObjectHash) { using namespace eoaix; HashMap<TestKey, TestKey> htable; TestKey t1(1, "1str"); TestKey t2(2, "2str"); TestKey t4(4, "4str"); htable.insert(t1, t2); htable.insert(t2, t1); TestKey* t3 = htable.find_val(t1); // std::cout << t3->toString() << std::endl; //std::cout << t2.toString() << std::endl; EXPECT_EQ(t3->toString(), t2.toString()); EXPECT_EQ(htable.contains(t1), true); EXPECT_EQ(htable.contains(t4), false) << "htable.contains(t4) != true\n"; }
static Vector<Ref<CSSFontFace>> constructFamilyFontFaces(const String& familyName, HashMap<String, Vector<Ref<CSSFontFace>>, CaseFoldingHash>& locallyInstalledFontFaces) { auto result = Vector<Ref<CSSFontFace>>(); ASSERT(!locallyInstalledFontFaces.contains(familyName)); Vector<FontTraitsMask> traitsMasks = FontCache::singleton().getTraitsInFamily(familyName); if (!traitsMasks.isEmpty()) { Vector<Ref<CSSFontFace>> faces = { }; for (auto mask : traitsMasks) { Ref<CSSFontFace> face = CSSFontFace::create(mask, nullptr, true); face->addSource(std::make_unique<CSSFontFaceSource>(familyName)); ASSERT(face->isValid()); faces.append(WTF::move(face)); } locallyInstalledFontFaces.add(familyName, WTF::move(faces)); } return result; }
CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const CSSProperty* const * properties, int numProperties) : CSSStyleDeclaration(parent) , m_node(0) , m_strictParsing(!parent || parent->useStrictParsing()) #ifndef NDEBUG , m_iteratorCount(0) #endif { m_properties.reserveInitialCapacity(numProperties); HashMap<int, bool> candidates; for (int i = 0; i < numProperties; ++i) { const CSSProperty *property = properties[i]; ASSERT(property); bool important = property->isImportant(); if (candidates.contains(property->id())) { if (!important && candidates.get(property->id())) continue; removeProperty(property->id(), false); } m_properties.append(*property); candidates.set(property->id(), important); } }
static String mimeTypeForExtension(const String& file) { static HashMap<String, String, CaseFoldingHash> extensionToMime; if (extensionToMime.isEmpty()) { extensionToMime.set("txt", "text/plain"); extensionToMime.set("html", "text/html"); extensionToMime.set("htm", "text/html"); extensionToMime.set("png", "image/png"); extensionToMime.set("jpeg", "image/jpeg"); extensionToMime.set("jpg", "image/jpeg"); extensionToMime.set("gif", "image/gif"); extensionToMime.set("ico", "image/x-icon"); extensionToMime.set("js", "text/javascript"); } int dot = file.reverseFind('.'); String mime("text/plain"); if (dot != -1) { String ext = file.substring(dot + 1); if (extensionToMime.contains(ext)) mime = extensionToMime.get(ext); } return mime; }
static void testU32Map() { HashMap<U32, U32> map; enum { maxI = 1024 * 1024 }; for(Uptr i = 0;i < maxI;++i) { errorUnless(!map.contains(U32(i))); } errorUnless(map.size() == 0); for(Uptr i = 0;i < maxI;++i) { errorUnless(!map.contains(U32(i))); errorUnless(!map.get(U32(i))); errorUnless(map.add(U32(i),U32(i * 2))); errorUnless(map.contains(U32(i))); errorUnless(map.get(U32(i))); errorUnless(*map.get(U32(i)) == U32(i * 2)); errorUnless(map.size() == i + 1); } for(Uptr i = 0;i < maxI;++i) { errorUnless(map.contains(U32(i))); errorUnless(map.remove(U32(i))); errorUnless(!map.contains(U32(i))); errorUnless(map.size() == maxI - i - 1); } for(Uptr i = 0;i < maxI;++i) { errorUnless(!map.contains(U32(i))); } }
static SocketStreamHandle* getHandleFromId(void* id) { if (!gActiveHandles.contains(id)) return 0; return gActiveHandles.get(id); }
bool DOMPatchSupport::innerPatchChildren(ContainerNode* parentNode, const Vector<OwnPtr<Digest> >& oldList, const Vector<OwnPtr<Digest> >& newList, ExceptionState& exceptionState) { pair<ResultMap, ResultMap> resultMaps = diff(oldList, newList); ResultMap& oldMap = resultMaps.first; ResultMap& newMap = resultMaps.second; Digest* oldHead = 0; Digest* oldBody = 0; // 1. First strip everything except for the nodes that retain. Collect pending merges. HashMap<Digest*, Digest*> merges; HashSet<size_t, WTF::IntHash<size_t>, WTF::UnsignedWithZeroKeyHashTraits<size_t> > usedNewOrdinals; for (size_t i = 0; i < oldList.size(); ++i) { if (oldMap[i].first) { if (usedNewOrdinals.add(oldMap[i].second).isNewEntry) continue; oldMap[i].first = 0; oldMap[i].second = 0; } // Always match <head> and <body> tags with each other - we can't remove them from the DOM // upon patching. if (isHTMLHeadElement(*oldList[i]->m_node)) { oldHead = oldList[i].get(); continue; } if (isHTMLBodyElement(*oldList[i]->m_node)) { oldBody = oldList[i].get(); continue; } // Check if this change is between stable nodes. If it is, consider it as "modified". if (!m_unusedNodesMap.contains(oldList[i]->m_sha1) && (!i || oldMap[i - 1].first) && (i == oldMap.size() - 1 || oldMap[i + 1].first)) { size_t anchorCandidate = i ? oldMap[i - 1].second + 1 : 0; size_t anchorAfter = (i == oldMap.size() - 1) ? anchorCandidate + 1 : oldMap[i + 1].second; if (anchorAfter - anchorCandidate == 1 && anchorCandidate < newList.size()) merges.set(newList[anchorCandidate].get(), oldList[i].get()); else { if (!removeChildAndMoveToNew(oldList[i].get(), exceptionState)) return false; } } else { if (!removeChildAndMoveToNew(oldList[i].get(), exceptionState)) return false; } } // Mark retained nodes as used, do not reuse node more than once. HashSet<size_t, WTF::IntHash<size_t>, WTF::UnsignedWithZeroKeyHashTraits<size_t> > usedOldOrdinals; for (size_t i = 0; i < newList.size(); ++i) { if (!newMap[i].first) continue; size_t oldOrdinal = newMap[i].second; if (usedOldOrdinals.contains(oldOrdinal)) { // Do not map node more than once newMap[i].first = 0; newMap[i].second = 0; continue; } usedOldOrdinals.add(oldOrdinal); markNodeAsUsed(newMap[i].first); } // Mark <head> and <body> nodes for merge. if (oldHead || oldBody) { for (size_t i = 0; i < newList.size(); ++i) { if (oldHead && isHTMLHeadElement(*newList[i]->m_node)) merges.set(newList[i].get(), oldHead); if (oldBody && isHTMLBodyElement(*newList[i]->m_node)) merges.set(newList[i].get(), oldBody); } } // 2. Patch nodes marked for merge. for (HashMap<Digest*, Digest*>::iterator it = merges.begin(); it != merges.end(); ++it) { if (!innerPatchNode(it->value, it->key, exceptionState)) return false; } // 3. Insert missing nodes. for (size_t i = 0; i < newMap.size(); ++i) { if (newMap[i].first || merges.contains(newList[i].get())) continue; if (!insertBeforeAndMarkAsUsed(parentNode, newList[i].get(), parentNode->traverseToChildAt(i), exceptionState)) return false; } // 4. Then put all nodes that retained into their slots (sort by new index). for (size_t i = 0; i < oldMap.size(); ++i) { if (!oldMap[i].first) continue; RefPtrWillBeRawPtr<Node> node = oldMap[i].first->m_node; Node* anchorNode = parentNode->traverseToChildAt(oldMap[i].second); if (node == anchorNode) continue; if (isHTMLBodyElement(*node) || isHTMLHeadElement(*node)) continue; // Never move head or body, move the rest of the nodes around them. if (!m_domEditor->insertBefore(parentNode, node.release(), anchorNode, exceptionState)) return false; } return true; }
void SamplingTool::dump(ExecState* exec) { // Tidies up SunSpider output by removing short scripts - such a small number of samples would likely not be useful anyhow. if (m_sampleCount < 10) return; // (1) Build and sort 'opcodeSampleInfo' array. OpcodeSampleInfo opcodeSampleInfo[numOpcodeIDs]; for (int i = 0; i < numOpcodeIDs; ++i) { opcodeSampleInfo[i].opcode = static_cast<OpcodeID>(i); opcodeSampleInfo[i].count = m_opcodeSamples[i]; opcodeSampleInfo[i].countInCTIFunctions = m_opcodeSamplesInCTIFunctions[i]; } qsort(opcodeSampleInfo, numOpcodeIDs, sizeof(OpcodeSampleInfo), compareOpcodeIndicesSampling); // (2) Print Opcode sampling results. printf("\nBytecode samples [*]\n"); printf(" sample %% of %% of | cti cti %%\n"); printf("opcode count VM total | count of self\n"); printf("------------------------------------------------------- | ----------------\n"); for (int i = 0; i < numOpcodeIDs; ++i) { long long count = opcodeSampleInfo[i].count; if (!count) continue; OpcodeID opcodeID = opcodeSampleInfo[i].opcode; const char* opcodeName = opcodeNames[opcodeID]; const char* opcodePadding = padOpcodeName(opcodeID, 28); double percentOfVM = (static_cast<double>(count) * 100) / m_opcodeSampleCount; double percentOfTotal = (static_cast<double>(count) * 100) / m_sampleCount; long long countInCTIFunctions = opcodeSampleInfo[i].countInCTIFunctions; double percentInCTIFunctions = (static_cast<double>(countInCTIFunctions) * 100) / count; fprintf(stdout, "%s:%s%-6lld %.3f%%\t%.3f%%\t | %-6lld %.3f%%\n", opcodeName, opcodePadding, count, percentOfVM, percentOfTotal, countInCTIFunctions, percentInCTIFunctions); } printf("\n[*] Samples inside host code are not charged to any Bytecode.\n\n"); printf("\tSamples inside VM:\t\t%lld / %lld (%.3f%%)\n", m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_opcodeSampleCount) * 100) / m_sampleCount); printf("\tSamples inside host code:\t%lld / %lld (%.3f%%)\n\n", m_sampleCount - m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_sampleCount - m_opcodeSampleCount) * 100) / m_sampleCount); printf("\tsample count:\tsamples inside this opcode\n"); printf("\t%% of VM:\tsample count / all opcode samples\n"); printf("\t%% of total:\tsample count / all samples\n"); printf("\t--------------\n"); printf("\tcti count:\tsamples inside a CTI function called by this opcode\n"); printf("\tcti %% of self:\tcti count / sample count\n"); #if ENABLE(CODEBLOCK_SAMPLING) // (3) Build and sort 'codeBlockSamples' array. int scopeCount = m_scopeSampleMap->size(); Vector<ScopeSampleRecord*> codeBlockSamples(scopeCount); ScopeSampleRecordMap::iterator iter = m_scopeSampleMap->begin(); for (int i = 0; i < scopeCount; ++i, ++iter) codeBlockSamples[i] = iter->second; qsort(codeBlockSamples.begin(), scopeCount, sizeof(ScopeSampleRecord*), compareScopeSampleRecords); // (4) Print data from 'codeBlockSamples' array. printf("\nCodeBlock samples\n\n"); for (int i = 0; i < scopeCount; ++i) { ScopeSampleRecord* record = codeBlockSamples[i]; CodeBlock* codeBlock = record->m_codeBlock; double blockPercent = (record->m_sampleCount * 100.0) / m_sampleCount; if (blockPercent >= 1) { //Instruction* code = codeBlock->instructions().begin(); printf("#%d: %s:%d: %d / %lld (%.3f%%)\n", i + 1, record->m_scope->sourceURL().UTF8String().c_str(), codeBlock->lineNumberForBytecodeOffset(exec, 0), record->m_sampleCount, m_sampleCount, blockPercent); if (i < 10) { HashMap<unsigned,unsigned> lineCounts; codeBlock->dump(exec); printf(" Opcode and line number samples [*]\n\n"); for (unsigned op = 0; op < record->m_size; ++op) { int count = record->m_samples[op]; if (count) { printf(" [% 4d] has sample count: % 4d\n", op, count); unsigned line = codeBlock->lineNumberForBytecodeOffset(exec, op); lineCounts.set(line, (lineCounts.contains(line) ? lineCounts.get(line) : 0) + count); } } printf("\n"); int linesCount = lineCounts.size(); Vector<LineCountInfo> lineCountInfo(linesCount); int lineno = 0; for (HashMap<unsigned,unsigned>::iterator iter = lineCounts.begin(); iter != lineCounts.end(); ++iter, ++lineno) { lineCountInfo[lineno].line = iter->first; lineCountInfo[lineno].count = iter->second; } qsort(lineCountInfo.begin(), linesCount, sizeof(LineCountInfo), compareLineCountInfoSampling); for (lineno = 0; lineno < linesCount; ++lineno) { printf(" Line #%d has sample count %d.\n", lineCountInfo[lineno].line, lineCountInfo[lineno].count); } printf("\n"); printf(" [*] Samples inside host code are charged to the calling Bytecode.\n"); printf(" Samples on a call / return boundary are not charged to a specific opcode or line.\n\n"); printf(" Samples on a call / return boundary: %d / %d (%.3f%%)\n\n", record->m_sampleCount - record->m_opcodeSampleCount, record->m_sampleCount, (static_cast<double>(record->m_sampleCount - record->m_opcodeSampleCount) * 100) / record->m_sampleCount); } } } #else UNUSED_PARAM(exec); #endif }
String CSSPrimitiveValue::customSerializeResolvingVariables(const HashMap<AtomicString, String>& variables) const { if (m_primitiveUnitType == CSS_VARIABLE_NAME && variables.contains(m_value.string)) return variables.get(m_value.string); return customCssText(); }
ObjectReference BinaryPropertyListPlan::stringObjectReference(const String& string) const { ASSERT(m_strings.contains(string)); return m_strings.get(string); }
inline CalculationValue& CalculationValueMap::get(unsigned handle) const { ASSERT(m_map.contains(handle)); return *m_map.find(handle)->value.value; }
static void resolveKeyframes(StyleResolver* resolver, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, const AtomicString& name, TimingFunction* defaultTimingFunction, Vector<std::pair<KeyframeEffectModel::KeyframeVector, RefPtr<TimingFunction> > >& keyframesAndTimingFunctions) { ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled()); // When the element is null, use its parent for scoping purposes. const Element* elementForScoping = element ? element : &parentElement; const StyleRuleKeyframes* keyframesRule = CSSAnimations::matchScopedKeyframesRule(resolver, elementForScoping, name.impl()); if (!keyframesRule) return; const Vector<RefPtr<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes(); if (styleKeyframes.isEmpty()) return; // Construct and populate the style for each keyframe PropertySet specifiedProperties; KeyframeEffectModel::KeyframeVector keyframes; HashMap<double, RefPtr<TimingFunction> > perKeyframeTimingFunctions; for (size_t i = 0; i < styleKeyframes.size(); ++i) { const StyleKeyframe* styleKeyframe = styleKeyframes[i].get(); // It's OK to pass a null element here. RefPtr<RenderStyle> keyframeStyle = resolver->styleForKeyframe(element, style, parentStyle, styleKeyframe, name); RefPtr<Keyframe> keyframe = Keyframe::create(); const Vector<double>& offsets = styleKeyframe->keys(); ASSERT(!offsets.isEmpty()); keyframe->setOffset(offsets[0]); TimingFunction* timingFunction = defaultTimingFunction; const StylePropertySet* properties = styleKeyframe->properties(); for (unsigned j = 0; j < properties->propertyCount(); j++) { CSSPropertyID property = properties->propertyAt(j).id(); specifiedProperties.add(property); if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction) timingFunction = KeyframeValue::timingFunction(*keyframeStyle); else if (CSSAnimations::isAnimatableProperty(property)) keyframe->setPropertyValue(property, CSSAnimatableValueFactory::create(property, *keyframeStyle).get()); } keyframes.append(keyframe); // The last keyframe specified at a given offset is used. perKeyframeTimingFunctions.set(offsets[0], timingFunction); for (size_t j = 1; j < offsets.size(); ++j) { keyframes.append(keyframe->cloneWithOffset(offsets[j])); perKeyframeTimingFunctions.set(offsets[j], timingFunction); } } ASSERT(!keyframes.isEmpty()); if (!perKeyframeTimingFunctions.contains(0)) perKeyframeTimingFunctions.set(0, defaultTimingFunction); for (PropertySet::const_iterator iter = specifiedProperties.begin(); iter != specifiedProperties.end(); ++iter) { const CSSPropertyID property = *iter; ASSERT(property != CSSPropertyInvalid); blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(property)); } // Remove duplicate keyframes. In CSS the last keyframe at a given offset takes priority. std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffsets); size_t targetIndex = 0; for (size_t i = 1; i < keyframes.size(); i++) { if (keyframes[i]->offset() != keyframes[targetIndex]->offset()) targetIndex++; if (targetIndex != i) keyframes[targetIndex] = keyframes[i]; } keyframes.shrink(targetIndex + 1); // Add 0% and 100% keyframes if absent. RefPtr<Keyframe> startKeyframe = keyframes[0]; if (startKeyframe->offset()) { startKeyframe = Keyframe::create(); startKeyframe->setOffset(0); keyframes.prepend(startKeyframe); } RefPtr<Keyframe> endKeyframe = keyframes[keyframes.size() - 1]; if (endKeyframe->offset() != 1) { endKeyframe = Keyframe::create(); endKeyframe->setOffset(1); keyframes.append(endKeyframe); } ASSERT(keyframes.size() >= 2); ASSERT(!keyframes.first()->offset()); ASSERT(keyframes.last()->offset() == 1); // Snapshot current property values for 0% and 100% if missing. PropertySet allProperties; size_t numKeyframes = keyframes.size(); for (size_t i = 0; i < numKeyframes; i++) { const PropertySet& keyframeProperties = keyframes[i]->properties(); for (PropertySet::const_iterator iter = keyframeProperties.begin(); iter != keyframeProperties.end(); ++iter) allProperties.add(*iter); } const PropertySet& startKeyframeProperties = startKeyframe->properties(); const PropertySet& endKeyframeProperties = endKeyframe->properties(); bool missingStartValues = startKeyframeProperties.size() < allProperties.size(); bool missingEndValues = endKeyframeProperties.size() < allProperties.size(); if (missingStartValues || missingEndValues) { for (PropertySet::const_iterator iter = allProperties.begin(); iter != allProperties.end(); ++iter) { const CSSPropertyID property = *iter; bool startNeedsValue = missingStartValues && !startKeyframeProperties.contains(property); bool endNeedsValue = missingEndValues && !endKeyframeProperties.contains(property); if (!startNeedsValue && !endNeedsValue) continue; RefPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::create(property, style); if (startNeedsValue) startKeyframe->setPropertyValue(property, snapshotValue.get()); if (endNeedsValue) endKeyframe->setPropertyValue(property, snapshotValue.get()); } } ASSERT(startKeyframe->properties().size() == allProperties.size()); ASSERT(endKeyframe->properties().size() == allProperties.size()); // Determine how many keyframes specify each property. Note that this must // be done after we've filled in end keyframes. typedef HashCountedSet<CSSPropertyID> PropertyCountedSet; PropertyCountedSet propertyCounts; for (size_t i = 0; i < numKeyframes; ++i) { const PropertySet& properties = keyframes[i]->properties(); for (PropertySet::const_iterator iter = properties.begin(); iter != properties.end(); ++iter) propertyCounts.add(*iter); } // Split keyframes into groups, where each group contains only keyframes // which specify all properties used in that group. Each group is animated // in a separate animation, to allow per-keyframe timing functions to be // applied correctly. for (PropertyCountedSet::const_iterator iter = propertyCounts.begin(); iter != propertyCounts.end(); ++iter) { const CSSPropertyID property = iter->key; const size_t count = iter->value; ASSERT(count <= numKeyframes); if (count == numKeyframes) continue; KeyframeEffectModel::KeyframeVector splitOutKeyframes; for (size_t i = 0; i < numKeyframes; i++) { Keyframe* keyframe = keyframes[i].get(); if (!keyframe->properties().contains(property)) { ASSERT(i && i != numKeyframes - 1); continue; } RefPtr<Keyframe> clonedKeyframe = Keyframe::create(); clonedKeyframe->setOffset(keyframe->offset()); clonedKeyframe->setComposite(keyframe->composite()); clonedKeyframe->setPropertyValue(property, keyframe->propertyValue(property)); splitOutKeyframes.append(clonedKeyframe); // Note that it's OK if this keyframe ends up having no // properties. This can only happen when none of the properties // are specified in all keyframes, in which case we won't animate // anything with these keyframes. keyframe->clearPropertyValue(property); } ASSERT(!splitOutKeyframes.first()->offset()); ASSERT(splitOutKeyframes.last()->offset() == 1); #ifndef NDEBUG for (size_t j = 0; j < splitOutKeyframes.size(); ++j) ASSERT(splitOutKeyframes[j]->properties().size() == 1); #endif keyframesAndTimingFunctions.append(std::make_pair(splitOutKeyframes, generateTimingFunction(splitOutKeyframes, perKeyframeTimingFunctions))); } unsigned numPropertiesSpecifiedInAllKeyframes = keyframes.first()->properties().size(); #ifndef NDEBUG for (size_t i = 1; i < numKeyframes; ++i) ASSERT(keyframes[i]->properties().size() == numPropertiesSpecifiedInAllKeyframes); #endif // If the animation specifies any keyframes, we always provide at least one // vector of resolved keyframes, even if no properties are animated. if (numPropertiesSpecifiedInAllKeyframes || keyframesAndTimingFunctions.isEmpty()) keyframesAndTimingFunctions.append(std::make_pair(keyframes, generateTimingFunction(keyframes, perKeyframeTimingFunctions))); }
inline void CalculationValueMap::ref(unsigned handle) { ASSERT(m_map.contains(handle)); ++m_map.find(handle)->value.referenceCountMinusOne; }
bool fixSSA(Procedure& proc) { PhaseScope phaseScope(proc, "fixSSA"); // Collect the stack "variables". If there aren't any, then we don't have anything to do. // That's a fairly common case. HashMap<StackSlotValue*, Type> stackVariable; for (Value* value : proc.values()) { if (StackSlotValue* stack = value->as<StackSlotValue>()) { if (stack->kind() == StackSlotKind::Anonymous) stackVariable.add(stack, Void); } } if (stackVariable.isEmpty()) return false; // Make sure that we know how to optimize all of these. We only know how to handle Load and // Store on anonymous variables. for (Value* value : proc.values()) { auto reject = [&] (Value* value) { if (StackSlotValue* stack = value->as<StackSlotValue>()) stackVariable.remove(stack); }; auto handleAccess = [&] (Value* access, Type type) { StackSlotValue* stack = access->lastChild()->as<StackSlotValue>(); if (!stack) return; if (value->as<MemoryValue>()->offset()) { stackVariable.remove(stack); return; } auto result = stackVariable.find(stack); if (result == stackVariable.end()) return; if (result->value == Void) { result->value = type; return; } if (result->value == type) return; stackVariable.remove(result); }; switch (value->opcode()) { case Load: // We're OK with loads from stack variables at an offset of zero. handleAccess(value, value->type()); break; case Store: // We're OK with stores to stack variables, but not storing stack variables. reject(value->child(0)); handleAccess(value, value->child(0)->type()); break; default: for (Value* child : value->children()) reject(child); break; } } Vector<StackSlotValue*> deadValues; for (auto& entry : stackVariable) { if (entry.value == Void) deadValues.append(entry.key); } for (StackSlotValue* deadValue : deadValues) { deadValue->replaceWithNop(); stackVariable.remove(deadValue); } if (stackVariable.isEmpty()) return false; // We know that we have variables to optimize, so do that now. breakCriticalEdges(proc); SSACalculator ssa(proc); // Create a SSACalculator::Variable for every stack variable. Vector<StackSlotValue*> variableToStack; HashMap<StackSlotValue*, SSACalculator::Variable*> stackToVariable; for (auto& entry : stackVariable) { StackSlotValue* stack = entry.key; SSACalculator::Variable* variable = ssa.newVariable(); RELEASE_ASSERT(variable->index() == variableToStack.size()); variableToStack.append(stack); stackToVariable.add(stack, variable); } // Create Defs for all of the stores to the stack variable. for (BasicBlock* block : proc) { for (Value* value : *block) { if (value->opcode() != Store) continue; StackSlotValue* stack = value->child(1)->as<StackSlotValue>(); if (!stack) continue; if (SSACalculator::Variable* variable = stackToVariable.get(stack)) ssa.newDef(variable, block, value->child(0)); } } // Decide where Phis are to be inserted. This creates them but does not insert them. ssa.computePhis( [&] (SSACalculator::Variable* variable, BasicBlock* block) -> Value* { StackSlotValue* stack = variableToStack[variable->index()]; Value* phi = proc.add<Value>(Phi, stackVariable.get(stack), stack->origin()); if (verbose) { dataLog( "Adding Phi for ", pointerDump(stack), " at ", *block, ": ", deepDump(proc, phi), "\n"); } return phi; }); // Now perform the conversion. InsertionSet insertionSet(proc); HashMap<StackSlotValue*, Value*> mapping; for (BasicBlock* block : proc.blocksInPreOrder()) { mapping.clear(); for (auto& entry : stackToVariable) { StackSlotValue* stack = entry.key; SSACalculator::Variable* variable = entry.value; SSACalculator::Def* def = ssa.reachingDefAtHead(block, variable); if (def) mapping.set(stack, def->value()); } for (SSACalculator::Def* phiDef : ssa.phisForBlock(block)) { StackSlotValue* stack = variableToStack[phiDef->variable()->index()]; insertionSet.insertValue(0, phiDef->value()); mapping.set(stack, phiDef->value()); } for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) { Value* value = block->at(valueIndex); value->performSubstitution(); switch (value->opcode()) { case Load: { if (StackSlotValue* stack = value->child(0)->as<StackSlotValue>()) { if (Value* replacement = mapping.get(stack)) value->replaceWithIdentity(replacement); } break; } case Store: { if (StackSlotValue* stack = value->child(1)->as<StackSlotValue>()) { if (stackToVariable.contains(stack)) { mapping.set(stack, value->child(0)); value->replaceWithNop(); } } break; } default: break; } } unsigned upsilonInsertionPoint = block->size() - 1; Origin upsilonOrigin = block->last()->origin(); for (BasicBlock* successorBlock : block->successorBlocks()) { for (SSACalculator::Def* phiDef : ssa.phisForBlock(successorBlock)) { Value* phi = phiDef->value(); SSACalculator::Variable* variable = phiDef->variable(); StackSlotValue* stack = variableToStack[variable->index()]; Value* mappedValue = mapping.get(stack); if (verbose) { dataLog( "Mapped value for ", *stack, " with successor Phi ", *phi, " at end of ", *block, ": ", pointerDump(mappedValue), "\n"); } if (!mappedValue) mappedValue = insertionSet.insertBottom(upsilonInsertionPoint, phi); insertionSet.insert<UpsilonValue>( upsilonInsertionPoint, upsilonOrigin, mappedValue, phi); } } insertionSet.execute(block); } // Finally, kill the stack slots. for (StackSlotValue* stack : variableToStack) stack->replaceWithNop(); if (verbose) { dataLog("B3 after SSA conversion:\n"); dataLog(proc); } return true; }
TEST(TestHashMapContains, addItemCheckContainsReturnTrue) { HashMap Test; Test.add("Ford", "Tang"); ASSERT_EQ(true, Test.contains("Ford")); }