Skip to content

Commit 5d1f780

Browse files
LiedtkeV8-internal LUCI CQ
authored andcommitted
Add type information for prototype properties of most builtins
Bug: 408162715 Change-Id: Ie31469d93273f83c7b60ecc79549a2262f1ddf4b Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8432039 Reviewed-by: Carl Smith <[email protected]> Commit-Queue: Matthias Liedtke <[email protected]>
1 parent cfadd26 commit 5d1f780

File tree

3 files changed

+103
-7
lines changed

3 files changed

+103
-7
lines changed

Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -330,19 +330,24 @@ public class JavaScriptEnvironment: ComponentBase {
330330

331331
registerObjectGroup(.jsObjectConstructor)
332332
registerObjectGroup(.jsPromiseConstructor)
333+
registerObjectGroup(.jsPromisePrototype)
333334
registerObjectGroup(.jsArrayConstructor)
334335
registerObjectGroup(.jsStringConstructor)
336+
registerObjectGroup(.jsStringPrototype)
335337
registerObjectGroup(.jsSymbolConstructor)
336338
registerObjectGroup(.jsBigIntConstructor)
337339
registerObjectGroup(.jsBooleanConstructor)
338340
registerObjectGroup(.jsNumberConstructor)
339341
registerObjectGroup(.jsMathObject)
340342
registerObjectGroup(.jsDate)
341343
registerObjectGroup(.jsDateConstructor)
344+
registerObjectGroup(.jsDatePrototype)
342345
registerObjectGroup(.jsJSONObject)
343346
registerObjectGroup(.jsReflectObject)
344347
registerObjectGroup(.jsArrayBufferConstructor)
348+
registerObjectGroup(.jsArrayBufferPrototype)
345349
registerObjectGroup(.jsSharedArrayBufferConstructor)
350+
registerObjectGroup(.jsSharedArrayBufferPrototype)
346351
for variant in ["Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "AggregateError", "URIError", "SuppressedError"] {
347352
registerObjectGroup(.jsError(variant))
348353
}
@@ -885,6 +890,28 @@ public extension ILType {
885890
static let wasmTable = ILType.object(ofGroup: "WasmTable", withProperties: ["length"], withMethods: ["get", "grow", "set"])
886891
}
887892

893+
public extension ObjectGroup {
894+
// Creates an object group representing a "prototype" object on a built-in, like Date.prototype.
895+
// These objects are somewhat special in JavaScript as they describe an object which has
896+
// methods on them for which you shall not call them with the bound this as a receiver, e.g.
897+
// `Date.prototype.getTime()` fails as `Date.prototype` is not a `Date`.
898+
// Therefore the methods are registered as properties, so Fuzzilli doesn't generate calls for
899+
// them. Instead Fuzzilli generates GetProperty operations for them which will then be typed as
900+
// an `ILType.unboundFunction` which knows the required receiver type (in the example `Date`),
901+
// so Fuzzilli can generate sequences like `Date.prototype.getTime.call(new Date())`.
902+
static func createPrototypeObjectGroup(_ receiver: ObjectGroup) -> ObjectGroup {
903+
let name = receiver.name + ".prototype"
904+
let properties = Dictionary(uniqueKeysWithValues: receiver.methods.map {
905+
($0.0, ILType.unboundFunction($0.1.first, receiver: receiver.instanceType)) })
906+
return ObjectGroup(
907+
name: name,
908+
instanceType: .object(ofGroup: name, withProperties: properties.map {$0.0}, withMethods: []),
909+
properties: properties,
910+
methods: [:]
911+
)
912+
}
913+
}
914+
888915
// Type information for the object groups that we use to model the JavaScript runtime environment.
889916
// The general rules here are:
890917
// * "output" type information (properties and return values) should be as precise as possible
@@ -1259,12 +1286,14 @@ public extension ObjectGroup {
12591286
]
12601287
)
12611288

1289+
static let jsPromisePrototype = createPrototypeObjectGroup(jsPromises)
1290+
12621291
/// ObjectGroup modelling the JavaScript Promise constructor builtin
12631292
static let jsPromiseConstructor = ObjectGroup(
12641293
name: "PromiseConstructor",
12651294
instanceType: .jsPromiseConstructor,
12661295
properties: [
1267-
"prototype" : .object()
1296+
"prototype" : jsPromisePrototype.instanceType
12681297
],
12691298
methods: [
12701299
"resolve" : [.jsAnything] => .jsPromise,
@@ -1330,12 +1359,14 @@ public extension ObjectGroup {
13301359
]
13311360
)
13321361

1362+
static let jsDatePrototype = createPrototypeObjectGroup(jsDate)
1363+
13331364
/// ObjectGroup modelling the JavaScript Date constructor
13341365
static let jsDateConstructor = ObjectGroup(
13351366
name: "DateConstructor",
13361367
instanceType: .jsDateConstructor,
13371368
properties: [
1338-
"prototype" : .object()
1369+
"prototype" : jsDatePrototype.instanceType
13391370
],
13401371
methods: [
13411372
"UTC" : [.number, .opt(.number), .opt(.number), .opt(.number), .opt(.number), .opt(.number), .opt(.number)] => .jsDate,
@@ -1391,32 +1422,38 @@ public extension ObjectGroup {
13911422
]
13921423
)
13931424

1425+
static let jsArrayBufferPrototype = createPrototypeObjectGroup(jsArrayBuffers)
1426+
13941427
static let jsArrayBufferConstructor = ObjectGroup(
13951428
name: "ArrayBufferConstructor",
13961429
instanceType: .jsArrayBufferConstructor,
13971430
properties: [
1398-
"prototype" : .object()
1431+
"prototype" : jsArrayBufferPrototype.instanceType
13991432
],
14001433
methods: [
14011434
"isView" : [.jsAnything] => .boolean
14021435
]
14031436
)
14041437

1438+
static let jsSharedArrayBufferPrototype = createPrototypeObjectGroup(jsSharedArrayBuffers)
1439+
14051440
static let jsSharedArrayBufferConstructor = ObjectGroup(
14061441
name: "SharedArrayBufferConstructor",
14071442
instanceType: .jsSharedArrayBufferConstructor,
14081443
properties: [
1409-
"prototype" : .object()
1444+
"prototype" : jsSharedArrayBufferPrototype.instanceType
14101445
],
14111446
methods: [:]
14121447
)
14131448

1449+
static let jsStringPrototype = createPrototypeObjectGroup(jsStrings)
1450+
14141451
/// Object group modelling the JavaScript String constructor builtin
14151452
static let jsStringConstructor = ObjectGroup(
14161453
name: "StringConstructor",
14171454
instanceType: .jsStringConstructor,
14181455
properties: [
1419-
"prototype" : .object()
1456+
"prototype" : jsStringPrototype.instanceType
14201457
],
14211458
methods: [
14221459
"fromCharCode" : [.jsAnything...] => .jsString,

Tests/FuzzilliTests/JSTyperTests.swift

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,17 +1626,50 @@ class JSTyperTests: XCTestCase {
16261626
XCTAssertEqual(b.type(of: reexportedJsTag), b.type(of: jsTag))
16271627
}
16281628

1629-
func testArrayBuiltinPrototype() {
1629+
func testBuiltinPrototypes() {
16301630
let fuzzer = makeMockFuzzer()
16311631
let b = fuzzer.makeBuilder()
16321632

1633+
// Array.prototype
16331634
let arrayBuiltin = b.createNamedVariable(forBuiltin: "Array")
16341635
XCTAssert(b.type(of: arrayBuiltin).Is(.object(ofGroup: "ArrayConstructor")))
16351636
let arrayProto = b.getProperty("prototype", of: arrayBuiltin)
16361637
XCTAssert(b.type(of: arrayProto).Is(.object(ofGroup: "Array")))
16371638
let signatures = b.methodSignatures(of: "indexOf", on: arrayProto)
16381639
XCTAssertEqual([[.jsAnything, .opt(.integer)] => .integer], signatures)
16391640
let indexOf = b.getProperty("indexOf", of: arrayProto)
1640-
XCTAssert(b.type(of: indexOf).Is(.unboundFunction([.jsAnything, .opt(.integer)] => .integer, receiver: .jsArray)))
1641+
XCTAssertEqual(b.type(of: indexOf), .unboundFunction([.jsAnything, .opt(.integer)] => .integer, receiver: .jsArray))
1642+
1643+
// Date.prototype
1644+
let dateBuiltin = b.createNamedVariable(forBuiltin: "Date")
1645+
XCTAssert(b.type(of: dateBuiltin).Is(.object(ofGroup: "DateConstructor")))
1646+
let dateProto = b.getProperty("prototype", of: dateBuiltin)
1647+
XCTAssert(b.type(of: dateProto).Is(.object(ofGroup: "Date.prototype")))
1648+
let getTime = b.getProperty("getTime", of: dateProto)
1649+
XCTAssertEqual(b.type(of: getTime), .unboundFunction([] => .number, receiver: .jsDate))
1650+
1651+
// Promise.prototype
1652+
let promiseBuiltin = b.createNamedVariable(forBuiltin: "Promise")
1653+
XCTAssert(b.type(of: promiseBuiltin).Is(.object(ofGroup: "PromiseConstructor")))
1654+
let promiseProto = b.getProperty("prototype", of: promiseBuiltin)
1655+
XCTAssert(b.type(of: dateProto).Is(.object(ofGroup: "Date.prototype")))
1656+
let then = b.getProperty("then", of: promiseProto)
1657+
XCTAssertEqual(b.type(of: then), .unboundFunction([.function()] => .jsPromise, receiver: .jsPromise))
1658+
1659+
// ArrayBuffer.prototype
1660+
let arrayBufferBuiltin = b.createNamedVariable(forBuiltin: "ArrayBuffer")
1661+
XCTAssert(b.type(of: arrayBufferBuiltin).Is(.object(ofGroup: "ArrayBufferConstructor")))
1662+
let arrayBufferProto = b.getProperty("prototype", of: arrayBufferBuiltin)
1663+
XCTAssert(b.type(of: arrayBufferProto).Is(.object(ofGroup: "ArrayBuffer.prototype")))
1664+
let resize = b.getProperty("resize", of: arrayBufferProto)
1665+
XCTAssertEqual(b.type(of: resize), .unboundFunction([.integer] => .undefined, receiver: .jsArrayBuffer))
1666+
1667+
// ArrayBuffer.prototype
1668+
let sharedArrayBufferBuiltin = b.createNamedVariable(forBuiltin: "SharedArrayBuffer")
1669+
XCTAssert(b.type(of: sharedArrayBufferBuiltin).Is(.object(ofGroup: "SharedArrayBufferConstructor")))
1670+
let sharedArrayBufferProto = b.getProperty("prototype", of: sharedArrayBufferBuiltin)
1671+
XCTAssert(b.type(of: sharedArrayBufferProto).Is(.object(ofGroup: "SharedArrayBuffer.prototype")))
1672+
let grow = b.getProperty("grow", of: sharedArrayBufferProto)
1673+
XCTAssertEqual(b.type(of: grow), .unboundFunction([.number] => .undefined, receiver: .jsSharedArrayBuffer))
16411674
}
16421675
}

Tests/FuzzilliTests/LifterTest.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3233,4 +3233,30 @@ class LifterTests: XCTestCase {
32333233
let js = lifter.lift(mutatedProg)
32343234
XCTAssertTrue(js.contains("Wasmlifting failed"))
32353235
}
3236+
3237+
func testBuiltinPrototypeCall() {
3238+
let fuzzer = makeMockFuzzer()
3239+
let b = fuzzer.makeBuilder()
3240+
3241+
let dateBuiltin = b.createNamedVariable(forBuiltin: "Date")
3242+
XCTAssert(b.type(of: dateBuiltin).Is(.object(ofGroup: "DateConstructor")))
3243+
let dateProto = b.getProperty("prototype", of: dateBuiltin)
3244+
XCTAssert(b.type(of: dateProto).Is(.object(ofGroup: "Date.prototype")))
3245+
let getTime = b.getProperty("getTime", of: dateProto)
3246+
XCTAssertEqual(b.type(of: getTime), .unboundFunction([] => .number, receiver: .jsDate))
3247+
let date = b.construct(dateBuiltin)
3248+
XCTAssertEqual(b.type(of: date), .jsDate)
3249+
b.callMethod("call", on: getTime, withArgs: [date])
3250+
3251+
let program = b.finalize()
3252+
let actual = fuzzer.lifter.lift(program)
3253+
3254+
let expected = """
3255+
const v2 = Date.prototype.getTime;
3256+
const v3 = new Date();
3257+
v2.call(v3);
3258+
3259+
"""
3260+
XCTAssertEqual(actual, expected)
3261+
}
32363262
}

0 commit comments

Comments
 (0)