Skip to content

Commit

Permalink
FEAT: Support default arguments in functions #WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
0xe committed May 28, 2024
1 parent 085d561 commit 482d3d8
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 1 deletion.
26 changes: 26 additions & 0 deletions src/org/mozilla/javascript/IRFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,32 @@ private Node transformFunction(FunctionNode fn) {
decompiler.addToken(Token.EOL);
}

/* Process default parameters */
if (fn.getDefaultParams() != null) {
Object[] defaultParams = fn.getDefaultParams();
for (int i = defaultParams.length - 1; i > 0; ) {
AstNode rhs = (AstNode) defaultParams[i];
String name = (String) defaultParams[i - 1];
/* TODO: default params - error handling */
body.addChildToFront(
createIf(
createBinary(
Token.SHEQ,
new Name(fn.getPosition(), name),
new Name(fn.getPosition(), "undefined")),
new Node(
Token.EXPR_VOID,
createAssignment(
Token.ASSIGN,
new Name(fn.getPosition(), name),
transform(rhs)),
body.getLineno()),
null,
body.getLineno()));
i -= 2;
}
}

if (destructuring != null) {
body.addChildToFront(new Node(Token.EXPR_VOID, destructuring, lineno));
}
Expand Down
26 changes: 25 additions & 1 deletion src/org/mozilla/javascript/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,29 @@ private void parseFunctionParams(FunctionNode fnNode) throws IOException {
addError("msg.dup.param.strict", paramName);
paramNames.add(paramName);
}
if (matchToken(Token.ASSIGN, true)) {
/* TODO: Same for arrowFunctionParams below. Refactor into a helper. */
AstNode rhs = assignExpr();
Object[] existing = fnNode.getDefaultParams();
Object[] current;
int current_size = 0;
if (existing == null) {
existing = new Object[2];
fnNode.setDefaultParams(existing);
current = existing;
} else {
current =
new Object
[existing.length
+ 2]; /* TODO: A more flexible structure? */
System.arraycopy(existing, 0, current, 0, existing.length);
current_size = existing.length;
existing = null;
}
current[current_size] = paramName;
current[current.length - 1] = rhs;
fnNode.setDefaultParams(current);
}
} else {
fnNode.addParam(makeErrorNode());
}
Expand Down Expand Up @@ -1046,7 +1069,8 @@ private void arrowFunctionParams(
FunctionNode fnNode,
AstNode params,
Map<String, Node> destructuring,
Set<String> paramNames) {
Set<String> paramNames)
throws IOException {
if (params instanceof ArrayLiteral || params instanceof ObjectLiteral) {
markDestructuring(params);
fnNode.addParam(params);
Expand Down
10 changes: 10 additions & 0 deletions src/org/mozilla/javascript/ast/FunctionNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ public static enum Form {
private int rp = -1;
private boolean hasRestParameter;

public Object[] getDefaultParams() {
return defaultParams;
}

public void setDefaultParams(Object[] defaultParams) {
this.defaultParams = defaultParams;
}

Object[] defaultParams;

// codegen variables
private int functionType;
private boolean needsActivation;
Expand Down
37 changes: 37 additions & 0 deletions testsrc/org/mozilla/javascript/tests/FunctionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,31 @@ public void secondFunctionWithSameNameStrict() throws Exception {
assertEvaluates("functionfunc(){result+=norm(func);}outer", script);
}

@Test
public void functionDefaultArgsBasic() throws Exception {
final String script = "function foo(a = 2) {" + " return a;" + "}";
assertIntEvaluates(32, script + "\nfoo(32)");
assertIntEvaluates(2, script + "\nfoo()");
assertIntEvaluates(2, script + "\nfoo(undefined)");
}

@Test
public void functionDefaultArgsMulti() throws Exception {
final String script = "function foo(a = 2, b = 23) {" + " return a + b;" + "}";
assertIntEvaluates(55, script + "\nfoo(32)");
assertIntEvaluates(25, script + "\nfoo()");
assertIntEvaluates(34, script + "\nfoo(32, 2)");
assertIntEvaluates(34, script + "\nfoo(undefined, undefined)");
}

@Test
public void functionDefaultArgsUsage() throws Exception {
final String script = "function foo(a = 2, b = a * 2) {" + " return a + b;" + "}";
assertIntEvaluates(96, script + "\nfoo(32)");
assertIntEvaluates(6, script + "\nfoo()");
assertIntEvaluates(34, script + "\nfoo(32, 2)");
}

@Test
public void functioNamesExceptionsStrict() throws Exception {
final String script =
Expand Down Expand Up @@ -149,4 +174,16 @@ private static void assertEvaluates(final Object expected, final String source)
return null;
});
}

private static void assertIntEvaluates(final Object expected, final String source) {
Utils.runWithAllOptimizationLevels(
cx -> {
final Scriptable scope = cx.initStandardObjects();
final Object rep = cx.evaluateString(scope, source, "test.js", 0, null);
if (rep instanceof Double)
assertEquals((int) expected, ((Double) rep).intValue());
else assertEquals(expected, rep);
return null;
});
}
}

0 comments on commit 482d3d8

Please sign in to comment.