Skip to content

Commit 5798638

Browse files
Feature/super expression (#468)
The enabled JSOperations are: CallSuperConstructor, SetSuperProperty, GetSuperProperty, SetComputedSuperProperty, GetComputedSuperProperty, CallSuperMethod, UpdateSuperProperty
1 parent c51e072 commit 5798638

File tree

5 files changed

+407
-0
lines changed

5 files changed

+407
-0
lines changed

Sources/Fuzzilli/Compiler/Compiler.swift

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,30 @@ public class JavaScriptCompiler {
696696
}
697697
}
698698
}
699+
case .superMemberExpression(let superMemberExpression):
700+
guard superMemberExpression.isOptional == false else {
701+
throw CompilerError.unsupportedFeatureError("Optional chaining is not supported in super member expressions")
702+
}
703+
704+
guard let property = superMemberExpression.property else {
705+
throw CompilerError.invalidNodeError("Missing property in super member expression")
706+
}
707+
708+
switch property {
709+
case .name(let name):
710+
if let op = assignmentOperator {
711+
// Example: super.foo += 1
712+
emit(UpdateSuperProperty(propertyName: name, operator: op), withInputs: [rhs])
713+
} else {
714+
// Example: super.foo = 1
715+
emit(SetSuperProperty(propertyName: name), withInputs: [rhs])
716+
}
717+
718+
case .expression(let expr):
719+
let property = try compileExpression(expr)
720+
// Example: super[expr] = 1
721+
emit(SetComputedSuperProperty(), withInputs: [property, rhs])
722+
}
699723

700724

701725
case .identifier(let identifier):
@@ -924,6 +948,7 @@ public class JavaScriptCompiler {
924948

925949
// See if this is a function or a method call
926950
if case .memberExpression(let memberExpression) = callExpression.callee.expression {
951+
// obj.foo(...) or obj[expr](...)
927952
let object = try compileExpression(memberExpression.object)
928953
guard let property = memberExpression.property else { throw CompilerError.invalidNodeError("missing property in member expression in call expression") }
929954
switch property {
@@ -941,6 +966,18 @@ public class JavaScriptCompiler {
941966
return emit(CallComputedMethod(numArguments: arguments.count, isGuarded: callExpression.isOptional), withInputs: [object, method] + arguments).output
942967
}
943968
}
969+
} else if case .superMemberExpression(let superMemberExpression) = callExpression.callee.expression {
970+
// super.foo(...)
971+
guard !isSpreading else {
972+
throw CompilerError.unsupportedFeatureError("Spread calls with super are not supported")
973+
}
974+
guard case .name(let methodName) = superMemberExpression.property else {
975+
throw CompilerError.invalidNodeError("Super method calls must use a property name")
976+
}
977+
guard !callExpression.isOptional else {
978+
throw CompilerError.unsupportedFeatureError("Optional chaining with super method calls is not supported")
979+
}
980+
return emit(CallSuperMethod(methodName: methodName, numArguments: arguments.count), withInputs: arguments).output
944981
// Now check if it is a V8 intrinsic function
945982
} else if case .v8IntrinsicIdentifier(let v8Intrinsic) = callExpression.callee.expression {
946983
guard !isSpreading else { throw CompilerError.unsupportedFeatureError("Not currently supporting spread calls to V8 intrinsics") }
@@ -957,6 +994,21 @@ public class JavaScriptCompiler {
957994
}
958995
}
959996

997+
case .callSuperConstructor(let callSuperConstructor):
998+
let (arguments, spreads) = try compileCallArguments(callSuperConstructor.arguments)
999+
let isSpreading = spreads.contains(true)
1000+
1001+
if isSpreading {
1002+
throw CompilerError.unsupportedFeatureError("Spread arguments are not supported in super constructor calls")
1003+
}
1004+
guard !callSuperConstructor.isOptional else {
1005+
throw CompilerError.unsupportedFeatureError("Optional chaining is not supported in super constructor calls")
1006+
}
1007+
emit(CallSuperConstructor(numArguments: arguments.count), withInputs: arguments)
1008+
// 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)
1009+
return lookupIdentifier("this")! // we can force unwrap because |this| always exists in the context where |super| exists
1010+
1011+
9601012
case .newExpression(let newExpression):
9611013
let callee = try compileExpression(newExpression.callee)
9621014
let (arguments, spreads) = try compileCallArguments(newExpression.arguments)
@@ -981,6 +1033,27 @@ public class JavaScriptCompiler {
9811033
return emit(GetComputedProperty(isGuarded: memberExpression.isOptional), withInputs: [object, property]).output
9821034
}
9831035
}
1036+
1037+
case .superMemberExpression(let superMemberExpression):
1038+
guard superMemberExpression.isOptional == false else {
1039+
throw CompilerError.unsupportedFeatureError("Optional chaining is not supported in super member expressions")
1040+
}
1041+
guard let property = superMemberExpression.property else {
1042+
throw CompilerError.invalidNodeError("Missing property in super member expression")
1043+
}
1044+
1045+
switch property {
1046+
case .name(let name):
1047+
return emit(GetSuperProperty(propertyName: name), withInputs: []).output
1048+
1049+
case .expression(let expr):
1050+
if case .numberLiteral(let literal) = expr.expression, let _ = Int64(exactly: literal.value) {
1051+
throw CompilerError.unsupportedFeatureError("GetElement is not supported in super member expressions")
1052+
} else {
1053+
let compiledProperty = try compileExpression(expr)
1054+
return emit(GetComputedSuperProperty(), withInputs: [compiledProperty]).output
1055+
}
1056+
}
9841057

9851058
case .unaryExpression(let unaryExpression):
9861059
if unaryExpression.operator == "typeof" {

Sources/Fuzzilli/Compiler/Parser/parser.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,12 @@ function parse(script, proto) {
512512
}
513513
case 'CallExpression':
514514
case 'OptionalCallExpression': {
515+
if (node.callee.type === 'Super') {
516+
let arguments = node.arguments.map(visitExpression);
517+
let isOptional = node.type === 'OptionalCallExpression';
518+
return makeExpression('CallSuperConstructor', { arguments, isOptional });
519+
}
520+
515521
let callee = visitExpression(node.callee);
516522
let arguments = node.arguments.map(visitExpression);
517523
let isOptional = node.type === 'OptionalCallExpression';
@@ -524,6 +530,18 @@ function parse(script, proto) {
524530
}
525531
case 'MemberExpression':
526532
case 'OptionalMemberExpression': {
533+
if (node.object && node.object.type === 'Super') {
534+
let out = {};
535+
if (node.computed) {
536+
out.expression = visitExpression(node.property);
537+
} else {
538+
assert(node.property.type === 'Identifier', "Expected node.property.type to be exactly 'Identifier'");
539+
assert(node.property.name != 'Super', "super.super(...) is not allowed");
540+
out.name = node.property.name;
541+
}
542+
out.isOptional = node.type === 'OptionalMemberExpression';
543+
return makeExpression('SuperMemberExpression', out);
544+
}
527545
let object = visitExpression(node.object);
528546
let out = { object };
529547
if (node.computed) {

0 commit comments

Comments
 (0)