Merge mozilla-central to mozilla-inbound

This commit is contained in:
Daniel Varga 2019-05-21 07:22:34 +03:00
commit f7e7132f8f
500 changed files with 9416 additions and 60089 deletions

96
Cargo.lock generated
View File

@ -736,10 +736,10 @@ dependencies = [
[[package]]
name = "cssparser"
version = "0.25.3"
version = "0.25.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser-macros 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -753,14 +753,14 @@ dependencies = [
[[package]]
name = "cssparser-macros"
version = "0.3.3"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -768,17 +768,17 @@ name = "cstr"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cstr-macros 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cstr-macros 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cstr-macros"
version = "0.1.3"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1204,7 +1204,7 @@ name = "geckoservo"
version = "0.0.1"
dependencies = [
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.5 (registry+https://github.com/rust-lang/crates.io-index)",
"cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1681,7 +1681,7 @@ name = "malloc_size_of"
version = "0.0.1"
dependencies = [
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.5 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)",
"hashglobe 0.1.0",
"selectors 0.21.0",
@ -1846,8 +1846,8 @@ name = "mozilla-central-workspace-hack"
version = "0.1.0"
dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.88 (git+https://github.com/servo/serde?branch=deserialize_from_enums10)",
"syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2018,13 +2018,12 @@ dependencies = [
[[package]]
name = "num-derive"
version = "0.2.2"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -2249,14 +2248,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "prefs_parser"
version = "0.0.1"
[[package]]
name = "proc-macro2"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "0.4.27"
@ -2300,14 +2291,6 @@ name = "quick-error"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.11"
@ -2667,7 +2650,7 @@ name = "selectors"
version = "0.21.0"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.5 (registry+https://github.com/rust-lang/crates.io-index)",
"derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2884,7 +2867,7 @@ dependencies = [
"bindgen 0.49.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.5 (registry+https://github.com/rust-lang/crates.io-index)",
"derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)",
"fallible 0.0.1",
@ -2900,7 +2883,7 @@ dependencies = [
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"nsstring 0.1.0",
"num-derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2946,7 +2929,7 @@ version = "0.0.1"
dependencies = [
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.5 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"malloc_size_of 0.0.1",
@ -2962,7 +2945,7 @@ name = "stylo_tests"
version = "0.0.1"
dependencies = [
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.5 (registry+https://github.com/rust-lang/crates.io-index)",
"cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"geckoservo 0.0.1",
@ -2983,26 +2966,6 @@ name = "svg_fmt"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.15.30"
@ -3137,7 +3100,7 @@ dependencies = [
name = "to_shmem"
version = "0.0.1"
dependencies = [
"cssparser 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.5 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_arc 0.1.1",
"smallbitvec 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3696,8 +3659,9 @@ name = "xpcom_macros"
version = "0.1.0"
dependencies = [
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -3807,10 +3771,10 @@ dependencies = [
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
"checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b"
"checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a"
"checksum cssparser 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ba1ab4e1814be64bf6b6064ff532db0e34087f11b37706d6c96a21d32478761d"
"checksum cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f3a5383ae18dbfdeb569ed62019f5bddb2a95cd2d3833313c475a0d014777805"
"checksum cssparser 0.25.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e06795910fc2f585a75bdc9690fbcc51e83519f07b6eb981db43944643c04933"
"checksum cssparser-macros 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b16e382d9b983fdb9ac6a36b37fdeb84ce3ea81f749febfee3463cfa7f24275e"
"checksum cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b6557bdb1dc9647eae1cf7f5601b14cd45fc3c7ccf2df618387416fe542da6ea"
"checksum cstr-macros 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0472c17c83d3ec1af32fb6ee2b3ad56ae0b6e69355d63d1d30602055c34324a8"
"checksum cstr-macros 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0f12dd847ec773fc98d75edba5394cb87d0f35e7ee548a4c81849ca6374b3d48"
"checksum cubeb 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "db3f0df2ad5cb453126364a77921466ba6c1034e8bd9247f326cdb31430dbc2a"
"checksum cubeb-backend 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "442cd5cfb980ff62730525278ce320d9b2ff635b725857ad3176832664262fec"
"checksum cubeb-core 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0161f9327864922ba7a172c90bd86bc9094938433eca415e2c75629954045022"
@ -3914,7 +3878,7 @@ dependencies = [
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
"checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b"
"checksum nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9c349f68f25f596b9f44cf0e7c69752a5c633b0550c3ff849518bfba0233774a"
"checksum num-derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d2c31b75c36a993d30c7a13d70513cb93f02acafdd5b7ba250f9b0e18615de7"
"checksum num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124"
"checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10"
@ -3943,11 +3907,9 @@ dependencies = [
"checksum png 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9adebf7fb91ccf5eac9da1a8e00e83cb8ae882c3e8d8e4ad59da73cb8c82a2c9"
"checksum podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e5422a1ee1bc57cc47ae717b0137314258138f38fd5f3cea083f43a9725383a0"
"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
"checksum proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "77997c53ae6edd6d187fec07ec41b207063b5ee6f33680e9fa86d405cdd313d4"
"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
"checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
"checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4"
"checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8"
"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd"
@ -4009,8 +3971,6 @@ dependencies = [
"checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum svg_fmt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c666f0fed8e1e20e057af770af9077d72f3d5a33157b8537c1475dd8ffd6d32b"
"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
"checksum syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4b5274d4a0a3d2749d5c158dc64d3403e60554dc61194648787ada5212473d"
"checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b0ab4982b8945c35cc1c46a83a9094c414f6828a099ce5dcaa8ee2b04642dcb"

View File

@ -751,8 +751,8 @@ void NotificationController::WillRefresh(mozilla::TimeStamp aTime) {
MOZ_ASSERT(mDocument->AccessibleOrTrueContainer(containerNode),
"Text node having rendered text hasn't accessible document!");
Accessible* container = mDocument->AccessibleOrTrueContainer(
containerNode, DocAccessible::eNoContainerIfARIAHidden);
Accessible* container =
mDocument->AccessibleOrTrueContainer(containerNode, true);
if (container) {
nsTArray<nsCOMPtr<nsIContent>>* list =
mContentInsertions.LookupOrAdd(container);

View File

@ -23,10 +23,10 @@ namespace mozilla {
namespace a11y {
inline Accessible* DocAccessible::AccessibleOrTrueContainer(
nsINode* aNode, int aIgnoreARIAHidden) const {
nsINode* aNode, bool aNoContainerIfPruned) const {
// HTML comboboxes have no-content list accessible as an intermediate
// containing all options.
Accessible* container = GetAccessibleOrContainer(aNode, aIgnoreARIAHidden);
Accessible* container = GetAccessibleOrContainer(aNode, aNoContainerIfPruned);
if (container && container->IsHTMLCombobox()) {
return container->FirstChild();
}

View File

@ -1132,8 +1132,8 @@ Accessible* DocAccessible::GetAccessibleByUniqueIDInSubtree(void* aUniqueID) {
return nullptr;
}
Accessible* DocAccessible::GetAccessibleOrContainer(nsINode* aNode,
int aARIAHiddenFlag) const {
Accessible* DocAccessible::GetAccessibleOrContainer(
nsINode* aNode, bool aNoContainerIfPruned) const {
if (!aNode || !aNode->GetComposedDoc()) {
return nullptr;
}
@ -1157,11 +1157,26 @@ Accessible* DocAccessible::GetAccessibleOrContainer(nsINode* aNode,
MOZ_ASSERT(currNode);
for (; currNode; currNode = currNode->GetFlattenedTreeParentNode()) {
// No container if is inside of aria-hidden subtree.
if (aARIAHiddenFlag == eNoContainerIfARIAHidden && currNode->IsElement() &&
if (aNoContainerIfPruned && currNode->IsElement() &&
aria::HasDefinedARIAHidden(currNode->AsElement())) {
return nullptr;
}
// Check if node is in an unselected deck panel
if (aNoContainerIfPruned && currNode->IsXULElement()) {
if (nsIFrame* frame = currNode->AsContent()->GetPrimaryFrame()) {
nsDeckFrame* deckFrame = do_QueryFrame(frame->GetParent());
if (deckFrame && deckFrame->GetSelectedBox() != frame) {
// If deck is not a <tabpanels>, return null
nsIContent* parentFrameContent = deckFrame->GetContent();
if (!parentFrameContent ||
!parentFrameContent->IsXULElement(nsGkAtoms::tabpanels)) {
return nullptr;
}
}
}
}
if (Accessible* accessible = GetAccessible(currNode)) {
return accessible;
}
@ -1666,8 +1681,7 @@ bool InsertIterator::Next() {
// what means there's no container. Ignore the insertion too.
nsIContent* prevNode = mNodes->SafeElementAt(mNodesIdx - 1);
nsIContent* node = mNodes->ElementAt(mNodesIdx++);
Accessible* container = Document()->AccessibleOrTrueContainer(
node, DocAccessible::eNoContainerIfARIAHidden);
Accessible* container = Document()->AccessibleOrTrueContainer(node, true);
if (container != Context()) {
continue;
}

View File

@ -284,11 +284,12 @@ class DocAccessible : public HyperTextAccessibleWrap,
/**
* Return an accessible for the given DOM node or container accessible if
* the node is not accessible.
* the node is not accessible. If aNoContainerIfPruned is true it will return
* null if the node is in a pruned subtree (eg. aria-hidden or unselected deck
* panel)
*/
enum { eIgnoreARIAHidden = 0, eNoContainerIfARIAHidden = 1 };
Accessible* GetAccessibleOrContainer(
nsINode* aNode, int aARIAHiddenFlag = eIgnoreARIAHidden) const;
Accessible* GetAccessibleOrContainer(nsINode* aNode,
bool aNoContainerIfPruned = false) const;
/**
* Return a container accessible for the given DOM node.
@ -302,7 +303,7 @@ class DocAccessible : public HyperTextAccessibleWrap,
* container for it.
*/
Accessible* AccessibleOrTrueContainer(
nsINode* aNode, int aARIAHiddenFlag = eIgnoreARIAHidden) const;
nsINode* aNode, bool aNoContainerIfPruned = false) const;
/**
* Return an accessible for the given node or its first accessible descendant.

View File

@ -64,10 +64,55 @@
}
}
function showDeckPanel(aContainerID, aPanelID)
{
this.container = getAccessible(aContainerID);
this.deckNode = getNode(aPanelID);
var tree =
{ GROUPING: [ // role="group"
{ GROUPING: [ // grouping of panel 2
{ PUSHBUTTON: [] } // push button in panel 2
] }
] };
this.unexpectedEventSeq = [
new invokerChecker(EVENT_REORDER, this.container)
];
this.invoke = function showDeckPanel_invoke()
{
// This stops the refreh driver from doing its regular ticks, and leaves
// us in control. 100 is an arbitrary positive number to advance the clock
// it is not checked or used anywhere.
window.windowUtils.advanceTimeAndRefresh(100);
testAccessibleTree(this.container, tree);
this.deckNode.style.display = "-moz-box";
// This flushes our DOM mutations and forces any pending mutation events.
window.windowUtils.advanceTimeAndRefresh(100);
}
this.finalCheck = function showDeckPanel_finalCheck()
{
testAccessibleTree(this.container, tree);
// Return to regular refresh driver ticks.
window.windowUtils.restoreNormalRefresh();
}
this.getID = function showDeckPanel_getID()
{
return "show deck panel";
}
}
var gQueue = null;
function doTest()
{
gQueue = new eventQueue();
gQueue.push(new showDeckPanel("container", "hidden"));
gQueue.push(new switchDeckPanel("container", "deck"));
gQueue.invoke(); // will call SimpleTest.finish();
}
@ -99,6 +144,7 @@
<groupbox>
<button label="This is the second page"/>
</groupbox>
<hbox id="hidden" style="display: none;"><label>This is the third page</label></hbox>
</deck>
</vbox>

View File

@ -24,8 +24,8 @@
{
this.listboxNode = getNode(aListboxID);
this.listitemNode = document.createElement("richlistitem");
var label = document.createElement("label");
this.listitemNode = document.createXULElement("richlistitem");
var label = document.createXULElement("label");
label.setAttribute("value", "item1");
this.listitemNode.appendChild(label);

View File

@ -60,6 +60,11 @@ static mozilla::LauncherVoidResult PostCreationSetup(
(0x00000001ULL << 60)
#endif // !defined(PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON)
#if !defined(PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF)
# define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF \
(0x00000002ULL << 40)
#endif // !defined(PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF)
#if (_WIN32_WINNT < 0x0602)
BOOL WINAPI
SetProcessMitigationPolicy(PROCESS_MITIGATION_POLICY aMitigationPolicy,
@ -76,6 +81,14 @@ static void SetMitigationPolicies(mozilla::ProcThreadAttributes& aAttrs,
aAttrs.AddMitigationPolicy(
PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON);
}
#if defined(_M_ARM64)
// Disable CFG on older versions of ARM64 Windows to avoid a crash in COM.
if (!mozilla::IsWin10Sep2018UpdateOrLater()) {
aAttrs.AddMitigationPolicy(
PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF);
}
#endif // defined(_M_ARM64)
}
static mozilla::LauncherFlags ProcessCmdLine(int& aArgc, wchar_t* aArgv[]) {

View File

@ -2,7 +2,7 @@
// event.clipboardData.
add_task(async function() {
var textbox = document.createElement("textbox");
var textbox = document.createXULElement("textbox");
document.documentElement.appendChild(textbox);
textbox.focus();

View File

@ -336,7 +336,7 @@ function test_emitLatchedEvents(eventPrefix, initialDelta, cmd) {
}
function test_addCommand(prefName, id) {
let cmd = test_commandset.appendChild(document.createElement("command"));
let cmd = test_commandset.appendChild(document.createXULElement("command"));
cmd.setAttribute("id", id);
cmd.setAttribute("oncommand", "this.callCount++;");

View File

@ -28,10 +28,10 @@ browser.jar:
content/browser/aboutTabCrashed.xhtml (content/aboutTabCrashed.xhtml)
* content/browser/browser.css (content/browser.css)
content/browser/browser.js (content/browser.js)
#ifdef MOZ_BROWSER_XHTML
* content/browser/browser.xhtml (content/browser.xhtml)
#else
#ifdef MOZ_BROWSER_XUL
* content/browser/browser.xul (content/browser.xul)
#else
* content/browser/browser.xhtml (content/browser.xhtml)
#endif
content/browser/browser-addons.js (content/browser-addons.js)
content/browser/browser-allTabsMenu.js (content/browser-allTabsMenu.js)

View File

@ -59,8 +59,8 @@ BROWSER_CHROME_MANIFESTS += [
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
if CONFIG['MOZ_BROWSER_XHTML']:
DEFINES['MOZ_BROWSER_XHTML'] = CONFIG['MOZ_BROWSER_XHTML']
if CONFIG['MOZ_BROWSER_XUL']:
DEFINES['MOZ_BROWSER_XUL'] = CONFIG['MOZ_BROWSER_XUL']
DEFINES['APP_LICENSE_BLOCK'] = '%s/content/overrides/app-license.html' % SRCDIR

View File

@ -2316,11 +2316,20 @@ BrowserGlue.prototype = {
}
},
_migrateXULStoreForDocument(fromURL, toURL) {
Array.from(Services.xulStore.getIDsEnumerator(fromURL)).forEach((id) => {
Array.from(Services.xulStore.getAttributeEnumerator(fromURL, id)).forEach(attr => {
let value = Services.xulStore.getValue(fromURL, id, attr);
Services.xulStore.setValue(toURL, id, attr, value);
});
});
},
// eslint-disable-next-line complexity
_migrateUI: function BG__migrateUI() {
// Use an increasing number to keep track of the current migration state.
// Completely unrelated to the current Firefox release number.
const UI_VERSION = 81;
const UI_VERSION = 82;
const BROWSER_DOCURL = AppConstants.BROWSER_CHROME_URL;
let currentUIVersion;
@ -2624,6 +2633,11 @@ BrowserGlue.prototype = {
}
}
if (currentUIVersion < 82) {
this._migrateXULStoreForDocument("chrome://browser/content/browser.xul",
"chrome://browser/content/browser.xhtml");
}
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
},
@ -3370,16 +3384,16 @@ var DefaultBrowserCheck = {
_createPopup(win, notNowStrings, neverStrings) {
let doc = win.document;
let popup = doc.createElement("menupopup");
let popup = doc.createXULElement("menupopup");
popup.id = this.OPTIONPOPUP;
let notNowItem = doc.createElement("menuitem");
let notNowItem = doc.createXULElement("menuitem");
notNowItem.id = "defaultBrowserNotNow";
notNowItem.setAttribute("label", notNowStrings.label);
notNowItem.setAttribute("accesskey", notNowStrings.accesskey);
popup.appendChild(notNowItem);
let neverItem = doc.createElement("menuitem");
let neverItem = doc.createXULElement("menuitem");
neverItem.id = "defaultBrowserNever";
neverItem.setAttribute("label", neverStrings.label);
neverItem.setAttribute("accesskey", neverStrings.accesskey);

View File

@ -14,8 +14,8 @@ add_task(async function test_about_addons() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:addons", false);
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
ok(content.document.documentURI.startsWith("about:neterror"),
"about:addons should display the net error page");
ok(content.document.documentURI.startsWith("about:neterror?e=blockedByPolicy"),
content.document.documentURI + "should start with about:neterror?e=blockedByPolicy");
// There is currently a testing-specific race condition that causes this test
// to fail, but it is not a problem if we test after the first page load.

View File

@ -12,8 +12,8 @@ add_task(async function test_about_config() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:config", false);
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
ok(content.document.documentURI.startsWith("about:neterror"),
"about:config should display the net error page");
ok(content.document.documentURI.startsWith("about:neterror?e=blockedByPolicy"),
content.document.documentURI + "should start with about:neterror?e=blockedByPolicy");
// There is currently a testing-specific race condition that causes this test
// to fail, but it is not a problem if we test after the first page load.

View File

@ -14,8 +14,8 @@ add_task(async function test_about_profiles() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:profiles", false);
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
ok(content.document.documentURI.startsWith("about:neterror"),
"about:profiles should display the net error page");
ok(content.document.documentURI.startsWith("about:neterror?e=blockedByPolicy"),
content.document.documentURI + "should start with about:neterror?e=blockedByPolicy");
// There is currently a testing-specific race condition that causes this test
// to fail, but it is not a problem if we test after the first page load.

View File

@ -14,8 +14,8 @@ add_task(async function test_about_support() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:support", false);
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
ok(content.document.documentURI.startsWith("about:neterror"),
"about:support should display the net error page");
ok(content.document.documentURI.startsWith("about:neterror?e=blockedByPolicy"),
content.document.documentURI + "should start with about:neterror?e=blockedByPolicy");
// There is currently a testing-specific race condition that causes this test
// to fail, but it is not a problem if we test after the first page load.

View File

@ -217,7 +217,7 @@ var MigrationWizard = { /* exported MigrationWizard */
var sourceProfiles = this.spinResolve(this._migrator.getSourceProfiles());
for (let profile of sourceProfiles) {
var item = document.createElement("radio");
var item = document.createXULElement("radio");
item.id = profile.id;
item.setAttribute("label", profile.name);
profiles.appendChild(item);
@ -258,7 +258,7 @@ var MigrationWizard = { /* exported MigrationWizard */
for (var i = 0; i < 16; ++i) {
var itemID = (items >> i) & 0x1 ? Math.pow(2, i) : 0;
if (itemID > 0) {
var checkbox = document.createElement("checkbox");
var checkbox = document.createXULElement("checkbox");
checkbox.id = itemID;
checkbox.setAttribute("label",
MigrationUtils.getLocalizedString(itemID + "_" + this._source));
@ -344,7 +344,7 @@ var MigrationWizard = { /* exported MigrationWizard */
for (var i = 0; i < 16; ++i) {
itemID = (this._itemsFlags >> i) & 0x1 ? Math.pow(2, i) : 0;
if (itemID > 0) {
var label = document.createElement("label");
var label = document.createXULElement("label");
label.id = itemID + "_migrated";
try {
label.setAttribute("value",

View File

@ -38,10 +38,7 @@ class _BookmarkPanelHub {
this._handleMessageRequest = handleMessageRequest;
this._addImpression = addImpression;
this._dispatch = dispatch;
this._l10n = new DOMLocalization([
"browser/branding/sync-brand.ftl",
"browser/newtab/asrouter.ftl",
]);
this._l10n = new DOMLocalization();
this._initialized = true;
}
@ -92,6 +89,9 @@ class _BookmarkPanelHub {
};
if (response && response.content) {
// Only insert localization files if we need to show a message
win.MozXULElement.insertFTLIfNeeded("browser/newtab/asrouter.ftl");
win.MozXULElement.insertFTLIfNeeded("browser/branding/sync-brand.ftl");
this.showMessage(response.content, target, win);
this.sendImpression();
this.sendUserEventTelemetry("IMPRESSION", win);
@ -127,9 +127,10 @@ class _BookmarkPanelHub {
});
recommendation.style.color = message.color;
recommendation.style.background = `-moz-linear-gradient(-45deg, ${message.background_color_1} 0%, ${message.background_color_2} 70%)`;
const close = createElement("a");
const close = createElement("button");
close.setAttribute("id", "cfrClose");
close.setAttribute("aria-label", "close");
close.style.color = message.color;
this._l10n.setAttributes(close, message.close_button.tooltiptext);
close.addEventListener("click", e => {
this.sendUserEventTelemetry("DISMISS", win);
@ -149,7 +150,6 @@ class _BookmarkPanelHub {
recommendation.appendChild(title);
recommendation.appendChild(content);
recommendation.appendChild(cta);
this._l10n.translateElements([...recommendation.children]);
target.container.appendChild(recommendation);
}

View File

@ -291,7 +291,7 @@ class PageAction {
}
_createElementAndAppend({type, id}, parent) {
let element = this.window.document.createElement(type);
let element = this.window.document.createXULElement(type);
if (id) {
element.setAttribute("id", id);
}
@ -397,12 +397,12 @@ class PageAction {
stepsContainer.remove();
stepsContainer = stepsContainer.cloneNode(false);
} else {
stepsContainer = this.window.document.createElement("vbox");
stepsContainer = this.window.document.createXULElement("vbox");
stepsContainer.setAttribute("id", stepsContainerId);
}
footerText.parentNode.appendChild(stepsContainer);
for (let step of content.descriptionDetails.steps) {
const li = this.window.document.createElement("li");
const li = this.window.document.createXULElement("li");
this._l10n.setAttributes(li, step.string_id);
stepsContainer.appendChild(li);
}

View File

@ -487,7 +487,7 @@ var PlacesOrganizer = {
}
let backupDate = PlacesBackups.getDateForFile(backupFiles[i]);
let m = restorePopup.insertBefore(document.createElement("menuitem"),
let m = restorePopup.insertBefore(document.createXULElement("menuitem"),
document.getElementById("restoreFromFile"));
m.setAttribute("label", dateFormatter.format(backupDate) + sizeInfo);
m.setAttribute("value", OS.Path.basename(backupFiles[i]));
@ -496,7 +496,7 @@ var PlacesOrganizer = {
}
// Add the restoreFromFile item.
restorePopup.insertBefore(document.createElement("menuseparator"),
restorePopup.insertBefore(document.createXULElement("menuseparator"),
document.getElementById("restoreFromFile"));
})();
},
@ -972,7 +972,7 @@ var ViewMenu = {
var columns = content.columns;
for (var i = 0; i < columns.count; ++i) {
var column = columns.getColumnAt(i).element;
var menuitem = document.createElement("menuitem");
var menuitem = document.createXULElement("menuitem");
menuitem.id = "menucol_" + column.id;
menuitem.column = column;
var label = column.getAttribute("label");

View File

@ -170,11 +170,11 @@ class OrderedListBox {
}
createItem({id, label, value}) {
let listitem = document.createElement("richlistitem");
let listitem = document.createXULElement("richlistitem");
listitem.id = id;
listitem.setAttribute("value", value);
let labelEl = document.createElement("label");
let labelEl = document.createXULElement("label");
labelEl.textContent = label;
listitem.appendChild(labelEl);
@ -249,7 +249,7 @@ class SortedItemSelectList {
}
createItem({label, value, className, disabled}) {
let item = document.createElement("menuitem");
let item = document.createXULElement("menuitem");
item.setAttribute("label", label);
if (value)
item.value = value;

View File

@ -1,3 +1,9 @@
# This file is used by all AArch64 Win64 builds
ac_add_options --target=aarch64-windows-mingw32
# Temporary signal to toolchain.configure that our compiler is patched to
# support CFG, until such support can be assumed.
if test -z "$USE_ARTIFACT"; then
ac_add_options --enable-hardening
fi

View File

@ -31,10 +31,10 @@ fi
# Enable building ./signmar and running libmar signature tests
MOZ_ENABLE_SIGNMAR=1
if [ "${MOZ_BROWSER_XHTML}" = "1" ]; then
BROWSER_CHROME_URL=chrome://browser/content/browser.xhtml
else
if [ "${MOZ_BROWSER_XUL}" = "1" ]; then
BROWSER_CHROME_URL=chrome://browser/content/browser.xul
else
BROWSER_CHROME_URL=chrome://browser/content/browser.xhtml
fi
# MOZ_APP_DISPLAYNAME will be set by branding/configure.sh

View File

@ -99,13 +99,20 @@
text-align: start;
}
#editBookmarkPanelRecommendationCta:hover {
text-decoration: underline;
}
#editBookmarkPanelRecommendation #cfrClose {
position: absolute;
padding: 3px 2px;
inset-inline-end: 16px;
padding: 10px;
inset-inline-end: 8px;
top: 15px;
width: 12px;
height: 12px;
border: none;
border-radius: var(--toolbarbutton-border-radius);
background-color: transparent;
background-image: url(chrome://browser/skin/stop.svg);
background-size: 12px;
background-repeat: no-repeat;
@ -113,10 +120,17 @@
-moz-context-properties: fill, fill-opacity;
fill: currentColor;
fill-opacity: 0.6;
cursor: pointer;
}
#editBookmarkPanelRecommendation #cfrClose:hover {
fill-opacity: 1;
background-color: var(--toolbarbutton-hover-background);
}
#editBookmarkPanelRecommendation #cfrClose:active,
#editBookmarkPanelRecommendation #cfrClose:focus {
box-shadow: var(--focus-ring-box-shadow);
}
html|div#editBookmarkPanelFaviconContainer {

View File

@ -43,14 +43,6 @@
cursor: default;
}
.textbox-search-clear:not([disabled]):hover {
background-position: -16px 0;
}
.textbox-search-clear:not([disabled]):hover:active {
background-position: -32px 0;
}
.client .item.tab > .item-title-container {
padding-inline-start: 26px;
}

26
build/build-clang/clang-8-mingw.json Normal file → Executable file
View File

@ -1,20 +1,32 @@
{
"llvm_revision": "348363",
"llvm_revision": "356265",
"stages": "3",
"build_libcxx": true,
"build_type": "Release",
"assertions": false,
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
"lld_repo": "https://llvm.org/svn/llvm-project/lld/trunk",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
"libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk",
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_800/final",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_800/final",
"lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_800/final",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_800/final",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_800/final",
"libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_800/final",
"python_path": "/usr/bin/python2.7",
"gcc_dir": "/builds/worker/workspace/build/src/gcc",
"cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
"cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
"as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
"patches": [
"mingwclang-llvm-objcopy-COFF-Remove-a-superfluous-namespace-qua.patch",
"mingwclang-llvm-objcopy-COFF-Add-support-for-removing-sections.patch",
"mingwclang-llvm-objcopy-COFF-Implement-strip-debug.patch",
"mingwclang-llvm-objcopy-COFF-Implement-only-keep-debug.patch",
"mingwclang-llvm-objcopy-COFF-Implement-only-section.patch",
"mingwclang-llvm-objcopy-Consistently-use-createStringError-inst.patch",
"mingwclang-llvm-objcopy-COFF-Update-symbol-indices-in-weak-exte.patch",
"mingwclang-llvm-objcopy-Return-Error-from-Buffer-allocate-ELF-W.patch",
"mingwclang-Reapply-llvm-objcopy-COFF-Implement-add-gnu-debuglin.patch",
"mingwclang-llvm-objcopy-COFF-Clear-the-unwritten-tail-of-coff_s.patch",
"mingwclang-llvm-objcopy-COFF-Fix-handling-of-aux-symbols-for-bi.patch",
"mingwclang-llvm-objcopy-COFF-Error-out-on-use-of-unhandled-opti.patch"
]
}

View File

@ -17,6 +17,7 @@
"workaround-issue38586.patch",
"unpoison-thread-stacks.patch",
"downgrade-mangling-error.patch",
"r355141-arm64-cfg.patch",
"loosen-msvc-detection.patch",
"revert-r355311.patch"
]

View File

@ -0,0 +1,260 @@
From 840d70f854a1d550924ced1d00160efcc7b8549a Mon Sep 17 00:00:00 2001
From: Martin Storsjo <martin@martin.st>
Date: Wed, 23 Jan 2019 08:25:28 +0000
Subject: [PATCH] Reapply: [llvm-objcopy] [COFF] Implement --add-gnu-debuglink
This was reverted since it broke a couple buildbots. The reason
for the breakage is not yet known, but this time, the test has
got more diagnostics added, to hopefully allow figuring out
what goes wrong.
Differential Revision: https://reviews.llvm.org/D57007
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351931 91177308-0d34-0410-b5e6-96231b3b80d8
---
.../llvm-objcopy/COFF/add-gnu-debuglink.test | 48 +++++++++++++++
tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 61 +++++++++++++++++++
tools/llvm-objcopy/COFF/Object.cpp | 2 +-
tools/llvm-objcopy/COFF/Object.h | 26 +++++++-
tools/llvm-objcopy/COFF/Reader.cpp | 4 +-
tools/llvm-objcopy/COFF/Writer.cpp | 9 +--
6 files changed, 143 insertions(+), 7 deletions(-)
create mode 100644 test/tools/llvm-objcopy/COFF/add-gnu-debuglink.test
diff --git a/llvm/test/tools/llvm-objcopy/COFF/add-gnu-debuglink.test b/llvm/test/tools/llvm-objcopy/COFF/add-gnu-debuglink.test
new file mode 100644
index 00000000000..cf3a9bba920
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/COFF/add-gnu-debuglink.test
@@ -0,0 +1,48 @@
+RUN: yaml2obj %p/Inputs/x86_64-exe.yaml > %t.in123.exe
+
+# Using a debuglink filename with a length that is a multiple of 4, to
+# showcase padding in CONTENTS below.
+
+RUN: llvm-objcopy --add-gnu-debuglink=%t.in123.exe %t.in123.exe %t.out.exe
+
+# Temporary debugging of issues with this test:
+RUN: ls -l %t.out.exe || true
+RUN: od -Ax -t x1 %t.out.exe || true
+RUN: llvm-readobj -sections %t.out.exe || true
+
+RUN: llvm-readobj -sections %t.out.exe | FileCheck %s --check-prefix=SECTIONS
+RUN: llvm-objdump -s %t.out.exe | FileCheck %s --check-prefix=CONTENTS
+
+# Show the last of the preexisting sections, which is used for choosing
+# a virtual address for the generated one.
+
+SECTIONS: Section {
+SECTIONS: Number: 4
+SECTIONS-NEXT: Name: .pdata
+SECTIONS-NEXT: VirtualSize: 0x18
+SECTIONS-NEXT: VirtualAddress: 0x4000
+SECTIONS-NEXT: RawDataSize: 512
+SECTIONS: Section {
+SECTIONS-NEXT: Number: 5
+SECTIONS-NEXT: Name: .gnu_debuglink
+SECTIONS-NEXT: VirtualSize: 0x2C
+SECTIONS-NEXT: VirtualAddress: 0x5000
+SECTIONS-NEXT: RawDataSize: 512
+SECTIONS-NEXT: PointerToRawData:
+SECTIONS-NEXT: PointerToRelocations:
+SECTIONS-NEXT: PointerToLineNumbers:
+SECTIONS-NEXT: RelocationCount:
+SECTIONS-NEXT: LineNumberCount:
+SECTIONS-NEXT: Characteristics [ (0x42000040)
+SECTIONS-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
+SECTIONS-NEXT: IMAGE_SCN_MEM_DISCARDABLE (0x2000000)
+SECTIONS-NEXT: IMAGE_SCN_MEM_READ (0x40000000)
+SECTIONS-NEXT: ]
+
+# Note: The last 4 bytes here are the crc of the referenced file - if the
+# yaml2obj generated file changes, this crc changes.
+
+CONTENTS: Contents of section .gnu_debuglink:
+CONTENTS: 40005000 6164642d 676e752d 64656275 676c696e add-gnu-debuglin
+CONTENTS: 40005010 6b2e7465 73742e74 6d702e69 6e313233 k.test.tmp.in123
+CONTENTS: 40005020 2e657865 00000000 7929adc3 .exe
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index 8d8f53d13d8..20adbe11e7a 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -17,6 +17,8 @@
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Errc.h"
+#include "llvm/Support/JamCRC.h"
+#include "llvm/Support/Path.h"
#include <cassert>
namespace llvm {
@@ -30,6 +32,61 @@ static bool isDebugSection(const Section &Sec) {
return Sec.Name.startswith(".debug");
}
+static uint64_t getNextRVA(const Object &Obj) {
+ if (Obj.getSections().empty())
+ return 0;
+ const Section &Last = Obj.getSections().back();
+ return alignTo(Last.Header.VirtualAddress + Last.Header.VirtualSize,
+ Obj.PeHeader.SectionAlignment);
+}
+
+static uint32_t getCRC32(StringRef Data) {
+ JamCRC CRC;
+ CRC.update(ArrayRef<char>(Data.data(), Data.size()));
+ // The CRC32 value needs to be complemented because the JamCRC dosn't
+ // finalize the CRC32 value. It also dosn't negate the initial CRC32 value
+ // but it starts by default at 0xFFFFFFFF which is the complement of zero.
+ return ~CRC.getCRC();
+}
+
+static std::vector<uint8_t> createGnuDebugLinkSectionContents(StringRef File) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> LinkTargetOrErr =
+ MemoryBuffer::getFile(File);
+ if (!LinkTargetOrErr)
+ error("'" + File + "': " + LinkTargetOrErr.getError().message());
+ auto LinkTarget = std::move(*LinkTargetOrErr);
+ uint32_t CRC32 = getCRC32(LinkTarget->getBuffer());
+
+ StringRef FileName = sys::path::filename(File);
+ size_t CRCPos = alignTo(FileName.size() + 1, 4);
+ std::vector<uint8_t> Data(CRCPos + 4);
+ memcpy(Data.data(), FileName.data(), FileName.size());
+ support::endian::write32le(Data.data() + CRCPos, CRC32);
+ return Data;
+}
+
+static void addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) {
+ uint32_t StartRVA = getNextRVA(Obj);
+
+ std::vector<Section> Sections;
+ Section Sec;
+ Sec.setOwnedContents(createGnuDebugLinkSectionContents(DebugLinkFile));
+ Sec.Name = ".gnu_debuglink";
+ Sec.Header.VirtualSize = Sec.getContents().size();
+ Sec.Header.VirtualAddress = StartRVA;
+ Sec.Header.SizeOfRawData =
+ alignTo(Sec.Header.VirtualSize, Obj.PeHeader.FileAlignment);
+ // Sec.Header.PointerToRawData is filled in by the writer.
+ Sec.Header.PointerToRelocations = 0;
+ Sec.Header.PointerToLinenumbers = 0;
+ // Sec.Header.NumberOfRelocations is filled in by the writer.
+ Sec.Header.NumberOfLinenumbers = 0;
+ Sec.Header.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA |
+ IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE;
+ Sections.push_back(Sec);
+ Obj.addSections(Sections);
+}
+
static Error handleArgs(const CopyConfig &Config, Object &Obj) {
// Perform the actual section removals.
Obj.removeSections([&Config](const Section &Sec) {
@@ -109,6 +166,10 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
return false;
});
+
+ if (!Config.AddGnuDebugLink.empty())
+ addGnuDebugLink(Obj, Config.AddGnuDebugLink);
+
return Error::success();
}
diff --git a/llvm/tools/llvm-objcopy/COFF/Object.cpp b/llvm/tools/llvm-objcopy/COFF/Object.cpp
index 83435dffa98..8c382c1faef 100644
--- a/llvm/tools/llvm-objcopy/COFF/Object.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Object.cpp
@@ -129,7 +129,7 @@ void Object::removeSections(function_ref<bool(const Section &)> ToRemove) {
void Object::truncateSections(function_ref<bool(const Section &)> ToTruncate) {
for (Section &Sec : Sections) {
if (ToTruncate(Sec)) {
- Sec.Contents = ArrayRef<uint8_t>();
+ Sec.clearContents();
Sec.Relocs.clear();
Sec.Header.SizeOfRawData = 0;
}
diff --git a/llvm/tools/llvm-objcopy/COFF/Object.h b/llvm/tools/llvm-objcopy/COFF/Object.h
index 0630f9c5ff8..afa272286ef 100644
--- a/llvm/tools/llvm-objcopy/COFF/Object.h
+++ b/llvm/tools/llvm-objcopy/COFF/Object.h
@@ -35,11 +35,35 @@ struct Relocation {
struct Section {
object::coff_section Header;
- ArrayRef<uint8_t> Contents;
std::vector<Relocation> Relocs;
StringRef Name;
ssize_t UniqueId;
size_t Index;
+
+ ArrayRef<uint8_t> getContents() const {
+ if (!OwnedContents.empty())
+ return OwnedContents;
+ return ContentsRef;
+ }
+
+ void setContentsRef(ArrayRef<uint8_t> Data) {
+ OwnedContents.clear();
+ ContentsRef = Data;
+ }
+
+ void setOwnedContents(std::vector<uint8_t> &&Data) {
+ ContentsRef = ArrayRef<uint8_t>();
+ OwnedContents = std::move(Data);
+ }
+
+ void clearContents() {
+ ContentsRef = ArrayRef<uint8_t>();
+ OwnedContents.clear();
+ }
+
+private:
+ ArrayRef<uint8_t> ContentsRef;
+ std::vector<uint8_t> OwnedContents;
};
struct Symbol {
diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.cpp b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
index 2446277cc2b..87dd60a43cf 100644
--- a/llvm/tools/llvm-objcopy/COFF/Reader.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
@@ -69,8 +69,10 @@ Error COFFReader::readSections(Object &Obj) const {
Sections.push_back(Section());
Section &S = Sections.back();
S.Header = *Sec;
- if (auto EC = COFFObj.getSectionContents(Sec, S.Contents))
+ ArrayRef<uint8_t> Contents;
+ if (auto EC = COFFObj.getSectionContents(Sec, Contents))
return errorCodeToError(EC);
+ S.setContentsRef(Contents);
ArrayRef<coff_relocation> Relocs = COFFObj.getRelocations(Sec);
for (const coff_relocation &R : Relocs)
S.Relocs.push_back(R);
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
index db3589bb119..05e46291c39 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
@@ -286,14 +286,15 @@ void COFFWriter::writeHeaders(bool IsBigObj) {
void COFFWriter::writeSections() {
for (const auto &S : Obj.getSections()) {
uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData;
- std::copy(S.Contents.begin(), S.Contents.end(), Ptr);
+ ArrayRef<uint8_t> Contents = S.getContents();
+ std::copy(Contents.begin(), Contents.end(), Ptr);
// For executable sections, pad the remainder of the raw data size with
// 0xcc, which is int3 on x86.
if ((S.Header.Characteristics & IMAGE_SCN_CNT_CODE) &&
- S.Header.SizeOfRawData > S.Contents.size())
- memset(Ptr + S.Contents.size(), 0xcc,
- S.Header.SizeOfRawData - S.Contents.size());
+ S.Header.SizeOfRawData > Contents.size())
+ memset(Ptr + Contents.size(), 0xcc,
+ S.Header.SizeOfRawData - Contents.size());
Ptr += S.Header.SizeOfRawData;
for (const auto &R : S.Relocs) {
--
2.17.1

View File

@ -0,0 +1,665 @@
From 2ccafacb7ddd740054dbee06655749ebc55a4d86 Mon Sep 17 00:00:00 2001
From: Martin Storsjo <martin@martin.st>
Date: Sat, 19 Jan 2019 19:42:35 +0000
Subject: [PATCH] [llvm-objcopy] [COFF] Add support for removing sections
Differential Revision: https://reviews.llvm.org/D56683
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351660 91177308-0d34-0410-b5e6-96231b3b80d8
---
.../llvm-objcopy/COFF/remove-section.test | 210 ++++++++++++++++++
tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 10 +-
tools/llvm-objcopy/COFF/Object.cpp | 63 ++++++
tools/llvm-objcopy/COFF/Object.h | 27 ++-
tools/llvm-objcopy/COFF/Reader.cpp | 31 ++-
tools/llvm-objcopy/COFF/Writer.cpp | 68 ++++--
tools/llvm-objcopy/COFF/Writer.h | 1 +
7 files changed, 391 insertions(+), 19 deletions(-)
create mode 100644 test/tools/llvm-objcopy/COFF/remove-section.test
diff --git a/llvm/test/tools/llvm-objcopy/COFF/remove-section.test b/llvm/test/tools/llvm-objcopy/COFF/remove-section.test
new file mode 100644
index 00000000000..b3dfb0b98cb
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/COFF/remove-section.test
@@ -0,0 +1,210 @@
+# RUN: yaml2obj %s > %t.in.o
+#
+# RUN: llvm-objdump -section-headers %t.in.o | FileCheck %s --check-prefixes=SECTIONS-PRE
+# RUN: llvm-objdump -t %t.in.o | FileCheck %s --check-prefixes=SYMBOLS-PRE
+#
+# RUN: llvm-objcopy -R .bss %t.in.o %t.remove-bss.o
+# RUN: llvm-objdump -section-headers %t.remove-bss.o | FileCheck %s --check-prefix=SECTIONS-REMOVE-BSS
+# RUN: llvm-objdump -t %t.remove-bss.o | FileCheck %s --check-prefix=SYMBOLS-REMOVE-BSS
+#
+# RUN: llvm-objcopy --remove-section .bss %t.in.o %t.cmp.o
+# RUN: cmp %t.remove-bss.o %t.cmp.o
+#
+# RUN: llvm-objcopy -R .text %t.in.o %t.remove-text.o
+# RUN: llvm-objdump -section-headers %t.remove-text.o | FileCheck %s --check-prefix=SECTIONS-REMOVE-TEXT
+# RUN: llvm-objdump -t %t.remove-text.o | FileCheck %s --check-prefix=SYMBOLS-REMOVE-TEXT
+#
+# RUN: not llvm-objcopy -R .comdat %t.in.o %t.remove-comdat.o 2>&1 | FileCheck %s --check-prefix=ERROR-RELOC
+#
+# RUN: llvm-objcopy -R .text -R .comdat %t.in.o %t.remove-text-comdat.o
+# RUN: llvm-objdump -section-headers %t.remove-text-comdat.o | FileCheck %s --check-prefix=SECTIONS-REMOVE-TEXT-COMDAT
+# RUN: llvm-objdump -t %t.remove-text-comdat.o | FileCheck %s --check-prefix=SYMBOLS-REMOVE-TEXT-COMDAT
+#
+#
+# SECTIONS-PRE: Sections:
+# SECTIONS-PRE-NEXT: Idx Name
+# SECTIONS-PRE-NEXT: 0 .text
+# SECTIONS-PRE-NEXT: 1 .bss
+# SECTIONS-PRE-NEXT: 2 .comdat
+# SECTIONS-PRE-NEXT: 3 .associative
+# SECTIONS-PRE-EMPTY:
+#
+# SYMBOLS-PRE: SYMBOL TABLE:
+# SYMBOLS-PRE-NEXT: {{.*}}(sec -1){{.*}} @feat.00
+# SYMBOLS-PRE-NEXT: {{.*}}(sec 1){{.*}} .text
+# SYMBOLS-PRE-NEXT: AUX scnlen {{.*}} assoc 1 comdat 0
+# SYMBOLS-PRE-NEXT: {{.*}}(sec 2){{.*}} .bss
+# SYMBOLS-PRE-NEXT: AUX scnlen {{.*}} assoc 2 comdat 0
+# SYMBOLS-PRE-NEXT: {{.*}}(sec 4){{.*}} .associative
+# SYMBOLS-PRE-NEXT: AUX scnlen {{.*}} assoc 3 comdat 5
+# SYMBOLS-PRE-NEXT: {{.*}}(sec 3){{.*}} .comdat
+# SYMBOLS-PRE-NEXT: AUX scnlen {{.*}} assoc 3 comdat 2
+# SYMBOLS-PRE-NEXT: {{.*}}(sec 3){{.*}} foo
+# SYMBOLS-PRE-NEXT: {{.*}}(sec 1){{.*}} main
+# SYMBOLS-PRE-EMPTY:
+#
+#
+# Removing the .bss section removes one symbol and its aux symbol,
+# and updates the section indices in symbols pointing to later
+# symbols, including the aux section defintitions.
+#
+# Testing that the absolute symbol @feat.00 survives the section number
+# mangling.
+#
+# SECTIONS-REMOVE-BSS: Sections:
+# SECTIONS-REMOVE-BSS-NEXT: Idx Name
+# SECTIONS-REMOVE-BSS-NEXT: 0 .text
+# SECTIONS-REMOVE-BSS-NEXT: 1 .comdat
+# SECTIONS-REMOVE-BSS-NEXT: 2 .associative
+# SECTIONS-REMOVE-BSS-EMPTY:
+#
+# SYMBOLS-REMOVE-BSS: SYMBOL TABLE:
+# SYMBOLS-REMOVE-BSS-NEXT: {{.*}}(sec -1){{.*}} @feat.00
+# SYMBOLS-REMOVE-BSS-NEXT: {{.*}}(sec 1){{.*}} .text
+# SYMBOLS-REMOVE-BSS-NEXT: AUX scnlen {{.*}} assoc 1 comdat 0
+# SYMBOLS-REMOVE-BSS-NEXT: {{.*}}(sec 3){{.*}} .associative
+# SYMBOLS-REMOVE-BSS-NEXT: AUX scnlen {{.*}} assoc 2 comdat 5
+# SYMBOLS-REMOVE-BSS-NEXT: {{.*}}(sec 2){{.*}} .comdat
+# SYMBOLS-REMOVE-BSS-NEXT: AUX scnlen {{.*}} assoc 2 comdat 2
+# SYMBOLS-REMOVE-BSS-NEXT: {{.*}}(sec 2){{.*}} foo
+# SYMBOLS-REMOVE-BSS-NEXT: {{.*}}(sec 1){{.*}} main
+# SYMBOLS-REMOVE-BSS-EMPTY:
+#
+#
+# Removing the .text section is ok and just removes the external symbol
+# referring to it.
+#
+# SECTIONS-REMOVE-TEXT: Sections:
+# SECTIONS-REMOVE-TEXT-NEXT: Idx Name
+# SECTIONS-REMOVE-TEXT-NEXT: 0 .bss
+# SECTIONS-REMOVE-TEXT-NEXT: 1 .comdat
+# SECTIONS-REMOVE-TEXT-NEXT: 2 .associative
+# SECTIONS-REMOVE-TEXT-EMPTY:
+#
+# SYMBOLS-REMOVE-TEXT: SYMBOL TABLE:
+# SYMBOLS-REMOVE-TEXT-NEXT: {{.*}}(sec -1){{.*}} @feat.00
+# SYMBOLS-REMOVE-TEXT-NEXT: {{.*}}(sec 1){{.*}} .bss
+# SYMBOLS-REMOVE-TEXT-NEXT: AUX scnlen {{.*}} assoc 1 comdat 0
+# SYMBOLS-REMOVE-TEXT-NEXT: {{.*}}(sec 3){{.*}} .associative
+# SYMBOLS-REMOVE-TEXT-NEXT: AUX scnlen {{.*}} assoc 2 comdat 5
+# SYMBOLS-REMOVE-TEXT-NEXT: {{.*}}(sec 2){{.*}} .comdat
+# SYMBOLS-REMOVE-TEXT-NEXT: AUX scnlen {{.*}} assoc 2 comdat 2
+# SYMBOLS-REMOVE-TEXT-NEXT: {{.*}}(sec 2){{.*}} foo
+# SYMBOLS-REMOVE-TEXT-EMPTY:
+#
+#
+# Removing the .comdat section fails, since the .text section has relocations
+# against it.
+#
+# ERROR-RELOC: Relocation target foo ({{.*}}) not found
+#
+#
+# Removing the .comdat section and .text (with a relocation against .comdat)
+# works, as it also removes the .associative section transitively.
+#
+# SECTIONS-REMOVE-TEXT-COMDAT: Sections:
+# SECTIONS-REMOVE-TEXT-COMDAT-NEXT: Idx Name
+# SECTIONS-REMOVE-TEXT-COMDAT-NEXT: 0 .bss
+# SECTIONS-REMOVE-TEXT-COMDAT-EMPTY:
+#
+# SYMBOLS-REMOVE-TEXT-COMDAT: SYMBOL TABLE:
+# SYMBOLS-REMOVE-TEXT-COMDAT-NEXT: {{.*}}(sec -1){{.*}} @feat.00
+# SYMBOLS-REMOVE-TEXT-COMDAT-NEXT: {{.*}}(sec 1){{.*}} .bss
+# SYMBOLS-REMOVE-TEXT-COMDAT-NEXT: AUX scnlen {{.*}} assoc 1 comdat 0
+# SYMBOLS-REMOVE-TEXT-COMDAT-EMPTY:
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ ]
+ Alignment: 4
+ SectionData: 488B0500000000C3
+ Relocations:
+ - VirtualAddress: 3
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: .bss
+ Characteristics: [ ]
+ Alignment: 4
+ SectionData: ''
+ - Name: .comdat
+ Characteristics: [ IMAGE_SCN_LNK_COMDAT ]
+ Alignment: 1
+ SectionData: '2A000000'
+ - Name: .associative
+ Characteristics: [ IMAGE_SCN_LNK_COMDAT ]
+ Alignment: 1
+ SectionData: '0000000000000000'
+symbols:
+ - Name: '@feat.00'
+ Value: 0
+ SectionNumber: -1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 583624169
+ Number: 1
+ - Name: .bss
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 2
+ - Name: .associative
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 3
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: .comdat
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 4
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 3482275674
+ Number: 3
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: foo
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: main
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index 437dccbd3d5..dd2e4829218 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -27,9 +27,17 @@ using namespace object;
using namespace COFF;
static Error handleArgs(const CopyConfig &Config, Object &Obj) {
+ // Perform the actual section removals.
+ Obj.removeSections([&Config](const Section &Sec) {
+ if (is_contained(Config.ToRemove, Sec.Name))
+ return true;
+
+ return false;
+ });
+
// StripAll removes all symbols and thus also removes all relocations.
if (Config.StripAll || Config.StripAllGNU)
- for (Section &Sec : Obj.Sections)
+ for (Section &Sec : Obj.getMutableSections())
Sec.Relocs.clear();
// If we need to do per-symbol removals, initialize the Referenced field.
diff --git a/llvm/tools/llvm-objcopy/COFF/Object.cpp b/llvm/tools/llvm-objcopy/COFF/Object.cpp
index e58e161e7d2..e19cea6aa9d 100644
--- a/llvm/tools/llvm-objcopy/COFF/Object.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Object.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "Object.h"
+#include "llvm/ADT/DenseSet.h"
#include <algorithm>
namespace llvm {
@@ -64,6 +65,68 @@ Error Object::markSymbols() {
return Error::success();
}
+void Object::addSections(ArrayRef<Section> NewSections) {
+ for (Section S : NewSections) {
+ S.UniqueId = NextSectionUniqueId++;
+ Sections.emplace_back(S);
+ }
+ updateSections();
+}
+
+void Object::updateSections() {
+ SectionMap = DenseMap<ssize_t, Section *>(Sections.size());
+ size_t Index = 1;
+ for (Section &S : Sections) {
+ SectionMap[S.UniqueId] = &S;
+ S.Index = Index++;
+ }
+}
+
+const Section *Object::findSection(ssize_t UniqueId) const {
+ auto It = SectionMap.find(UniqueId);
+ if (It == SectionMap.end())
+ return nullptr;
+ return It->second;
+}
+
+void Object::removeSections(function_ref<bool(const Section &)> ToRemove) {
+ DenseSet<ssize_t> AssociatedSections;
+ auto RemoveAssociated = [&AssociatedSections](const Section &Sec) {
+ return AssociatedSections.count(Sec.UniqueId) == 1;
+ };
+ do {
+ DenseSet<ssize_t> RemovedSections;
+ Sections.erase(
+ std::remove_if(std::begin(Sections), std::end(Sections),
+ [ToRemove, &RemovedSections](const Section &Sec) {
+ bool Remove = ToRemove(Sec);
+ if (Remove)
+ RemovedSections.insert(Sec.UniqueId);
+ return Remove;
+ }),
+ std::end(Sections));
+ // Remove all symbols referring to the removed sections.
+ AssociatedSections.clear();
+ Symbols.erase(
+ std::remove_if(
+ std::begin(Symbols), std::end(Symbols),
+ [&RemovedSections, &AssociatedSections](const Symbol &Sym) {
+ // If there are sections that are associative to a removed
+ // section,
+ // remove those as well as nothing will include them (and we can't
+ // leave them dangling).
+ if (RemovedSections.count(Sym.AssociativeComdatTargetSectionId) ==
+ 1)
+ AssociatedSections.insert(Sym.TargetSectionId);
+ return RemovedSections.count(Sym.TargetSectionId) == 1;
+ }),
+ std::end(Symbols));
+ ToRemove = RemoveAssociated;
+ } while (!AssociatedSections.empty());
+ updateSections();
+ updateSymbols();
+}
+
} // end namespace coff
} // end namespace objcopy
} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/COFF/Object.h b/llvm/tools/llvm-objcopy/COFF/Object.h
index e6147c40b7c..a73e93620d3 100644
--- a/llvm/tools/llvm-objcopy/COFF/Object.h
+++ b/llvm/tools/llvm-objcopy/COFF/Object.h
@@ -37,12 +37,16 @@ struct Section {
ArrayRef<uint8_t> Contents;
std::vector<Relocation> Relocs;
StringRef Name;
+ ssize_t UniqueId;
+ size_t Index;
};
struct Symbol {
object::coff_symbol32 Sym;
StringRef Name;
- ArrayRef<uint8_t> AuxData;
+ std::vector<uint8_t> AuxData;
+ ssize_t TargetSectionId;
+ ssize_t AssociativeComdatTargetSectionId = 0;
size_t UniqueId;
size_t RawIndex;
bool Referenced;
@@ -61,7 +65,6 @@ struct Object {
uint32_t BaseOfData = 0; // pe32plus_header lacks this field.
std::vector<object::data_directory> DataDirectories;
- std::vector<Section> Sections;
ArrayRef<Symbol> getSymbols() const { return Symbols; }
// This allows mutating individual Symbols, but not mutating the list
@@ -79,14 +82,34 @@ struct Object {
// all sections.
Error markSymbols();
+ ArrayRef<Section> getSections() const { return Sections; }
+ // This allows mutating individual Sections, but not mutating the list
+ // of symbols itself.
+ iterator_range<std::vector<Section>::iterator> getMutableSections() {
+ return make_range(Sections.begin(), Sections.end());
+ }
+
+ const Section *findSection(ssize_t UniqueId) const;
+
+ void addSections(ArrayRef<Section> NewSections);
+ void removeSections(function_ref<bool(const Section &)> ToRemove);
+
private:
std::vector<Symbol> Symbols;
DenseMap<size_t, Symbol *> SymbolMap;
size_t NextSymbolUniqueId = 0;
+ std::vector<Section> Sections;
+ DenseMap<ssize_t, Section *> SectionMap;
+
+ ssize_t NextSectionUniqueId = 1; // Allow a UniqueId 0 to mean undefined.
+
// Update SymbolMap and RawIndex in each Symbol.
void updateSymbols();
+
+ // Update SectionMap and Index in each Section.
+ void updateSections();
};
// Copy between coff_symbol16 and coff_symbol32.
diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.cpp b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
index d794042ae24..c8abe2913a2 100644
--- a/llvm/tools/llvm-objcopy/COFF/Reader.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
@@ -11,6 +11,7 @@
#include "llvm-objcopy.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/COFF.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/ErrorHandling.h"
#include <cstddef>
@@ -21,6 +22,7 @@ namespace objcopy {
namespace coff {
using namespace object;
+using namespace COFF;
Error COFFReader::readExecutableHeaders(Object &Obj) const {
const dos_header *DH = COFFObj.getDOSHeader();
@@ -58,13 +60,14 @@ Error COFFReader::readExecutableHeaders(Object &Obj) const {
}
Error COFFReader::readSections(Object &Obj) const {
+ std::vector<Section> Sections;
// Section indexing starts from 1.
for (size_t I = 1, E = COFFObj.getNumberOfSections(); I <= E; I++) {
const coff_section *Sec;
if (auto EC = COFFObj.getSection(I, Sec))
return errorCodeToError(EC);
- Obj.Sections.push_back(Section());
- Section &S = Obj.Sections.back();
+ Sections.push_back(Section());
+ Section &S = Sections.back();
S.Header = *Sec;
if (auto EC = COFFObj.getSectionContents(Sec, S.Contents))
return errorCodeToError(EC);
@@ -77,12 +80,14 @@ Error COFFReader::readSections(Object &Obj) const {
return make_error<StringError>("Extended relocations not supported yet",
object_error::parse_failed);
}
+ Obj.addSections(Sections);
return Error::success();
}
Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const {
std::vector<Symbol> Symbols;
Symbols.reserve(COFFObj.getRawNumberOfSymbols());
+ ArrayRef<Section> Sections = Obj.getSections();
for (uint32_t I = 0, E = COFFObj.getRawNumberOfSymbols(); I < E;) {
Expected<COFFSymbolRef> SymOrErr = COFFObj.getSymbol(I);
if (!SymOrErr)
@@ -103,6 +108,26 @@ Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const {
Sym.AuxData = COFFObj.getSymbolAuxData(SymRef);
assert((Sym.AuxData.size() %
(IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16))) == 0);
+ // Find the unique id of the section
+ if (SymRef.getSectionNumber() <=
+ 0) // Special symbol (undefined/absolute/debug)
+ Sym.TargetSectionId = SymRef.getSectionNumber();
+ else if (static_cast<uint32_t>(SymRef.getSectionNumber() - 1) <
+ Sections.size())
+ Sym.TargetSectionId = Sections[SymRef.getSectionNumber() - 1].UniqueId;
+ else
+ return make_error<StringError>("Section number out of range",
+ object_error::parse_failed);
+ // For section definitions, check if it is comdat associative, and if
+ // it is, find the target section unique id.
+ const coff_aux_section_definition *SD = SymRef.getSectionDefinition();
+ if (SD && SD->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
+ int32_t Index = SD->getNumber(IsBigObj);
+ if (Index <= 0 || static_cast<uint32_t>(Index - 1) >= Sections.size())
+ return make_error<StringError>("Unexpected associative section index",
+ object_error::parse_failed);
+ Sym.AssociativeComdatTargetSectionId = Sections[Index - 1].UniqueId;
+ }
I += 1 + SymRef.getNumberOfAuxSymbols();
}
Obj.addSymbols(Symbols);
@@ -116,7 +141,7 @@ Error COFFReader::setRelocTargets(Object &Obj) const {
for (size_t I = 0; I < Sym.Sym.NumberOfAuxSymbols; I++)
RawSymbolTable.push_back(nullptr);
}
- for (Section &Sec : Obj.Sections) {
+ for (Section &Sec : Obj.getMutableSections()) {
for (Relocation &R : Sec.Relocs) {
if (R.Reloc.SymbolTableIndex >= RawSymbolTable.size())
return make_error<StringError>("SymbolTableIndex out of range",
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
index c347810dd24..9fb7812672b 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
@@ -25,7 +25,7 @@ using namespace object;
using namespace COFF;
Error COFFWriter::finalizeRelocTargets() {
- for (Section &Sec : Obj.Sections) {
+ for (Section &Sec : Obj.getMutableSections()) {
for (Relocation &R : Sec.Relocs) {
const Symbol *Sym = Obj.findSymbol(R.Target);
if (Sym == nullptr)
@@ -39,8 +39,48 @@ Error COFFWriter::finalizeRelocTargets() {
return Error::success();
}
+Error COFFWriter::finalizeSectionNumbers() {
+ for (Symbol &Sym : Obj.getMutableSymbols()) {
+ if (Sym.TargetSectionId <= 0) {
+ // Undefined, or a special kind of symbol. These negative values
+ // are stored in the SectionNumber field which is unsigned.
+ Sym.Sym.SectionNumber = static_cast<uint32_t>(Sym.TargetSectionId);
+ } else {
+ const Section *Sec = Obj.findSection(Sym.TargetSectionId);
+ if (Sec == nullptr)
+ return make_error<StringError>("Symbol " + Sym.Name +
+ " points to a removed section",
+ object_error::invalid_symbol_index);
+ Sym.Sym.SectionNumber = Sec->Index;
+
+ if (Sym.Sym.NumberOfAuxSymbols == 1 &&
+ Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC) {
+ coff_aux_section_definition *SD =
+ reinterpret_cast<coff_aux_section_definition *>(Sym.AuxData.data());
+ uint32_t SDSectionNumber;
+ if (Sym.AssociativeComdatTargetSectionId == 0) {
+ // Not a comdat associative section; just set the Number field to
+ // the number of the section itself.
+ SDSectionNumber = Sec->Index;
+ } else {
+ Sec = Obj.findSection(Sym.AssociativeComdatTargetSectionId);
+ if (Sec == nullptr)
+ return make_error<StringError>(
+ "Symbol " + Sym.Name + " is associative to a removed section",
+ object_error::invalid_symbol_index);
+ SDSectionNumber = Sec->Index;
+ }
+ // Update the section definition with the new section number.
+ SD->NumberLowPart = static_cast<uint16_t>(SDSectionNumber);
+ SD->NumberHighPart = static_cast<uint16_t>(SDSectionNumber >> 16);
+ }
+ }
+ }
+ return Error::success();
+}
+
void COFFWriter::layoutSections() {
- for (auto &S : Obj.Sections) {
+ for (auto &S : Obj.getMutableSections()) {
if (S.Header.SizeOfRawData > 0)
S.Header.PointerToRawData = FileSize;
FileSize += S.Header.SizeOfRawData; // For executables, this is already
@@ -57,7 +97,7 @@ void COFFWriter::layoutSections() {
}
size_t COFFWriter::finalizeStringTable() {
- for (auto &S : Obj.Sections)
+ for (const auto &S : Obj.getSections())
if (S.Name.size() > COFF::NameSize)
StrTabBuilder.add(S.Name);
@@ -67,7 +107,7 @@ size_t COFFWriter::finalizeStringTable() {
StrTabBuilder.finalize();
- for (auto &S : Obj.Sections) {
+ for (auto &S : Obj.getMutableSections()) {
if (S.Name.size() > COFF::NameSize) {
snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d",
(int)StrTabBuilder.getOffset(S.Name));
@@ -97,6 +137,8 @@ std::pair<size_t, size_t> COFFWriter::finalizeSymbolTable() {
Error COFFWriter::finalize(bool IsBigObj) {
if (Error E = finalizeRelocTargets())
return E;
+ if (Error E = finalizeSectionNumbers())
+ return E;
size_t SizeOfHeaders = 0;
FileAlignment = 1;
@@ -113,10 +155,10 @@ Error COFFWriter::finalize(bool IsBigObj) {
SizeOfHeaders +=
PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size();
}
- Obj.CoffFileHeader.NumberOfSections = Obj.Sections.size();
+ Obj.CoffFileHeader.NumberOfSections = Obj.getSections().size();
SizeOfHeaders +=
IsBigObj ? sizeof(coff_bigobj_file_header) : sizeof(coff_file_header);
- SizeOfHeaders += sizeof(coff_section) * Obj.Sections.size();
+ SizeOfHeaders += sizeof(coff_section) * Obj.getSections().size();
SizeOfHeaders = alignTo(SizeOfHeaders, FileAlignment);
Obj.CoffFileHeader.SizeOfOptionalHeader =
@@ -131,8 +173,8 @@ Error COFFWriter::finalize(bool IsBigObj) {
Obj.PeHeader.SizeOfHeaders = SizeOfHeaders;
Obj.PeHeader.SizeOfInitializedData = SizeOfInitializedData;
- if (!Obj.Sections.empty()) {
- const Section &S = Obj.Sections.back();
+ if (!Obj.getSections().empty()) {
+ const Section &S = Obj.getSections().back();
Obj.PeHeader.SizeOfImage =
alignTo(S.Header.VirtualAddress + S.Header.VirtualSize,
Obj.PeHeader.SectionAlignment);
@@ -198,7 +240,7 @@ void COFFWriter::writeHeaders(bool IsBigObj) {
BigObjHeader.unused4 = 0;
// The value in Obj.CoffFileHeader.NumberOfSections is truncated, thus
// get the original one instead.
- BigObjHeader.NumberOfSections = Obj.Sections.size();
+ BigObjHeader.NumberOfSections = Obj.getSections().size();
BigObjHeader.PointerToSymbolTable = Obj.CoffFileHeader.PointerToSymbolTable;
BigObjHeader.NumberOfSymbols = Obj.CoffFileHeader.NumberOfSymbols;
@@ -223,14 +265,14 @@ void COFFWriter::writeHeaders(bool IsBigObj) {
Ptr += sizeof(DD);
}
}
- for (const auto &S : Obj.Sections) {
+ for (const auto &S : Obj.getSections()) {
memcpy(Ptr, &S.Header, sizeof(S.Header));
Ptr += sizeof(S.Header);
}
}
void COFFWriter::writeSections() {
- for (const auto &S : Obj.Sections) {
+ for (const auto &S : Obj.getSections()) {
uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData;
std::copy(S.Contents.begin(), S.Contents.end(), Ptr);
@@ -295,7 +337,7 @@ Error COFFWriter::patchDebugDirectory() {
const data_directory *Dir = &Obj.DataDirectories[DEBUG_DIRECTORY];
if (Dir->Size <= 0)
return Error::success();
- for (const auto &S : Obj.Sections) {
+ for (const auto &S : Obj.getSections()) {
if (Dir->RelativeVirtualAddress >= S.Header.VirtualAddress &&
Dir->RelativeVirtualAddress <
S.Header.VirtualAddress + S.Header.SizeOfRawData) {
@@ -324,7 +366,7 @@ Error COFFWriter::patchDebugDirectory() {
}
Error COFFWriter::write() {
- bool IsBigObj = Obj.Sections.size() > MaxNumberOfSections16;
+ bool IsBigObj = Obj.getSections().size() > MaxNumberOfSections16;
if (IsBigObj && Obj.IsPE)
return make_error<StringError>("Too many sections for executable",
object_error::parse_failed);
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.h b/llvm/tools/llvm-objcopy/COFF/Writer.h
index 52fef385926..a967a103df9 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.h
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.h
@@ -31,6 +31,7 @@ class COFFWriter {
StringTableBuilder StrTabBuilder;
Error finalizeRelocTargets();
+ Error finalizeSectionNumbers();
void layoutSections();
size_t finalizeStringTable();
template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable();
--
2.17.1

View File

@ -0,0 +1,28 @@
From 1284ee3c47bab17ec081b5169633aea4f8abfd30 Mon Sep 17 00:00:00 2001
From: Martin Storsjo <martin@martin.st>
Date: Wed, 23 Jan 2019 09:12:53 +0000
Subject: [PATCH] [llvm-objcopy] [COFF] Clear the unwritten tail of
coff_section::Header::Name
This should fix the add-gnu-debuglink test on all buildbots.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351934 91177308-0d34-0410-b5e6-96231b3b80d8
---
tools/llvm-objcopy/COFF/Writer.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
index 05e46291c39..db897e2ff33 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
@@ -121,6 +121,7 @@ size_t COFFWriter::finalizeStringTable() {
for (auto &S : Obj.getMutableSections()) {
if (S.Name.size() > COFF::NameSize) {
+ memset(S.Header.Name, 0, sizeof(S.Header.Name));
snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d",
(int)StrTabBuilder.getOffset(S.Name));
} else {
--
2.17.1

View File

@ -0,0 +1,43 @@
From abacd83232acf69d7cbacd53fc2f9aae66c1a32e Mon Sep 17 00:00:00 2001
From: Martin Storsjo <martin@martin.st>
Date: Wed, 23 Jan 2019 11:54:55 +0000
Subject: [PATCH] [llvm-objcopy] [COFF] Error out on use of unhandled options
Prefer erroring out than silently not doing what was requested.
Differential Revision: https://reviews.llvm.org/D57045
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351948 91177308-0d34-0410-b5e6-96231b3b80d8
---
tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index 64b4e79a4e0..b7b3d3cb629 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -170,6 +170,21 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
if (!Config.AddGnuDebugLink.empty())
addGnuDebugLink(Obj, Config.AddGnuDebugLink);
+ if (!Config.BuildIdLinkDir.empty() || Config.BuildIdLinkInput ||
+ Config.BuildIdLinkOutput || !Config.SplitDWO.empty() ||
+ !Config.SymbolsPrefix.empty() || !Config.AddSection.empty() ||
+ !Config.DumpSection.empty() || !Config.KeepSection.empty() ||
+ !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() ||
+ !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() ||
+ !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() ||
+ !Config.SymbolsToRename.empty() || Config.ExtractDWO ||
+ Config.KeepFileSymbols || Config.LocalizeHidden || Config.PreserveDates ||
+ Config.StripDWO || Config.StripNonAlloc || Config.StripSections ||
+ Config.Weaken || Config.DecompressDebugSections) {
+ return createStringError(llvm::errc::invalid_argument,
+ "Option not supported by llvm-objcopy for COFF");
+ }
+
return Error::success();
}
--
2.17.1

View File

@ -0,0 +1,377 @@
From 74c7d422cba163635394ec32f2b243b1de502a18 Mon Sep 17 00:00:00 2001
From: Martin Storsjo <martin@martin.st>
Date: Wed, 23 Jan 2019 11:54:51 +0000
Subject: [PATCH] [llvm-objcopy] [COFF] Fix handling of aux symbols for big
objects
The aux symbols were stored in an opaque std::vector<uint8_t>,
with contents interpreted according to the rest of the symbol.
All aux symbol types but one fit in 18 bytes (sizeof(coff_symbol16)),
and if written to a bigobj, two extra padding bytes are written (as
sizeof(coff_symbol32) is 20). In the storage agnostic intermediate
representation, store the aux symbols as a series of coff_symbol16
sized opaque blobs. (In practice, all such aux symbols only consist
of one aux symbol, so this is more flexible than what reality needs.)
The special case is the file aux symbols, which are written in
potentially more than one aux symbol slot, without any padding,
as one single long string. This can't be stored in the same opaque
vector of fixed sized aux symbol entries. The file aux symbols will
occupy a different number of aux symbol slots depending on the type
of output object file. As nothing in the intermediate process needs
to have accurate raw symbol indices, updating that is moved into the
writer class.
Differential Revision: https://reviews.llvm.org/D57009
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351947 91177308-0d34-0410-b5e6-96231b3b80d8
---
.../llvm-objcopy/COFF/Inputs/bigobj.o.gz | Bin 0 -> 7841 bytes
test/tools/llvm-objcopy/COFF/bigobj.test | 35 +++++++++++++
.../llvm-objcopy/ELF/auto-remove-shndx.test | 2 +-
.../tools/llvm-objcopy/ELF/many-sections.test | 2 +-
test/tools/llvm-objcopy/ELF/remove-shndx.test | 2 +-
.../tools/llvm-objcopy/ELF/strict-no-add.test | 2 +-
.../llvm-objcopy/{ELF => }/Inputs/ungzip.py | 0
tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 6 +--
tools/llvm-objcopy/COFF/Object.cpp | 6 +--
tools/llvm-objcopy/COFF/Object.h | 18 ++++++-
tools/llvm-objcopy/COFF/Reader.cpp | 21 ++++++--
tools/llvm-objcopy/COFF/Writer.cpp | 49 +++++++++++++-----
tools/llvm-objcopy/COFF/Writer.h | 2 +-
13 files changed, 115 insertions(+), 30 deletions(-)
create mode 100644 test/tools/llvm-objcopy/COFF/Inputs/bigobj.o.gz
create mode 100644 test/tools/llvm-objcopy/COFF/bigobj.test
rename test/tools/llvm-objcopy/{ELF => }/Inputs/ungzip.py (100%)
diff --git a/llvm/test/tools/llvm-objcopy/COFF/Inputs/bigobj.o.gz b/llvm/test/tools/llvm-objcopy/COFF/Inputs/bigobj.o.gz
new file mode 100644
index 0000000000000000000000000000000000000000..6435f4785ff76e0c6bca12f3e57bc6ad8888bece
GIT binary patch
literal 7841
zcmb2|=3r3v^@w3&etUMmo^zoH`-kGKM_vfJsF<z%6+Sz#g6qq)pf0Hj#_r`4B7DML
zW(h1l`+!x)t@&W-R@I~lyKmU3Ti&1Z=iKur;uBMy1MR!_ywlsouYY&-*4H1mU!Qw=
z`RpIDkw;!V4(WOF_)B3`(Vt|~S>?L-b)Wx@^wig`HIMAw|N8V<v65oeyETs611{)_
zm27RwTe)ENN|7SLgM~83N6}~qjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk
zz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kgRokHNw|D97mKRo|%+Z6Ym
z*X{Ku$4#4*Vqm}gyYR^b^Sc?_E*<<cFE#Q=*rt6OBmJeLZ=aoe_u9R>-w!^l5biDe
p{d@nvw*mU6<NG83=jxn4_wt_G?yUy#YeOF~KhH^=cj<;Y0{~9Br>p<~
literal 0
HcmV?d00001
diff --git a/llvm/test/tools/llvm-objcopy/COFF/bigobj.test b/llvm/test/tools/llvm-objcopy/COFF/bigobj.test
new file mode 100644
index 00000000000..17968f12b8a
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/COFF/bigobj.test
@@ -0,0 +1,35 @@
+RUN: %python %p/../Inputs/ungzip.py %p/Inputs/bigobj.o.gz > %t.in.o
+
+RUN: llvm-objdump -t %t.in.o | FileCheck %s --check-prefixes=SYMBOLS,SYMBOLS-BIG,SYMBOLS-ORIG
+
+# Do a plain copy, to check that section numbers in symbols referring
+# to sections outside of the small object format are handled correctly.
+RUN: llvm-objcopy -R '.text$4' %t.in.o %t.small.o
+RUN: llvm-objdump -t %t.in.o | FileCheck %s --check-prefixes=SYMBOLS,SYMBOLS-BIG,SYMBOLS-ORIG
+
+# Remove a section, making the section count fit into a small object.
+RUN: llvm-objcopy -R '.text$4' %t.in.o %t.small.o
+RUN: llvm-objdump -t %t.small.o | FileCheck %s --check-prefixes=SYMBOLS,SYMBOLS-SMALL,SYMBOLS-REMOVED-SMALL
+
+# Add a .gnu_debuglink section, forcing the object back to big format.
+RUN: llvm-objcopy --add-gnu-debuglink=%t.in.o %t.small.o %t.big.o
+ llvm-objdump -t %t.big.o | FileCheck %s --check-prefixes=SYMBOLS,SYMBOLS-BIG,SYMBOLS-REMOVED-BIG
+
+# In big object format, the .file symbol occupies one symbol table entry for
+# the auxillary data, but needs two entries in the small format, forcing the
+# raw symbol indices of later symbols to change.
+SYMBOLS: SYMBOL TABLE:
+SYMBOLS-NEXT: [ 0]{{.*}} (nx 1) {{.*}} .text
+SYMBOLS-NEXT: AUX scnlen
+SYMBOLS-SMALL-NEXT: [ 2]{{.*}} (nx 2) {{.*}} .file
+SYMBOLS-BIG-NEXT: [ 2]{{.*}} (nx 1) {{.*}} .file
+SYMBOLS-NEXT: AUX abcdefghijklmnopqrs
+SYMBOLS-SMALL-NEXT: [ 5]{{.*}} (nx 0) {{.*}} foo
+SYMBOLS-BIG-NEXT: [ 4]{{.*}} (nx 0) {{.*}} foo
+
+# Check that the section numbers outside of signed 16 bit int range
+# are represented properly. After removing one section, the section
+# numbers decrease.
+SYMBOLS-ORIG: [ 5](sec 65280){{.*}} symbol65280
+SYMBOLS-REMOVED-SMALL: [ 6](sec 65279){{.*}} symbol65280
+SYMBOLS-REMOVED-BIG: [ 5](sec 65279){{.*}} symbol65280
diff --git a/llvm/test/tools/llvm-objcopy/ELF/auto-remove-shndx.test b/llvm/test/tools/llvm-objcopy/ELF/auto-remove-shndx.test
index 5a23493fa94..8e6c788bf48 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/auto-remove-shndx.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/auto-remove-shndx.test
@@ -1,4 +1,4 @@
-# RUN: %python %p/Inputs/ungzip.py %p/Inputs/many-sections.o.gz > %t
+# RUN: %python %p/../Inputs/ungzip.py %p/Inputs/many-sections.o.gz > %t
# RUN: llvm-objcopy -R .text -R s0 -R s1 -R s2 -R s3 -R s4 -R s5 -R s6 %t %t2
# RUN: llvm-readobj --sections %t2 | FileCheck --check-prefix=SECS %s
diff --git a/llvm/test/tools/llvm-objcopy/ELF/many-sections.test b/llvm/test/tools/llvm-objcopy/ELF/many-sections.test
index 57239f32e4a..1dd41cfb10c 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/many-sections.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/many-sections.test
@@ -1,4 +1,4 @@
-RUN: %python %p/Inputs/ungzip.py %p/Inputs/many-sections.o.gz > %t
+RUN: %python %p/../Inputs/ungzip.py %p/Inputs/many-sections.o.gz > %t
RUN: llvm-objcopy %t %t2
RUN: llvm-readobj --file-headers %t2 | FileCheck --check-prefix=EHDR %s
RUN: llvm-readobj --sections %t2 | FileCheck --check-prefix=SECS %s
diff --git a/llvm/test/tools/llvm-objcopy/ELF/remove-shndx.test b/llvm/test/tools/llvm-objcopy/ELF/remove-shndx.test
index 6cc3a1a291f..53ca8e7f220 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/remove-shndx.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/remove-shndx.test
@@ -1,6 +1,6 @@
# This test checks to see that a .symtab_shndx section is added to any binary
# that needs it, even if the original was removed.
-RUN: %python %p/Inputs/ungzip.py %p/Inputs/many-sections.o.gz > %t
+RUN: %python %p/../Inputs/ungzip.py %p/Inputs/many-sections.o.gz > %t
RUN: llvm-objcopy -R .symtab_shndx %t %t2
RUN: llvm-readobj --sections %t2 | FileCheck %s
diff --git a/llvm/test/tools/llvm-objcopy/ELF/strict-no-add.test b/llvm/test/tools/llvm-objcopy/ELF/strict-no-add.test
index 4f24df31bf9..348ab7c4fbd 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/strict-no-add.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/strict-no-add.test
@@ -1,7 +1,7 @@
# This test makes sure that sections added at the end that don't have symbols
# defined in them don't trigger the creation of a large index table.
-RUN: %python %p/Inputs/ungzip.py %p/Inputs/many-sections.o.gz > %t.0
+RUN: %python %p/../Inputs/ungzip.py %p/Inputs/many-sections.o.gz > %t.0
RUN: cat %p/Inputs/alloc-symtab.o > %t
RUN: llvm-objcopy -R .text -R s0 -R s1 -R s2 -R s3 -R s4 -R s5 -R s6 %t.0 %t2
RUN: llvm-objcopy --add-section=.s0=%t --add-section=.s1=%t --add-section=.s2=%t %t2 %t2
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/ungzip.py b/llvm/test/tools/llvm-objcopy/Inputs/ungzip.py
similarity index 100%
rename from test/tools/llvm-objcopy/ELF/Inputs/ungzip.py
rename to test/tools/llvm-objcopy/Inputs/ungzip.py
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index 20adbe11e7a..64b4e79a4e0 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -37,7 +37,7 @@ static uint64_t getNextRVA(const Object &Obj) {
return 0;
const Section &Last = Obj.getSections().back();
return alignTo(Last.Header.VirtualAddress + Last.Header.VirtualSize,
- Obj.PeHeader.SectionAlignment);
+ Obj.IsPE ? Obj.PeHeader.SectionAlignment : 1);
}
static uint32_t getCRC32(StringRef Data) {
@@ -74,8 +74,8 @@ static void addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) {
Sec.Name = ".gnu_debuglink";
Sec.Header.VirtualSize = Sec.getContents().size();
Sec.Header.VirtualAddress = StartRVA;
- Sec.Header.SizeOfRawData =
- alignTo(Sec.Header.VirtualSize, Obj.PeHeader.FileAlignment);
+ Sec.Header.SizeOfRawData = alignTo(Sec.Header.VirtualSize,
+ Obj.IsPE ? Obj.PeHeader.FileAlignment : 1);
// Sec.Header.PointerToRawData is filled in by the writer.
Sec.Header.PointerToRelocations = 0;
Sec.Header.PointerToLinenumbers = 0;
diff --git a/llvm/tools/llvm-objcopy/COFF/Object.cpp b/llvm/tools/llvm-objcopy/COFF/Object.cpp
index 8c382c1faef..0ad5a05a144 100644
--- a/llvm/tools/llvm-objcopy/COFF/Object.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Object.cpp
@@ -26,12 +26,8 @@ void Object::addSymbols(ArrayRef<Symbol> NewSymbols) {
void Object::updateSymbols() {
SymbolMap = DenseMap<size_t, Symbol *>(Symbols.size());
- size_t RawSymIndex = 0;
- for (Symbol &Sym : Symbols) {
+ for (Symbol &Sym : Symbols)
SymbolMap[Sym.UniqueId] = &Sym;
- Sym.RawIndex = RawSymIndex;
- RawSymIndex += 1 + Sym.Sym.NumberOfAuxSymbols;
- }
}
const Symbol *Object::findSymbol(size_t UniqueId) const {
diff --git a/llvm/tools/llvm-objcopy/COFF/Object.h b/llvm/tools/llvm-objcopy/COFF/Object.h
index afa272286ef..21475b06862 100644
--- a/llvm/tools/llvm-objcopy/COFF/Object.h
+++ b/llvm/tools/llvm-objcopy/COFF/Object.h
@@ -66,10 +66,24 @@ private:
std::vector<uint8_t> OwnedContents;
};
+struct AuxSymbol {
+ AuxSymbol(ArrayRef<uint8_t> In) {
+ assert(In.size() == sizeof(Opaque));
+ std::copy(In.begin(), In.end(), Opaque);
+ }
+
+ ArrayRef<uint8_t> getRef() const {
+ return ArrayRef<uint8_t>(Opaque, sizeof(Opaque));
+ }
+
+ uint8_t Opaque[sizeof(object::coff_symbol16)];
+};
+
struct Symbol {
object::coff_symbol32 Sym;
StringRef Name;
- std::vector<uint8_t> AuxData;
+ std::vector<AuxSymbol> AuxData;
+ StringRef AuxFile;
ssize_t TargetSectionId;
ssize_t AssociativeComdatTargetSectionId = 0;
Optional<size_t> WeakTargetSymbolId;
@@ -132,7 +146,7 @@ private:
ssize_t NextSectionUniqueId = 1; // Allow a UniqueId 0 to mean undefined.
- // Update SymbolMap and RawIndex in each Symbol.
+ // Update SymbolMap.
void updateSymbols();
// Update SectionMap and Index in each Section.
diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.cpp b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
index 87dd60a43cf..7270bbf94de 100644
--- a/llvm/tools/llvm-objcopy/COFF/Reader.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
@@ -107,9 +107,24 @@ Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const {
*reinterpret_cast<const coff_symbol16 *>(SymRef.getRawPtr()));
if (auto EC = COFFObj.getSymbolName(SymRef, Sym.Name))
return errorCodeToError(EC);
- Sym.AuxData = COFFObj.getSymbolAuxData(SymRef);
- assert((Sym.AuxData.size() %
- (IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16))) == 0);
+
+ ArrayRef<uint8_t> AuxData = COFFObj.getSymbolAuxData(SymRef);
+ size_t SymSize = IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16);
+ assert(AuxData.size() == SymSize * SymRef.getNumberOfAuxSymbols());
+ // The auxillary symbols are structs of sizeof(coff_symbol16) each.
+ // In the big object format (where symbols are coff_symbol32), each
+ // auxillary symbol is padded with 2 bytes at the end. Copy each
+ // auxillary symbol to the Sym.AuxData vector. For file symbols,
+ // the whole range of aux symbols are interpreted as one null padded
+ // string instead.
+ if (SymRef.isFileRecord())
+ Sym.AuxFile = StringRef(reinterpret_cast<const char *>(AuxData.data()),
+ AuxData.size())
+ .rtrim('\0');
+ else
+ for (size_t I = 0; I < SymRef.getNumberOfAuxSymbols(); I++)
+ Sym.AuxData.push_back(AuxData.slice(I * SymSize, sizeof(AuxSymbol)));
+
// Find the unique id of the section
if (SymRef.getSectionNumber() <=
0) // Special symbol (undefined/absolute/debug)
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
index db897e2ff33..6e69c597217 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
@@ -55,7 +55,8 @@ Error COFFWriter::finalizeSymbolContents() {
if (Sym.Sym.NumberOfAuxSymbols == 1 &&
Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC) {
coff_aux_section_definition *SD =
- reinterpret_cast<coff_aux_section_definition *>(Sym.AuxData.data());
+ reinterpret_cast<coff_aux_section_definition *>(
+ Sym.AuxData[0].Opaque);
uint32_t SDSectionNumber;
if (Sym.AssociativeComdatTargetSectionId == 0) {
// Not a comdat associative section; just set the Number field to
@@ -79,7 +80,7 @@ Error COFFWriter::finalizeSymbolContents() {
// we want to set. Only >= 1 would be required, but only == 1 makes sense.
if (Sym.WeakTargetSymbolId && Sym.Sym.NumberOfAuxSymbols == 1) {
coff_aux_weak_external *WE =
- reinterpret_cast<coff_aux_weak_external *>(Sym.AuxData.data());
+ reinterpret_cast<coff_aux_weak_external *>(Sym.AuxData[0].Opaque);
const Symbol *Target = Obj.findSymbol(*Sym.WeakTargetSymbolId);
if (Target == nullptr)
return createStringError(object_error::invalid_symbol_index,
@@ -141,13 +142,26 @@ size_t COFFWriter::finalizeStringTable() {
template <class SymbolTy>
std::pair<size_t, size_t> COFFWriter::finalizeSymbolTable() {
- size_t SymTabSize = Obj.getSymbols().size() * sizeof(SymbolTy);
- for (const auto &S : Obj.getSymbols())
- SymTabSize += S.AuxData.size();
- return std::make_pair(SymTabSize, sizeof(SymbolTy));
+ size_t RawSymIndex = 0;
+ for (auto &S : Obj.getMutableSymbols()) {
+ // Symbols normally have NumberOfAuxSymbols set correctly all the time.
+ // For file symbols, we need to know the output file's symbol size to be
+ // able to calculate the number of slots it occupies.
+ if (!S.AuxFile.empty())
+ S.Sym.NumberOfAuxSymbols =
+ alignTo(S.AuxFile.size(), sizeof(SymbolTy)) / sizeof(SymbolTy);
+ S.RawIndex = RawSymIndex;
+ RawSymIndex += 1 + S.Sym.NumberOfAuxSymbols;
+ }
+ return std::make_pair(RawSymIndex * sizeof(SymbolTy), sizeof(SymbolTy));
}
Error COFFWriter::finalize(bool IsBigObj) {
+ size_t SymTabSize, SymbolSize;
+ std::tie(SymTabSize, SymbolSize) = IsBigObj
+ ? finalizeSymbolTable<coff_symbol32>()
+ : finalizeSymbolTable<coff_symbol16>();
+
if (Error E = finalizeRelocTargets())
return E;
if (Error E = finalizeSymbolContents())
@@ -199,10 +213,6 @@ Error COFFWriter::finalize(bool IsBigObj) {
}
size_t StrTabSize = finalizeStringTable();
- size_t SymTabSize, SymbolSize;
- std::tie(SymTabSize, SymbolSize) = IsBigObj
- ? finalizeSymbolTable<coff_symbol32>()
- : finalizeSymbolTable<coff_symbol16>();
size_t PointerToSymbolTable = FileSize;
// StrTabSize <= 4 is the size of an empty string table, only consisting
@@ -312,8 +322,23 @@ template <class SymbolTy> void COFFWriter::writeSymbolStringTables() {
copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr),
S.Sym);
Ptr += sizeof(SymbolTy);
- std::copy(S.AuxData.begin(), S.AuxData.end(), Ptr);
- Ptr += S.AuxData.size();
+ if (!S.AuxFile.empty()) {
+ // For file symbols, just write the string into the aux symbol slots,
+ // assuming that the unwritten parts are initialized to zero in the memory
+ // mapped file.
+ std::copy(S.AuxFile.begin(), S.AuxFile.end(), Ptr);
+ Ptr += S.Sym.NumberOfAuxSymbols * sizeof(SymbolTy);
+ } else {
+ // For other auxillary symbols, write their opaque payload into one symbol
+ // table slot each. For big object files, the symbols are larger than the
+ // opaque auxillary symbol struct and we leave padding at the end of each
+ // entry.
+ for (const AuxSymbol &AuxSym : S.AuxData) {
+ ArrayRef<uint8_t> Ref = AuxSym.getRef();
+ std::copy(Ref.begin(), Ref.end(), Ptr);
+ Ptr += sizeof(SymbolTy);
+ }
+ }
}
if (StrTabBuilder.getSize() > 4 || !Obj.IsPE) {
// Always write a string table in object files, even an empty one.
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.h b/llvm/tools/llvm-objcopy/COFF/Writer.h
index 9b1cfa91d00..681a8d5e4a6 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.h
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.h
@@ -30,11 +30,11 @@ class COFFWriter {
size_t SizeOfInitializedData;
StringTableBuilder StrTabBuilder;
+ template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable();
Error finalizeRelocTargets();
Error finalizeSymbolContents();
void layoutSections();
size_t finalizeStringTable();
- template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable();
Error finalize(bool IsBigObj);
--
2.17.1

View File

@ -0,0 +1,222 @@
From 526aa2e94355b7feb3bf7774a6e1899f68e94ad8 Mon Sep 17 00:00:00 2001
From: Martin Storsjo <martin@martin.st>
Date: Sat, 19 Jan 2019 19:42:48 +0000
Subject: [PATCH] [llvm-objcopy] [COFF] Implement --only-keep-debug
Differential Revision: https://reviews.llvm.org/D56840
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351662 91177308-0d34-0410-b5e6-96231b3b80d8
---
.../COFF/Inputs/only-keep-sections.yaml | 77 +++++++++++++++++++
.../llvm-objcopy/COFF/only-keep-debug.test | 58 ++++++++++++++
tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 10 +++
tools/llvm-objcopy/COFF/Object.cpp | 10 +++
tools/llvm-objcopy/COFF/Object.h | 1 +
5 files changed, 156 insertions(+)
create mode 100644 test/tools/llvm-objcopy/COFF/Inputs/only-keep-sections.yaml
create mode 100644 test/tools/llvm-objcopy/COFF/only-keep-debug.test
diff --git a/llvm/test/tools/llvm-objcopy/COFF/Inputs/only-keep-sections.yaml b/llvm/test/tools/llvm-objcopy/COFF/Inputs/only-keep-sections.yaml
new file mode 100644
index 00000000000..b5437e10763
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/COFF/Inputs/only-keep-sections.yaml
@@ -0,0 +1,77 @@
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4144
+ ImageBase: 1073741824
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE ]
+ VirtualAddress: 4096
+ VirtualSize: 4
+ SectionData: C3C3C3C3
+ - Name: .rdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA ]
+ VirtualAddress: 8192
+ VirtualSize: 4
+ SectionData: 2A000000
+ - Name: .buildid
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA ]
+ VirtualAddress: 12288
+ VirtualSize: 4
+ SectionData: 2B000000
+ - Name: .reloc
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE ]
+ VirtualAddress: 16384
+ VirtualSize: 4
+ SectionData: 2C000000
+ - Name: .debug_discardable
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE ]
+ VirtualAddress: 20480
+ VirtualSize: 4
+ SectionData: 2D000000
+ - Name: .debug_undiscardable
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA ]
+ VirtualAddress: 24576
+ VirtualSize: 4
+ SectionData: 2E000000
+ - Name: .unflagged
+ Characteristics: [ ]
+ VirtualAddress: 28672
+ VirtualSize: 4
+ SectionData: 2F000000
+symbols:
+ - Name: main
+ Value: 2
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: debug_discardable_sym
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: debug_undiscardable_sym
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-objcopy/COFF/only-keep-debug.test b/llvm/test/tools/llvm-objcopy/COFF/only-keep-debug.test
new file mode 100644
index 00000000000..5518d4000fc
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/COFF/only-keep-debug.test
@@ -0,0 +1,58 @@
+RUN: yaml2obj %p/Inputs/only-keep-sections.yaml > %t.in.exe
+
+RUN: llvm-objcopy --only-keep-debug %t.in.exe %t.out.exe
+RUN: llvm-readobj --sections %t.out.exe | FileCheck %s --check-prefix=SECTIONS
+RUN: llvm-objdump -t %t.out.exe | FileCheck %s --check-prefix=SYMBOLS
+
+Check that all non-debug/buildid sections with IMAGE_SCN_CNT_CODE
+or IMAGE_SCN_CNT_INITIALIZED_DATA are truncated, and no others.
+
+SECTIONS: Sections [
+SECTIONS-NEXT: Section {
+SECTIONS-NEXT: Number: 1
+SECTIONS-NEXT: Name: .text
+SECTIONS-NEXT: VirtualSize: 0x4
+SECTIONS-NEXT: VirtualAddress:
+SECTIONS-NEXT: RawDataSize: 0
+SECTIONS: Section {
+SECTIONS-NEXT: Number: 2
+SECTIONS-NEXT: Name: .rdata
+SECTIONS-NEXT: VirtualSize: 0x4
+SECTIONS-NEXT: VirtualAddress:
+SECTIONS-NEXT: RawDataSize: 0
+SECTIONS: Section {
+SECTIONS-NEXT: Number: 3
+SECTIONS-NEXT: Name: .buildid
+SECTIONS-NEXT: VirtualSize: 0x4
+SECTIONS-NEXT: VirtualAddress:
+SECTIONS-NEXT: RawDataSize: 512
+SECTIONS: Section {
+SECTIONS-NEXT: Number: 4
+SECTIONS-NEXT: Name: .reloc
+SECTIONS-NEXT: VirtualSize: 0x4
+SECTIONS-NEXT: VirtualAddress:
+SECTIONS-NEXT: RawDataSize: 0
+SECTIONS: Section {
+SECTIONS-NEXT: Number: 5
+SECTIONS-NEXT: Name: .debug_discardable
+SECTIONS-NEXT: VirtualSize: 0x4
+SECTIONS-NEXT: VirtualAddress:
+SECTIONS-NEXT: RawDataSize: 512
+SECTIONS: Section {
+SECTIONS-NEXT: Number: 6
+SECTIONS-NEXT: Name: .debug_undiscardable
+SECTIONS-NEXT: VirtualSize: 0x4
+SECTIONS-NEXT: VirtualAddress:
+SECTIONS-NEXT: RawDataSize: 512
+SECTIONS: Section {
+SECTIONS-NEXT: Number: 7
+SECTIONS-NEXT: Name: .unflagged
+SECTIONS-NEXT: VirtualSize: 0x4
+SECTIONS-NEXT: VirtualAddress:
+SECTIONS-NEXT: RawDataSize: 512
+
+SYMBOLS: SYMBOL TABLE:
+SYMBOLS-NEXT: main
+SYMBOLS-NEXT: debug_discardable_sym
+SYMBOLS-NEXT: debug_undiscardable_sym
+SYMBOLS-EMPTY:
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index 13d8efde37c..60afbf7bb54 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -46,6 +46,16 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
return false;
});
+ if (Config.OnlyKeepDebug) {
+ // For --only-keep-debug, we keep all other sections, but remove their
+ // content. The VirtualSize field in the section header is kept intact.
+ Obj.truncateSections([](const Section &Sec) {
+ return !isDebugSection(Sec) && Sec.Name != ".buildid" &&
+ ((Sec.Header.Characteristics &
+ (IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA)) != 0);
+ });
+ }
+
// StripAll removes all symbols and thus also removes all relocations.
if (Config.StripAll || Config.StripAllGNU)
for (Section &Sec : Obj.getMutableSections())
diff --git a/llvm/tools/llvm-objcopy/COFF/Object.cpp b/llvm/tools/llvm-objcopy/COFF/Object.cpp
index e19cea6aa9d..fc87d9e574d 100644
--- a/llvm/tools/llvm-objcopy/COFF/Object.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Object.cpp
@@ -127,6 +127,16 @@ void Object::removeSections(function_ref<bool(const Section &)> ToRemove) {
updateSymbols();
}
+void Object::truncateSections(function_ref<bool(const Section &)> ToTruncate) {
+ for (Section &Sec : Sections) {
+ if (ToTruncate(Sec)) {
+ Sec.Contents = ArrayRef<uint8_t>();
+ Sec.Relocs.clear();
+ Sec.Header.SizeOfRawData = 0;
+ }
+ }
+}
+
} // end namespace coff
} // end namespace objcopy
} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/COFF/Object.h b/llvm/tools/llvm-objcopy/COFF/Object.h
index a73e93620d3..8e200369f0b 100644
--- a/llvm/tools/llvm-objcopy/COFF/Object.h
+++ b/llvm/tools/llvm-objcopy/COFF/Object.h
@@ -93,6 +93,7 @@ struct Object {
void addSections(ArrayRef<Section> NewSections);
void removeSections(function_ref<bool(const Section &)> ToRemove);
+ void truncateSections(function_ref<bool(const Section &)> ToTruncate);
private:
std::vector<Symbol> Symbols;
--
2.17.1

View File

@ -0,0 +1,61 @@
From 17dcf25b3ade15605ca27150e4440bcc75caed65 Mon Sep 17 00:00:00 2001
From: Martin Storsjo <martin@martin.st>
Date: Sat, 19 Jan 2019 19:42:54 +0000
Subject: [PATCH] [llvm-objcopy] [COFF] Implement --only-section
Differential Revision: https://reviews.llvm.org/D56873
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351663 91177308-0d34-0410-b5e6-96231b3b80d8
---
.../tools/llvm-objcopy/COFF/only-section.test | 21 +++++++++++++++++++
tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 6 ++++++
2 files changed, 27 insertions(+)
create mode 100644 test/tools/llvm-objcopy/COFF/only-section.test
diff --git a/llvm/test/tools/llvm-objcopy/COFF/only-section.test b/llvm/test/tools/llvm-objcopy/COFF/only-section.test
new file mode 100644
index 00000000000..42492ed80ff
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/COFF/only-section.test
@@ -0,0 +1,21 @@
+RUN: yaml2obj %p/Inputs/only-keep-sections.yaml > %t.in.exe
+
+RUN: llvm-objcopy --only-section .debug_discardable %t.in.exe %t.out.exe
+RUN: llvm-objdump --section-headers -t %t.out.exe | FileCheck %s --check-prefixes=SECTIONS,SECTIONS-DEBUG,SYMBOLS,SYMBOLS-DEBUG
+
+Adding another section stripping option makes it return the intersection of
+kept sections - in this case keeping only .text.
+
+RUN: llvm-objcopy --only-section .debug_discardable --only-section .text --strip-debug %t.in.exe %t.combination.exe
+RUN: llvm-objdump --section-headers -t %t.combination.exe | FileCheck %s --check-prefixes=SECTIONS,SECTIONS-TEXT,SYMBOLS,SYMBOLS-TEXT
+
+SECTIONS: Sections:
+SECTIONS-NEXT: Idx Name
+SECTIONS-DEBUG-NEXT: .debug_discardable
+SECTIONS-TEXT-NEXT: .text
+SECTIONS-EMPTY:
+
+SYMBOLS: SYMBOL TABLE:
+SYMBOLS-DEBUG-NEXT: debug_discardable_sym
+SYMBOLS-TEXT-NEXT: main
+SYMBOLS-EMPTY:
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index 60afbf7bb54..99929d10a1f 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -33,6 +33,12 @@ static bool isDebugSection(const Section &Sec) {
static Error handleArgs(const CopyConfig &Config, Object &Obj) {
// Perform the actual section removals.
Obj.removeSections([&Config](const Section &Sec) {
+ // Contrary to --only-keep-debug, --only-section fully removes sections that
+ // aren't mentioned.
+ if (!Config.OnlySection.empty() &&
+ !is_contained(Config.OnlySection, Sec.Name))
+ return true;
+
if (Config.StripDebug || Config.StripAll || Config.StripAllGNU ||
Config.DiscardAll || Config.StripUnneeded) {
if (isDebugSection(Sec) &&
--
2.17.1

View File

@ -0,0 +1,160 @@
From 2b6e1b7585d6bd997ea4e4233c904a6d2c11ad33 Mon Sep 17 00:00:00 2001
From: Martin Storsjo <martin@martin.st>
Date: Sat, 19 Jan 2019 19:42:41 +0000
Subject: [PATCH] [llvm-objcopy] [COFF] Implement --strip-debug
Also remove sections similarly for --strip-all, --discard-all,
--strip-unneeded.
Differential Revision: https://reviews.llvm.org/D56839
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351661 91177308-0d34-0410-b5e6-96231b3b80d8
---
test/tools/llvm-objcopy/COFF/strip-debug.test | 109 ++++++++++++++++++
tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 11 ++
2 files changed, 120 insertions(+)
create mode 100644 test/tools/llvm-objcopy/COFF/strip-debug.test
diff --git a/llvm/test/tools/llvm-objcopy/COFF/strip-debug.test b/llvm/test/tools/llvm-objcopy/COFF/strip-debug.test
new file mode 100644
index 00000000000..97fa96aac70
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/COFF/strip-debug.test
@@ -0,0 +1,109 @@
+# RUN: yaml2obj %s > %t.in.o
+#
+# RUN: llvm-objdump --section-headers %t.in.o | FileCheck %s --check-prefixes=SECTIONS,SECTIONS-PRE
+# RUN: llvm-objdump -t %t.in.o | FileCheck %s --check-prefixes=SYMBOLS,SYMBOLS-PRE
+#
+# RUN: llvm-objcopy --strip-debug %t.in.o %t.out.o
+# RUN: llvm-objdump --section-headers %t.out.o | FileCheck %s --check-prefixes=SECTIONS
+# RUN: llvm-objdump -t %t.out.o | FileCheck %s --check-prefixes=SYMBOLS
+#
+# Test that --strip-all, --strip-all-gnu, --discard-all and --strip-unneeded,
+# plus llvm-strip without arguments all produce a similiar set of sections
+# (while they remove symbols differently).
+#
+# RUN: llvm-objcopy --strip-all %t.in.o %t.strip-all.o
+# RUN: llvm-objdump --section-headers %t.strip-all.o | FileCheck %s --check-prefixes=SECTIONS
+#
+# RUN: llvm-objcopy --strip-all-gnu %t.in.o %t.strip-all-gnu.o
+# RUN: llvm-objdump --section-headers %t.strip-all-gnu.o | FileCheck %s --check-prefixes=SECTIONS
+#
+# RUN: llvm-objcopy --discard-all %t.in.o %t.discard-all.o
+# RUN: llvm-objdump --section-headers %t.discard-all.o | FileCheck %s --check-prefixes=SECTIONS
+#
+# RUN: llvm-objcopy --discard-all %t.in.o %t.strip-unneeded.o
+# RUN: llvm-objdump --section-headers %t.strip-unneeded.o | FileCheck %s --check-prefixes=SECTIONS
+#
+# SECTIONS: Sections:
+# SECTIONS-NEXT: Idx Name
+# SECTIONS-NEXT: 0 .text
+# SECTIONS-NEXT: 1 .data
+# SECTIONS-NEXT: 2 .bss
+# SECTIONS-NEXT: 3 .xdata
+# SECTIONS-NEXT: 4 .reloc
+# SECTIONS-PRE-NEXT: 5 .debug_discardable
+# SECTIONS-NEXT: {{.*}} .debug_undiscardable
+# SECTIONS-NEXT: {{.*}} .llvm_addrsig
+# SECTIONS-EMPTY:
+#
+# Test that --strip-debug doesn't remove e.g. unreferenced local symbols.
+#
+# SYMBOLS: SYMBOL TABLE:
+# SYMBOLS-NEXT: external
+# SYMBOLS-NEXT: local_unreferenced
+# SYMBOLS-PRE-NEXT: debug_discardable_sym
+# SYMBOLS-NEXT: debug_undiscardable_sym
+# SYMBOLS-EMPTY:
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ ]
+ Alignment: 4
+ SectionData: 00000000
+ - Name: .data
+ Characteristics: [ ]
+ Alignment: 4
+ SectionData: 00000000
+ - Name: .bss
+ Characteristics: [ ]
+ Alignment: 4
+ SectionData: 00000000
+ - Name: .xdata
+ Characteristics: [ ]
+ Alignment: 4
+ SectionData: 00000000
+ - Name: .reloc
+ Characteristics: [ IMAGE_SCN_MEM_DISCARDABLE ]
+ Alignment: 4
+ SectionData: 00000000
+ - Name: .debug_discardable
+ Characteristics: [ IMAGE_SCN_MEM_DISCARDABLE ]
+ Alignment: 4
+ SectionData: 00000000
+ - Name: .debug_undiscardable
+ Characteristics: [ ]
+ Alignment: 4
+ SectionData: 00000000
+ - Name: .llvm_addrsig
+ Characteristics: [ IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 4
+ SectionData: 00000000
+symbols:
+ - Name: external
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: local_unreferenced
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: debug_discardable_sym
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: debug_undiscardable_sym
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index dd2e4829218..13d8efde37c 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -26,9 +26,20 @@ namespace coff {
using namespace object;
using namespace COFF;
+static bool isDebugSection(const Section &Sec) {
+ return Sec.Name.startswith(".debug");
+}
+
static Error handleArgs(const CopyConfig &Config, Object &Obj) {
// Perform the actual section removals.
Obj.removeSections([&Config](const Section &Sec) {
+ if (Config.StripDebug || Config.StripAll || Config.StripAllGNU ||
+ Config.DiscardAll || Config.StripUnneeded) {
+ if (isDebugSection(Sec) &&
+ (Sec.Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0)
+ return true;
+ }
+
if (is_contained(Config.ToRemove, Sec.Name))
return true;
--
2.17.1

View File

@ -0,0 +1,27 @@
From a495c9ae6fb3367e6b59d8d245273ed3669754f0 Mon Sep 17 00:00:00 2001
From: Martin Storsjo <martin@martin.st>
Date: Sat, 19 Jan 2019 19:42:23 +0000
Subject: [PATCH] [llvm-objcopy] [COFF] Remove a superfluous namespace
qualification. NFC.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351658 91177308-0d34-0410-b5e6-96231b3b80d8
---
tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index ceebf600b3a..437dccbd3d5 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -78,7 +78,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
}
void executeObjcopyOnBinary(const CopyConfig &Config,
- object::COFFObjectFile &In, Buffer &Out) {
+ COFFObjectFile &In, Buffer &Out) {
COFFReader Reader(In);
Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create();
if (!ObjOrErr)
--
2.17.1

View File

@ -0,0 +1,224 @@
From d37f67c7311cd371d9ff1afd398bc92f309e6baf Mon Sep 17 00:00:00 2001
From: Martin Storsjo <martin@martin.st>
Date: Tue, 22 Jan 2019 10:58:09 +0000
Subject: [PATCH] [llvm-objcopy] [COFF] Update symbol indices in weak externals
Differential Revision: https://reviews.llvm.org/D57006
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351800 91177308-0d34-0410-b5e6-96231b3b80d8
---
.../llvm-objcopy/COFF/weak-external.test | 49 +++++++++++++++++++
tools/llvm-objcopy/COFF/Object.h | 2 +
tools/llvm-objcopy/COFF/Reader.cpp | 24 ++++++++-
tools/llvm-objcopy/COFF/Reader.h | 2 +-
tools/llvm-objcopy/COFF/Writer.cpp | 16 +++++-
tools/llvm-objcopy/COFF/Writer.h | 2 +-
6 files changed, 89 insertions(+), 6 deletions(-)
create mode 100644 test/tools/llvm-objcopy/COFF/weak-external.test
diff --git a/llvm/test/tools/llvm-objcopy/COFF/weak-external.test b/llvm/test/tools/llvm-objcopy/COFF/weak-external.test
new file mode 100644
index 00000000000..d36a53b4eb1
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/COFF/weak-external.test
@@ -0,0 +1,49 @@
+# RUN: yaml2obj %s > %t.in.o
+
+# RUN: llvm-objdump -t %t.in.o | FileCheck %s --check-prefixes=SYMBOLS,SYMBOLS-PRE
+
+# RUN: llvm-objcopy -N func %t.in.o %t.out.o
+# RUN: llvm-objdump -t %t.out.o | FileCheck %s --check-prefixes=SYMBOLS,SYMBOLS-POST
+
+# RUN: not llvm-objcopy -N .weak.foobar.file1 %t.in.o %t.err.o 2>&1 | FileCheck %s --check-prefix=ERROR
+
+# SYMBOLS: SYMBOL TABLE:
+# SYMBOLS-PRE-NEXT: func
+# SYMBOLS-NEXT: .weak.foobar.file1
+# SYMBOLS-NEXT: foobar
+# SYMBOLS-PRE-NEXT: AUX indx 1
+# SYMBOLS-POST-NEXT: AUX indx 0
+# SYMBOLS-EMPTY:
+
+# ERROR: Symbol 'foobar' is missing its weak target
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ ]
+symbols:
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: .weak.foobar.file1
+ Value: 1
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: foobar
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_WEAK_EXTERNAL
+ WeakExternal:
+ TagIndex: 1
+ Characteristics: IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY
+...
diff --git a/llvm/tools/llvm-objcopy/COFF/Object.h b/llvm/tools/llvm-objcopy/COFF/Object.h
index 8e200369f0b..0630f9c5ff8 100644
--- a/llvm/tools/llvm-objcopy/COFF/Object.h
+++ b/llvm/tools/llvm-objcopy/COFF/Object.h
@@ -11,6 +11,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/BinaryFormat/COFF.h"
@@ -47,6 +48,7 @@ struct Symbol {
std::vector<uint8_t> AuxData;
ssize_t TargetSectionId;
ssize_t AssociativeComdatTargetSectionId = 0;
+ Optional<size_t> WeakTargetSymbolId;
size_t UniqueId;
size_t RawIndex;
bool Referenced;
diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.cpp b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
index 20ff32a59dc..2446277cc2b 100644
--- a/llvm/tools/llvm-objcopy/COFF/Reader.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
@@ -121,12 +121,18 @@ Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const {
// For section definitions, check if it is comdat associative, and if
// it is, find the target section unique id.
const coff_aux_section_definition *SD = SymRef.getSectionDefinition();
+ const coff_aux_weak_external *WE = SymRef.getWeakExternal();
if (SD && SD->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
int32_t Index = SD->getNumber(IsBigObj);
if (Index <= 0 || static_cast<uint32_t>(Index - 1) >= Sections.size())
return createStringError(object_error::parse_failed,
"Unexpected associative section index");
Sym.AssociativeComdatTargetSectionId = Sections[Index - 1].UniqueId;
+ } else if (WE) {
+ // This is a raw symbol index for now, but store it in the Symbol
+ // until we've added them to the Object, which assigns the final
+ // unique ids.
+ Sym.WeakTargetSymbolId = WE->TagIndex;
}
I += 1 + SymRef.getNumberOfAuxSymbols();
}
@@ -134,13 +140,27 @@ Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const {
return Error::success();
}
-Error COFFReader::setRelocTargets(Object &Obj) const {
+Error COFFReader::setSymbolTargets(Object &Obj) const {
std::vector<const Symbol *> RawSymbolTable;
for (const Symbol &Sym : Obj.getSymbols()) {
RawSymbolTable.push_back(&Sym);
for (size_t I = 0; I < Sym.Sym.NumberOfAuxSymbols; I++)
RawSymbolTable.push_back(nullptr);
}
+ for (Symbol &Sym : Obj.getMutableSymbols()) {
+ // Convert WeakTargetSymbolId from the original raw symbol index to
+ // a proper unique id.
+ if (Sym.WeakTargetSymbolId) {
+ if (*Sym.WeakTargetSymbolId >= RawSymbolTable.size())
+ return createStringError(object_error::parse_failed,
+ "Weak external reference out of range");
+ const Symbol *Target = RawSymbolTable[*Sym.WeakTargetSymbolId];
+ if (Target == nullptr)
+ return createStringError(object_error::parse_failed,
+ "Invalid SymbolTableIndex");
+ Sym.WeakTargetSymbolId = Target->UniqueId;
+ }
+ }
for (Section &Sec : Obj.getMutableSections()) {
for (Relocation &R : Sec.Relocs) {
if (R.Reloc.SymbolTableIndex >= RawSymbolTable.size())
@@ -184,7 +204,7 @@ Expected<std::unique_ptr<Object>> COFFReader::create() const {
return std::move(E);
if (Error E = readSymbols(*Obj, IsBigObj))
return std::move(E);
- if (Error E = setRelocTargets(*Obj))
+ if (Error E = setSymbolTargets(*Obj))
return std::move(E);
return std::move(Obj);
diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.h b/llvm/tools/llvm-objcopy/COFF/Reader.h
index 4493705e73c..ec15369db0b 100644
--- a/llvm/tools/llvm-objcopy/COFF/Reader.h
+++ b/llvm/tools/llvm-objcopy/COFF/Reader.h
@@ -28,7 +28,7 @@ class COFFReader {
Error readExecutableHeaders(Object &Obj) const;
Error readSections(Object &Obj) const;
Error readSymbols(Object &Obj, bool IsBigObj) const;
- Error setRelocTargets(Object &Obj) const;
+ Error setSymbolTargets(Object &Obj) const;
public:
explicit COFFReader(const COFFObjectFile &O) : COFFObj(O) {}
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
index 0321f94a896..4f57131d5ab 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
@@ -38,7 +38,7 @@ Error COFFWriter::finalizeRelocTargets() {
return Error::success();
}
-Error COFFWriter::finalizeSectionNumbers() {
+Error COFFWriter::finalizeSymbolContents() {
for (Symbol &Sym : Obj.getMutableSymbols()) {
if (Sym.TargetSectionId <= 0) {
// Undefined, or a special kind of symbol. These negative values
@@ -75,6 +75,18 @@ Error COFFWriter::finalizeSectionNumbers() {
SD->NumberHighPart = static_cast<uint16_t>(SDSectionNumber >> 16);
}
}
+ // Check that we actually have got AuxData to match the weak symbol target
+ // we want to set. Only >= 1 would be required, but only == 1 makes sense.
+ if (Sym.WeakTargetSymbolId && Sym.Sym.NumberOfAuxSymbols == 1) {
+ coff_aux_weak_external *WE =
+ reinterpret_cast<coff_aux_weak_external *>(Sym.AuxData.data());
+ const Symbol *Target = Obj.findSymbol(*Sym.WeakTargetSymbolId);
+ if (Target == nullptr)
+ return createStringError(object_error::invalid_symbol_index,
+ "Symbol '%s' is missing its weak target",
+ Sym.Name.str().c_str());
+ WE->TagIndex = Target->RawIndex;
+ }
}
return Error::success();
}
@@ -137,7 +149,7 @@ std::pair<size_t, size_t> COFFWriter::finalizeSymbolTable() {
Error COFFWriter::finalize(bool IsBigObj) {
if (Error E = finalizeRelocTargets())
return E;
- if (Error E = finalizeSectionNumbers())
+ if (Error E = finalizeSymbolContents())
return E;
size_t SizeOfHeaders = 0;
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.h b/llvm/tools/llvm-objcopy/COFF/Writer.h
index a967a103df9..9b1cfa91d00 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.h
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.h
@@ -31,7 +31,7 @@ class COFFWriter {
StringTableBuilder StrTabBuilder;
Error finalizeRelocTargets();
- Error finalizeSectionNumbers();
+ Error finalizeSymbolContents();
void layoutSections();
size_t finalizeStringTable();
template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable();
--
2.17.1

View File

@ -0,0 +1,242 @@
From d3b89a1637cddee1c61e59257cfe92227ead29e5 Mon Sep 17 00:00:00 2001
From: Martin Storsjo <martin@martin.st>
Date: Tue, 22 Jan 2019 10:57:59 +0000
Subject: [PATCH] [llvm-objcopy] Consistently use createStringError instead of
make_error<StringError>
This was requested in the review of D57006.
Also add missing quotes around symbol names in error messages.
Differential Revision: https://reviews.llvm.org/D57014
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351799 91177308-0d34-0410-b5e6-96231b3b80d8
---
.../llvm-objcopy/COFF/remove-section.test | 2 +-
tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 8 ++---
tools/llvm-objcopy/COFF/Object.cpp | 5 ++-
tools/llvm-objcopy/COFF/Reader.cpp | 24 +++++++-------
tools/llvm-objcopy/COFF/Writer.cpp | 33 +++++++++----------
tools/llvm-objcopy/ELF/ELFObjcopy.cpp | 10 +++---
6 files changed, 40 insertions(+), 42 deletions(-)
diff --git a/llvm/test/tools/llvm-objcopy/COFF/remove-section.test b/llvm/test/tools/llvm-objcopy/COFF/remove-section.test
index b3dfb0b98cb..6dc8f6a6c2e 100644
--- a/test/tools/llvm-objcopy/COFF/remove-section.test
+++ b/llvm/test/tools/llvm-objcopy/COFF/remove-section.test
@@ -96,7 +96,7 @@
# Removing the .comdat section fails, since the .text section has relocations
# against it.
#
-# ERROR-RELOC: Relocation target foo ({{.*}}) not found
+# ERROR-RELOC: Relocation target 'foo' ({{.*}}) not found
#
#
# Removing the .comdat section and .text (with a relocation against .comdat)
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index 99929d10a1f..8d8f53d13d8 100644
--- a/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -84,10 +84,10 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
// Explicitly removing a referenced symbol is an error.
if (Sym.Referenced)
reportError(Config.OutputFilename,
- make_error<StringError>(
- "not stripping symbol '" + Sym.Name +
- "' because it is named in a relocation.",
- llvm::errc::invalid_argument));
+ createStringError(llvm::errc::invalid_argument,
+ "not stripping symbol '%s' because it is "
+ "named in a relocation.",
+ Sym.Name.str().c_str()));
return true;
}
diff --git a/llvm/tools/llvm-objcopy/COFF/Object.cpp b/llvm/tools/llvm-objcopy/COFF/Object.cpp
index fc87d9e574d..83435dffa98 100644
--- a/tools/llvm-objcopy/COFF/Object.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Object.cpp
@@ -56,9 +56,8 @@ Error Object::markSymbols() {
for (const Relocation &R : Sec.Relocs) {
auto It = SymbolMap.find(R.Target);
if (It == SymbolMap.end())
- return make_error<StringError>("Relocation target " + Twine(R.Target) +
- " not found",
- object_error::invalid_symbol_index);
+ return createStringError(object_error::invalid_symbol_index,
+ "Relocation target %zu not found", R.Target);
It->second->Referenced = true;
}
}
diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.cpp b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
index c8abe2913a2..20ff32a59dc 100644
--- a/tools/llvm-objcopy/COFF/Reader.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
@@ -77,8 +77,8 @@ Error COFFReader::readSections(Object &Obj) const {
if (auto EC = COFFObj.getSectionName(Sec, S.Name))
return errorCodeToError(EC);
if (Sec->hasExtendedRelocations())
- return make_error<StringError>("Extended relocations not supported yet",
- object_error::parse_failed);
+ return createStringError(object_error::parse_failed,
+ "Extended relocations not supported yet");
}
Obj.addSections(Sections);
return Error::success();
@@ -116,16 +116,16 @@ Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const {
Sections.size())
Sym.TargetSectionId = Sections[SymRef.getSectionNumber() - 1].UniqueId;
else
- return make_error<StringError>("Section number out of range",
- object_error::parse_failed);
+ return createStringError(object_error::parse_failed,
+ "Section number out of range");
// For section definitions, check if it is comdat associative, and if
// it is, find the target section unique id.
const coff_aux_section_definition *SD = SymRef.getSectionDefinition();
if (SD && SD->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
int32_t Index = SD->getNumber(IsBigObj);
if (Index <= 0 || static_cast<uint32_t>(Index - 1) >= Sections.size())
- return make_error<StringError>("Unexpected associative section index",
- object_error::parse_failed);
+ return createStringError(object_error::parse_failed,
+ "Unexpected associative section index");
Sym.AssociativeComdatTargetSectionId = Sections[Index - 1].UniqueId;
}
I += 1 + SymRef.getNumberOfAuxSymbols();
@@ -144,12 +144,12 @@ Error COFFReader::setRelocTargets(Object &Obj) const {
for (Section &Sec : Obj.getMutableSections()) {
for (Relocation &R : Sec.Relocs) {
if (R.Reloc.SymbolTableIndex >= RawSymbolTable.size())
- return make_error<StringError>("SymbolTableIndex out of range",
- object_error::parse_failed);
+ return createStringError(object_error::parse_failed,
+ "SymbolTableIndex out of range");
const Symbol *Sym = RawSymbolTable[R.Reloc.SymbolTableIndex];
if (Sym == nullptr)
- return make_error<StringError>("Invalid SymbolTableIndex",
- object_error::parse_failed);
+ return createStringError(object_error::parse_failed,
+ "Invalid SymbolTableIndex");
R.Target = Sym->UniqueId;
R.TargetName = Sym->Name;
}
@@ -169,8 +169,8 @@ Expected<std::unique_ptr<Object>> COFFReader::create() const {
Obj->CoffFileHeader = *CFH;
} else {
if (!CBFH)
- return make_error<StringError>("No COFF file header returned",
- object_error::parse_failed);
+ return createStringError(object_error::parse_failed,
+ "No COFF file header returned");
// Only copying the few fields from the bigobj header that we need
// and won't recreate in the end.
Obj->CoffFileHeader.Machine = CBFH->Machine;
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
index 9fb7812672b..0321f94a896 100644
--- a/tools/llvm-objcopy/COFF/Writer.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
@@ -29,10 +29,9 @@ Error COFFWriter::finalizeRelocTargets() {
for (Relocation &R : Sec.Relocs) {
const Symbol *Sym = Obj.findSymbol(R.Target);
if (Sym == nullptr)
- return make_error<StringError>("Relocation target " + R.TargetName +
- " (" + Twine(R.Target) +
- ") not found",
- object_error::invalid_symbol_index);
+ return createStringError(object_error::invalid_symbol_index,
+ "Relocation target '%s' (%zu) not found",
+ R.TargetName.str().c_str(), R.Target);
R.Reloc.SymbolTableIndex = Sym->RawIndex;
}
}
@@ -48,9 +47,9 @@ Error COFFWriter::finalizeSectionNumbers() {
} else {
const Section *Sec = Obj.findSection(Sym.TargetSectionId);
if (Sec == nullptr)
- return make_error<StringError>("Symbol " + Sym.Name +
- " points to a removed section",
- object_error::invalid_symbol_index);
+ return createStringError(object_error::invalid_symbol_index,
+ "Symbol '%s' points to a removed section",
+ Sym.Name.str().c_str());
Sym.Sym.SectionNumber = Sec->Index;
if (Sym.Sym.NumberOfAuxSymbols == 1 &&
@@ -65,9 +64,10 @@ Error COFFWriter::finalizeSectionNumbers() {
} else {
Sec = Obj.findSection(Sym.AssociativeComdatTargetSectionId);
if (Sec == nullptr)
- return make_error<StringError>(
- "Symbol " + Sym.Name + " is associative to a removed section",
- object_error::invalid_symbol_index);
+ return createStringError(
+ object_error::invalid_symbol_index,
+ "Symbol '%s' is associative to a removed section",
+ Sym.Name.str().c_str());
SDSectionNumber = Sec->Index;
}
// Update the section definition with the new section number.
@@ -343,9 +343,8 @@ Error COFFWriter::patchDebugDirectory() {
S.Header.VirtualAddress + S.Header.SizeOfRawData) {
if (Dir->RelativeVirtualAddress + Dir->Size >
S.Header.VirtualAddress + S.Header.SizeOfRawData)
- return make_error<StringError>(
- "Debug directory extends past end of section",
- object_error::parse_failed);
+ return createStringError(object_error::parse_failed,
+ "Debug directory extends past end of section");
size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress;
uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset;
@@ -361,15 +360,15 @@ Error COFFWriter::patchDebugDirectory() {
return Error::success();
}
}
- return make_error<StringError>("Debug directory not found",
- object_error::parse_failed);
+ return createStringError(object_error::parse_failed,
+ "Debug directory not found");
}
Error COFFWriter::write() {
bool IsBigObj = Obj.getSections().size() > MaxNumberOfSections16;
if (IsBigObj && Obj.IsPE)
- return make_error<StringError>("Too many sections for executable",
- object_error::parse_failed);
+ return createStringError(object_error::parse_failed,
+ "Too many sections for executable");
return write(IsBigObj);
}
diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
index db0cd76ced4..a2996395c1f 100644
--- a/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
@@ -185,9 +185,10 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
for (auto &Sec : Obj.sections()) {
if (Sec.Name == SecName) {
if (Sec.OriginalData.empty())
- return make_error<StringError>("Can't dump section \"" + SecName +
- "\": it has no contents",
- object_error::parse_failed);
+ return createStringError(
+ object_error::parse_failed,
+ "Can't dump section \"%s\": it has no contents",
+ SecName.str().c_str());
Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
FileOutputBuffer::create(Filename, Sec.OriginalData.size());
if (!BufferOrErr)
@@ -200,8 +201,7 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
return Error::success();
}
}
- return make_error<StringError>("Section not found",
- object_error::parse_failed);
+ return createStringError(object_error::parse_failed, "Section not found");
}
static bool isCompressed(const SectionBase &Section) {
--
2.17.1

View File

@ -0,0 +1,330 @@
From 8cf7aa39d7c9461e2d765f6d4fa7e0925571695f Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht@google.com>
Date: Tue, 22 Jan 2019 23:49:16 +0000
Subject: [PATCH] [llvm-objcopy] Return Error from Buffer::allocate(),
[ELF]Writer::finalize(), and [ELF]Writer::commit()
Summary:
This patch changes a few methods to return Error instead of manually calling error/reportError to abort. This will make it easier to extract into a library.
Note that error() takes just a string (this patch also adds an overload that takes an Error), while reportError() takes string + [error/code]. To help unify things, use FileError to associate a given filename with an error. Note that this takes some special care (for now), e.g. calling reportError(FileName, <something that could be FileError>) will duplicate the filename. The goal is to eventually remove reportError() and have every error associated with a file to be a FileError, and just one error handling block at the tool level.
This change was suggested in D56806. I took it a little further than suggested, but completely fixing llvm-objcopy will take a couple more patches. If this approach looks good, I'll commit this and apply similar patche(s) for the rest.
This change is NFC in terms of non-error related code, although the error message changes in one context.
Reviewers: alexshap, jhenderson, jakehehrlich, mstorsjo, espindola
Reviewed By: alexshap, jhenderson
Subscribers: llvm-commits, emaste, arichardson
Differential Revision: https://reviews.llvm.org/D56930
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351896 91177308-0d34-0410-b5e6-96231b3b80d8
---
.../ELF/fail-no-output-directory.test | 2 +-
tools/llvm-objcopy/Buffer.cpp | 20 ++++++++++++-----
tools/llvm-objcopy/Buffer.h | 6 ++---
tools/llvm-objcopy/COFF/Writer.cpp | 3 ++-
tools/llvm-objcopy/ELF/ELFObjcopy.cpp | 18 ++++++++++-----
tools/llvm-objcopy/ELF/Object.cpp | 22 ++++++++++---------
tools/llvm-objcopy/ELF/Object.h | 12 +++++-----
tools/llvm-objcopy/llvm-objcopy.cpp | 15 +++++++++++--
tools/llvm-objcopy/llvm-objcopy.h | 1 +
9 files changed, 64 insertions(+), 35 deletions(-)
diff --git a/llvm/test/tools/llvm-objcopy/ELF/fail-no-output-directory.test b/llvm/test/tools/llvm-objcopy/ELF/fail-no-output-directory.test
index f66b2e09fce..732046fa925 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/fail-no-output-directory.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/fail-no-output-directory.test
@@ -1,6 +1,6 @@
# RUN: yaml2obj %s > %t
# RUN: not llvm-objcopy %t no/such/dir 2>&1 | FileCheck %s
-# CHECK: failed to open no/such/dir:
+# CHECK: error: 'no/such/dir': No such file or directory
!ELF
FileHeader:
diff --git a/llvm/tools/llvm-objcopy/Buffer.cpp b/llvm/tools/llvm-objcopy/Buffer.cpp
index 2da03dee1af..1789097f276 100644
--- a/llvm/tools/llvm-objcopy/Buffer.cpp
+++ b/llvm/tools/llvm-objcopy/Buffer.cpp
@@ -17,23 +17,31 @@ namespace objcopy {
Buffer::~Buffer() {}
-void FileBuffer::allocate(size_t Size) {
+Error FileBuffer::allocate(size_t Size) {
Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable);
- handleAllErrors(BufferOrErr.takeError(), [this](const ErrorInfoBase &E) {
- error("failed to open " + getName() + ": " + E.message());
- });
+ // FileOutputBuffer::create() returns an Error that is just a wrapper around
+ // std::error_code. Wrap it in FileError to include the actual filename.
+ if (!BufferOrErr)
+ return createFileError(getName(), BufferOrErr.takeError());
Buf = std::move(*BufferOrErr);
+ return Error::success();
}
-Error FileBuffer::commit() { return Buf->commit(); }
+Error FileBuffer::commit() {
+ Error Err = Buf->commit();
+ // FileOutputBuffer::commit() returns an Error that is just a wrapper around
+ // std::error_code. Wrap it in FileError to include the actual filename.
+ return Err ? createFileError(getName(), std::move(Err)) : std::move(Err);
+}
uint8_t *FileBuffer::getBufferStart() {
return reinterpret_cast<uint8_t *>(Buf->getBufferStart());
}
-void MemBuffer::allocate(size_t Size) {
+Error MemBuffer::allocate(size_t Size) {
Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName());
+ return Error::success();
}
Error MemBuffer::commit() { return Error::success(); }
diff --git a/llvm/tools/llvm-objcopy/Buffer.h b/llvm/tools/llvm-objcopy/Buffer.h
index 482777fe05c..40670accac2 100644
--- a/llvm/tools/llvm-objcopy/Buffer.h
+++ b/llvm/tools/llvm-objcopy/Buffer.h
@@ -27,7 +27,7 @@ class Buffer {
public:
virtual ~Buffer();
- virtual void allocate(size_t Size) = 0;
+ virtual Error allocate(size_t Size) = 0;
virtual uint8_t *getBufferStart() = 0;
virtual Error commit() = 0;
@@ -39,7 +39,7 @@ class FileBuffer : public Buffer {
std::unique_ptr<FileOutputBuffer> Buf;
public:
- void allocate(size_t Size) override;
+ Error allocate(size_t Size) override;
uint8_t *getBufferStart() override;
Error commit() override;
@@ -50,7 +50,7 @@ class MemBuffer : public Buffer {
std::unique_ptr<WritableMemoryBuffer> Buf;
public:
- void allocate(size_t Size) override;
+ Error allocate(size_t Size) override;
uint8_t *getBufferStart() override;
Error commit() override;
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
index 4f57131d5ab..db3589bb119 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
@@ -324,7 +324,8 @@ Error COFFWriter::write(bool IsBigObj) {
if (Error E = finalize(IsBigObj))
return E;
- Buf.allocate(FileSize);
+ if (Error E = Buf.allocate(FileSize))
+ return E;
writeHeaders(IsBigObj);
writeSections();
diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
index a2996395c1f..2a52f1f9951 100644
--- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
@@ -176,8 +176,10 @@ static void splitDWOToFile(const CopyConfig &Config, const Reader &Reader,
DWOFile->Machine = Config.OutputArch.getValue().EMachine;
FileBuffer FB(File);
auto Writer = createWriter(Config, *DWOFile, FB, OutputElfType);
- Writer->finalize();
- Writer->write();
+ if (Error E = Writer->finalize())
+ error(std::move(E));
+ if (Error E = Writer->write())
+ error(std::move(E));
}
static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
@@ -542,8 +544,10 @@ void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
handleArgs(Config, *Obj, Reader, OutputElfType);
std::unique_ptr<Writer> Writer =
createWriter(Config, *Obj, Out, OutputElfType);
- Writer->finalize();
- Writer->write();
+ if (Error E = Writer->finalize())
+ error(std::move(E));
+ if (Error E = Writer->write())
+ error(std::move(E));
}
void executeObjcopyOnBinary(const CopyConfig &Config,
@@ -570,8 +574,10 @@ void executeObjcopyOnBinary(const CopyConfig &Config,
handleArgs(Config, *Obj, Reader, OutputElfType);
std::unique_ptr<Writer> Writer =
createWriter(Config, *Obj, Out, OutputElfType);
- Writer->finalize();
- Writer->write();
+ if (Error E = Writer->finalize())
+ error(std::move(E));
+ if (Error E = Writer->write())
+ error(std::move(E));
if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) {
linkToBuildIdDir(Config, Config.OutputFilename,
Config.BuildIdLinkOutput.getValue(), BuildIdBytes);
diff --git a/llvm/tools/llvm-objcopy/ELF/Object.cpp b/llvm/tools/llvm-objcopy/ELF/Object.cpp
index fecb752a39f..ef5dc5d7951 100644
--- a/llvm/tools/llvm-objcopy/ELF/Object.cpp
+++ b/llvm/tools/llvm-objcopy/ELF/Object.cpp
@@ -1488,17 +1488,16 @@ template <class ELFT> size_t ELFWriter<ELFT>::totalSize() const {
NullSectionSize;
}
-template <class ELFT> void ELFWriter<ELFT>::write() {
+template <class ELFT> Error ELFWriter<ELFT>::write() {
writeEhdr();
writePhdrs();
writeSectionData();
if (WriteSectionHeaders)
writeShdrs();
- if (auto E = Buf.commit())
- reportError(Buf.getName(), errorToErrorCode(std::move(E)));
+ return Buf.commit();
}
-template <class ELFT> void ELFWriter<ELFT>::finalize() {
+template <class ELFT> Error ELFWriter<ELFT>::finalize() {
// It could happen that SectionNames has been removed and yet the user wants
// a section header table output. We need to throw an error if a user tries
// to do that.
@@ -1582,21 +1581,22 @@ template <class ELFT> void ELFWriter<ELFT>::finalize() {
Section.finalize();
}
- Buf.allocate(totalSize());
+ if (Error E = Buf.allocate(totalSize()))
+ return E;
SecWriter = llvm::make_unique<ELFSectionWriter<ELFT>>(Buf);
+ return Error::success();
}
-void BinaryWriter::write() {
+Error BinaryWriter::write() {
for (auto &Section : Obj.sections()) {
if ((Section.Flags & SHF_ALLOC) == 0)
continue;
Section.accept(*SecWriter);
}
- if (auto E = Buf.commit())
- reportError(Buf.getName(), errorToErrorCode(std::move(E)));
+ return Buf.commit();
}
-void BinaryWriter::finalize() {
+Error BinaryWriter::finalize() {
// TODO: Create a filter range to construct OrderedSegments from so that this
// code can be deduped with assignOffsets above. This should also solve the
// todo below for LayoutSections.
@@ -1675,8 +1675,10 @@ void BinaryWriter::finalize() {
TotalSize = std::max(TotalSize, Section->Offset + Section->Size);
}
- Buf.allocate(TotalSize);
+ if (Error E = Buf.allocate(TotalSize))
+ return E;
SecWriter = llvm::make_unique<BinarySectionWriter>(Buf);
+ return Error::success();
}
template class ELFBuilder<ELF64LE>;
diff --git a/llvm/tools/llvm-objcopy/ELF/Object.h b/llvm/tools/llvm-objcopy/ELF/Object.h
index 0dcb0d888bc..9e2b64be9dc 100644
--- a/llvm/tools/llvm-objcopy/ELF/Object.h
+++ b/llvm/tools/llvm-objcopy/ELF/Object.h
@@ -193,8 +193,8 @@ protected:
public:
virtual ~Writer();
- virtual void finalize() = 0;
- virtual void write() = 0;
+ virtual Error finalize() = 0;
+ virtual Error write() = 0;
Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {}
};
@@ -226,8 +226,8 @@ public:
virtual ~ELFWriter() {}
bool WriteSectionHeaders = true;
- void finalize() override;
- void write() override;
+ Error finalize() override;
+ Error write() override;
ELFWriter(Object &Obj, Buffer &Buf, bool WSH)
: Writer(Obj, Buf), WriteSectionHeaders(WSH) {}
};
@@ -240,8 +240,8 @@ private:
public:
~BinaryWriter() {}
- void finalize() override;
- void write() override;
+ Error finalize() override;
+ Error write() override;
BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {}
};
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
index d27395f2ae0..75d513546b7 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -56,6 +56,16 @@ LLVM_ATTRIBUTE_NORETURN void error(Twine Message) {
exit(1);
}
+LLVM_ATTRIBUTE_NORETURN void error(Error E) {
+ assert(E);
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS);
+ OS.flush();
+ WithColor::error(errs(), ToolName) << Buf;
+ exit(1);
+}
+
LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) {
assert(EC);
WithColor::error(errs(), ToolName)
@@ -100,10 +110,11 @@ static Error deepWriteArchive(StringRef ArcName,
// NewArchiveMember still requires them even though writeArchive does not
// write them on disk.
FileBuffer FB(Member.MemberName);
- FB.allocate(Member.Buf->getBufferSize());
+ if (Error E = FB.allocate(Member.Buf->getBufferSize()))
+ return E;
std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(),
FB.getBufferStart());
- if (auto E = FB.commit())
+ if (Error E = FB.commit())
return E;
}
return Error::success();
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.h b/llvm/tools/llvm-objcopy/llvm-objcopy.h
index 46d8339576c..18a789ca1f8 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.h
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.h
@@ -19,6 +19,7 @@ namespace llvm {
namespace objcopy {
LLVM_ATTRIBUTE_NORETURN extern void error(Twine Message);
+LLVM_ATTRIBUTE_NORETURN extern void error(Error E);
LLVM_ATTRIBUTE_NORETURN extern void reportError(StringRef File, Error E);
LLVM_ATTRIBUTE_NORETURN extern void reportError(StringRef File,
std::error_code EC);
--
2.17.1

View File

@ -0,0 +1,112 @@
[COFF] Add address-taken import thunks to the fid table
https://bugs.llvm.org/show_bug.cgi?id=39799
https://reviews.llvm.org/D58739
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1390,19 +1390,47 @@
// symbol in an executable section.
static void maybeAddAddressTakenFunction(SymbolRVASet &AddressTakenSyms,
Symbol *S) {
- auto *D = dyn_cast_or_null<DefinedCOFF>(S);
-
- // Ignore undefined symbols and references to non-functions (e.g. globals and
- // labels).
- if (!D ||
- D->getCOFFSymbol().getComplexType() != COFF::IMAGE_SYM_DTYPE_FUNCTION)
+ if (!S)
return;
- // Mark the symbol as address taken if it's in an executable section.
- Chunk *RefChunk = D->getChunk();
- OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr;
- if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE)
- addSymbolToRVASet(AddressTakenSyms, D);
+ switch (S->kind()) {
+ case Symbol::DefinedLocalImportKind:
+ case Symbol::DefinedImportDataKind:
+ // Defines an __imp_ pointer, so it is data, so it is ignored.
+ break;
+ case Symbol::DefinedCommonKind:
+ // Common is always data, so it is ignored.
+ break;
+ case Symbol::DefinedAbsoluteKind:
+ case Symbol::DefinedSyntheticKind:
+ // Absolute is never code, synthetic generally isn't and usually isn't
+ // determinable.
+ break;
+ case Symbol::LazyKind:
+ case Symbol::UndefinedKind:
+ // Undefined symbols resolve to zero, so they don't have an RVA. Lazy
+ // symbols shouldn't have relocations.
+ break;
+
+ case Symbol::DefinedImportThunkKind:
+ // Thunks are always code, include them.
+ addSymbolToRVASet(AddressTakenSyms, cast<Defined>(S));
+ break;
+
+ case Symbol::DefinedRegularKind: {
+ // This is a regular, defined, symbol from a COFF file. Mark the symbol as
+ // address taken if the symbol type is function and it's in an executable
+ // section.
+ auto *D = cast<DefinedRegular>(S);
+ if (D->getCOFFSymbol().getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION) {
+ Chunk *RefChunk = D->getChunk();
+ OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr;
+ if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE)
+ addSymbolToRVASet(AddressTakenSyms, D);
+ }
+ break;
+ }
+ }
}
// Visit all relocations from all section contributions of this object file and
--- a/lld/test/COFF/guardcf-thunk.s
+++ b/lld/test/COFF/guardcf-thunk.s
@@ -0,0 +1,43 @@
+# REQUIRES: x86
+
+# Make a DLL that exports exportfn1.
+# RUN: yaml2obj < %p/Inputs/export.yaml > %t.obj
+# RUN: lld-link /out:%t.dll /dll %t.obj /export:exportfn1 /implib:%t.lib
+
+# Make an obj that takes the address of that exported function.
+# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t2.obj
+# RUN: lld-link -entry:main -guard:cf %t2.obj %t.lib -nodefaultlib -out:%t.exe
+# RUN: llvm-readobj -coff-load-config %t.exe | FileCheck %s
+
+# Check that the gfids table contains *exactly* two entries, one for exportfn1
+# and one for main.
+# CHECK: GuardFidTable [
+# CHECK-NEXT: 0x{{[0-9A-Fa-f]+0$}}
+# CHECK-NEXT: 0x{{[0-9A-Fa-f]+0$}}
+# CHECK-NEXT: ]
+
+
+ .def @feat.00;
+ .scl 3;
+ .type 0;
+ .endef
+ .globl @feat.00
+@feat.00 = 0x001
+
+ .section .text,"rx"
+ .def main; .scl 2; .type 32; .endef
+ .global main
+main:
+ leaq exportfn1(%rip), %rax
+ retq
+
+ .section .rdata,"dr"
+.globl _load_config_used
+_load_config_used:
+ .long 256
+ .fill 124, 1, 0
+ .quad __guard_fids_table
+ .quad __guard_fids_count
+ .long __guard_flags
+ .fill 128, 1, 0
+

View File

@ -1666,9 +1666,10 @@ def security_hardening_cflags(hardening_flag, asan, optimize, c_compiler, target
js_ldflags.append("-Wl,--dynamicbase")
# Control Flow Guard (CFG) ----------------------------
# See Bug 1525588 for why this doesn't work on Windows ARM
# On aarch64, this is enabled only with explicit --enable-hardening
# (roughly: automation) due to a dependency on a patched clang-cl.
if c_compiler.type == 'clang-cl' and c_compiler.version >= '8' and \
target.cpu != 'aarch64':
(target.cpu != 'aarch64' or hardening_flag):
flags.append("-guard:cf")
js_flags.append("-guard:cf")
# nolongjmp is needed because clang doesn't emit the CFG tables of

View File

@ -19,8 +19,8 @@ syn = { features = ["clone-impls", "default", "derive", "extra-traits", "full",
log = { features = ["release_max_level_info", "release_max_level_warn", "std"], version = "0.4.6" }
serde = { features = ["default", "rc", "serde_derive", "std"], version = "1.0.66" }
serde_derive = { features = ["default", "deserialize_in_place"], version = "1.0.66" }
quote = { features = ["default", "proc-macro"], version = "0.5.2" }
proc-macro2 = { features = ["default", "proc-macro"], version = "0.3.5" }
quote = { features = ["default", "proc-macro"], version = "0.6.11" }
proc-macro2 = { features = ["default", "proc-macro"], version = "0.4.27" }
[target."cfg(windows)".dependencies.winapi]
version = "0.3.6"

View File

@ -18,7 +18,7 @@ function execOut(...args) {
let out;
let err;
try {
out = execFileSync(...args);
out = execFileSync(...args, { silent: true });
} catch (e) {
out = e.stdout;
err = e.stderr;
@ -26,8 +26,7 @@ function execOut(...args) {
return { out: out.toString(), err: err && err.toString() };
}
function logErrors(tool, text, regexp) {
const errors = text.match(regexp) || [];
function logErrors(tool, errors) {
for (const error of errors) {
console.log(`TEST-UNEXPECTED-FAIL ${tool} | ${error}`);
}
@ -64,23 +63,37 @@ function eslint() {
logStart("Eslint");
const { out } = execOut("yarn", ["lint:js"]);
console.log(out);
const errors = logErrors("eslint", out, / {2}error {2}(.*)/g);
const errors = logErrors("eslint", out.match(/ {2}error {2}(.*)/g) || []);
return errors.length == 0;
}
function jest() {
logStart("Jest");
const { out, err } = execOut("yarn", ["test"]);
console.log(err);
const errors = logErrors("jest", err || "", / {4}✕(.*)/g);
return errors.length == 0;
const { out } = execOut("yarn", ["test-ci"]);
// Remove the non-JSON logs mixed with the JSON output by yarn.
const jsonOut = out.substring(out.indexOf("{"), out.lastIndexOf("}") + 1);
const results = JSON.parse(jsonOut);
const failed = results.numFailedTests == 0;
// The individual failing tests are in jammed into the same message string :/
const errors = [].concat(
...results.testResults.map(r =>
r.message.split("\n").filter(l => l.includes("●"))
)
);
logErrors("jest", errors);
return failed;
}
function stylelint() {
logStart("Stylelint");
const { out } = execOut("yarn", ["lint:css"]);
console.log(out);
const errors = logErrors("stylelint", out, / {2}✖(.*)/g);
const errors = logErrors("stylelint", out.match(/ {2}✖(.*)/g) || []);
return errors.length == 0;
}
@ -91,8 +104,7 @@ function jsxAccessibility() {
console.log(out);
const errors = logErrors(
"eslint (jsx accessibility)",
out,
/ {2}error {2}(.*)/g
out.match(/ {2}error {2}(.*)/g) || []
);
return errors.length == 0;
}
@ -100,8 +112,8 @@ function jsxAccessibility() {
function lintMd() {
logStart("Remark");
const { out, err } = execOut("yarn", ["lint:md"]);
const errors = logErrors("remark", err || "", /warning(.+)/g);
const { err } = execOut("yarn", ["lint:md"]);
const errors = logErrors("remark", (err || "").match(/warning(.+)/g) || []);
return errors.length == 0;
}
@ -127,7 +139,8 @@ console.log({
jestPassed,
styleLintPassed,
jsxAccessibilityPassed,
remarkPassed
remarkPassed,
});
process.exitCode = success ? 0 : 1;
console.log("CODE", process.exitCode);

View File

@ -30,6 +30,7 @@
"mochih": "yarn mochi -- --headless --",
"mochici": "mochii --mc ./firefox --ci --default-test-path devtools/client/debugger --headless --",
"test": "TZ=Africa/Nairobi jest",
"test-ci": "TZ=Africa/Nairobi jest --json",
"test:watch": "jest --watch",
"test:coverage": "yarn test --coverage",
"test:all": "yarn test; yarn lint; yarn flow",

View File

@ -102,7 +102,7 @@ exports[`ObjectInspector - dimTopLevelWindow renders sub-level window 1`] = `
role="treeitem"
>
<span
className="tree-indent"
className="tree-indent tree-last-indent"
>
</span>
@ -254,7 +254,7 @@ exports[`ObjectInspector - dimTopLevelWindow renders window as expected when dim
role="treeitem"
>
<span
className="tree-indent"
className="tree-indent tree-last-indent"
>
</span>

View File

@ -47,11 +47,10 @@ describe("network request", () => {
it("timed out fetch", async () => {
global.fetch.mockImplementation(async () => {});
try {
await networkRequest("foo");
} catch (e) {
// eslint-disable-next-line jest/valid-expect-in-promise
networkRequest("foo").catch(e => {
expect(e.message).toEqual("Connect timeout error");
}
});
jest.runAllTimers();
});

View File

@ -119,32 +119,4 @@ describe("worker utils", () => {
],
});
});
it("test a task completing when the worker has shutdown", async () => {
const dispatcher = new WorkerDispatcher();
const postMessageMock = jest.fn();
const addEventListenerMock = jest.fn();
const terminateMock = jest.fn();
global.Worker = jest.fn(() => {
return {
postMessage: postMessageMock,
addEventListener: addEventListenerMock,
terminate: terminateMock,
};
});
dispatcher.start();
const task = dispatcher.task("foo");
try {
await task("bar");
} catch (e) {
expect(e).toEqual("Oops, The worker has shutdown!");
}
const listener = addEventListenerMock.mock.calls[0][1];
dispatcher.stop();
listener({ data: { id: 1 } });
});
});

View File

@ -4,29 +4,88 @@
// @flow
import { uniq, remove } from "lodash";
import { asyncStore } from "../utils/prefs";
import {
getActiveEventListeners,
getEventListenerExpanded,
} from "../selectors";
import type { ThunkArgs } from "./types";
import type { EventListenerBreakpoints } from "../types";
export function addEventListeners(events: EventListenerBreakpoints) {
return async ({ dispatch, client }: ThunkArgs) => {
await dispatch({
type: "ADD_EVENT_LISTENERS",
events,
});
const newList = await asyncStore.eventListenerBreakpoints;
client.setEventListenerBreakpoints(newList);
async function updateBreakpoints(dispatch, client, newEvents: string[]) {
dispatch({ type: "UPDATE_EVENT_LISTENERS", active: newEvents });
const current = await asyncStore.eventListenerBreakpoints;
asyncStore.eventListenerBreakpoints = {
...current,
active: newEvents,
};
client.setEventListenerBreakpoints(newEvents);
}
async function updateExpanded(dispatch, newExpanded: string[]) {
dispatch({
type: "UPDATE_EVENT_LISTENER_EXPANDED",
expanded: newExpanded,
});
const current = await asyncStore.eventListenerBreakpoints;
asyncStore.eventListenerBreakpoints = {
...current,
expanded: newExpanded,
};
}
export function removeEventListeners(events: EventListenerBreakpoints) {
return async ({ dispatch, client }: ThunkArgs) => {
await dispatch({
type: "REMOVE_EVENT_LISTENERS",
events,
});
const newList = await asyncStore.eventListenerBreakpoints;
client.setEventListenerBreakpoints(newList);
export function addEventListenerBreakpoints(eventsToAdd: string[]) {
return async ({ dispatch, client, getState }: ThunkArgs) => {
const activeListenerBreakpoints = await getActiveEventListeners(getState());
const newEvents = uniq([...eventsToAdd, ...activeListenerBreakpoints]);
updateBreakpoints(dispatch, client, newEvents);
};
}
export function removeEventListenerBreakpoints(eventsToRemove: string[]) {
return async ({ dispatch, client, getState }: ThunkArgs) => {
const activeListenerBreakpoints = await getActiveEventListeners(getState());
const newEvents = remove(
activeListenerBreakpoints,
event => !eventsToRemove.includes(event)
);
updateBreakpoints(dispatch, client, newEvents);
};
}
export function addEventListenerExpanded(category: string) {
return async ({ dispatch, getState }: ThunkArgs) => {
const expanded = await getEventListenerExpanded(getState());
const newExpanded = uniq([...expanded, category]);
await updateExpanded(dispatch, newExpanded);
};
}
export function removeEventListenerExpanded(category: string) {
return async ({ dispatch, getState }: ThunkArgs) => {
const expanded = await getEventListenerExpanded(getState());
const newExpanded = expanded.filter(expand => expand != category);
updateExpanded(dispatch, newExpanded);
};
}
export function getEventListenerBreakpointTypes() {
return async ({ dispatch, client }: ThunkArgs) => {
const categories = await client.getEventListenerBreakpointTypes();
dispatch({ type: "RECEIVE_EVENT_LISTENER_TYPES", categories });
};
}

View File

@ -16,7 +16,7 @@ import {
import { makeWhyNormal } from "../../../utils/test-mockup";
import * as parser from "../../../workers/parser/index";
import { parserWorker } from "../../../test/tests-setup";
const { isStepping } = selectors;
@ -147,7 +147,7 @@ describe("pause", () => {
await dispatch(actions.newGeneratedSource(makeSource("foo1")));
await dispatch(actions.paused(mockPauseInfo));
const cx = selectors.getThreadContext(getState());
const getNextStepSpy = jest.spyOn(parser, "getNextStep");
const getNextStepSpy = jest.spyOn(parserWorker, "getNextStep");
dispatch(actions.stepOver(cx));
expect(getNextStepSpy).not.toHaveBeenCalled();
expect(isStepping(getState(), "FakeThread")).toBeTruthy();
@ -166,7 +166,7 @@ describe("pause", () => {
await dispatch(actions.paused(mockPauseInfo));
const cx = selectors.getThreadContext(getState());
const getNextStepSpy = jest.spyOn(parser, "getNextStep");
const getNextStepSpy = jest.spyOn(parserWorker, "getNextStep");
dispatch(actions.stepOver(cx));
expect(getNextStepSpy).toHaveBeenCalled();
getNextStepSpy.mockRestore();
@ -188,7 +188,7 @@ describe("pause", () => {
await dispatch(actions.paused(mockPauseInfo));
const cx = selectors.getThreadContext(getState());
const getNextStepSpy = jest.spyOn(parser, "getNextStep");
const getNextStepSpy = jest.spyOn(parserWorker, "getNextStep");
dispatch(actions.stepOver(cx));
expect(getNextStepSpy).toHaveBeenCalled();
getNextStepSpy.mockRestore();

View File

@ -124,27 +124,6 @@ Array [
]
`;
exports[`project text search should search a specific source 2`] = `
Array [
Object {
"filepath": "http://localhost:8000/examples/bar",
"matches": Array [
Object {
"column": 9,
"line": 1,
"match": "bla",
"matchIndex": 9,
"sourceId": "bar",
"type": "MATCH",
"value": "function bla(x, y) {",
},
],
"sourceId": "bar",
"type": "RESULT",
},
]
`;
exports[`project text search should search all the loaded sources based on the query 1`] = `
Array [
Object {

View File

@ -159,6 +159,30 @@ export type {
export type { panelPositionType } from "./UIAction";
export type { ASTAction } from "./ASTAction";
type ActiveEventListener = string;
type EventListenerEvent = { name: string, id: ActiveEventListener };
type EventListenerCategory = { name: string, events: EventListenerEvent[] };
export type EventListenerActiveList = ActiveEventListener[];
export type EventListenerCategoryList = EventListenerCategory[];
export type EventListenerExpandedList = string[];
export type EventListenerAction =
| {|
+type: "UPDATE_EVENT_LISTENERS",
+active: EventListenerActiveList,
|}
| {|
+type: "RECEIVE_EVENT_LISTENER_TYPES",
+categories: EventListenerCategoryList,
|}
| {|
+type: "UPDATE_EVENT_LISTENER_EXPANDED",
+expanded: EventListenerExpandedList,
|};
/**
* Actions: Source, Breakpoint, and Navigation
*
@ -180,4 +204,5 @@ export type Action =
| FileTextSearchAction
| ProjectTextSearchAction
| DebuggeeAction
| SourceTreeAction;
| SourceTreeAction
| EventListenerAction;

View File

@ -47,6 +47,9 @@ export async function onConnect(connection: any, actions: Object) {
skipBreakpoints: prefs.skipPausing,
});
// Retrieve possible event listener breakpoints
actions.getEventListenerBreakpointTypes();
// In Firefox, we need to initially request all of the sources. This
// usually fires off individual `newSource` notifications as the
// debugger finds them, but there may be existing sources already in

View File

@ -16,7 +16,6 @@ import type {
BreakpointLocation,
BreakpointOptions,
PendingLocation,
EventListenerBreakpoints,
Frame,
FrameId,
GeneratedSourceData,
@ -36,6 +35,8 @@ import type {
SourcesPacket,
} from "./types";
import type { EventListenerCategoryList } from "../../actions/types";
let workerClients: Object;
let threadClient: ThreadClient;
let tabTarget: TabTarget;
@ -361,8 +362,17 @@ function interrupt(thread: string): Promise<*> {
return lookupThreadClient(thread).interrupt();
}
function setEventListenerBreakpoints(eventTypes: EventListenerBreakpoints) {
// TODO: Figure out what sendpoint we want to hit
async function setEventListenerBreakpoints(ids: string[]) {
await threadClient.setActiveEventBreakpoints(ids);
await forEachWorkerThread(thread => thread.setActiveEventBreakpoints(ids));
}
// eslint-disable-next-line
async function getEventListenerBreakpointTypes(): Promise<
EventListenerCategoryList
> {
const { value } = await threadClient.getAvailableEventBreakpoints();
return value;
}
function pauseGrip(thread: string, func: Function): ObjectClient {
@ -525,6 +535,7 @@ const clientCommands = {
sendPacket,
setSkipPausing,
setEventListenerBreakpoints,
getEventListenerBreakpointTypes,
waitForWorkers,
detachWorkers,
hasWasmSupport,

View File

@ -25,6 +25,8 @@ import type {
Range,
} from "../../types";
import type { EventListenerCategoryList } from "../../actions/types";
type URL = string;
/**
@ -369,7 +371,10 @@ export type ThreadClient = {
actor: ActorId,
request: (payload: Object) => Promise<*>,
url: string,
setEventListenerBreakpoints: (string[]) => void,
setActiveEventBreakpoints: (string[]) => void,
getAvailableEventBreakpoints: () => Promise<{|
value: EventListenerCategoryList,
|}>,
skipBreakpoints: boolean => Promise<{| skip: boolean |}>,
};

View File

@ -15,6 +15,7 @@ import {
bootstrapStore,
bootstrapWorkers,
} from "../utils/bootstrap";
import { initialBreakpointsState } from "../reducers/breakpoints";
import type { Panel } from "./firefox/types";
@ -47,7 +48,12 @@ async function loadInitialState() {
const breakpoints = initialBreakpointsState(xhrBreakpoints);
return { pendingBreakpoints, tabs, breakpoints, eventListenerBreakpoints };
return {
pendingBreakpoints,
tabs,
breakpoints,
eventListenerBreakpoints,
};
}
function getClient(connection: any) {

View File

@ -9,141 +9,121 @@ import classnames from "classnames";
import { connect } from "../../utils/connect";
import actions from "../../actions";
import { getActiveEventListeners } from "../../selectors";
import {
getActiveEventListeners,
getEventListenerBreakpointTypes,
getEventListenerExpanded,
} from "../../selectors";
import AccessibleImage from "../shared/AccessibleImage";
import type { EventListenerBreakpoints } from "../../types";
import type {
EventListenerActiveList,
EventListenerCategoryList,
EventListenerExpandedList,
} from "../../actions/types";
import "./EventListeners.css";
const CATEGORIES = {
Mouse: ["click", "mouseover", "dblclick"],
Keyboard: ["keyup", "keydown"],
};
type Props = {
addEventListeners: typeof actions.addEventListeners,
removeEventListeners: typeof actions.removeEventListeners,
activeEventListeners: EventListenerBreakpoints,
categories: EventListenerCategoryList,
expandedCategories: EventListenerExpandedList,
activeEventListeners: EventListenerActiveList,
addEventListeners: typeof actions.addEventListenerBreakpoints,
removeEventListeners: typeof actions.removeEventListenerBreakpoints,
addEventListenerExpanded: typeof actions.addEventListenerExpanded,
removeEventListenerExpanded: typeof actions.removeEventListenerExpanded,
};
type State = {
expandedCategories: string[],
};
function getKey(category: string, eventType: string) {
return `${category}:${eventType}`;
}
class EventListeners extends Component<Props, State> {
constructor(props) {
super(props);
this.state = {
expandedCategories: [],
};
}
onCategoryToggle(category, event) {
const { expandedCategories } = this.state;
class EventListeners extends Component<Props> {
onCategoryToggle(category) {
const {
expandedCategories,
removeEventListenerExpanded,
addEventListenerExpanded,
} = this.props;
if (expandedCategories.includes(category)) {
this.setState({
expandedCategories: expandedCategories.filter(
eventCategory => eventCategory !== category
),
});
removeEventListenerExpanded(category);
} else {
this.setState({
expandedCategories: [...expandedCategories, category],
});
addEventListenerExpanded(category);
}
}
onCategoryClick(category, isChecked) {
const { addEventListeners, removeEventListeners } = this.props;
const events = CATEGORIES[category].map(eventType =>
getKey(category, eventType)
);
const eventsIds = category.events.map(event => event.id);
if (isChecked) {
addEventListeners(events);
addEventListeners(eventsIds);
} else {
removeEventListeners(events);
removeEventListeners(eventsIds);
}
}
onEventTypeClick(eventType, isChecked) {
onEventTypeClick(eventId, isChecked) {
const { addEventListeners, removeEventListeners } = this.props;
if (isChecked) {
addEventListeners([eventType]);
addEventListeners([eventId]);
} else {
removeEventListeners([eventType]);
removeEventListeners([eventId]);
}
}
renderCategoryHeading(category) {
const { expandedCategories } = this.state;
const { activeEventListeners } = this.props;
const { activeEventListeners, expandedCategories } = this.props;
const { events } = category;
const eventTypes = CATEGORIES[category];
const expanded = expandedCategories.includes(category);
const checked = eventTypes.every(eventType =>
activeEventListeners.includes(getKey(category, eventType))
);
const expanded = expandedCategories.includes(category.name);
const checked = events.every(({ id }) => activeEventListeners.includes(id));
const indeterminate =
!checked &&
eventTypes.some(eventType =>
activeEventListeners.includes(getKey(category, eventType))
);
!checked && events.some(({ id }) => activeEventListeners.includes(id));
return (
<div className="event-listener-header">
<button
className="event-listener-expand"
onClick={e => this.onCategoryToggle(category, e)}
onClick={() => this.onCategoryToggle(category.name)}
>
<AccessibleImage className={classnames("arrow", { expanded })} />
</button>
<label className="event-listener-label">
<input
type="checkbox"
value={category}
value={category.name}
onChange={e => this.onCategoryClick(category, e.target.checked)}
checked={checked}
ref={el => el && (el.indeterminate = indeterminate)}
/>
<span className="event-listener-category">{category}</span>
<span className="event-listener-category">{category.name}</span>
</label>
</div>
);
}
renderCategoryListing(category) {
const { activeEventListeners } = this.props;
const { expandedCategories } = this.state;
const { activeEventListeners, expandedCategories } = this.props;
const expanded = expandedCategories.includes(category);
const expanded = expandedCategories.includes(category.name);
if (!expanded) {
return null;
}
return (
<ul>
{CATEGORIES[category].map(eventType => {
const key = getKey(category, eventType);
{category.events.map(event => {
return (
<li className="event-listener-event" key={key}>
<li className="event-listener-event" key={event.id}>
<label className="event-listener-label">
<input
type="checkbox"
value={key}
onChange={e => this.onEventTypeClick(key, e.target.checked)}
checked={activeEventListeners.includes(key)}
value={event.id}
onChange={e =>
this.onEventTypeClick(event.id, e.target.checked)
}
checked={activeEventListeners.includes(event.id)}
/>
<span className="event-listener-name">{eventType}</span>
<span className="event-listener-name">{event.name}</span>
</label>
</li>
);
@ -153,12 +133,14 @@ class EventListeners extends Component<Props, State> {
}
render() {
const { categories } = this.props;
return (
<div className="event-listeners-content">
<ul className="event-listeners-list">
{Object.keys(CATEGORIES).map(category => {
{categories.map((category, index) => {
return (
<li className="event-listener-group" key={category}>
<li className="event-listener-group" key={index}>
{this.renderCategoryHeading(category)}
{this.renderCategoryListing(category)}
</li>
@ -170,14 +152,20 @@ class EventListeners extends Component<Props, State> {
}
}
const mapStateToProps = state => ({
activeEventListeners: getActiveEventListeners(state),
});
const mapStateToProps = state => {
return {
activeEventListeners: getActiveEventListeners(state),
categories: getEventListenerBreakpointTypes(state),
expandedCategories: getEventListenerExpanded(state),
};
};
export default connect(
mapStateToProps,
{
addEventListeners: actions.addEventListeners,
removeEventListeners: actions.removeEventListeners,
addEventListeners: actions.addEventListenerBreakpoints,
removeEventListeners: actions.removeEventListenerBreakpoints,
addEventListenerExpanded: actions.addEventListenerExpanded,
removeEventListenerExpanded: actions.removeEventListenerExpanded,
}
)(EventListeners);

View File

@ -0,0 +1,82 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
// @flow
import React from "react";
import { shallow } from "enzyme";
import EventListeners from "../EventListeners";
function getCategories() {
return [
{
name: "Category 1",
events: [
{ name: "Subcategory 1", id: "category1.subcategory1" },
{ name: "Subcategory 2", id: "category1.subcategory2" },
],
},
{
name: "Category 2",
events: [
{ name: "Subcategory 3", id: "category2.subcategory1" },
{ name: "Subcategory 4", id: "category2.subcategory2" },
],
},
];
}
function generateDefaults(overrides = {}) {
const defaults = {
activeEventListeners: [],
expandedCategories: [],
categories: [],
};
return { ...defaults, ...overrides };
}
function render(overrides = {}) {
const props = generateDefaults(overrides);
// $FlowIgnore
const component = shallow(<EventListeners.WrappedComponent {...props} />);
return { component, props };
}
describe("EventListeners", () => {
it("should render", async () => {
const { component } = render();
expect(component).toMatchSnapshot();
});
it("should render categories appropriately", async () => {
const props = {
...generateDefaults(),
categories: getCategories(),
};
const { component } = render(props);
expect(component).toMatchSnapshot();
});
it("should render expanded categories appropriately", async () => {
const props = {
...generateDefaults(),
categories: getCategories(),
expandedCategories: ["Category 2"],
};
const { component } = render(props);
expect(component).toMatchSnapshot();
});
it("should render checked subcategories appropriately", async () => {
const props = {
...generateDefaults(),
categories: getCategories(),
activeEventListeners: ["category1.subcategory2"],
expandedCategories: ["Category 1"],
};
const { component } = render(props);
expect(component).toMatchSnapshot();
});
});

View File

@ -0,0 +1,320 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EventListeners should render 1`] = `
<div
className="event-listeners-content"
>
<ul
className="event-listeners-list"
/>
</div>
`;
exports[`EventListeners should render categories appropriately 1`] = `
<div
className="event-listeners-content"
>
<ul
className="event-listeners-list"
>
<li
className="event-listener-group"
key="0"
>
<div
className="event-listener-header"
>
<button
className="event-listener-expand"
onClick={[Function]}
>
<AccessibleImage
className="arrow"
/>
</button>
<label
className="event-listener-label"
>
<input
checked={false}
onChange={[Function]}
type="checkbox"
value="Category 1"
/>
<span
className="event-listener-category"
>
Category 1
</span>
</label>
</div>
</li>
<li
className="event-listener-group"
key="1"
>
<div
className="event-listener-header"
>
<button
className="event-listener-expand"
onClick={[Function]}
>
<AccessibleImage
className="arrow"
/>
</button>
<label
className="event-listener-label"
>
<input
checked={false}
onChange={[Function]}
type="checkbox"
value="Category 2"
/>
<span
className="event-listener-category"
>
Category 2
</span>
</label>
</div>
</li>
</ul>
</div>
`;
exports[`EventListeners should render checked subcategories appropriately 1`] = `
<div
className="event-listeners-content"
>
<ul
className="event-listeners-list"
>
<li
className="event-listener-group"
key="0"
>
<div
className="event-listener-header"
>
<button
className="event-listener-expand"
onClick={[Function]}
>
<AccessibleImage
className="arrow expanded"
/>
</button>
<label
className="event-listener-label"
>
<input
checked={false}
onChange={[Function]}
type="checkbox"
value="Category 1"
/>
<span
className="event-listener-category"
>
Category 1
</span>
</label>
</div>
<ul>
<li
className="event-listener-event"
key="category1.subcategory1"
>
<label
className="event-listener-label"
>
<input
checked={false}
onChange={[Function]}
type="checkbox"
value="category1.subcategory1"
/>
<span
className="event-listener-name"
>
Subcategory 1
</span>
</label>
</li>
<li
className="event-listener-event"
key="category1.subcategory2"
>
<label
className="event-listener-label"
>
<input
checked={true}
onChange={[Function]}
type="checkbox"
value="category1.subcategory2"
/>
<span
className="event-listener-name"
>
Subcategory 2
</span>
</label>
</li>
</ul>
</li>
<li
className="event-listener-group"
key="1"
>
<div
className="event-listener-header"
>
<button
className="event-listener-expand"
onClick={[Function]}
>
<AccessibleImage
className="arrow"
/>
</button>
<label
className="event-listener-label"
>
<input
checked={false}
onChange={[Function]}
type="checkbox"
value="Category 2"
/>
<span
className="event-listener-category"
>
Category 2
</span>
</label>
</div>
</li>
</ul>
</div>
`;
exports[`EventListeners should render expanded categories appropriately 1`] = `
<div
className="event-listeners-content"
>
<ul
className="event-listeners-list"
>
<li
className="event-listener-group"
key="0"
>
<div
className="event-listener-header"
>
<button
className="event-listener-expand"
onClick={[Function]}
>
<AccessibleImage
className="arrow"
/>
</button>
<label
className="event-listener-label"
>
<input
checked={false}
onChange={[Function]}
type="checkbox"
value="Category 1"
/>
<span
className="event-listener-category"
>
Category 1
</span>
</label>
</div>
</li>
<li
className="event-listener-group"
key="1"
>
<div
className="event-listener-header"
>
<button
className="event-listener-expand"
onClick={[Function]}
>
<AccessibleImage
className="arrow expanded"
/>
</button>
<label
className="event-listener-label"
>
<input
checked={false}
onChange={[Function]}
type="checkbox"
value="Category 2"
/>
<span
className="event-listener-category"
>
Category 2
</span>
</label>
</div>
<ul>
<li
className="event-listener-event"
key="category2.subcategory1"
>
<label
className="event-listener-label"
>
<input
checked={false}
onChange={[Function]}
type="checkbox"
value="category2.subcategory1"
/>
<span
className="event-listener-name"
>
Subcategory 3
</span>
</label>
</li>
<li
className="event-listener-event"
key="category2.subcategory2"
>
<label
className="event-listener-label"
>
<input
checked={false}
onChange={[Function]}
type="checkbox"
value="category2.subcategory2"
/>
<span
className="event-listener-name"
>
Subcategory 4
</span>
</label>
</li>
</ul>
</li>
</ul>
</div>
`;

View File

@ -59,10 +59,6 @@ describe("Popover", () => {
</Popover>
);
const div = document.createElement("div");
const event = { currentTarget: div };
beforeEach(() => {
onMouseLeave.mockClear();
onKeyDown.mockClear();
@ -72,31 +68,6 @@ describe("Popover", () => {
it("render (tooltip)", () => expect(tooltip).toMatchSnapshot());
it("calls mouseLeave", () => {
popover.find(".popover").simulate("mouseleave", event);
expect(onMouseLeave).toHaveBeenCalled();
});
it("calls mouseLeave (tooltip)", () => {
tooltip.find(".tooltip").simulate("mouseleave", event);
expect(onMouseLeave).toHaveBeenCalled();
});
it("no mouse leave on bracket or gap", () => {
popover.find(".bracket-arrow").simulate("mouseleave", event);
expect(onMouseLeave).not.toHaveBeenCalled();
});
it("calls keyDown", () => {
popover.find(".popover").simulate("keydown", { key: "Escape" });
expect(onKeyDown).toHaveBeenCalled();
});
it("calls keyDown (tooltip)", () => {
tooltip.find(".tooltip").simulate("keydown", { key: "Escape" });
expect(onKeyDown).toHaveBeenCalled();
});
it("mount popover", () => {
const mountedPopover = mount(
<Popover

View File

@ -26,8 +26,6 @@ exports[`Popover mount popover 1`] = `
>
<div
className="popover orientation-right"
onKeyDown={[MockFunction]}
onMouseLeave={[MockFunction]}
style={
Object {
"left": 505,
@ -88,8 +86,6 @@ exports[`Popover mount tooltip 1`] = `
>
<div
className="tooltip"
onKeyDown={[MockFunction]}
onMouseLeave={[MockFunction]}
style={
Object {
"left": -8,
@ -111,8 +107,6 @@ exports[`Popover mount tooltip 1`] = `
exports[`Popover render (tooltip) 1`] = `
<div
className="tooltip"
onKeyDown={[MockFunction]}
onMouseLeave={[MockFunction]}
style={
Object {
"left": 0,
@ -156,8 +150,6 @@ exports[`Popover render 1`] = `
>
<div
className="popover orientation-right"
onKeyDown={[MockFunction]}
onMouseLeave={[MockFunction]}
style={
Object {
"left": 505,

View File

@ -337,7 +337,7 @@ exports[`ProjectSearch found search results 1`] = `
role="treeitem"
>
<span
className="tree-indent"
className="tree-indent tree-last-indent"
>
</span>
@ -405,7 +405,7 @@ exports[`ProjectSearch found search results 1`] = `
role="treeitem"
>
<span
className="tree-indent"
className="tree-indent tree-last-indent"
>
</span>
@ -473,7 +473,7 @@ exports[`ProjectSearch found search results 1`] = `
role="treeitem"
>
<span
className="tree-indent"
className="tree-indent tree-last-indent"
>
</span>
@ -613,7 +613,7 @@ exports[`ProjectSearch found search results 1`] = `
role="treeitem"
>
<span
className="tree-indent"
className="tree-indent tree-last-indent"
>
</span>
@ -681,7 +681,7 @@ exports[`ProjectSearch found search results 1`] = `
role="treeitem"
>
<span
className="tree-indent"
className="tree-indent tree-last-indent"
>
</span>

View File

@ -4,45 +4,61 @@
// @flow
import { uniq } from "lodash";
import type { State } from "./types";
import type {
EventListenerAction,
EventListenerActiveList,
EventListenerCategoryList,
EventListenerExpandedList,
} from "../actions/types";
import { asyncStore } from "../utils/prefs";
import type { EventListenerBreakpoints } from "../types";
export type EventListenersState = {
active: EventListenerActiveList,
categories: EventListenerCategoryList,
expanded: EventListenerExpandedList,
};
type OuterState = { eventListenerBreakpoints: EventListenerBreakpoints };
export function initialEventListenerState(): EventListenersState {
return {
active: [],
categories: [],
expanded: [],
};
}
function update(state: EventListenerBreakpoints = [], action: any) {
function update(
state: EventListenersState = initialEventListenerState(),
action: EventListenerAction
) {
switch (action.type) {
case "ADD_EVENT_LISTENERS":
return updateEventTypes("add", state, action.events);
case "UPDATE_EVENT_LISTENERS":
return { ...state, active: action.active };
case "REMOVE_EVENT_LISTENERS":
return updateEventTypes("remove", state, action.events);
case "RECEIVE_EVENT_LISTENER_TYPES":
return { ...state, categories: action.categories };
case "UPDATE_EVENT_LISTENER_EXPANDED":
return { ...state, expanded: action.expanded };
default:
return state;
}
}
function updateEventTypes(
addOrRemove: string,
currentEvents: EventListenerBreakpoints,
events: EventListenerBreakpoints
): EventListenerBreakpoints {
let newEventListeners;
if (addOrRemove === "add") {
newEventListeners = uniq([...currentEvents, ...events]);
} else {
newEventListeners = currentEvents.filter(event => !events.includes(event));
}
asyncStore.eventListenerBreakpoints = newEventListeners;
return newEventListeners;
export function getActiveEventListeners(state: State): EventListenerActiveList {
return state.eventListenerBreakpoints.active;
}
export function getActiveEventListeners(state: OuterState) {
return state.eventListenerBreakpoints;
export function getEventListenerBreakpointTypes(
state: State
): EventListenerCategoryList {
return state.eventListenerBreakpoints.categories;
}
export function getEventListenerExpanded(
state: State
): EventListenerExpandedList {
return state.eventListenerBreakpoints.expanded;
}
export default update;

View File

@ -24,11 +24,13 @@ import type { SourceActorsState } from "./source-actors";
import type { TabList } from "./tabs";
import type { UIState } from "./ui";
import type { QuickOpenState } from "./quick-open";
import type { EventListenersState } from "./event-listeners";
export type State = {
ast: ASTState,
breakpoints: BreakpointsState,
expressions: Record<ExpressionState>,
eventListenerBreakpoints: EventListenersState,
debuggee: DebuggeeState,
fileSearch: Record<FileSearchState>,
pause: PauseState,

View File

@ -64,6 +64,7 @@ function formatException(reason, p) {
}
export const parserWorker = new ParserDispatcher();
export const evaluationsParser = new ParserDispatcher();
beforeAll(() => {
startSourceMapWorker(
@ -74,6 +75,7 @@ beforeAll(() => {
path.join(rootPath, "src/workers/pretty-print/worker.js")
);
parserWorker.start(path.join(rootPath, "src/workers/parser/worker.js"));
evaluationsParser.start(path.join(rootPath, "src/workers/parser/worker.js"));
startSearchWorker(path.join(rootPath, "src/workers/search/worker.js"));
process.on("unhandledRejection", formatException);
});
@ -82,6 +84,7 @@ afterAll(() => {
stopSourceMapWorker();
stopPrettyPrintWorker();
parserWorker.stop();
evaluationsParser.stop();
stopSearchWorker();
process.removeListener("unhandledRejection", formatException);
});
@ -90,6 +93,7 @@ afterEach(() => {});
beforeEach(async () => {
parserWorker.clear();
evaluationsParser.clear();
clearHistory();
clearDocuments();
prefs.projectDirectoryRoot = "";

View File

@ -12,7 +12,7 @@ import { asyncStoreHelper } from "./asyncStoreHelper";
// Schema version to bump when the async store format has changed incompatibly
// and old stores should be cleared. This needs to match the prefs schema
// version in devtools/client/preferences/debugger.js.
const prefsSchemaVersion = "1.0.9";
const prefsSchemaVersion = "1.0.10";
const pref = Services.pref;
if (isDevelopment()) {
@ -134,7 +134,7 @@ export const asyncStore = asyncStoreHelper("debugger", {
pendingBreakpoints: ["pending-breakpoints", {}],
tabs: ["tabs", []],
xhrBreakpoints: ["xhr-breakpoints", []],
eventListenerBreakpoints: ["event-listener-breakpoints", []],
eventListenerBreakpoints: ["event-listener-breakpoints", undefined],
});
export function verifyPrefSchema() {
@ -143,6 +143,7 @@ export function verifyPrefSchema() {
asyncStore.pendingBreakpoints = {};
asyncStore.tabs = [];
asyncStore.xhrBreakpoints = [];
asyncStore.eventListenerBreakpoints = undefined;
prefs.debuggerPrefsSchemaVersion = prefsSchemaVersion;
}
}

View File

@ -15,7 +15,7 @@ import reducers from "../reducers";
import actions from "../actions";
import * as selectors from "../selectors";
import { getHistory } from "../test/utils/history";
import { parserWorker } from "../test/tests-setup";
import { parserWorker, evaluationsParser } from "../test/tests-setup";
import configureStore from "../actions/utils/create-store";
import sourceQueue from "../utils/source-queue";
import type { Source, OriginalSourceData, GeneratedSourceData } from "../types";
@ -44,6 +44,7 @@ function createStore(client: any, initialState: any = {}, sourceMapsMock: any) {
client,
sourceMaps: sourceMapsMock !== undefined ? sourceMapsMock : sourceMaps,
parser: parserWorker,
evaluationsParser,
};
},
})(combineReducers(reducers), initialState);

View File

@ -39,14 +39,14 @@ exports.MarkerDOMUtils = {
buildTitle: function(doc, marker) {
const blueprint = MarkerBlueprintUtils.getBlueprintFor(marker);
const hbox = doc.createElement("hbox");
const hbox = doc.createXULElement("hbox");
hbox.setAttribute("align", "center");
const bullet = doc.createElement("hbox");
const bullet = doc.createXULElement("hbox");
bullet.className = `marker-details-bullet marker-color-${blueprint.colorName}`;
const title = MarkerBlueprintUtils.getMarkerLabel(marker);
const label = doc.createElement("label");
const label = doc.createXULElement("label");
label.className = "marker-details-type";
label.setAttribute("value", title);
@ -87,15 +87,15 @@ exports.MarkerDOMUtils = {
* @return Node
*/
buildNameValueLabel: function(doc, field, value) {
const hbox = doc.createElement("hbox");
const hbox = doc.createXULElement("hbox");
hbox.className = "marker-details-labelcontainer";
const nameLabel = doc.createElement("label");
const nameLabel = doc.createXULElement("label");
nameLabel.className = "plain marker-details-name-label";
nameLabel.setAttribute("value", field);
hbox.appendChild(nameLabel);
const valueLabel = doc.createElement("label");
const valueLabel = doc.createXULElement("label");
valueLabel.className = "plain marker-details-value-label";
valueLabel.setAttribute("value", value);
hbox.appendChild(valueLabel);
@ -119,7 +119,7 @@ exports.MarkerDOMUtils = {
container.className = "marker-details-stack";
container.setAttribute("type", type);
const nameLabel = doc.createElement("label");
const nameLabel = doc.createXULElement("label");
nameLabel.className = "plain marker-details-name-label";
nameLabel.setAttribute("value", L10N.getStr(`marker.field.${type}`));
container.appendChild(nameLabel);
@ -144,8 +144,8 @@ exports.MarkerDOMUtils = {
// cause is in this frame and should be displayed.
if (wasAsyncParent) {
const asyncStr = L10N.getFormatStr("marker.field.asyncStack", frame.asyncCause);
const asyncBox = doc.createElement("hbox");
const asyncLabel = doc.createElement("label");
const asyncBox = doc.createXULElement("hbox");
const asyncLabel = doc.createXULElement("label");
asyncLabel.className = "devtools-monospace";
asyncLabel.setAttribute("value", asyncStr);
asyncBox.appendChild(asyncLabel);
@ -153,28 +153,28 @@ exports.MarkerDOMUtils = {
wasAsyncParent = false;
}
const hbox = doc.createElement("hbox");
const hbox = doc.createXULElement("hbox");
if (displayName) {
const functionLabel = doc.createElement("label");
const functionLabel = doc.createXULElement("label");
functionLabel.className = "devtools-monospace";
functionLabel.setAttribute("value", displayName);
hbox.appendChild(functionLabel);
}
if (url) {
const linkNode = doc.createElement("a");
const linkNode = doc.createXULElement("a");
linkNode.className = "waterfall-marker-location devtools-source-link";
linkNode.href = url;
linkNode.draggable = false;
linkNode.setAttribute("title", url);
const urlLabel = doc.createElement("label");
const urlLabel = doc.createXULElement("label");
urlLabel.className = "filename";
urlLabel.setAttribute("value", getSourceNames(url).short);
linkNode.appendChild(urlLabel);
const lineLabel = doc.createElement("label");
const lineLabel = doc.createXULElement("label");
lineLabel.className = "line-number";
lineLabel.setAttribute("value", `:${line}`);
linkNode.appendChild(lineLabel);
@ -191,7 +191,7 @@ exports.MarkerDOMUtils = {
}
if (!displayName && !url) {
const unknownLabel = doc.createElement("label");
const unknownLabel = doc.createXULElement("label");
unknownLabel.setAttribute("value", L10N.getStr("marker.value.unknownFrame"));
hbox.appendChild(unknownLabel);
}
@ -221,10 +221,10 @@ exports.MarkerDOMUtils = {
const elements = [];
if (options.allocations && shouldShowAllocationsTrigger(marker)) {
const hbox = doc.createElement("hbox");
const hbox = doc.createXULElement("hbox");
hbox.className = "marker-details-customcontainer";
const label = doc.createElement("label");
const label = doc.createXULElement("label");
label.className = "custom-button";
label.setAttribute("value", "Show allocation triggers");
label.setAttribute("type", "show-allocations");

View File

@ -199,7 +199,7 @@ CallView.prototype = extend(AbstractTreeItem.prototype, {
this.level));
}
const targetNode = document.createElement("hbox");
const targetNode = document.createXULElement("hbox");
targetNode.className = "call-tree-item";
targetNode.setAttribute("origin", frameInfo.isContent ? "content" : "chrome");
targetNode.setAttribute("category", frameInfo.categoryData.abbrev || "");
@ -243,7 +243,7 @@ CallView.prototype = extend(AbstractTreeItem.prototype, {
* Invoked by `_displaySelf`.
*/
_createCell: function(doc, value, type) {
const cell = doc.createElement("description");
const cell = doc.createXULElement("description");
cell.className = "plain call-tree-cell";
cell.setAttribute("type", type);
cell.setAttribute("crop", "end");
@ -253,7 +253,7 @@ CallView.prototype = extend(AbstractTreeItem.prototype, {
},
_createFunctionCell: function(doc, arrowNode, frameName, frameInfo, frameLevel) {
const cell = doc.createElement("hbox");
const cell = doc.createXULElement("hbox");
cell.className = "call-tree-cell";
cell.style.marginInlineStart = (frameLevel * CALL_TREE_INDENTATION) + "px";
cell.setAttribute("type", "function");
@ -262,7 +262,7 @@ CallView.prototype = extend(AbstractTreeItem.prototype, {
// Render optimization hint if this frame has opt data.
if (this.root.showOptimizationHint && frameInfo.hasOptimizations &&
!frameInfo.isMetaCategory) {
const icon = doc.createElement("description");
const icon = doc.createXULElement("description");
icon.setAttribute("tooltiptext", VIEW_OPTIMIZATIONS_TOOLTIP);
icon.className = "opt-icon";
cell.appendChild(icon);
@ -271,7 +271,7 @@ CallView.prototype = extend(AbstractTreeItem.prototype, {
// Don't render a name label node if there's no function name. A different
// location label node will be rendered instead.
if (frameName) {
const nameNode = doc.createElement("description");
const nameNode = doc.createXULElement("description");
nameNode.className = "plain call-tree-name";
nameNode.textContent = frameName;
cell.appendChild(nameNode);
@ -304,7 +304,7 @@ CallView.prototype = extend(AbstractTreeItem.prototype, {
_appendFunctionDetailsCells: function(doc, cell, frameInfo) {
if (frameInfo.fileName) {
const urlNode = doc.createElement("description");
const urlNode = doc.createXULElement("description");
urlNode.className = "plain call-tree-url";
urlNode.textContent = frameInfo.fileName;
urlNode.setAttribute("tooltiptext", URL_LABEL_TOOLTIP + " → " + frameInfo.url);
@ -313,28 +313,28 @@ CallView.prototype = extend(AbstractTreeItem.prototype, {
}
if (frameInfo.line) {
const lineNode = doc.createElement("description");
const lineNode = doc.createXULElement("description");
lineNode.className = "plain call-tree-line";
lineNode.textContent = ":" + frameInfo.line;
cell.appendChild(lineNode);
}
if (frameInfo.column) {
const columnNode = doc.createElement("description");
const columnNode = doc.createXULElement("description");
columnNode.className = "plain call-tree-column";
columnNode.textContent = ":" + frameInfo.column;
cell.appendChild(columnNode);
}
if (frameInfo.host) {
const hostNode = doc.createElement("description");
const hostNode = doc.createXULElement("description");
hostNode.className = "plain call-tree-host";
hostNode.textContent = frameInfo.host;
cell.appendChild(hostNode);
}
if (frameInfo.categoryData.label) {
const categoryNode = doc.createElement("description");
const categoryNode = doc.createXULElement("description");
categoryNode.className = "plain call-tree-category";
categoryNode.style.color = frameInfo.categoryData.color;
categoryNode.textContent = frameInfo.categoryData.label;

View File

@ -20,7 +20,7 @@ pref("devtools.debugger.workers", false);
// The default Debugger UI settings
// This schema version needs to match that in devtools/client/debugger/src/utils/prefs.js.
pref("devtools.debugger.prefs-schema-version", "1.0.9");
pref("devtools.debugger.prefs-schema-version", "1.0.10");
pref("devtools.debugger.ui.panes-workers-and-sources-width", 200);
pref("devtools.debugger.ui.panes-instruments-width", 300);
pref("devtools.debugger.ui.panes-visible-on-startup", false);
@ -46,7 +46,6 @@ pref("devtools.debugger.tabsBlackBoxed", "[]");
pref("devtools.debugger.pending-selected-location", "{}");
pref("devtools.debugger.pending-breakpoints", "{}");
pref("devtools.debugger.expressions", "[]");
pref("devtools.debugger.event-listener-breakpoints", "[]");
pref("devtools.debugger.file-search-case-sensitive", false);
pref("devtools.debugger.file-search-whole-word", false);
pref("devtools.debugger.file-search-regex-match", false);

View File

@ -37,7 +37,7 @@ this.EXPORTED_SYMBOLS = ["AbstractTreeItem"];
*
* MyCustomTreeItem.prototype = extend(AbstractTreeItem.prototype, {
* _displaySelf: function(document, arrowNode) {
* let node = document.createElement("hbox");
* let node = document.createXULElement("hbox");
* ...
* // Append the provided arrow node wherever you want.
* node.appendChild(arrowNode);
@ -443,7 +443,7 @@ AbstractTreeItem.prototype = {
const document = this.document;
const arrowNode = this._arrowNode = document.createElement("hbox");
const arrowNode = this._arrowNode = document.createXULElement("hbox");
arrowNode.className = "arrow theme-twisty";
arrowNode.addEventListener("mousedown", this._onArrowClick);

View File

@ -38,6 +38,8 @@ const PREF_SIDEBAR_WIDTH = "devtools.styleeditor.mediaSidebarWidth";
const PREF_NAV_WIDTH = "devtools.styleeditor.navSidebarWidth";
const PREF_ORIG_SOURCES = "devtools.source-map.client-service.enabled";
const HTML_NS = "http://www.w3.org/1999/xhtml";
/**
* StyleEditorUI is controls and builds the UI of the Style Editor, including
* maintaining a list of editors for each stylesheet on a debuggee.
@ -897,12 +899,12 @@ StyleEditorUI.prototype = {
}
inSource = true;
const div = this._panelDoc.createElement("div");
const div = this._panelDoc.createElementNS(HTML_NS, "div");
div.className = "media-rule-label";
div.addEventListener("click",
this._jumpToLocation.bind(this, location));
const cond = this._panelDoc.createElement("div");
const cond = this._panelDoc.createElementNS(HTML_NS, "div");
cond.className = "media-rule-condition";
if (!rule.matches) {
cond.classList.add("media-condition-unmatched");
@ -914,7 +916,7 @@ StyleEditorUI.prototype = {
}
div.appendChild(cond);
const link = this._panelDoc.createElement("div");
const link = this._panelDoc.createElementNS(HTML_NS, "div");
link.className = "media-rule-line theme-link";
if (location.line != -1) {
link.textContent = ":" + location.line;
@ -950,7 +952,7 @@ StyleEditorUI.prototype = {
);
element.appendChild(node);
const link = this._panelDoc.createElement("a");
const link = this._panelDoc.createElementNS(HTML_NS, "a");
link.href = "#";
link.className = "media-responsive-mode-toggle";
link.textContent = rawText.substring(match.index, matchEnd);

View File

@ -37,14 +37,14 @@ function getTemplatesJSON() {
const templatelistNode = document.querySelector("#templatelist");
templatelistNode.innerHTML = "";
for (const template of list) {
const richlistitemNode = document.createElement("richlistitem");
const imageNode = document.createElement("image");
const richlistitemNode = document.createXULElement("richlistitem");
const imageNode = document.createXULElement("image");
imageNode.setAttribute("src", template.icon);
const labelNode = document.createElement("label");
const labelNode = document.createXULElement("label");
labelNode.setAttribute("value", template.name);
const descriptionNode = document.createElement("description");
const descriptionNode = document.createXULElement("description");
descriptionNode.textContent = template.description;
const vboxNode = document.createElement("vbox");
const vboxNode = document.createXULElement("vbox");
vboxNode.setAttribute("flex", "1");
richlistitemNode.appendChild(imageNode);
vboxNode.appendChild(labelNode);

View File

@ -14,6 +14,37 @@ This will run all DAMP tests, you can filter by test name with:
```
This command will run all tests which contains "console" in their name.
### Command line options
#### Running tests only once
```bash
./mach talos-test --activeTests damp --cycles 1 --tppagecycles 1
```
`--cycles` will limit the number of Firefox restart to only one, while
`--tppagecycles` will limit the number of test re-run in each firefox start to one.
This is often helpful when debugging one particular subtest.
#### Taking screenshots
```bash
DEBUG_DEVTOOLS_SCREENSHOTS=1 ./mach talos-test --activeTests damp
```
When passing `DEBUG_DEVTOOLS_SCREENSHOTS` env variable, screenshots will be taken after each subtest
was run. The screenshot will be opened in new tabs and their title
includes the subtest label. Firefox won't automatically close so that you can view the screenshots.
#### Recording a profile
```bash
./mach talos-test --activeTests damp --geckoProfile --geckoProfileEntries 100000000
```
This will automatically record the tests and open the profile. You may use the following command in order
to focus on just one subtest run:
```bash
./mach talos-test --activeTests damp --subtests custom.webconsole --cycles 1 --tppagecycles 1 --geckoProfile --geckoProfileEntries 100000000
```
## How to run it on try?
```bash
@ -107,7 +138,7 @@ The [main script](http://searchfox.org/mozilla-central/source/testing/talos/talo
## How to see the performance trends?
You can find the dedicated performance dashboard for DevTools at http://firefox-dev.tools/performance-dashboard. You will find links to trend charts for various tools:
* [Inspector dashboard](firefox-dev.tools/performance-dashboard/tools/inspector.html?days=60&filterstddev=true)
* [Inspector dashboard](http://firefox-dev.tools/performance-dashboard/tools/inspector.html?days=60&filterstddev=true)
* [Console dashboard](http://firefox-dev.tools/performance-dashboard/tools/console.html?days=60&filterstddev=true)
* [Netmonitor dashboard](http://firefox-dev.tools/performance-dashboard/tools/netmonitor.html?days=60&filterstddev=true)
* [Debugger dashboard](http://firefox-dev.tools/performance-dashboard/tools/debugger.html?days=60&filterstddev=true)

View File

@ -90,6 +90,7 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
"justify-content",
"justify-items",
"justify-self",
"line-break",
"list-style-image",
"list-style-position",
"list-style-type",
@ -310,6 +311,7 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
"-moz-outline-radius-bottomright",
"-moz-outline-radius-topleft",
"-moz-outline-radius-topright",
"offset-distance",
"padding-bottom",
"padding-left",
"padding-right",

View File

@ -3093,6 +3093,7 @@ exports.CSS_PROPERTIES = {
"scale",
"translate",
"offset-path",
"offset-distance",
"scroll-behavior",
"scroll-snap-align",
"scroll-snap-type",
@ -3183,6 +3184,7 @@ exports.CSS_PROPERTIES = {
"text-emphasis-position",
"text-emphasis-color",
"-moz-tab-size",
"line-break",
"-webkit-text-fill-color",
"-webkit-text-stroke-color",
"-webkit-text-stroke-width",
@ -7323,6 +7325,24 @@ exports.CSS_PROPERTIES = {
"unset"
]
},
"line-break": {
"isInherited": true,
"subproperties": [
"line-break"
],
"supports": [],
"values": [
"anywhere",
"auto",
"inherit",
"initial",
"loose",
"normal",
"revert",
"strict",
"unset"
]
},
"line-height": {
"isInherited": true,
"subproperties": [
@ -8232,6 +8252,19 @@ exports.CSS_PROPERTIES = {
"unset"
]
},
"offset-distance": {
"isInherited": false,
"subproperties": [
"offset-distance"
],
"supports": [],
"values": [
"inherit",
"initial",
"revert",
"unset"
]
},
"offset-path": {
"isInherited": false,
"subproperties": [
@ -10598,6 +10631,10 @@ exports.PREFERENCES = [
"translate",
"layout.css.individual-transform.enabled"
],
[
"offset-distance",
"layout.css.motion-path.enabled"
],
[
"scroll-snap-points-x",
"layout.css.scroll-snap.enabled"

View File

@ -124,7 +124,7 @@ CssProperties.prototype = {
isValidOnClient(name, value, doc) {
let dummyElement = this._dummyElements.get(doc);
if (!dummyElement) {
dummyElement = doc.createElement("div");
dummyElement = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
this._dummyElements.set(doc, dummyElement);
}

View File

@ -6,6 +6,7 @@
// Tests that the markup localization works properly.
const { localizeMarkup, LocalizationHelper } = require("devtools/shared/l10n");
const HTML_NS = "http://www.w3.org/1999/xhtml";
add_task(async function() {
info("Check that the strings used for this test are still valid");
@ -19,38 +20,38 @@ add_task(async function() {
ok(str1 && str2 && str3, "If this failed, strings should be updated in the test");
info("Create the test markup");
const div = document.createElement("div");
const div = document.createElementNS(HTML_NS, "div");
div.setAttribute("data-localization-bundle",
"devtools/client/locales/startup.properties");
const div0 = document.createElement("div");
const div0 = document.createElementNS(HTML_NS, "div");
div0.setAttribute("id", "d0");
div0.setAttribute("data-localization", "content=inspector.someInvalidKey");
div.appendChild(div0);
const div1 = document.createElement("div");
const div1 = document.createElementNS(HTML_NS, "div");
div1.setAttribute("id", "d1");
div1.setAttribute("data-localization", "content=inspector.label");
div.appendChild(div1);
div1.append("Text will disappear");
const div2 = document.createElement("div");
const div2 = document.createElementNS(HTML_NS, "div");
div2.setAttribute("id", "d2");
div2.setAttribute("data-localization",
"content=inspector.label;title=inspector.accesskey");
div.appendChild(div2);
const div3 = document.createElement("div");
const div3 = document.createElementNS(HTML_NS, "div");
div3.setAttribute("id", "d3");
div3.setAttribute("data-localization",
"content=inspector.label;title=inspector.accesskey");
div.appendChild(div3);
const div4 = document.createElement("div");
const div4 = document.createElementNS(HTML_NS, "div");
div4.setAttribute("id", "d4");
div4.setAttribute("data-localization", "aria-label=inspector.label");
div.appendChild(div4);
div4.append("Some content");
const toolboxDiv = document.createElement("div");
const toolboxDiv = document.createElementNS(HTML_NS, "div");
toolboxDiv.setAttribute("data-localization-bundle",
"devtools/client/locales/toolbox.properties");
div.appendChild(toolboxDiv);
const div5 = document.createElement("div");
const div5 = document.createElementNS(HTML_NS, "div");
div5.setAttribute("id", "d5");
div5.setAttribute("data-localization", "content=toolbox.defaultTitle");
toolboxDiv.appendChild(div5);

View File

@ -20,7 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=453650
nextTest();
function* runTests() {
var iframe = document.createElement("iframe");
var iframe = document.createXULElement("iframe");
iframe.style.width = "300px";
iframe.style.height = "300px";
iframe.setAttribute("src", "data:text/html,<h1 id='h'>hello</h1>");

View File

@ -38,7 +38,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=846906
var document = webNavigation.document;
ok(document, "Should be able to get document");
var iframe = document.createElement("iframe");
var iframe = document.createXULElement("iframe");
ok(iframe, "Should be able to create iframe");
iframe.onload = function () {

View File

@ -38,7 +38,7 @@ var gFrame;
// matters when these tests fail (produces better error messages).
var tests = [
function testInheritFromParent(cb) {
gFrame = document.createElement("iframe");
gFrame = document.createXULElement("iframe");
loadListener(gFrame, function () {
is(window.inheritedFromParent, true, "load in type=content iframe inherited principal of same type parent");
cb();
@ -63,7 +63,7 @@ var tests = [
'<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>';
let newWin = window.openDialog(xulWinURL, "chrome_window", "chrome");
loadListener(newWin, function () {
let frame = newWin.document.createElement("iframe");
let frame = newWin.document.createXULElement("iframe");
frame.setAttribute("type", "content");
frame.setAttribute("src", "javascript:'1';");
loadListener(frame, function () {

View File

@ -11910,7 +11910,6 @@ void Document::SetContentTypeInternal(const nsACString& aType) {
mCachedEncoder = nullptr;
mContentType = aType;
mContentTypeForWriteCalls = aType;
}
nsILoadContext* Document::GetLoadContext() const { return mDocumentContainer; }

View File

@ -4419,10 +4419,6 @@ class Document : public nsINode,
nsCString mContentType;
protected:
// For document.write() we may need a different content type than
// mContentType.
nsCString mContentTypeForWriteCalls;
// The document's security info
nsCOMPtr<nsISupports> mSecurityInfo;

View File

@ -304,6 +304,46 @@ bool TabGroup::IsBackground() const {
return mForegroundCount == 0;
}
nsresult TabGroup::QueuePostMessageEvent(
already_AddRefed<nsIRunnable>&& aRunnable) {
if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
if (!mPostMessageEventQueue) {
nsCOMPtr<nsISerialEventTarget> target = GetMainThreadSerialEventTarget();
mPostMessageEventQueue = ThrottledEventQueue::Create(
target, "PostMessage Queue",
nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS);
nsresult rv = mPostMessageEventQueue->SetIsPaused(false);
MOZ_ALWAYS_SUCCEEDS(rv);
}
// Ensure the queue is enabled
if (mPostMessageEventQueue->IsPaused()) {
nsresult rv = mPostMessageEventQueue->SetIsPaused(false);
MOZ_ALWAYS_SUCCEEDS(rv);
}
if (mPostMessageEventQueue) {
mPostMessageEventQueue->Dispatch(std::move(aRunnable),
NS_DISPATCH_NORMAL);
return NS_OK;
}
}
return NS_ERROR_FAILURE;
}
void TabGroup::FlushPostMessageEvents() {
if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
if (mPostMessageEventQueue) {
nsresult rv = mPostMessageEventQueue->SetIsPaused(true);
MOZ_ALWAYS_SUCCEEDS(rv);
nsCOMPtr<nsIRunnable> event;
while ((event = mPostMessageEventQueue->GetEvent())) {
Dispatch(TaskCategory::Other, event.forget());
}
}
}
}
uint32_t TabGroup::Count(bool aActiveOnly) const {
if (!aActiveOnly) {
return mDocGroups.Count();

View File

@ -23,6 +23,7 @@ class nsPIDOMWindowOuter;
namespace mozilla {
class AbstractThread;
class ThrottledEventQueue;
namespace dom {
class Document;
class BrowserChild;
@ -143,6 +144,10 @@ class TabGroup final : public SchedulerGroup,
// can be throttled.
static bool HasOnlyThrottableTabs();
nsresult QueuePostMessageEvent(already_AddRefed<nsIRunnable>&& aRunnable);
void FlushPostMessageEvents();
private:
virtual AbstractThread* AbstractMainThreadForImpl(
TaskCategory aCategory) override;
@ -166,6 +171,10 @@ class TabGroup final : public SchedulerGroup,
uint32_t mForegroundCount;
static LinkedList<TabGroup>* sTabGroups;
// A queue to store postMessage events during page load, the queue will be
// flushed once the page is loaded
RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue;
};
} // namespace dom

View File

@ -2482,6 +2482,18 @@ void nsPIDOMWindowInner::SetAudioCapture(bool aCapture) {
}
void nsPIDOMWindowInner::SetActiveLoadingState(bool aIsLoading) {
if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
if (!aIsLoading) {
Document* doc = GetExtantDoc();
if (doc) {
if (doc->IsTopLevelContentDocument()) {
mozilla::dom::TabGroup* tabGroup = doc->GetDocGroup()->GetTabGroup();
tabGroup->FlushPostMessageEvents();
}
}
}
}
if (!nsGlobalWindowInner::Cast(this)->IsChromeWindow()) {
mTimeoutManager->SetLoading(aIsLoading);
}

View File

@ -6057,6 +6057,19 @@ void nsGlobalWindowOuter::PostMessageMozOuter(JSContext* aCx,
return;
}
if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
if (mDoc) {
Document* doc = mDoc->GetTopLevelContentDocument();
if (doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE) {
// As long as the top level is loading, we can dispatch events to the
// queue because the queue will be flushed eventually
mozilla::dom::TabGroup* tabGroup = TabGroup();
aError = tabGroup->QueuePostMessageEvent(event.forget());
return;
}
}
}
aError = Dispatch(TaskCategory::Other, event.forget());
}

View File

@ -15,6 +15,7 @@
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/CompilationAndEvaluation.h"
#include "js/Modules.h" // JS::CompileModule, JS::GetModuleScript, JS::Module{Instantiate,Evaluate}
#include "js/OffThreadScriptCompilation.h"
#include "js/SourceText.h"
#include "nsIScriptContext.h"

View File

@ -11,16 +11,20 @@
#include "nsHyphenator.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/intl/LineBreaker.h"
#include "mozilla/intl/MozLocale.h"
using mozilla::intl::LineBreaker;
using mozilla::intl::Locale;
nsLineBreaker::nsLineBreaker()
: mCurrentWordLanguage(nullptr),
mCurrentWordContainsMixedLang(false),
mCurrentWordContainsComplexChar(false),
mScriptIsChineseOrJapanese(false),
mAfterBreakableSpace(false),
mBreakHere(false),
mWordBreak(LineBreaker::kWordBreak_Normal) {}
mWordBreak(LineBreaker::WordBreak::Normal),
mStrictness(LineBreaker::Strictness::Auto) {}
nsLineBreaker::~nsLineBreaker() {
NS_ASSERTION(mCurrentWord.Length() == 0,
@ -57,21 +61,28 @@ static void SetupCapitalization(const char16_t* aWord, uint32_t aLength,
nsresult nsLineBreaker::FlushCurrentWord() {
uint32_t length = mCurrentWord.Length();
AutoTArray<uint8_t, 4000> breakState;
if (!breakState.AppendElements(length)) return NS_ERROR_OUT_OF_MEMORY;
if (!breakState.AppendElements(length)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsTArray<bool> capitalizationState;
if (!mCurrentWordContainsComplexChar) {
if (mStrictness == LineBreaker::Strictness::Anywhere) {
memset(breakState.Elements(),
gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL,
length * sizeof(uint8_t));
} else if (!mCurrentWordContainsComplexChar) {
// For break-strict set everything internal to "break", otherwise
// to "no break"!
memset(breakState.Elements(),
mWordBreak == LineBreaker::kWordBreak_BreakAll
mWordBreak == LineBreaker::WordBreak::BreakAll
? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL
: gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE,
length * sizeof(uint8_t));
} else {
nsContentUtils::LineBreaker()->GetJISx4051Breaks(
mCurrentWord.Elements(), length, mWordBreak, breakState.Elements());
mCurrentWord.Elements(), length, mWordBreak, mStrictness,
mScriptIsChineseOrJapanese, breakState.Elements());
}
bool autoHyphenate = mCurrentWordLanguage && !mCurrentWordContainsMixedLang;
@ -225,7 +236,8 @@ nsresult nsLineBreaker::AppendText(nsAtom* aHyphenationLanguage,
if (aSink && !noBreaksNeeded) {
breakState[offset] =
mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ||
(mWordBreak == LineBreaker::kWordBreak_BreakAll)
mWordBreak == LineBreaker::WordBreak::BreakAll ||
mStrictness == LineBreaker::Strictness::Anywhere
? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL
: gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
}
@ -240,8 +252,8 @@ nsresult nsLineBreaker::AppendText(nsAtom* aHyphenationLanguage,
// set it to false
uint8_t currentStart = breakState[wordStart];
nsContentUtils::LineBreaker()->GetJISx4051Breaks(
aText + wordStart, offset - wordStart, mWordBreak,
breakState.Elements() + wordStart);
aText + wordStart, offset - wordStart, mWordBreak, mStrictness,
mScriptIsChineseOrJapanese, breakState.Elements() + wordStart);
breakState[wordStart] = currentStart;
}
if (hyphenator) {
@ -383,7 +395,8 @@ nsresult nsLineBreaker::AppendText(nsAtom* aHyphenationLanguage,
// will be set by nsILineBreaker, we don't consider CJK at this point.
breakState[offset] =
mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ||
(mWordBreak == LineBreaker::kWordBreak_BreakAll)
mWordBreak == LineBreaker::WordBreak::BreakAll ||
mStrictness == LineBreaker::Strictness::Anywhere
? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL
: gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
}
@ -397,8 +410,8 @@ nsresult nsLineBreaker::AppendText(nsAtom* aHyphenationLanguage,
// set it to false
uint8_t currentStart = breakState[wordStart];
nsContentUtils::LineBreaker()->GetJISx4051Breaks(
aText + wordStart, offset - wordStart, mWordBreak,
breakState.Elements() + wordStart);
aText + wordStart, offset - wordStart, mWordBreak, mStrictness,
mScriptIsChineseOrJapanese, breakState.Elements() + wordStart);
breakState[wordStart] = currentStart;
}
wordHasComplexChar = false;
@ -439,7 +452,18 @@ nsresult nsLineBreaker::AppendText(nsAtom* aHyphenationLanguage,
void nsLineBreaker::UpdateCurrentWordLanguage(nsAtom* aHyphenationLanguage) {
if (mCurrentWordLanguage && mCurrentWordLanguage != aHyphenationLanguage) {
mCurrentWordContainsMixedLang = true;
mScriptIsChineseOrJapanese = false;
} else {
if (aHyphenationLanguage && !mCurrentWordLanguage) {
Locale loc = Locale(nsAtomCString(aHyphenationLanguage));
if (loc.GetScript().IsEmpty()) {
loc.AddLikelySubtags();
}
const nsCString& script = loc.GetScript();
mScriptIsChineseOrJapanese =
script.EqualsLiteral("Hans") || script.EqualsLiteral("Hant") ||
script.EqualsLiteral("Jpan") || script.EqualsLiteral("Hrkt");
}
mCurrentWordLanguage = aHyphenationLanguage;
}
}

View File

@ -175,9 +175,9 @@ class nsLineBreaker {
/*
* Set word-break mode for linebreaker. This is set by word-break property.
* @param aMode is LineBreaker::kWordBreak_* value.
* @param aMode is LineBreaker::WordBreak::* value.
*/
void SetWordBreak(uint8_t aMode) {
void SetWordBreak(mozilla::intl::LineBreaker::WordBreak aMode) {
// If current word is non-empty and mode is changing, flush the breaker.
if (aMode != mWordBreak && !mCurrentWord.IsEmpty()) {
nsresult rv = FlushCurrentWord();
@ -187,13 +187,32 @@ class nsLineBreaker {
// If previous mode was break-all, we should allow a break here.
// XXX (jfkthame) css-text spec seems unclear on this, raised question in
// https://github.com/w3c/csswg-drafts/issues/3897
if (mWordBreak == mozilla::intl::LineBreaker::kWordBreak_BreakAll) {
if (mWordBreak == mozilla::intl::LineBreaker::WordBreak::BreakAll) {
mBreakHere = true;
}
}
mWordBreak = aMode;
}
/*
* Set line-break rule strictness mode for linebreaker. This is set by the
* line-break property.
* @param aMode is LineBreaker::Strictness::* value.
*/
void SetStrictness(mozilla::intl::LineBreaker::Strictness aMode) {
if (aMode != mStrictness && !mCurrentWord.IsEmpty()) {
nsresult rv = FlushCurrentWord();
if (NS_FAILED(rv)) {
NS_WARNING("FlushCurrentWord failed, line-breaks may be wrong");
}
// If previous mode was anywhere, we should allow a break here.
if (mStrictness == mozilla::intl::LineBreaker::Strictness::Anywhere) {
mBreakHere = true;
}
}
mStrictness = aMode;
}
private:
// This is a list of text sources that make up the "current word" (i.e.,
// run of text which does not contain any whitespace). All the mLengths
@ -232,6 +251,7 @@ class nsLineBreaker {
nsAtom* mCurrentWordLanguage;
bool mCurrentWordContainsMixedLang;
bool mCurrentWordContainsComplexChar;
bool mScriptIsChineseOrJapanese;
// True if the previous character was breakable whitespace
bool mAfterBreakableSpace;
@ -239,7 +259,9 @@ class nsLineBreaker {
// a run of breakable whitespace ends here
bool mBreakHere;
// line break mode by "word-break" style
uint8_t mWordBreak;
mozilla::intl::LineBreaker::WordBreak mWordBreak;
// strictness of break rules, from line-break property
mozilla::intl::LineBreaker::Strictness mStrictness;
};
#endif /*NSLINEBREAKER_H_*/

View File

@ -173,7 +173,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682
messageManager.removeDelayedFrameScript(toBeRemovedScript);
var oldValue = globalListenerCallCount;
var b = document.createElement("browser");
var b = document.createXULElement("browser");
b.setAttribute("type", "content");
document.documentElement.appendChild(b);
is(globalListenerCallCount, oldValue + 1,

View File

@ -45,7 +45,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=990812
}
});
var browser = document.createElement("browser");
var browser = document.createXULElement("browser");
browser.setAttribute("messagemanagergroup", "test");
browser.setAttribute("src", "about:mozilla");
browser.setAttribute("type", "content");

View File

@ -39,7 +39,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=990812
}
});
var browser = document.createElement("browser");
var browser = document.createXULElement("browser");
browser.setAttribute("messagemanagergroup", "test");
browser.setAttribute("src", "about:mozilla");
browser.setAttribute("type", "content");

View File

@ -45,7 +45,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=120684
is(list[1], undefined);
// Removing element which isn't in the list shouldn't do anything.
list.remove(document.createElement("foo"));
list.remove(document.createXULElement("foo"));
is(list.length, 1, "Length should be 1.");
is(list[0], document.documentElement);
@ -53,9 +53,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=120684
is(list.length, 0, "Length should be 0.");
is(list[0], undefined);
var e1 = document.createElement("foo");
var e2 = document.createElement("foo");
var e3 = document.createElement("foo");
var e1 = document.createXULElement("foo");
var e2 = document.createXULElement("foo");
var e3 = document.createXULElement("foo");
list.append(e1);
list.append(e2);

View File

@ -40,7 +40,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=683852
is(box.contains(anon), false, "Element should not contain anonymous element in it!");
is(anon.contains(anon), true, "Anonymous element should contain itself.")
is(document.documentElement.contains(box), true, "Element should contain element in it!");
is(document.contains(document.createElement("foo")), false, "Document shouldn't contain element which is't in the document");
is(document.contains(document.createXULElement("foo")), false, "Document shouldn't contain element which is't in the document");
is(document.contains(document.createTextNode("foo")), false, "Document shouldn't contain text node which is't in the document");
var link = document.getElementById("link");

View File

@ -141,7 +141,7 @@
}
};
let frame = document.createElement("iframe");
let frame = document.createXULElement("iframe");
frame.onload = function() {
obs.addObserver(observer, TOPIC);
// Create dummy DOMRequestHelper specific to checkWindowDestruction()

View File

@ -82,7 +82,7 @@ Connection::CreateAsyncStatement(const nsACString&,
}
NS_IMETHODIMP
Connection::ExecuteAsync(mozIStorageBaseStatement**, uint32_t,
Connection::ExecuteAsync(const nsTArray<RefPtr<mozIStorageBaseStatement>>&,
mozIStorageStatementCallback*,
mozIStoragePendingStatement**) {
// async methods are not supported

View File

@ -43,7 +43,7 @@ var test = function (isContent) {
// The following code creates a new div for each event in eventDefs,
// attaches a listener to listen for the event, and then generates
// a fake event at the center of the div.
let div = document.createElement("div");
let div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
div.style.width = "10px";
div.style.height = "10px";
div.style.backgroundColor = "red";

View File

@ -0,0 +1,7 @@
<iframe></iframe>
<script>
var doc = frames[0].document;
doc.open();
doc.open();
doc.close();
</script>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<iframe srcdoc="<iframe></iframe>"></iframe>
<script>
onload = function() {
parent = frames[0];
child = parent[0];
child.onunload = function() {
parent.document.open();
}
parent.document.open();
parent.document.write("Hello");
}
</script>

Some files were not shown because too many files have changed in this diff Show More