Skip to content

Commit

Permalink
Feature/super expression (#468)
Browse files Browse the repository at this point in the history
The enabled JSOperations are: CallSuperConstructor, SetSuperProperty, GetSuperProperty, SetComputedSuperProperty, GetComputedSuperProperty, CallSuperMethod, UpdateSuperProperty
  • Loading branch information
TobiasWienand authored Nov 21, 2024
1 parent c51e072 commit 5798638
Show file tree
Hide file tree
Showing 5 changed files with 407 additions and 0 deletions.
73 changes: 73 additions & 0 deletions Sources/Fuzzilli/Compiler/Compiler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,30 @@ public class JavaScriptCompiler {
}
}
}
case .superMemberExpression(let superMemberExpression):
guard superMemberExpression.isOptional == false else {
throw CompilerError.unsupportedFeatureError("Optional chaining is not supported in super member expressions")
}

guard let property = superMemberExpression.property else {
throw CompilerError.invalidNodeError("Missing property in super member expression")
}

switch property {
case .name(let name):
if let op = assignmentOperator {
// Example: super.foo += 1
emit(UpdateSuperProperty(propertyName: name, operator: op), withInputs: [rhs])
} else {
// Example: super.foo = 1
emit(SetSuperProperty(propertyName: name), withInputs: [rhs])
}

case .expression(let expr):
let property = try compileExpression(expr)
// Example: super[expr] = 1
emit(SetComputedSuperProperty(), withInputs: [property, rhs])
}


case .identifier(let identifier):
Expand Down Expand Up @@ -924,6 +948,7 @@ public class JavaScriptCompiler {

// See if this is a function or a method call
if case .memberExpression(let memberExpression) = callExpression.callee.expression {
// obj.foo(...) or obj[expr](...)
let object = try compileExpression(memberExpression.object)
guard let property = memberExpression.property else { throw CompilerError.invalidNodeError("missing property in member expression in call expression") }
switch property {
Expand All @@ -941,6 +966,18 @@ public class JavaScriptCompiler {
return emit(CallComputedMethod(numArguments: arguments.count, isGuarded: callExpression.isOptional), withInputs: [object, method] + arguments).output
}
}
} else if case .superMemberExpression(let superMemberExpression) = callExpression.callee.expression {
// super.foo(...)
guard !isSpreading else {
throw CompilerError.unsupportedFeatureError("Spread calls with super are not supported")
}
guard case .name(let methodName) = superMemberExpression.property else {
throw CompilerError.invalidNodeError("Super method calls must use a property name")
}
guard !callExpression.isOptional else {
throw CompilerError.unsupportedFeatureError("Optional chaining with super method calls is not supported")
}
return emit(CallSuperMethod(methodName: methodName, numArguments: arguments.count), withInputs: arguments).output
// Now check if it is a V8 intrinsic function
} else if case .v8IntrinsicIdentifier(let v8Intrinsic) = callExpression.callee.expression {
guard !isSpreading else { throw CompilerError.unsupportedFeatureError("Not currently supporting spread calls to V8 intrinsics") }
Expand All @@ -957,6 +994,21 @@ public class JavaScriptCompiler {
}
}

case .callSuperConstructor(let callSuperConstructor):
let (arguments, spreads) = try compileCallArguments(callSuperConstructor.arguments)
let isSpreading = spreads.contains(true)

if isSpreading {
throw CompilerError.unsupportedFeatureError("Spread arguments are not supported in super constructor calls")
}
guard !callSuperConstructor.isOptional else {
throw CompilerError.unsupportedFeatureError("Optional chaining is not supported in super constructor calls")
}
emit(CallSuperConstructor(numArguments: arguments.count), withInputs: arguments)
// In JS, the result of calling the super constructor is just |this|, but in FuzzIL the operation doesn't have an output (because |this| is always available anyway)
return lookupIdentifier("this")! // we can force unwrap because |this| always exists in the context where |super| exists


case .newExpression(let newExpression):
let callee = try compileExpression(newExpression.callee)
let (arguments, spreads) = try compileCallArguments(newExpression.arguments)
Expand All @@ -981,6 +1033,27 @@ public class JavaScriptCompiler {
return emit(GetComputedProperty(isGuarded: memberExpression.isOptional), withInputs: [object, property]).output
}
}

case .superMemberExpression(let superMemberExpression):
guard superMemberExpression.isOptional == false else {
throw CompilerError.unsupportedFeatureError("Optional chaining is not supported in super member expressions")
}
guard let property = superMemberExpression.property else {
throw CompilerError.invalidNodeError("Missing property in super member expression")
}

switch property {
case .name(let name):
return emit(GetSuperProperty(propertyName: name), withInputs: []).output

case .expression(let expr):
if case .numberLiteral(let literal) = expr.expression, let _ = Int64(exactly: literal.value) {
throw CompilerError.unsupportedFeatureError("GetElement is not supported in super member expressions")
} else {
let compiledProperty = try compileExpression(expr)
return emit(GetComputedSuperProperty(), withInputs: [compiledProperty]).output
}
}

case .unaryExpression(let unaryExpression):
if unaryExpression.operator == "typeof" {
Expand Down
18 changes: 18 additions & 0 deletions Sources/Fuzzilli/Compiler/Parser/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,12 @@ function parse(script, proto) {
}
case 'CallExpression':
case 'OptionalCallExpression': {
if (node.callee.type === 'Super') {
let arguments = node.arguments.map(visitExpression);
let isOptional = node.type === 'OptionalCallExpression';
return makeExpression('CallSuperConstructor', { arguments, isOptional });
}

let callee = visitExpression(node.callee);
let arguments = node.arguments.map(visitExpression);
let isOptional = node.type === 'OptionalCallExpression';
Expand All @@ -524,6 +530,18 @@ function parse(script, proto) {
}
case 'MemberExpression':
case 'OptionalMemberExpression': {
if (node.object && node.object.type === 'Super') {
let out = {};
if (node.computed) {
out.expression = visitExpression(node.property);
} else {
assert(node.property.type === 'Identifier', "Expected node.property.type to be exactly 'Identifier'");
assert(node.property.name != 'Super', "super.super(...) is not allowed");
out.name = node.property.name;
}
out.isOptional = node.type === 'OptionalMemberExpression';
return makeExpression('SuperMemberExpression', out);
}
let object = visitExpression(node.object);
let out = { object };
if (node.computed) {
Expand Down
Loading

0 comments on commit 5798638

Please sign in to comment.