Skip to content

Commit 68a3a36

Browse files
ManishearthV8-internal LUCI CQ
authored andcommitted
[temporal] Correctly generate constructors when requested
Currently if the code needs to look up a TemporalFooConstructor, it looks for the functionAndConstructor type, and will often hit the .function or .constructor branches, which produce garbage. Updated generateTypeInternal to at least generate calls to *known* constructors. I don't think there's much value in letting the fuzzer try and stumble its way into the right constructor call. Bug: 439921647 Change-Id: I6a6a69647ce855078501d26efa4a94f2b024b180 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8578552 Reviewed-by: Matthias Liedtke <mliedtke@google.com> Commit-Queue: Manish Goregaokar <manishearth@google.com> Auto-Submit: Manish Goregaokar <manishearth@google.com>
1 parent 2f24d8c commit 68a3a36

3 files changed

Lines changed: 100 additions & 7 deletions

File tree

Sources/Fuzzilli/Base/ProgramBuilder.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,13 +600,35 @@ public class ProgramBuilder {
600600
return generateTypeInternal(type)
601601
}
602602

603+
// If the type is a builtin constructor like Promise or Temporal.Instant, generate
604+
// a path to it from field accesses.
605+
private func maybeGenerateConstructorAsPath(_ type: ILType) -> Variable? {
606+
guard let group = type.group else {
607+
return nil
608+
}
609+
guard let path = self.fuzzer.environment.getPathIfConstructor(ofGroup: group) else {
610+
return nil
611+
}
612+
var current = createNamedVariable(forBuiltin: path[0])
613+
for element in path.dropFirst() {
614+
current = getProperty(element, of: current)
615+
}
616+
assert(self.type(of: current).Is(type), "Registered constructorPath produces incorrect type for ObjectGroup \(group)")
617+
return current
618+
}
619+
603620
private func generateTypeInternal(_ type: ILType) -> Variable {
604621
if probability(0.9) && !type.isEnumeration {
605622
if let existingVariable = randomVariable(ofTypeOrSubtype: type) {
606623
return existingVariable
607624
}
608625
}
609626

627+
// For builtin constructors from the JavaScriptEnvironment, just generate them.
628+
if let ret = self.maybeGenerateConstructorAsPath(type) {
629+
return ret
630+
}
631+
610632
if numVariables >= argumentGenerationVariableBudget.top {
611633
if !argumentGenerationSignature.isEmpty {
612634
logger.warning("Reached variable generation limit in generateType for Signature: \(argumentGenerationSignature.top), returning a random variable for use as type \(type).")
@@ -635,11 +657,15 @@ public class ProgramBuilder {
635657
(.function(), {
636658
// TODO: We could technically generate a full function here but then we would enter the full code generation logic which could do anything.
637659
// Because we want to avoid this, we will just pick anything that can be a function.
660+
//
661+
// Note that builtin constructors are handled above in the maybeGenerateConstructorAsPath call.
638662
return self.randomVariable(forUseAs: .function())
639663
}),
640664
(.undefined, { return self.loadUndefined() }),
641665
(.constructor(), {
642666
// TODO: We have the same issue as above for functions.
667+
//
668+
// Note that builtin constructors are handled above in the maybeGenerateConstructorAsPath call.
643669
return self.randomVariable(forUseAs: .constructor())
644670
}),
645671
(.object(), {

Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,14 @@ public class JavaScriptEnvironment: ComponentBase {
728728
type.group.flatMap {producingGenerators[$0]}
729729
}
730730

731+
// If the object group refers to a constructor, get its path.
732+
public func getPathIfConstructor(ofGroup groupName: String) -> [String]? {
733+
guard let group = groups[groupName] else {
734+
return nil
735+
}
736+
return group.constructorPath
737+
}
738+
731739
public func getProducingProperties(ofType type: ILType) -> [(group: String, property: String)] {
732740
guard let array = producingProperties[type] else {
733741
return []
@@ -755,11 +763,13 @@ public struct ObjectGroup {
755763
public var properties: [String: ILType]
756764
public var methods: [String: [Signature]]
757765
public let parent: String?
766+
// Path to constructor function from `globalThis` if available (e.g. `["Temporal", "Instant"]`).
767+
public let constructorPath: [String]?
758768

759769
/// The type of instances of this group.
760770
public var instanceType: ILType
761771

762-
public init(name: String, instanceType: ILType?, properties: [String: ILType], overloads: [String: [Signature]], parent: String? = nil) {
772+
public init(name: String, constructorPath: String? = nil, instanceType: ILType?, properties: [String: ILType], overloads: [String: [Signature]], parent: String? = nil) {
763773
self.name = name
764774
self.properties = properties
765775
self.methods = overloads
@@ -777,10 +787,17 @@ public struct ObjectGroup {
777787
// Simply calculate the instance type based on the ObjectGroup information.
778788
self.instanceType = .object(ofGroup: name, withProperties: Array(properties.keys), withMethods: Array(methods.keys))
779789
}
790+
791+
if let constructorPath {
792+
assert(self.instanceType.Is(.constructor()) || self.instanceType.Is(.function()), "\(name) has a constructor path set but does not wrap a constructor")
793+
self.constructorPath = constructorPath.split(separator: ".").map({ String($0) })
794+
} else {
795+
self.constructorPath = nil
796+
}
780797
}
781798

782-
public init(name: String, instanceType: ILType?, properties: [String: ILType], methods: [String: Signature], parent: String? = nil) {
783-
self.init(name: name, instanceType: instanceType, properties: properties, overloads: methods.mapValues({[$0]}), parent: parent)
799+
public init(name: String, constructorPath: String? = nil, instanceType: ILType?, properties: [String: ILType], methods: [String: Signature], parent: String? = nil) {
800+
self.init(name: name, constructorPath: constructorPath, instanceType: instanceType, properties: properties, overloads: methods.mapValues({[$0]}), parent: parent)
784801
}
785802
}
786803

@@ -1539,6 +1556,7 @@ public extension ObjectGroup {
15391556
/// ObjectGroup modelling the JavaScript Promise constructor builtin
15401557
static let jsPromiseConstructor = ObjectGroup(
15411558
name: "PromiseConstructor",
1559+
constructorPath: "Promise",
15421560
instanceType: .jsPromiseConstructor,
15431561
properties: [
15441562
"prototype" : jsPromisePrototype.instanceType
@@ -1612,6 +1630,7 @@ public extension ObjectGroup {
16121630
/// ObjectGroup modelling the JavaScript Date constructor
16131631
static let jsDateConstructor = ObjectGroup(
16141632
name: "DateConstructor",
1633+
constructorPath: "Date",
16151634
instanceType: .jsDateConstructor,
16161635
properties: [
16171636
"prototype" : jsDatePrototype.instanceType
@@ -1626,6 +1645,7 @@ public extension ObjectGroup {
16261645
/// ObjectGroup modelling the JavaScript Object constructor builtin
16271646
static let jsObjectConstructor = ObjectGroup(
16281647
name: "ObjectConstructor",
1648+
constructorPath: "Object",
16291649
instanceType: .jsObjectConstructor,
16301650
properties: [
16311651
"prototype" : .object(), // TODO
@@ -1658,6 +1678,7 @@ public extension ObjectGroup {
16581678
/// ObjectGroup modelling the JavaScript Array constructor builtin
16591679
static let jsArrayConstructor = ObjectGroup(
16601680
name: "ArrayConstructor",
1681+
constructorPath: "Array",
16611682
instanceType: .jsArrayConstructor,
16621683
properties: [
16631684
// This might seem wrong, note however that `Array.isArray(Array.prototype)` is true.
@@ -1674,6 +1695,7 @@ public extension ObjectGroup {
16741695

16751696
static let jsArrayBufferConstructor = ObjectGroup(
16761697
name: "ArrayBufferConstructor",
1698+
constructorPath: "ArrayBuffer",
16771699
instanceType: .jsArrayBufferConstructor,
16781700
properties: [
16791701
"prototype" : jsArrayBufferPrototype.instanceType
@@ -1687,6 +1709,7 @@ public extension ObjectGroup {
16871709

16881710
static let jsSharedArrayBufferConstructor = ObjectGroup(
16891711
name: "SharedArrayBufferConstructor",
1712+
constructorPath: "SharedArrayBuffer",
16901713
instanceType: .jsSharedArrayBufferConstructor,
16911714
properties: [
16921715
"prototype" : jsSharedArrayBufferPrototype.instanceType
@@ -1699,6 +1722,7 @@ public extension ObjectGroup {
16991722
/// Object group modelling the JavaScript String constructor builtin
17001723
static let jsStringConstructor = ObjectGroup(
17011724
name: "StringConstructor",
1725+
constructorPath: "String",
17021726
instanceType: .jsStringConstructor,
17031727
properties: [
17041728
"prototype" : jsStringPrototype.instanceType
@@ -1713,6 +1737,7 @@ public extension ObjectGroup {
17131737
/// Object group modelling the JavaScript Symbol constructor builtin
17141738
static let jsSymbolConstructor = ObjectGroup(
17151739
name: "SymbolConstructor",
1740+
constructorPath: "Symbol",
17161741
instanceType: .jsSymbolConstructor,
17171742
properties: [
17181743
"iterator" : .jsSymbol,
@@ -1740,6 +1765,7 @@ public extension ObjectGroup {
17401765
/// Object group modelling the JavaScript BigInt constructor builtin
17411766
static let jsBigIntConstructor = ObjectGroup(
17421767
name: "BigIntConstructor",
1768+
constructorPath: "BigInt",
17431769
instanceType: .jsBigIntConstructor,
17441770
properties: [
17451771
"prototype" : .object()
@@ -1753,6 +1779,7 @@ public extension ObjectGroup {
17531779
/// Object group modelling the JavaScript Boolean constructor builtin
17541780
static let jsBooleanConstructor = ObjectGroup(
17551781
name: "BooleanConstructor",
1782+
constructorPath: "Boolean",
17561783
instanceType: .jsBooleanConstructor,
17571784
properties: [
17581785
"prototype" : .object()
@@ -1763,6 +1790,7 @@ public extension ObjectGroup {
17631790
/// Object group modelling the JavaScript Number constructor builtin
17641791
static let jsNumberConstructor = ObjectGroup(
17651792
name: "NumberConstructor",
1793+
constructorPath: "Number",
17661794
instanceType: .jsNumberConstructor,
17671795
properties: [
17681796
"prototype" : .object(),
@@ -1908,6 +1936,7 @@ public extension ObjectGroup {
19081936

19091937
static let jsWebAssemblyModuleConstructor = ObjectGroup(
19101938
name: "WebAssemblyModuleConstructor",
1939+
constructorPath: "WebAssembly.Module",
19111940
instanceType: .jsWebAssemblyModuleConstructor,
19121941
properties: [
19131942
"prototype": .object()
@@ -1923,6 +1952,7 @@ public extension ObjectGroup {
19231952

19241953
static let jsWebAssemblyGlobalConstructor = ObjectGroup(
19251954
name: "WebAssemblyGlobalConstructor",
1955+
constructorPath: "WebAssembly.Global",
19261956
instanceType: .jsWebAssemblyGlobalConstructor,
19271957
properties: [
19281958
"prototype": jsWebAssemblyGlobalPrototype.instanceType,
@@ -1932,6 +1962,7 @@ public extension ObjectGroup {
19321962

19331963
static let jsWebAssemblyInstanceConstructor = ObjectGroup(
19341964
name: "WebAssemblyInstanceConstructor",
1965+
constructorPath: "WebAssembly.Instance",
19351966
instanceType: .jsWebAssemblyInstanceConstructor,
19361967
properties: [
19371968
"prototype": .object(),
@@ -1952,6 +1983,7 @@ public extension ObjectGroup {
19521983

19531984
static let jsWebAssemblyMemoryConstructor = ObjectGroup(
19541985
name: "WebAssemblyMemoryConstructor",
1986+
constructorPath: "WebAssembly.Memory",
19551987
instanceType: .jsWebAssemblyMemoryConstructor,
19561988
properties: [
19571989
"prototype": jsWebAssemblyMemoryPrototype.instanceType,
@@ -1963,6 +1995,7 @@ public extension ObjectGroup {
19631995

19641996
static let jsWebAssemblyTableConstructor = ObjectGroup(
19651997
name: "WebAssemblyTableConstructor",
1998+
constructorPath: "WebAssembly.Table",
19661999
instanceType: .jsWebAssemblyTableConstructor,
19672000
properties: [
19682001
"prototype": jsWebAssemblyTablePrototype.instanceType,
@@ -1974,6 +2007,7 @@ public extension ObjectGroup {
19742007

19752008
static let jsWebAssemblyTagConstructor = ObjectGroup(
19762009
name: "WebAssemblyTagConstructor",
2010+
constructorPath: "WebAssembly.Tag",
19772011
instanceType: .jsWebAssemblyTagConstructor,
19782012
properties: [
19792013
"prototype": jsWebAssemblyTagPrototype.instanceType,
@@ -1997,6 +2031,7 @@ public extension ObjectGroup {
19972031

19982032
static let jsWebAssemblyExceptionConstructor = ObjectGroup(
19992033
name: "WebAssemblyExceptionConstructor",
2034+
constructorPath: "WebAssembly.Exception",
20002035
instanceType: .jsWebAssemblyExceptionConstructor,
20012036
properties: [
20022037
"prototype": jsWebAssemblyExceptionPrototype.instanceType,
@@ -2202,6 +2237,7 @@ public extension ObjectGroup {
22022237
/// ObjectGroup modelling the JavaScript Temporal.Instant constructor
22032238
static let jsTemporalInstantConstructor = ObjectGroup(
22042239
name: "TemporalInstantConstructor",
2240+
constructorPath: "Temporal.Instant",
22052241
instanceType: .jsTemporalInstantConstructor,
22062242
properties: [
22072243
"prototype" : jsTemporalInstantPrototype.instanceType
@@ -2250,6 +2286,7 @@ public extension ObjectGroup {
22502286
/// ObjectGroup modelling the JavaScript Temporal.Duration constructor
22512287
static let jsTemporalDurationConstructor = ObjectGroup(
22522288
name: "TemporalDurationConstructor",
2289+
constructorPath: "Temporal.Duration",
22532290
instanceType: .jsTemporalDurationConstructor,
22542291
properties: [
22552292
"prototype" : jsTemporalDurationPrototype.instanceType
@@ -2290,6 +2327,7 @@ public extension ObjectGroup {
22902327
/// ObjectGroup modelling the JavaScript Temporal.PlainTime constructor
22912328
static let jsTemporalPlainTimeConstructor = ObjectGroup(
22922329
name: "TemporalPlainTimeConstructor",
2330+
constructorPath: "Temporal.PlainTime",
22932331
instanceType: .jsTemporalPlainTimeConstructor,
22942332
properties: [
22952333
"prototype" : jsTemporalPlainTimePrototype.instanceType
@@ -2336,6 +2374,7 @@ public extension ObjectGroup {
23362374
/// ObjectGroup modelling the JavaScript Temporal.PlainYearMonth constructor
23372375
static let jsTemporalPlainYearMonthConstructor = ObjectGroup(
23382376
name: "TemporalPlainYearMonthConstructor",
2377+
constructorPath: "Temporal.PlainYearMonth",
23392378
instanceType: .jsTemporalPlainYearMonthConstructor,
23402379
properties: [
23412380
"prototype" : jsTemporalPlainYearMonthPrototype.instanceType
@@ -2369,6 +2408,7 @@ public extension ObjectGroup {
23692408
/// ObjectGroup modelling the JavaScript Temporal.PlainMonthDay constructor.
23702409
static let jsTemporalPlainMonthDayConstructor = ObjectGroup(
23712410
name: "TemporalPlainMonthDayConstructor",
2411+
constructorPath: "Temporal.PlainMonthDay",
23722412
instanceType: .jsTemporalPlainMonthDayConstructor,
23732413
properties: [
23742414
"prototype" : jsTemporalPlainMonthDayPrototype.instanceType
@@ -2412,6 +2452,7 @@ public extension ObjectGroup {
24122452
/// ObjectGroup modelling the JavaScript Temporal.PlainDate constructor
24132453
static let jsTemporalPlainDateConstructor = ObjectGroup(
24142454
name: "TemporalPlainDateConstructor",
2455+
constructorPath: "Temporal.PlainDate",
24152456
instanceType: .jsTemporalPlainDateConstructor,
24162457
properties: [
24172458
"prototype" : jsTemporalPlainDatePrototype.instanceType
@@ -2450,6 +2491,7 @@ public extension ObjectGroup {
24502491
/// ObjectGroup modelling the JavaScript Temporal.PlainDateTime constructor
24512492
static let jsTemporalPlainDateTimeConstructor = ObjectGroup(
24522493
name: "TemporalPlainDateTimeConstructor",
2494+
constructorPath: "Temporal.PlainDateTime",
24532495
instanceType: .jsTemporalPlainDateTimeConstructor,
24542496
properties: [
24552497
"prototype" : jsTemporalPlainDateTimePrototype.instanceType
@@ -2499,6 +2541,7 @@ public extension ObjectGroup {
24992541
/// ObjectGroup modelling the JavaScript Temporal.ZonedDateTime constructor
25002542
static let jsTemporalZonedDateTimeConstructor = ObjectGroup(
25012543
name: "TemporalZonedDateTimeConstructor",
2544+
constructorPath: "Temporal.ZonedDateTime",
25022545
instanceType: .jsTemporalZonedDateTimeConstructor,
25032546
properties: [
25042547
"prototype" : jsTemporalZonedDateTimePrototype.instanceType

Tests/FuzzilliTests/JSTyperTests.swift

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,9 +1714,9 @@ class JSTyperTests: XCTestCase {
17141714
var foundDate = false
17151715
var foundString = false
17161716
// Test that relativeTo arguments are correctly generated
1717-
// Annoyingly, we may generate undefined/.jsAnything here since the field may not exist. Instead
1718-
// we try a large number of times and ensure all the generators get called *eventually*
1719-
for _ in 1..<100 {
1717+
// Annoyingly, we may generate undefined/.jsAnything here since the field may not exist.
1718+
// We just call this a large number of times until we find everything.
1719+
for i in 1..<100 {
17201720
let fuzzer = makeMockFuzzer()
17211721
let b = fuzzer.makeBuilder()
17221722
let temporalBuiltin = b.createNamedVariable(forBuiltin: "Temporal")
@@ -1738,9 +1738,17 @@ class JSTyperTests: XCTestCase {
17381738
} else if type.Is(.jsTemporalPlainDate) {
17391739
XCTAssertEqual(type.group, "Temporal.PlainDate")
17401740
foundDate = true
1741+
} else {
1742+
// If we got here, it must be because we never generated a relativeTo
1743+
// argument
1744+
let obj = b.type(of: args[0])
1745+
XCTAssert(!obj.properties.contains("relativeTo"))
17411746
}
17421747

1743-
if foundZDT && foundString && foundDate && foundDT {
1748+
// We don't want to run the test for 100 iterations, we only
1749+
// want to run it for ~20 to ensure enough paths get tested,
1750+
// and only if we do not generate all paths do we wish to run it more.
1751+
if foundZDT && foundString && foundDate && foundDT && i > 20 {
17441752
break
17451753
}
17461754
}
@@ -1850,4 +1858,20 @@ class JSTyperTests: XCTestCase {
18501858
// Test that the returned variable matches the generated one
18511859
XCTAssertEqual(variable, returnedVar)
18521860
}
1861+
1862+
func testFindConstructor() {
1863+
for ctor in ["TemporalPlainMonthDayConstructor", "DateConstructor", "PromiseConstructor", "SymbolConstructor", "TemporalZonedDateTimeConstructor"] {
1864+
let fuzzer = makeMockFuzzer()
1865+
let b = fuzzer.makeBuilder()
1866+
let temporalBuiltin = b.createNamedVariable(forBuiltin: "Temporal")
1867+
let dateCtor = b.getProperty("PlainDate", of: temporalBuiltin)
1868+
let requestedCtor = fuzzer.environment.type(ofGroup: ctor)
1869+
let result = b.findOrGenerateType(requestedCtor)
1870+
1871+
// The typer should not pick up the PlainDateConstructor we have in scope,
1872+
// it should instead get the ctor from the global
1873+
XCTAssert(result != dateCtor)
1874+
XCTAssert(b.type(of: result).Is(requestedCtor))
1875+
}
1876+
}
18531877
}

0 commit comments

Comments
 (0)