diff options
-rw-r--r-- | src/tools/fuzzing.h | 5 | ||||
-rw-r--r-- | src/tools/fuzzing/fuzzing.cpp | 152 | ||||
-rw-r--r-- | test/passes/translate-to-fuzz_all-features_metrics_noprint.txt | 78 |
3 files changed, 124 insertions, 111 deletions
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 5b3c0037b..4aef9c372 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -315,7 +315,10 @@ private: Expression* makeBasicRef(Type type); Expression* makeCompoundRef(Type type); - Expression* makeString(); + Expression* makeStringConst(); + Expression* makeStringNewArray(); + Expression* makeStringNewCodePoint(); + Expression* makeStringConcat(); // Similar to makeBasic/CompoundRef, but indicates that this value will be // used in a place that will trap on null. For example, the reference of a diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 5760a26f1..0120dbef3 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -2494,6 +2494,9 @@ Expression* TranslateToFuzzReader::makeConst(Type type) { if (type.isNullable() && oneIn(8)) { return builder.makeRefNull(type.getHeapType()); } + if (type.getHeapType().isString()) { + return makeStringConst(); + } if (type.getHeapType().isBasic()) { return makeBasicRef(type); } else { @@ -2599,7 +2602,31 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) { return null; } case HeapType::string: { - return makeString(); + // In non-function contexts all we can do is string.const. + if (!funcContext) { + return makeStringConst(); + } + switch (upTo(9)) { + case 0: + case 1: + case 2: + return makeStringConst(); + case 3: + case 4: + case 5: + return makeStringNewCodePoint(); + case 6: + case 7: + // We less frequently make string.new_array as it may generate a lot + // of code for the array in some cases. + return makeStringNewArray(); + case 8: + // We much less frequently make string.concat as it will recursively + // generate two string children, i.e., it can lead to exponential + // growth. + return makeStringConcat(); + } + WASM_UNREACHABLE("bad switch"); } case HeapType::stringview_wtf16: // We fully support wtf16 strings. @@ -2714,79 +2741,72 @@ Expression* TranslateToFuzzReader::makeCompoundRef(Type type) { } } -Expression* TranslateToFuzzReader::makeString() { - // Fuzz with JS-style strings. +Expression* TranslateToFuzzReader::makeStringNewArray() { auto mutability = getMutability(); auto arrayHeapType = HeapType(Array(Field(Field::PackedType::i16, mutability))); auto nullability = getNullability(); auto arrayType = Type(arrayHeapType, nullability); - switch (upTo(3)) { - case 0: { - // Make a string from an array. We can only do this in functions. - if (funcContext) { - auto array = make(arrayType); - auto* start = make(Type::i32); - auto* end = make(Type::i32); - return builder.makeStringNew( - StringNewWTF16Array, array, start, end, false); - } - [[fallthrough]]; - } - case 1: { - // Make a string from a code point. We can only do this in functions. - if (funcContext) { - auto codePoint = make(Type::i32); - return builder.makeStringNew( - StringNewFromCodePoint, codePoint, nullptr, false); - } - [[fallthrough]]; - } - case 2: { - // Construct an interesting WTF-8 string from parts and use string.const. - std::stringstream wtf8; - bool lastWasLeadingSurrogate = false; - for (size_t i = 0, end = upTo(4); i < end; ++i) { - switch (upTo(6)) { - case 0: - // A simple ascii string. - wtf8 << std::to_string(upTo(1024)); - break; - case 1: - // '£' - wtf8 << "\xC2\xA3"; - break; - case 2: - // '€' - wtf8 << "\xE2\x82\xAC"; - break; - case 3: - // '𐍈' - wtf8 << "\xF0\x90\x8D\x88"; - break; - case 4: - // The leading surrogate in '𐍈' - wtf8 << "\xED\xA0\x80"; - lastWasLeadingSurrogate = true; - continue; - case 5: - if (lastWasLeadingSurrogate) { - // Avoid invalid WTF-8. - continue; - } - // The trailing surrogate in '𐍈' - wtf8 << "\xED\xBD\x88"; - break; + auto array = make(arrayType); + auto* start = make(Type::i32); + auto* end = make(Type::i32); + return builder.makeStringNew(StringNewWTF16Array, array, start, end, false); +} + +Expression* TranslateToFuzzReader::makeStringNewCodePoint() { + auto codePoint = make(Type::i32); + return builder.makeStringNew( + StringNewFromCodePoint, codePoint, nullptr, false); +} + +Expression* TranslateToFuzzReader::makeStringConst() { + // Construct an interesting WTF-8 string from parts and use string.const. + std::stringstream wtf8; + bool lastWasLeadingSurrogate = false; + for (size_t i = 0, end = upTo(4); i < end; ++i) { + switch (upTo(6)) { + case 0: + // A simple ascii string. + wtf8 << std::to_string(upTo(1024)); + break; + case 1: + // '£' + wtf8 << "\xC2\xA3"; + break; + case 2: + // '€' + wtf8 << "\xE2\x82\xAC"; + break; + case 3: + // '𐍈' + wtf8 << "\xF0\x90\x8D\x88"; + break; + case 4: + // The leading surrogate in '𐍈' + wtf8 << "\xED\xA0\x80"; + lastWasLeadingSurrogate = true; + continue; + case 5: + if (lastWasLeadingSurrogate) { + // Avoid invalid WTF-8. + continue; } - lastWasLeadingSurrogate = false; - } - std::stringstream wtf16; - // TODO: Use wtf16.view() once we have C++20. - String::convertWTF8ToWTF16(wtf16, wtf8.str()); - return builder.makeStringConst(wtf16.str()); + // The trailing surrogate in '𐍈' + wtf8 << "\xED\xBD\x88"; + break; } + lastWasLeadingSurrogate = false; } - WASM_UNREACHABLE("bad switch"); + std::stringstream wtf16; + // TODO: Use wtf16.view() once we have C++20. + String::convertWTF8ToWTF16(wtf16, wtf8.str()); + return builder.makeStringConst(wtf16.str()); +} + +Expression* TranslateToFuzzReader::makeStringConcat() { + auto left = make(Type(HeapType::string, getNullability())); + auto right = make(Type(HeapType::string, getNullability())); + return builder.makeStringConcat(left, right); } Expression* TranslateToFuzzReader::makeTrappingRefUse(HeapType type) { diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt index 4b66206d6..84f62758f 100644 --- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt +++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt @@ -1,5 +1,5 @@ total - [exports] : 4 + [exports] : 3 [funcs] : 5 [globals] : 14 [imports] : 5 @@ -8,47 +8,37 @@ total [table-data] : 1 [tables] : 1 [tags] : 1 - [total] : 710 - [vars] : 35 - ArrayGet : 1 - ArrayLen : 1 - ArrayNew : 16 - ArrayNewFixed : 1 - AtomicRMW : 1 - Binary : 77 - Block : 57 - Break : 8 - Call : 33 - Const : 159 - Drop : 7 - GlobalGet : 17 - GlobalSet : 14 - I31Get : 2 - If : 19 - Load : 22 - LocalGet : 66 - LocalSet : 37 + [total] : 387 + [vars] : 23 + ArrayNew : 1 + ArrayNewFixed : 3 + AtomicCmpxchg : 1 + AtomicNotify : 1 + Binary : 57 + Block : 33 + Break : 2 + Call : 4 + Const : 95 + Drop : 2 + GlobalGet : 15 + GlobalSet : 15 + If : 9 + Load : 16 + LocalGet : 41 + LocalSet : 20 Loop : 3 - MemoryFill : 1 - Nop : 7 - Pop : 4 - RefAs : 14 - RefCast : 3 - RefEq : 1 - RefFunc : 4 - RefI31 : 9 - RefIsNull : 3 - RefNull : 26 - RefTest : 2 - Return : 9 - SIMDExtract : 3 - StringConst : 2 - StringNew : 1 - StructGet : 1 - StructNew : 27 - Throw : 1 - Try : 5 - TupleExtract : 15 - TupleMake : 9 - Unary : 15 - Unreachable : 7 + Nop : 2 + Pop : 1 + RefAs : 3 + RefFunc : 2 + RefI31 : 4 + RefNull : 5 + Return : 3 + Store : 1 + StringConst : 1 + StructNew : 11 + Try : 1 + TupleExtract : 7 + TupleMake : 11 + Unary : 9 + Unreachable : 8 |