Skip to content

Commit 78e6dc7

Browse files
Samuel GroßV8-internal LUCI CQ
Samuel Groß
authored and
V8-internal LUCI CQ
committed
Redesign named variables in FuzzIL
This change replaces the three operations LoadNamedVariable, StoreNamedVariable, and DefineNamedVariable with a single new operation: CreateNamedVariable. This operation now simply creates a new FuzzIL variable that will be assigned a specific identifier during lifting. Optionally, the named variable can be declared using any of the available variable declaration modes: global, var, let, or const. Below is a small example of how CreateNamedVariable can be used: // Make an existing named variable (e.g. a builtin) available v0 <- CreateNamedVariable 'print', declarationMode: .none // Overwrite an existing named variable v1 <- CreateNamedVariable 'foo', declarationMode: .none v2 <- CallFunction v0, v1 v3 <- LoadString 'bar' Reassign v1, v3 // Declare a new named variable v4 <- CreateNamedVariable 'baz', declarationMode: .var, v1 v5 <- LoadString 'bla' Update v4 '+' v5 v5 <- CallFunction v0, v4 This will lift to JavaScript code similar to the following: print(foo); foo = "bar"; var baz = foo; baz += "bla"; print(baz); With this, we now have a single, flexible way of creating variables that have a specific name. We now use this: * For builtins, which are effectively just existing named variables in the global scope. This also now makes it (easily) possible to overwrite builtins. As such, The LoadBuiltin operation was removed. * During code generation, to sometimes create variables with specific names in generated code (we use random property names). * In the compiler. This is the main user of named variables and this is where this change has the most impact: we now compiler _every_ variable declaration to a CreateNamedVariable operation. This now makes it possible to correctly compiler any code that relies on variable names, for example due to using `eval`, with statements, or similar constructs. See some of the added/modified tests for examples. The consequence of this is that compiled code will now often have a lot of CreateNamedVariable operations. However, as these are now just regular FuzzIL variables, this change should not significantly affect the mutability of the programs. Furthermore, the CreateNamedVariable can be removed during minimization if they are not needed. Change-Id: Ide0589e1492c8e70fe6da174b7983d2d3d760ba3 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/7934186 Commit-Queue: Samuel Groß <[email protected]> Reviewed-by: Carl Smith <[email protected]>
1 parent 72753d8 commit 78e6dc7

36 files changed

+1158
-1266
lines changed

Sources/Fuzzilli/Base/ProgramBuilder.swift

+8-14
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ public class ProgramBuilder {
517517

518518
// We can be sure that we have such a builtin with a signature because the Environment checks this during initialization.
519519
let signature = fuzzer.environment.type(ofBuiltin: group).signature!
520-
let constructor = loadBuiltin(group)
520+
let constructor = createNamedVariable(forBuiltin: group)
521521
let arguments = findOrGenerateArgumentsInternal(forSignature: signature)
522522
let constructed = construct(constructor, withArgs: arguments)
523523

@@ -1939,11 +1939,6 @@ public class ProgramBuilder {
19391939
return emit(CreateTemplateString(parts: parts), withInputs: interpolatedValues).output
19401940
}
19411941

1942-
@discardableResult
1943-
public func loadBuiltin(_ name: String) -> Variable {
1944-
return emit(LoadBuiltin(builtinName: name)).output
1945-
}
1946-
19471942
@discardableResult
19481943
public func getProperty(_ name: String, of object: Variable, guard isGuarded: Bool = false) -> Variable {
19491944
return emit(GetProperty(propertyName: name, isGuarded: isGuarded), withInputs: [object]).output
@@ -2325,16 +2320,15 @@ public class ProgramBuilder {
23252320
}
23262321

23272322
@discardableResult
2328-
public func loadNamedVariable(_ name: String) -> Variable {
2329-
return emit(LoadNamedVariable(name)).output
2323+
public func createNamedVariable(_ name: String, declarationMode: NamedVariableDeclarationMode, initialValue: Variable? = nil) -> Variable {
2324+
assert((declarationMode == .none) == (initialValue == nil))
2325+
let inputs = initialValue != nil ? [initialValue!] : []
2326+
return emit(CreateNamedVariable(name, declarationMode: declarationMode), withInputs: inputs).output
23302327
}
23312328

2332-
public func storeNamedVariable(_ name: String, _ value: Variable) {
2333-
emit(StoreNamedVariable(name), withInputs: [value])
2334-
}
2335-
2336-
public func defineNamedVariable(_ name: String, _ value: Variable) {
2337-
emit(DefineNamedVariable(name), withInputs: [value])
2329+
@discardableResult
2330+
public func createNamedVariable(forBuiltin builtinName: String) -> Variable {
2331+
return createNamedVariable(builtinName, declarationMode: .none)
23382332
}
23392333

23402334
@discardableResult

Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift

+1-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public let codeGeneratorWeights = [
2525
"BooleanGenerator": 2,
2626
"UndefinedGenerator": 1,
2727
"NullGenerator": 1,
28+
"NamedVariableGenerator": 10,
2829
"ArrayGenerator": 10,
2930
"FloatArrayGenerator": 10,
3031
"IntArrayGenerator": 10,
@@ -133,9 +134,6 @@ public let codeGeneratorWeights = [
133134
"DestructObjectAndReassignGenerator": 5,
134135
"WithStatementGenerator": 3,
135136
"ComparisonGenerator": 10,
136-
"NamedVariableLoadGenerator": 3,
137-
"NamedVariableStoreGenerator": 3,
138-
"NamedVariableDefinitionGenerator": 3,
139137
"SuperMethodCallGenerator": 20,
140138

141139
// These will only be used inside class methods, and only if private properties were previously declared in that class.

Sources/Fuzzilli/CodeGen/CodeGenerators.swift

+42-49
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public let CodeGenerators: [CodeGenerator] = [
100100

101101
ValueGenerator("BuiltinObjectInstanceGenerator") { b, n in
102102
let builtin = chooseUniform(from: ["Array", "Map", "WeakMap", "Set", "WeakSet", "Date"])
103-
let constructor = b.loadBuiltin(builtin)
103+
let constructor = b.createNamedVariable(forBuiltin: builtin)
104104
if builtin == "Array" {
105105
let size = b.loadInt(b.randomSize(upTo: 0x1000))
106106
b.construct(constructor, withArgs: [size])
@@ -113,8 +113,8 @@ public let CodeGenerators: [CodeGenerator] = [
113113
ValueGenerator("TypedArrayGenerator") { b, n in
114114
for _ in 0..<n {
115115
let size = b.loadInt(b.randomSize(upTo: 0x1000))
116-
let constructor = b.loadBuiltin(
117-
chooseUniform(
116+
let constructor = b.createNamedVariable(
117+
forBuiltin: chooseUniform(
118118
from: ["Uint8Array", "Int8Array", "Uint16Array", "Int16Array", "Uint32Array", "Int32Array", "Float32Array", "Float64Array", "Uint8ClampedArray", "BigInt64Array", "BigUint64Array"]
119119
)
120120
)
@@ -277,7 +277,7 @@ public let CodeGenerators: [CodeGenerator] = [
277277

278278
CodeGenerator("DisposableVariableGenerator", inContext: .subroutine, inputs: .one) { b, val in
279279
assert(b.context.contains(.subroutine))
280-
let dispose = b.getProperty("dispose", of: b.loadBuiltin("Symbol"));
280+
let dispose = b.getProperty("dispose", of: b.createNamedVariable(forBuiltin: "Symbol"));
281281
let disposableVariable = b.buildObjectLiteral { obj in
282282
obj.addProperty("value", as: val)
283283
obj.addComputedMethod(dispose, with: .parameters(n:0)) { args in
@@ -289,7 +289,7 @@ public let CodeGenerators: [CodeGenerator] = [
289289

290290
CodeGenerator("AsyncDisposableVariableGenerator", inContext: .asyncFunction, inputs: .one) { b, val in
291291
assert(b.context.contains(.asyncFunction))
292-
let asyncDispose = b.getProperty("asyncDispose", of: b.loadBuiltin("Symbol"))
292+
let asyncDispose = b.getProperty("asyncDispose", of: b.createNamedVariable(forBuiltin: "Symbol"))
293293
let asyncDisposableVariable = b.buildObjectLiteral { obj in
294294
obj.addProperty("value", as: val)
295295
obj.addComputedMethod(asyncDispose, with: .parameters(n:0)) { args in
@@ -770,11 +770,26 @@ public let CodeGenerators: [CodeGenerator] = [
770770

771771
// We don't treat this as a ValueGenerator since it doesn't create a new value, it only accesses an existing one.
772772
CodeGenerator("BuiltinGenerator") { b in
773-
b.loadBuiltin(b.randomBuiltin())
773+
b.createNamedVariable(forBuiltin: b.randomBuiltin())
774+
},
775+
776+
CodeGenerator("NamedVariableGenerator") { b in
777+
// We're using the custom property names set from the environment for named variables.
778+
// It's not clear if there's something better since that set should be relatively small
779+
// (increasing the probability that named variables will be reused), and it also makes
780+
// sense to use property names if we're inside a `with` statement.
781+
let name = b.randomCustomPropertyName()
782+
let declarationMode = chooseUniform(from: NamedVariableDeclarationMode.allCases)
783+
if declarationMode != .none {
784+
b.createNamedVariable(name, declarationMode: declarationMode, initialValue: b.randomVariable())
785+
} else {
786+
b.createNamedVariable(name, declarationMode: declarationMode)
787+
}
774788
},
775789

776790
CodeGenerator("BuiltinOverwriteGenerator", inputs: .one) { b, value in
777-
b.storeNamedVariable(b.randomBuiltin(), value)
791+
let builtin = b.createNamedVariable(b.randomBuiltin(), declarationMode: .none)
792+
b.reassign(builtin, to: value)
778793
},
779794

780795
RecursiveCodeGenerator("PlainFunctionGenerator") { b in
@@ -1477,7 +1492,7 @@ public let CodeGenerators: [CodeGenerator] = [
14771492
//
14781493

14791494
CodeGenerator("WellKnownPropertyLoadGenerator", inputs: .preferred(.object())) { b, obj in
1480-
let Symbol = b.loadBuiltin("Symbol")
1495+
let Symbol = b.createNamedVariable(forBuiltin: "Symbol")
14811496
// The Symbol constructor is just a "side effect" of this generator and probably shouldn't be used by following generators.
14821497
b.hide(Symbol)
14831498
let name = chooseUniform(from: JavaScriptEnvironment.wellKnownSymbols)
@@ -1486,7 +1501,7 @@ public let CodeGenerators: [CodeGenerator] = [
14861501
},
14871502

14881503
CodeGenerator("WellKnownPropertyStoreGenerator", inputs: .preferred(.object())) { b, obj in
1489-
let Symbol = b.loadBuiltin("Symbol")
1504+
let Symbol = b.createNamedVariable(forBuiltin: "Symbol")
14901505
b.hide(Symbol)
14911506
let name = chooseUniform(from: JavaScriptEnvironment.wellKnownSymbols)
14921507
let propertyName = b.getProperty(name, of: Symbol)
@@ -1511,13 +1526,13 @@ public let CodeGenerators: [CodeGenerator] = [
15111526
CodeGenerator("MethodCallWithDifferentThisGenerator", inputs: .preferred(.object(), .object())) { b, obj, this in
15121527
guard let methodName = b.type(of: obj).randomMethod() else { return }
15131528
let arguments = b.randomArguments(forCallingMethod: methodName, on: obj)
1514-
let Reflect = b.loadBuiltin("Reflect")
1529+
let Reflect = b.createNamedVariable(forBuiltin: "Reflect")
15151530
let args = b.createArray(with: arguments)
15161531
b.callMethod("apply", on: Reflect, withArgs: [b.getProperty(methodName, of: obj), this, args])
15171532
},
15181533

15191534
CodeGenerator("ConstructWithDifferentNewTargetGenerator", inputs: .preferred(.constructor(), .constructor())) { b, newTarget, constructor in
1520-
let reflect = b.loadBuiltin("Reflect")
1535+
let reflect = b.createNamedVariable(forBuiltin: "Reflect")
15211536
let arguments = [constructor, b.createArray(with: b.randomArguments(forCalling: constructor)), newTarget]
15221537
b.callMethod("construct", on: reflect, withArgs: arguments)
15231538
},
@@ -1543,7 +1558,7 @@ public let CodeGenerators: [CodeGenerator] = [
15431558
}
15441559
let handler = b.createObject(with: handlerProperties)
15451560

1546-
let Proxy = b.loadBuiltin("Proxy")
1561+
let Proxy = b.createNamedVariable(forBuiltin: "Proxy")
15471562
b.hide(Proxy)// We want the proxy to be used by following code generators, not the Proxy constructor
15481563
b.construct(Proxy, withArgs: [target, handler])
15491564
},
@@ -1553,7 +1568,7 @@ public let CodeGenerators: [CodeGenerator] = [
15531568
// TODO could provide type hints here for the parameters.
15541569
b.buildRecursive()
15551570
}
1556-
let Promise = b.loadBuiltin("Promise")
1571+
let Promise = b.createNamedVariable(forBuiltin: "Promise")
15571572
b.hide(Promise) // We want the promise to be used by following code generators, not the Promise constructor
15581573
b.construct(Promise, withArgs: [handler])
15591574
},
@@ -1580,41 +1595,19 @@ public let CodeGenerators: [CodeGenerator] = [
15801595
// Generates a JavaScript 'with' statement
15811596
RecursiveCodeGenerator("WithStatementGenerator", inputs: .preferred(.object())) { b, obj in
15821597
b.buildWith(obj) {
1583-
withProbability(0.5, do: { () -> Void in
1584-
let propertyName = b.type(of: obj).randomProperty() ?? b.randomCustomPropertyName()
1585-
b.loadNamedVariable(propertyName)
1586-
}, else: { () -> Void in
1598+
for i in 1...3 {
15871599
let propertyName = b.type(of: obj).randomProperty() ?? b.randomCustomPropertyName()
1588-
let value = b.randomVariable()
1589-
b.storeNamedVariable(propertyName, value)
1590-
})
1600+
b.createNamedVariable(propertyName, declarationMode: .none)
1601+
}
15911602
b.buildRecursive()
15921603
}
15931604
},
15941605

1595-
CodeGenerator("NamedVariableLoadGenerator") { b in
1596-
// We're using the custom property names set from the environment for named variables.
1597-
// It's not clear if there's something better since that set should be relatively small
1598-
// (increasing the probability that named variables will be reused), and it also makes
1599-
// sense to use property names if we're inside a `with` statement.
1600-
b.loadNamedVariable(b.randomCustomPropertyName())
1601-
},
1602-
1603-
CodeGenerator("NamedVariableStoreGenerator") { b in
1604-
let value = b.randomVariable()
1605-
b.storeNamedVariable(b.randomCustomPropertyName(), value)
1606-
},
1607-
1608-
CodeGenerator("NamedVariableDefinitionGenerator") { b in
1609-
let value = b.randomVariable()
1610-
b.defineNamedVariable(b.randomCustomPropertyName(), value)
1611-
},
1612-
16131606
RecursiveCodeGenerator("EvalGenerator") { b in
16141607
let code = b.buildCodeString() {
16151608
b.buildRecursive()
16161609
}
1617-
let eval = b.loadBuiltin("eval")
1610+
let eval = b.createNamedVariable(forBuiltin: "eval")
16181611
b.callFunction(eval, withArgs: [code])
16191612
},
16201613

@@ -1629,7 +1622,7 @@ public let CodeGenerators: [CodeGenerator] = [
16291622
let numComputations = Int.random(in: 3...7)
16301623

16311624
// Common mathematical operations are exposed through the Math builtin in JavaScript.
1632-
let Math = b.loadBuiltin("Math")
1625+
let Math = b.createNamedVariable(forBuiltin: "Math")
16331626
b.hide(Math) // Following code generators should use the numbers generated below, not the Math object.
16341627

16351628
var values = b.randomVariables(upTo: Int.random(in: 1...3))
@@ -1676,7 +1669,7 @@ public let CodeGenerators: [CodeGenerator] = [
16761669
}
16771670
}
16781671
} else {
1679-
let toPrimitive = b.getProperty("toPrimitive", of: b.loadBuiltin("Symbol"))
1672+
let toPrimitive = b.getProperty("toPrimitive", of: b.createNamedVariable(forBuiltin: "Symbol"))
16801673
imitation = b.buildObjectLiteral { obj in
16811674
obj.addComputedMethod(toPrimitive, with: .parameters(n: 0)) { _ in
16821675
b.buildRecursive(n: 3)
@@ -1689,7 +1682,7 @@ public let CodeGenerators: [CodeGenerator] = [
16891682
// A lot of functions are also objects, so we could handle them either way. However, it probably makes more sense to handle
16901683
// them as a function since they would otherwise no longer be callable.
16911684
let handler = b.createObject(with: [:])
1692-
let Proxy = b.loadBuiltin("Proxy")
1685+
let Proxy = b.createNamedVariable(forBuiltin: "Proxy")
16931686
imitation = b.construct(Proxy, withArgs: [orig, handler])
16941687
} else if b.type(of: orig).Is(.object()) {
16951688
// Either make a class that extends that object's constructor or make a new object with the original object as prototype.
@@ -1724,13 +1717,13 @@ public let CodeGenerators: [CodeGenerator] = [
17241717
if maxSize < size {
17251718
maxSize = size
17261719
}
1727-
let ArrayBuffer = b.loadBuiltin("ArrayBuffer")
1720+
let ArrayBuffer = b.createNamedVariable(forBuiltin: "ArrayBuffer")
17281721
b.hide(ArrayBuffer)
17291722
let options = b.createObject(with: ["maxByteLength": b.loadInt(maxSize)])
17301723
let ab = b.construct(ArrayBuffer, withArgs: [b.loadInt(size), options])
17311724

1732-
let View = b.loadBuiltin(
1733-
chooseUniform(
1725+
let View = b.createNamedVariable(
1726+
forBuiltin: chooseUniform(
17341727
from: ["Uint8Array", "Int8Array", "Uint16Array", "Int16Array", "Uint32Array", "Int32Array", "Float32Array", "Float64Array", "Uint8ClampedArray", "BigInt64Array", "BigUint64Array", "DataView"]
17351728
)
17361729
)
@@ -1743,13 +1736,13 @@ public let CodeGenerators: [CodeGenerator] = [
17431736
if maxSize < size {
17441737
maxSize = size
17451738
}
1746-
let ArrayBuffer = b.loadBuiltin("SharedArrayBuffer")
1739+
let ArrayBuffer = b.createNamedVariable(forBuiltin: "SharedArrayBuffer")
17471740
b.hide(ArrayBuffer)
17481741
let options = b.createObject(with: ["maxByteLength": b.loadInt(maxSize)])
17491742
let ab = b.construct(ArrayBuffer, withArgs: [b.loadInt(size), options])
17501743

1751-
let View = b.loadBuiltin(
1752-
chooseUniform(
1744+
let View = b.createNamedVariable(
1745+
forBuiltin: chooseUniform(
17531746
from: ["Uint8Array", "Int8Array", "Uint16Array", "Int16Array", "Uint32Array", "Int32Array", "Float32Array", "Float64Array", "Uint8ClampedArray", "BigInt64Array", "BigUint64Array", "DataView"]
17541747
)
17551748
)
@@ -1802,7 +1795,7 @@ public let CodeGenerators: [CodeGenerator] = [
18021795
},
18031796

18041797
CodeGenerator("IteratorGenerator") { b in
1805-
let Symbol = b.loadBuiltin("Symbol")
1798+
let Symbol = b.createNamedVariable(forBuiltin: "Symbol")
18061799
b.hide(Symbol)
18071800
let iteratorSymbol = b.getProperty("iterator", of: Symbol)
18081801
b.hide(iteratorSymbol)

Sources/Fuzzilli/CodeGen/ProgramTemplates.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ public let ProgramTemplates = [
228228
b.build(n: 25)
229229

230230
// Generate random JSON payloads by stringifying random values
231-
let JSON = b.loadBuiltin("JSON")
231+
let JSON = b.createNamedVariable(forBuiltin: "JSON")
232232
var jsonPayloads = [Variable]()
233233
for _ in 0..<Int.random(in: 1...5) {
234234
let json = b.callMethod("stringify", on: JSON, withArgs: [b.randomVariable()])
@@ -242,7 +242,7 @@ public let ProgramTemplates = [
242242
// Helper function to pick a random index in the json string.
243243
let randIndex = b.buildPlainFunction(with: .parameters(.integer)) { args in
244244
let max = args[0]
245-
let Math = b.loadBuiltin("Math")
245+
let Math = b.createNamedVariable(forBuiltin: "Math")
246246
// We "hardcode" the random value here (instead of calling `Math.random()` in JS) so that testcases behave deterministically.
247247
var random = b.loadFloat(Double.random(in: 0..<1))
248248
random = b.binary(random, max, with: .Mul)
@@ -252,7 +252,7 @@ public let ProgramTemplates = [
252252

253253
// Flip a random character of the JSON string:
254254
// Select a random index at which to flip the character.
255-
let String = b.loadBuiltin("String")
255+
let String = b.createNamedVariable(forBuiltin: "String")
256256
let length = b.getProperty("length", of: json)
257257
let index = b.callFunction(randIndex, withArgs: [length])
258258

0 commit comments

Comments
 (0)