Skip to content

Commit dd0743a

Browse files
andreabergiacam.walter
andauthored
Optimize undefined lookup
* Create Undefined token to reduce lookups This creates a new token to treat undefined as a special case since it comes up so frequently. The vast majority of the time we end up using these optimizations, but there are a few cases where we just keep the previous behavior of treating undefined as a normal name: deleting and assigning. These cases are likely to be very infrequent so there would likely not be any noticible performance improvements, as well as a lot of similar code to what already exists. With the introduction of this new behavior, the main difference was to recreate looking up if undefined was redefined somewhere in this scope stack, which is the reason for the extra tests. --------- Co-authored-by: cam.walter <cam.walter@servicenow.com>
1 parent c0afafe commit dd0743a

11 files changed

Lines changed: 193 additions & 52 deletions

File tree

rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,7 @@ private void visitExpression(Node node, int contextFlags) {
725725
// Put undefined
726726
resolveForwardGoto(putUndefinedLabel);
727727
addIcode(Icode_POP);
728-
addStringOp(Token.NAME, "undefined");
728+
addIcode(Icode_UNDEF);
729729
resolveForwardGoto(afterLabel);
730730
} else if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) {
731731
addStringOp(
@@ -770,7 +770,7 @@ private void visitExpression(Node node, int contextFlags) {
770770
// Put undefined
771771
resolveForwardGoto(putUndefinedLabel);
772772
addIcode(Icode_POP);
773-
addStringOp(Token.NAME, "undefined");
773+
addIcode(Icode_UNDEF);
774774
resolveForwardGoto(afterLabel);
775775
} else if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) {
776776
visitExpression(child, 0);
@@ -1008,6 +1008,11 @@ private void visitExpression(Node node, int contextFlags) {
10081008
stackChange(1);
10091009
break;
10101010

1011+
case Token.UNDEFINED:
1012+
addIcode(Icode_UNDEF);
1013+
stackChange(1);
1014+
break;
1015+
10111016
case Token.ENUM_NEXT:
10121017
case Token.ENUM_ID:
10131018
addIndexOp(type, getLocalBlockRef(node));
@@ -1054,7 +1059,7 @@ private void visitExpression(Node node, int contextFlags) {
10541059
// Put undefined
10551060
resolveForwardGoto(putUndefinedLabel);
10561061
addIcode(Icode_POP);
1057-
addStringOp(Token.NAME, "undefined");
1062+
addIcode(Icode_UNDEF);
10581063
resolveForwardGoto(afterLabel);
10591064
} else {
10601065
addStringOp(type, (String) node.getProp(Node.NAME_PROP));
@@ -1265,7 +1270,7 @@ private CompleteOptionalCallJump completeOptionalCallJump() {
12651270

12661271
// Put undefined
12671272
addIcode(Icode_POP);
1268-
addStringOp(Token.NAME, "undefined");
1273+
addIcode(Icode_UNDEF);
12691274
int afterLabel = iCodeTop;
12701275
addGotoOp(Token.GOTO);
12711276

rhino/src/main/java/org/mozilla/javascript/IRFactory.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ private Node transform(AstNode node) {
196196
case Token.FALSE:
197197
case Token.THIS:
198198
case Token.NULL:
199+
case Token.UNDEFINED:
199200
case Token.DEBUGGER:
200201
return transformLiteral(node);
201202
case Token.SUPER:
@@ -660,7 +661,7 @@ private Node transformFunction(FunctionNode fn) {
660661
createBinary(
661662
Token.SHEQ,
662663
parser.createName(name),
663-
parser.createName("undefined")),
664+
new KeywordLiteral().setType(Token.UNDEFINED)),
664665
new Node(
665666
Token.EXPR_VOID,
666667
createAssignment(
@@ -1953,6 +1954,10 @@ private static Node createUnary(int nodeType, Node child) {
19531954
child.setType(Token.BINDNAME);
19541955
Node right = Node.newString(child.getString());
19551956
n = new Node(nodeType, child, right);
1957+
} else if (childType == Token.UNDEFINED) {
1958+
Node name = Node.newString(Token.BINDNAME, "undefined");
1959+
Node right = Node.newString("undefined");
1960+
n = new Node(nodeType, name, right);
19561961
} else if (childType == Token.GETPROP || childType == Token.GETELEM) {
19571962
Node left = child.getFirstChild();
19581963
Node right = child.getLastChild();
@@ -2426,6 +2431,7 @@ private static Node makeReference(Node node) {
24262431
int type = node.getType();
24272432
switch (type) {
24282433
case Token.NAME:
2434+
case Token.UNDEFINED:
24292435
case Token.GETPROP:
24302436
case Token.GETELEM:
24312437
case Token.GET_REF:
@@ -2443,6 +2449,7 @@ private static int isAlwaysDefinedBoolean(Node node) {
24432449
switch (node.getType()) {
24442450
case Token.FALSE:
24452451
case Token.NULL:
2452+
case Token.UNDEFINED:
24462453
return ALWAYS_FALSE_BOOLEAN;
24472454
case Token.TRUE:
24482455
return ALWAYS_TRUE_BOOLEAN;

rhino/src/main/java/org/mozilla/javascript/NodeTransformer.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -330,11 +330,9 @@ private void transformCompilationUnit_r(
330330
if (child.getType() == Token.EQ || child.getType() == Token.NE) {
331331
Node first = child.getFirstChild();
332332
Node last = child.getLastChild();
333-
if (first.getType() == Token.NAME
334-
&& first.getString().equals("undefined")) {
333+
if (first.getType() == Token.UNDEFINED) {
335334
child = last;
336-
} else if (last.getType() == Token.NAME
337-
&& last.getString().equals("undefined")) {
335+
} else if (last.getType() == Token.UNDEFINED) {
338336
child = first;
339337
}
340338
}

rhino/src/main/java/org/mozilla/javascript/Parser.java

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ public class Parser {
150150
private Map<String, LabeledStatement> labelSet;
151151
private List<Loop> loopSet;
152152
private List<Jump> loopAndSwitchSet;
153+
private boolean hasUndefinedBeenRedefined = false;
153154
// end of per function variables
154155

155156
// Lacking 2-token lookahead, labels become a problem.
@@ -865,7 +866,9 @@ private void parseFunctionParams(FunctionNode fnNode) throws IOException {
865866
restStartColumn = columnNumber();
866867
}
867868

868-
if (mustMatchToken(Token.NAME, "msg.no.parm", true)) {
869+
if (matchToken(Token.UNDEFINED, true)
870+
|| mustMatchToken(Token.NAME, "msg.no.parm", true)) {
871+
869872
if (!wasRest && fnNode.hasRestParameter()) {
870873
// Error: parameter after rest parameter
871874
reportError(
@@ -946,7 +949,7 @@ private FunctionNode function(int type, boolean isMethodDefiniton) throws IOExce
946949
AstNode memberExprNode = null;
947950

948951
do {
949-
if (matchToken(Token.NAME, true)) {
952+
if (matchToken(Token.NAME, true) || matchToken(Token.UNDEFINED, true)) {
950953
name = createNameNode(true, Token.NAME);
951954
if (inUseStrictDirective) {
952955
String id = name.getIdentifier();
@@ -1801,6 +1804,8 @@ private TryStatement tryStatement() throws IOException {
18011804
consumeToken();
18021805
peek = peekToken();
18031806
}
1807+
1808+
boolean previous = hasUndefinedBeenRedefined;
18041809
if (peek == Token.CATCH) {
18051810
while (matchToken(Token.CATCH, true)) {
18061811
int catchLineNum = lineNumber();
@@ -1821,14 +1826,19 @@ private TryStatement tryStatement() throws IOException {
18211826
{
18221827
matchToken(Token.LP, true);
18231828
lp = ts.tokenBeg;
1824-
mustMatchToken(Token.NAME, "msg.bad.catchcond", true);
1829+
if (!matchToken(Token.UNDEFINED, true)) {
1830+
mustMatchToken(Token.NAME, "msg.bad.catchcond", true);
1831+
}
18251832

18261833
varName = createNameNode();
18271834
Comment jsdocNodeForName = getAndResetJsDoc();
18281835
if (jsdocNodeForName != null) {
18291836
varName.setJsDocNode(jsdocNodeForName);
18301837
}
18311838
String varNameString = varName.getIdentifier();
1839+
if ("undefined".equals(varNameString)) {
1840+
hasUndefinedBeenRedefined = true;
1841+
}
18321842
if (inUseStrictDirective) {
18331843
if ("eval".equals(varNameString)
18341844
|| "arguments".equals(varNameString)) {
@@ -1868,6 +1878,7 @@ private TryStatement tryStatement() throws IOException {
18681878
try {
18691879
statements(catchScope);
18701880
} finally {
1881+
hasUndefinedBeenRedefined = previous;
18711882
popScope();
18721883
}
18731884

@@ -2036,13 +2047,22 @@ private WithStatement withStatement() throws IOException {
20362047
if (mustMatchToken(Token.RP, "msg.no.paren.after.with", true)) rp = ts.tokenBeg;
20372048

20382049
WithStatement pn = new WithStatement(pos);
2039-
AstNode body = getNextStatementAfterInlineComments(pn);
2040-
pn.setLength(getNodeEnd(body) - pos);
2041-
pn.setJsDocNode(withComment);
2042-
pn.setExpression(obj);
2043-
pn.setStatement(body);
2044-
pn.setParens(lp, rp);
2045-
pn.setLineColumnNumber(lineno, column);
2050+
2051+
boolean previous = hasUndefinedBeenRedefined;
2052+
try {
2053+
hasUndefinedBeenRedefined = true;
2054+
AstNode body = getNextStatementAfterInlineComments(pn);
2055+
2056+
pn.setLength(getNodeEnd(body) - pos);
2057+
pn.setJsDocNode(withComment);
2058+
pn.setExpression(obj);
2059+
pn.setStatement(body);
2060+
pn.setParens(lp, rp);
2061+
pn.setLineColumnNumber(lineno, column);
2062+
} finally {
2063+
hasUndefinedBeenRedefined = previous;
2064+
}
2065+
20462066
return pn;
20472067
}
20482068

@@ -2318,7 +2338,11 @@ private VariableDeclaration variables(int declType, int pos, boolean isStatement
23182338
markDestructuring(destructuring);
23192339
} else {
23202340
// Simple variable name
2321-
mustMatchToken(Token.NAME, "msg.bad.var", true);
2341+
if (tt == Token.UNDEFINED) {
2342+
consumeToken();
2343+
} else {
2344+
mustMatchToken(Token.NAME, "msg.bad.var", true);
2345+
}
23222346
name = createNameNode();
23232347
name.setLineColumnNumber(lineNumber(), columnNumber());
23242348
if (inUseStrictDirective) {
@@ -2412,6 +2436,8 @@ void defineSymbol(int declType, String name, boolean ignoreNotInBlock) {
24122436
return;
24132437
}
24142438
codeBug();
2439+
} else if ("undefined".equals(name)) {
2440+
hasUndefinedBeenRedefined = true;
24152441
}
24162442
Scope definingScope = currentScope.getDefiningScope(name);
24172443
Symbol symbol = definingScope != null ? definingScope.getSymbol(name) : null;
@@ -3382,6 +3408,20 @@ private AstNode primaryExpr() throws IOException {
33823408
re.setLineColumnNumber(lineNumber(), columnNumber());
33833409
return re;
33843410

3411+
case Token.UNDEFINED:
3412+
{
3413+
consumeToken();
3414+
pos = ts.tokenBeg;
3415+
end = ts.tokenEnd;
3416+
if (hasUndefinedBeenRedefined) {
3417+
return new Name(pos, end - pos, "undefined");
3418+
}
3419+
3420+
KeywordLiteral keywordLiteral = new KeywordLiteral(pos, end - pos, tt);
3421+
keywordLiteral.setLineColumnNumber(lineNumber(), columnNumber());
3422+
return keywordLiteral;
3423+
}
3424+
33853425
case Token.NULL:
33863426
case Token.THIS:
33873427
case Token.FALSE:
@@ -4347,6 +4387,7 @@ protected class PerFunctionVariables {
43474387
private Map<String, LabeledStatement> savedLabelSet;
43484388
private List<Loop> savedLoopSet;
43494389
private List<Jump> savedLoopAndSwitchSet;
4390+
private boolean savedHasUndefinedBeenRedefined;
43504391

43514392
PerFunctionVariables(FunctionNode fnNode) {
43524393
savedCurrentScriptOrFn = Parser.this.currentScriptOrFn;
@@ -4369,6 +4410,9 @@ protected class PerFunctionVariables {
43694410

43704411
savedInForInit = Parser.this.inForInit;
43714412
Parser.this.inForInit = false;
4413+
4414+
savedHasUndefinedBeenRedefined = Parser.this.hasUndefinedBeenRedefined;
4415+
// we want to inherit the current value
43724416
}
43734417

43744418
void restore() {
@@ -4379,6 +4423,7 @@ void restore() {
43794423
Parser.this.loopAndSwitchSet = savedLoopAndSwitchSet;
43804424
Parser.this.endFlags = savedEndFlags;
43814425
Parser.this.inForInit = savedInForInit;
4426+
Parser.this.hasUndefinedBeenRedefined = savedHasUndefinedBeenRedefined;
43824427
}
43834428
}
43844429

@@ -4555,14 +4600,20 @@ private void processDestructuringDefaults(
45554600
Node cond_inner =
45564601
new Node(
45574602
Token.HOOK,
4558-
new Node(Token.SHEQ, createName("undefined"), rightElem),
4603+
new Node(
4604+
Token.SHEQ,
4605+
new KeywordLiteral().setType(Token.UNDEFINED),
4606+
rightElem),
45594607
right,
45604608
rightElem);
45614609

45624610
Node cond =
45634611
new Node(
45644612
Token.HOOK,
4565-
new Node(Token.SHEQ, createName("undefined"), createName(name)),
4613+
new Node(
4614+
Token.SHEQ,
4615+
new KeywordLiteral().setType(Token.UNDEFINED),
4616+
createName(name)),
45664617
cond_inner,
45674618
left);
45684619

@@ -4613,10 +4664,12 @@ private void setupDefaultValues(
46134664
Node defaultRvalue =
46144665
transformer != null ? transformer.transform(defaultValue) : defaultValue;
46154666

4667+
Node undefined = new KeywordLiteral().setType(Token.UNDEFINED);
4668+
46164669
Node cond_default =
46174670
new Node(
46184671
Token.HOOK,
4619-
new Node(Token.SHEQ, createName(tempName), createName("undefined")),
4672+
new Node(Token.SHEQ, createName(tempName), undefined),
46204673
defaultRvalue,
46214674
createName(tempName));
46224675

@@ -4771,6 +4824,10 @@ protected Node simpleAssignment(Node left, Node right) {
47714824
protected Node simpleAssignment(Node left, Node right, Transformer transformer) {
47724825
int nodeType = left.getType();
47734826
switch (nodeType) {
4827+
case Token.UNDEFINED:
4828+
left = Node.newString(Token.BINDNAME, "undefined");
4829+
return new Node(Token.SETNAME, left, right);
4830+
47744831
case Token.NAME:
47754832
String name = ((Name) left).getIdentifier();
47764833
if (inUseStrictDirective && ("eval".equals(name) || "arguments".equals(name))) {

rhino/src/main/java/org/mozilla/javascript/Token.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ public static enum CommentType {
8686
NUMBER = NAME + 1,
8787
STRING = NUMBER + 1,
8888
NULL = STRING + 1,
89-
THIS = NULL + 1,
89+
UNDEFINED = NULL + 1,
90+
THIS = UNDEFINED + 1,
9091
FALSE = THIS + 1,
9192
TRUE = FALSE + 1,
9293
SHEQ = TRUE + 1, // shallow equality (===)
@@ -365,6 +366,8 @@ public static String typeToName(int token) {
365366
return "STRING";
366367
case NULL:
367368
return "NULL";
369+
case UNDEFINED:
370+
return "UNDEFINED";
368371
case THIS:
369372
return "THIS";
370373
case FALSE:
@@ -696,6 +699,8 @@ public static String keywordToName(int token) {
696699
return "true";
697700
case Token.TYPEOF:
698701
return "typeof";
702+
case Token.UNDEFINED:
703+
return "undefined";
699704
case Token.VAR:
700705
return "var";
701706
case Token.VOID:

rhino/src/main/java/org/mozilla/javascript/TokenStream.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ private static int stringToKeywordForJS(String name) {
114114
Id_this = Token.THIS,
115115
Id_true = Token.TRUE,
116116
Id_typeof = Token.TYPEOF,
117+
Id_undefined = Token.UNDEFINED,
117118
Id_var = Token.VAR,
118119
Id_void = Token.VOID,
119120
Id_while = Token.WHILE,
@@ -222,6 +223,9 @@ private static int stringToKeywordForJS(String name) {
222223
case "typeof":
223224
id = Id_typeof;
224225
break;
226+
case "undefined":
227+
id = Id_undefined;
228+
break;
225229
case "var":
226230
id = Id_var;
227231
break;
@@ -407,6 +411,7 @@ private static int stringToKeywordForES(String name, boolean isStrict) {
407411
Id_false = Token.FALSE,
408412
Id_null = Token.NULL,
409413
Id_true = Token.TRUE,
414+
Id_undefined = Token.UNDEFINED,
410415

411416
// Non ReservedWord, but Non IdentifierName in strict mode code.
412417
// 12.1.1 Static Semantics: Early Errors
@@ -556,6 +561,9 @@ private static int stringToKeywordForES(String name, boolean isStrict) {
556561
case "null":
557562
id = Id_null;
558563
break;
564+
case "undefined":
565+
id = Id_undefined;
566+
break;
559567
case "true":
560568
id = Id_true;
561569
break;

0 commit comments

Comments
 (0)