diff --git a/Sources/Fuzzilli/Compiler/Compiler.swift b/Sources/Fuzzilli/Compiler/Compiler.swift index 9e6020a41..f7cb45d1c 100644 --- a/Sources/Fuzzilli/Compiler/Compiler.swift +++ b/Sources/Fuzzilli/Compiler/Compiler.swift @@ -983,15 +983,56 @@ public class JavaScriptCompiler { } case .unaryExpression(let unaryExpression): - let argument = try compileExpression(unaryExpression.argument) - if unaryExpression.operator == "typeof" { + let argument = try compileExpression(unaryExpression.argument) return emit(TypeOf(), withInputs: [argument]).output + } else if unaryExpression.operator == "delete" { + guard case .memberExpression(let memberExpression) = unaryExpression.argument.expression else { + throw CompilerError.invalidNodeError("delete operator must be applied to a member expression") + } + + let obj = try compileExpression(memberExpression.object) + // isGuarded is true if the member expression is optional (e.g., obj?.prop) + let isGuarded = memberExpression.isOptional + + if !memberExpression.name.isEmpty { + // Deleting a non-computed property (e.g., delete obj.prop) + let propertyName = memberExpression.name + let instr = emit( + DeleteProperty(propertyName: propertyName, isGuarded: isGuarded), + withInputs: [obj] + ) + return instr.output + } else { + // Deleting a computed property (e.g., delete obj[expr]) + let propertyExpression = memberExpression.expression + let propertyExpr = propertyExpression.expression + let property = try compileExpression(propertyExpression) + + if case .numberLiteral(let numberLiteral) = propertyExpr { + // Delete an element (e.g., delete arr[42]) + let index = Int64(numberLiteral.value) + let instr = emit( + DeleteElement(index: index, isGuarded: isGuarded), + withInputs: [obj] + ) + return instr.output + } else { + // Use DeleteComputedProperty for other computed properties (e.g., delete obj["key"]) + let instr = emit( + DeleteComputedProperty(isGuarded: isGuarded), + withInputs: [obj, property] + ) + return instr.output + } + } + } else { + guard let op = UnaryOperator(rawValue: unaryExpression.operator) else { + throw CompilerError.invalidNodeError("invalid unary operator: \(unaryExpression.operator)") + } + let argument = try compileExpression(unaryExpression.argument) + return emit(UnaryOperation(op), withInputs: [argument]).output } - guard let op = UnaryOperator(rawValue: unaryExpression.operator) else { - throw CompilerError.invalidNodeError("invalid unary operator: \(unaryExpression.operator)") - } - return emit(UnaryOperation(op), withInputs: [argument]).output case .binaryExpression(let binaryExpression): let lhs = try compileExpression(binaryExpression.lhs) diff --git a/Tests/FuzzilliTests/CompilerTests/basic_delete.js b/Tests/FuzzilliTests/CompilerTests/basic_delete.js new file mode 100644 index 000000000..e84ba6a69 --- /dev/null +++ b/Tests/FuzzilliTests/CompilerTests/basic_delete.js @@ -0,0 +1,29 @@ +if (typeof output === 'undefined') output = console.log; + +const obj = { a: 1 }; +console.log(delete obj.a); +console.log(obj); + +const propName = 'b'; +obj[propName] = 2; +console.log(delete obj[propName]); +console.log(obj); + +const arr = [1, 2, 3]; +console.log(delete arr[1]); +console.log(arr); + +const index = 0; +console.log(delete arr[index]); +console.log(arr); + +const nestedObj = { a: { b: 2 } }; +console.log(delete nestedObj?.a?.b); +console.log(nestedObj); + +try { + delete null.a; +} catch(e) { + console.log(e.message); +} +console.log(delete null?.a); \ No newline at end of file