@@ -487,6 +487,13 @@ public class ProgramBuilder {
487487 return chooseUniform ( from: fuzzer. environment. customMethods)
488488 }
489489
490+ /// Returns a random custom private method name.
491+ ///
492+ /// As above but for private methods, where a # symbol will be prepended.
493+ public func randomCustomPrivateMethodName( ) -> String {
494+ return chooseUniform ( from: fuzzer. environment. customPrivateMethods)
495+ }
496+
490497 /// Returns either a builtin or a custom method name, with equal probability.
491498 public func randomMethodName( ) -> String {
492499 return probability ( 0.5 ) ? randomBuiltinMethodName ( ) : randomCustomMethodName ( )
@@ -730,6 +737,7 @@ public class ProgramBuilder {
730737 let ( pattern, flags) = self . randomRegExpPatternAndFlags ( )
731738 return self . loadRegExp ( pattern, flags)
732739 } ) ,
740+ ( . iterable, { return self . createArray ( with: [ self . randomJsVariable ( ) ] ) } ) ,
733741 ( . function( ) , {
734742 // TODO: We could technically generate a full function here but then we would enter the full code generation logic which could do anything.
735743 // Because we want to avoid this, we will just pick anything that can be a function.
@@ -3779,7 +3787,7 @@ public class ProgramBuilder {
37793787
37803788 public func wasmReturnCallDirect( signature: WasmSignature , function: Variable , functionArgs: [ Variable ] ) {
37813789 assert ( self . signature. outputTypes == signature. outputTypes)
3782- b. emit ( WasmReturnCallDirect ( signature : signature) ,
3790+ b. emit ( WasmReturnCallDirect ( parameterCount : signature. parameterTypes . count ) ,
37833791 withInputs: [ function] + functionArgs,
37843792 types: [ . wasmFunctionDef( signature) ] + signature. parameterTypes)
37853793 }
@@ -3794,8 +3802,9 @@ public class ProgramBuilder {
37943802
37953803 @discardableResult
37963804 public func wasmJsCall( function: Variable , withArgs args: [ Variable ] , withWasmSignature signature: WasmSignature ) -> Variable ? {
3797- let instr = b. emit ( WasmJsCall ( signature: signature) , withInputs: [ function] + args,
3798- types: [ . function( ) | . object( ofGroup: " WasmSuspendingObject " ) ] + signature. parameterTypes)
3805+ let signatureDef = b. wasmDefineAdHocSignatureType ( signature: signature)
3806+ let instr = b. emit ( WasmJsCall ( parameterCount: signature. parameterTypes. count, outputCount: signature. outputTypes. count) , withInputs: [ signatureDef, function] + args,
3807+ types: [ . wasmTypeDef( ) , . function( ) | . object( ofGroup: " WasmSuspendingObject " ) ] + signature. parameterTypes)
37993808 if signature. outputTypes. isEmpty {
38003809 assert ( !instr. hasOutputs)
38013810 return nil
@@ -3915,11 +3924,6 @@ public class ProgramBuilder {
39153924 b. emit ( WasmReassign ( ) , withInputs: [ variable, to] )
39163925 }
39173926
3918- public enum wasmBlockType {
3919- case typeIdx( Int )
3920- case valueType( ILType )
3921- }
3922-
39233927 // The first innerOutput of this block is a label variable, which is just there to explicitly mark control-flow and allow branches.
39243928 public func wasmBuildBlock( with signature: WasmSignature , args: [ Variable ] , body: ( Variable , [ Variable ] ) -> ( ) ) {
39253929 assert ( signature. outputTypes. count == 0 )
@@ -4436,10 +4440,18 @@ public class ProgramBuilder {
44364440
44374441 @discardableResult
44384442 public func wasmRefTest( _ ref: Variable , refType: ILType , typeDef: Variable ? = nil ) -> Variable {
4439- typeDef == nil
4440- ? b. emit ( WasmRefTest ( refType: refType) , withInputs: [ ref] ) . output
4441- : b. emit ( WasmRefTest ( refType: refType) , withInputs: [ ref, typeDef!] ) . output
4443+ let inputs = typeDef == nil ? [ ref] : [ ref, typeDef!]
4444+ let types : [ ILType ] = typeDef == nil ? [ . wasmGenericRef] : [ . wasmGenericRef, . wasmTypeDef( ) ]
4445+ return b. emit ( WasmRefTest ( refType: refType) , withInputs: inputs, types: types) . output
4446+ }
4447+
4448+ @discardableResult
4449+ public func wasmRefCast( _ ref: Variable , refType: ILType , typeDef: Variable ? = nil ) -> Variable {
4450+ let inputs = typeDef == nil ? [ ref] : [ ref, typeDef!]
4451+ let types : [ ILType ] = typeDef == nil ? [ . wasmGenericRef] : [ . wasmGenericRef, . wasmTypeDef( ) ]
4452+ return b. emit ( WasmRefCast ( refType: refType) , withInputs: inputs, types: types) . output
44424453 }
4454+
44434455 }
44444456
44454457 public class WasmModule {
@@ -4595,6 +4607,29 @@ public class ProgramBuilder {
45954607 return ( dynamicOffset, alignedStaticOffset)
45964608 }
45974609
4610+ func generateRandomWasmStructFields( ) -> ( fields: [ WasmStructTypeDescription . Field ] , indexTypes: [ Variable ] ) {
4611+ var indexTypes : [ Variable ] = [ ]
4612+
4613+ let fields = ( 0 ..< Int . random ( in: 0 ... 10 ) ) . map { _ in
4614+ var type : ILType
4615+ // TODO(mliedtke): Allow non-nullable reference types. Right now we can't do this as
4616+ // the WasmStructNewGenerator might then fail to generate a struct.
4617+ let nullability = true
4618+ if let elementType = randomVariable ( ofType: . wasmTypeDef( ) ) , probability ( 0.25 ) {
4619+ indexTypes. append ( elementType)
4620+ type = . wasmRef( . Index( ) , nullability: nullability)
4621+ } else {
4622+ type = chooseUniform ( from: [
4623+ . wasmPackedI8, . wasmPackedI16, . wasmi32, . wasmi64, . wasmf32, . wasmf64, . wasmSimd128,
4624+ ] + WasmAbstractHeapType. allCases. map { ILType . wasmRef ( $0, nullability: nullability) } )
4625+ }
4626+ return WasmStructTypeDescription . Field (
4627+ type: type, mutability: probability ( 0.75 ) )
4628+ }
4629+
4630+ return ( fields, indexTypes)
4631+ }
4632+
45984633 /// Produces a WasmGlobal that is valid to create in the given Context.
45994634 public func randomWasmGlobal( forContext context: Context ) -> WasmGlobal {
46004635 // TODO(pawkra): enable shared element types.
@@ -4669,8 +4704,9 @@ public class ProgramBuilder {
46694704 indexTypes. append ( elementType)
46704705 return ILType . wasmRef ( . Index( ) , nullability: nullability)
46714706 } else {
4672- // TODO(mliedtke): Extend list with abstract heap types.
4673- return chooseUniform ( from: [ . wasmi32, . wasmi64, . wasmf32, . wasmf64, . wasmSimd128] )
4707+ let nullability = !allowNonNullable || probability ( 0.5 )
4708+ return chooseUniform ( from: [ . wasmi32, . wasmi64, . wasmf32, . wasmf64, . wasmSimd128]
4709+ + WasmAbstractHeapType. allCases. map { ILType . wasmRef ( $0, nullability: nullability) } )
46744710 }
46754711 }
46764712 let signature = ( 0 ..< parameterCount) . map { _ in chooseType ( ) }
@@ -5009,22 +5045,28 @@ public class ProgramBuilder {
50095045 // so we instead register a generator that allows the fuzzer a greater chance of generating
50105046 // one when needed.
50115047 //
5012- // These can be registered on the JavaScriptEnvironment with addProducingGenerator()
5048+ // These can be registered on the JavaScriptEnvironment with addProducingGenerator().
5049+ // argument `predefined`: Provide values that should be used for the given properties of the
5050+ // options bag (if present) instead of finding or generating random values for them. The
5051+ // property might still be filtered out.
50135052 @discardableResult
5014- func createOptionsBag( _ bag: OptionsBag ) -> Variable {
5053+ func createOptionsBag( _ bag: OptionsBag , predefined : [ String : Variable ] = [ : ] ) -> Variable {
50155054 // We run .filter() to pick a subset of fields, but we generally want to set as many as possible
50165055 // and let the mutator prune things
5017- let dict : [ String : Variable ] = bag. properties. filter { _ in probability ( 0.8 ) } . mapValues {
5018- if $0. isEnumeration {
5019- return loadEnum ( $0)
5056+ let dict = [ String : Variable] ( uniqueKeysWithValues: bag. properties. filter { _ in probability ( 0.8 ) } . map {
5057+ let ( propertyName, type) = $0
5058+ if let predefinedVar = predefined [ propertyName] {
5059+ return ( propertyName, predefinedVar)
5060+ } else if type. isEnumeration {
5061+ return ( propertyName, loadEnum ( type) )
50205062 // relativeTo doesn't have an ObjectGroup so we cannot just register a producingGenerator for it
5021- } else if $0 . Is ( OptionsBag . jsTemporalRelativeTo) {
5022- return findOrGenerateType ( chooseUniform ( from: [ . jsTemporalZonedDateTime, . jsTemporalPlainDateTime,
5023- . jsTemporalPlainDate, . string] ) )
5063+ } else if type . Is ( OptionsBag . jsTemporalRelativeTo) {
5064+ return ( propertyName , findOrGenerateType ( chooseUniform ( from: [ . jsTemporalZonedDateTime, . jsTemporalPlainDateTime,
5065+ . jsTemporalPlainDate, . string] ) ) )
50245066 } else {
5025- return findOrGenerateType ( $0 )
5067+ return ( propertyName , findOrGenerateType ( type ) )
50265068 }
5027- }
5069+ } )
50285070 return createObject ( with: dict)
50295071 }
50305072
@@ -5393,6 +5435,29 @@ public class ProgramBuilder {
53935435 fileprivate static let allScripts = [ " Adlm " , " Afak " , " Aghb " , " Ahom " , " Arab " , " Aran " , " Armi " , " Armn " , " Avst " , " Bali " , " Bamu " , " Bass " , " Batk " , " Beng " , " Berf " , " Bhks " , " Blis " , " Bopo " , " Brah " , " Brai " , " Bugi " , " Buhd " , " Cakm " , " Cans " , " Cari " , " Cham " , " Cher " , " Chis " , " Chrs " , " Cirt " , " Copt " , " Cpmn " , " Cprt " , " Cyrl " , " Cyrs " , " Deva " , " Diak " , " Dogr " , " Dsrt " , " Dupl " , " Egyd " , " Egyh " , " Egyp " , " Elba " , " Elym " , " Ethi " , " Gara " , " Geok " , " Geor " , " Glag " , " Gong " , " Gonm " , " Goth " , " Gran " , " Grek " , " Gujr " , " Gukh " , " Guru " , " Hanb " , " Hang " , " Hani " , " Hano " , " Hans " , " Hant " , " Hatr " , " Hebr " , " Hira " , " Hluw " , " Hmng " , " Hmnp " , " Hntl " , " Hrkt " , " Hung " , " Inds " , " Ital " , " Jamo " , " Java " , " Jpan " , " Jurc " , " Kali " , " Kana " , " Kawi " , " Khar " , " Khmr " , " Khoj " , " Kitl " , " Kits " , " Knda " , " Kore " , " Kpel " , " Krai " , " Kthi " , " Lana " , " Laoo " , " Latf " , " Latg " , " Latn " , " Leke " , " Lepc " , " Limb " , " Lina " , " Linb " , " Lisu " , " Loma " , " Lyci " , " Lydi " , " Mahj " , " Maka " , " Mand " , " Mani " , " Marc " , " Maya " , " Medf " , " Mend " , " Merc " , " Mero " , " Mlym " , " Modi " , " Mong " , " Moon " , " Mroo " , " Mtei " , " Mult " , " Mymr " , " Nagm " , " Nand " , " Narb " , " Nbat " , " Newa " , " Nkdb " , " Nkgb " , " Nkoo " , " Nshu " , " Ogam " , " Olck " , " Onao " , " Orkh " , " Orya " , " Osge " , " Osma " , " Ougr " , " Palm " , " Pauc " , " Pcun " , " Pelm " , " Perm " , " Phag " , " Phli " , " Phlp " , " Phlv " , " Phnx " , " Piqd " , " Plrd " , " Prti " , " Psin " , " Qaaa-Qabx " , " Ranj " , " Rjng " , " Rohg " , " Roro " , " Runr " , " Samr " , " Sara " , " Sarb " , " Saur " , " Seal " , " Sgnw " , " Shaw " , " Shrd " , " Shui " , " Sidd " , " Sidt " , " Sind " , " Sinh " , " Sogd " , " Sogo " , " Sora " , " Soyo " , " Sund " , " Sunu " , " Sylo " , " Syrc " , " Syre " , " Syrj " , " Syrn " , " Tagb " , " Takr " , " Tale " , " Talu " , " Taml " , " Tang " , " Tavt " , " Tayo " , " Telu " , " Teng " , " Tfng " , " Tglg " , " Thaa " , " Thai " , " Tibt " , " Tirh " , " Tnsa " , " Todr " , " Tols " , " Toto " , " Tutg " , " Ugar " , " Vaii " , " Visp " , " Vith " , " Wara " , " Wcho " , " Wole " , " Xpeo " , " Xsux " , " Yezi " , " Yiii " , " Zanb " , " Zinh " , " Zmth " , " Zsye " , " Zsym " , " Zxxx " , " Zyyy " , " Zzzz " ]
53945436 fileprivate static let allAlpha = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ "
53955437 fileprivate static let allAlphaNum = allAlpha + " 0123456789 "
5438+ fileprivate static let allRegionsTwoDigit = [
5439+ " AD " , " AE " , " AF " , " AG " , " AI " , " AL " , " AM " , " AO " , " AQ " , " AR " , " AS " , " AT " ,
5440+ " AU " , " AW " , " AX " , " AZ " , " BA " , " BB " , " BD " , " BE " , " BF " , " BG " , " BH " , " BI " ,
5441+ " BJ " , " BL " , " BM " , " BN " , " BO " , " BQ " , " BR " , " BS " , " BT " , " BV " , " BW " , " BY " ,
5442+ " BZ " , " CA " , " CC " , " CD " , " CF " , " CG " , " CH " , " CI " , " CK " , " CL " , " CM " , " CN " ,
5443+ " CO " , " CR " , " CU " , " CV " , " CW " , " CX " , " CY " , " CZ " , " DE " , " DJ " , " DK " , " DM " ,
5444+ " DO " , " DZ " , " EC " , " EE " , " EG " , " EH " , " ER " , " ES " , " ET " , " FI " , " FJ " , " FK " ,
5445+ " FM " , " FO " , " FR " , " GA " , " GB " , " GD " , " GE " , " GF " , " GG " , " GH " , " GI " , " GL " ,
5446+ " GM " , " GN " , " GP " , " GQ " , " GR " , " GS " , " GT " , " GU " , " GW " , " GY " , " HK " , " HM " ,
5447+ " HN " , " HR " , " HT " , " HU " , " ID " , " IE " , " IL " , " IM " , " IN " , " IO " , " IQ " , " IR " ,
5448+ " IS " , " IT " , " JE " , " JM " , " JO " , " JP " , " KE " , " KG " , " KH " , " KI " , " KM " , " KN " ,
5449+ " KP " , " KR " , " KW " , " KY " , " KZ " , " LA " , " LB " , " LC " , " LI " , " LK " , " LR " , " LS " ,
5450+ " LT " , " LU " , " LV " , " LY " , " MA " , " MC " , " MD " , " ME " , " MF " , " MG " , " MH " , " MK " ,
5451+ " ML " , " MM " , " MN " , " MO " , " MP " , " MQ " , " MR " , " MS " , " MT " , " MU " , " MV " , " MW " ,
5452+ " MX " , " MY " , " MZ " , " NA " , " NC " , " NE " , " NF " , " NG " , " NI " , " NL " , " NO " , " NP " ,
5453+ " NR " , " NU " , " NZ " , " OM " , " PA " , " PE " , " PF " , " PG " , " PH " , " PK " , " PL " , " PM " ,
5454+ " PN " , " PR " , " PS " , " PT " , " PW " , " PY " , " QA " , " RE " , " RO " , " RS " , " RU " , " RW " ,
5455+ " SA " , " SB " , " SC " , " SD " , " SE " , " SG " , " SH " , " SI " , " SJ " , " SK " , " SL " , " SM " ,
5456+ " SN " , " SO " , " SR " , " SS " , " ST " , " SV " , " SX " , " SY " , " SZ " , " TC " , " TD " , " TF " ,
5457+ " TG " , " TH " , " TJ " , " TK " , " TL " , " TM " , " TN " , " TO " , " TR " , " TT " , " TV " , " TW " ,
5458+ " TZ " , " UA " , " UG " , " UM " , " US " , " UY " , " UZ " , " VA " , " VC " , " VE " , " VG " , " VI " ,
5459+ " VN " , " VU " , " WF " , " WS " , " YE " , " YT " , " ZA " , " ZM " , " ZW " ,
5460+ ]
53965461
53975462 @discardableResult
53985463 static func constructIntlUnit( ) -> String {
@@ -5419,7 +5484,7 @@ public class ProgramBuilder {
54195484 static func constructIntlRegionString( ) -> String {
54205485 // either two letters or three digits
54215486 if probability ( 0.5 ) {
5422- return String ( ( 0 ..< 2 ) . map { _ in allAlpha . randomElement ( ) ! } )
5487+ return allRegionsTwoDigit . randomElement ( ) !
54235488 } else {
54245489 return String ( format: " %03d " , Int . random ( in: 0 ... 999 ) )
54255490 }
@@ -5485,6 +5550,11 @@ public class ProgramBuilder {
54855550 return constructIntlType ( type: " Collator " , optionsBag: . jsIntlCollatorSettings)
54865551 }
54875552
5553+ @discardableResult
5554+ func constructIntlDisplayNames( ) -> Variable {
5555+ return constructIntlType ( type: " DisplayNames " , optionsBag: . jsIntlDisplayNamesSettings)
5556+ }
5557+
54885558 @discardableResult
54895559 func constructIntlListFormat( ) -> Variable {
54905560 return constructIntlType ( type: " ListFormat " , optionsBag: . jsIntlListFormatSettings)
@@ -5509,6 +5579,37 @@ public class ProgramBuilder {
55095579 func constructIntlSegmenter( ) -> Variable {
55105580 return constructIntlType ( type: " Segmenter " , optionsBag: . jsIntlSegmenterSettings)
55115581 }
5512- }
5513-
55145582
5583+ // Fuzz calls with the pattern new Intl.DisplayNames(locale, settings).of(code).
5584+ // These need to be generated together as there is a tight coupling between the `type` property
5585+ // in the settings optionsbag and the valid code values passed to the `of` method.
5586+ @discardableResult
5587+ func fuzzIntlDisplayNamesOf( ) -> Variable {
5588+ let intl = createNamedVariable ( forBuiltin: " Intl " )
5589+ let ctor = getProperty ( " DisplayNames " , of: intl)
5590+ let types = fuzzer. environment. getEnum ( ofName: " IntlDisplayNamesTypeEnum " ) !. enumValues
5591+ let type = types. randomElement ( ) !
5592+ let locale = loadString ( ProgramBuilder . constructIntlLocaleString ( ) )
5593+ let options = createOptionsBag ( . jsIntlDisplayNamesSettings,
5594+ predefined: [ " type " : loadString ( type) ] )
5595+ construct ( ctor, withArgs: [ locale, options] )
5596+ let code = switch type {
5597+ case " language " :
5598+ ProgramBuilder . constructIntlLanguageString ( )
5599+ case " region " :
5600+ ProgramBuilder . constructIntlRegionString ( )
5601+ case " script " :
5602+ ProgramBuilder . constructIntlScriptString ( )
5603+ case " currency " :
5604+ Locale . commonISOCurrencyCodes. randomElement ( ) !
5605+ case " calendar " :
5606+ fuzzer. environment. getEnum ( ofName: " temporalCalendar " ) !. enumValues. randomElement ( ) !
5607+ case " dateTimeField " :
5608+ [ " era " , " year " , " quarter " , " month " , " weekOfYear " , " weekday " , " day " ,
5609+ " dayPeriod " , " hour " , " minute " , " second " , " timeZoneName " ] . randomElement ( ) !
5610+ default :
5611+ String . random ( ofLength: 4 )
5612+ }
5613+ return callMethod ( " of " , on: ctor, withArgs: [ loadString ( code) ] )
5614+ }
5615+ }
0 commit comments