1
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3
* The contents of this file are subject to the Netscape Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/NPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is Rhino code, released
16
* The Initial Developer of the Original Code is Netscape
17
* Communications Corporation. Portions created by Netscape are
18
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
24
* Alternatively, the contents of this file may be used under the
25
* terms of the GNU Public License (the "GPL"), in which case the
26
* provisions of the GPL are applicable instead of those above.
27
* If you wish to allow use of your version of this file only
28
* under the terms of the GPL and not to allow others to use your
29
* version of this file under the NPL, indicate your decision by
30
* deleting the provisions above and replace them with the notice
31
* and other provisions required by the GPL. If you do not delete
32
* the provisions above, a recipient may use your version of this
33
* file under either the NPL or the GPL.
36
package org.mozilla.javascript;
39
* This class allows the creation of nodes, and follows the Factory pattern.
45
public class IRFactory {
47
public IRFactory(TokenStream ts, Scriptable scope) {
53
* Script (for associating file/url names with toplevel scripts.)
55
public Object createScript(Object body, String sourceName,
56
int baseLineno, int endLineno, Object source)
58
Node result = new Node(TokenStream.SCRIPT, sourceName);
59
Node children = ((Node) body).getFirstChild();
61
result.addChildrenToBack(children);
62
result.putProp(Node.SOURCENAME_PROP, sourceName);
63
result.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno));
64
result.putProp(Node.END_LINENO_PROP, new Integer(endLineno));
66
result.putProp(Node.SOURCE_PROP, source);
73
public Object createLeaf(int nodeType) {
74
return new Node(nodeType);
77
public Object createLeaf(int nodeType, String id) {
78
return new Node(nodeType, id);
81
public Object createLeaf(int nodeType, int nodeOp) {
82
return new Node(nodeType, new Integer(nodeOp));
86
* Statement leaf nodes.
89
public Object createSwitch(int lineno) {
90
return new Node(TokenStream.SWITCH, new Integer(lineno));
93
public Object createVariables(int lineno) {
94
return new Node(TokenStream.VAR, new Integer(lineno));
97
public Object createExprStatement(Object expr, int lineno) {
98
return new Node(TokenStream.EXPRSTMT, (Node) expr, new Integer(lineno));
104
public Object createName(String name) {
105
return new Node(TokenStream.NAME, name);
109
* String (for literals)
111
public Object createString(String string) {
112
return new Node(TokenStream.STRING, string);
116
* Number (for literals)
118
public Object createNumber(Number number) {
119
return new Node(TokenStream.NUMBER, number);
123
* Catch clause of try/catch/finally
124
* @param varName the name of the variable to bind to the exception
125
* @param catchCond the condition under which to catch the exception.
126
* May be null if no condition is given.
127
* @param stmts the statements in the catch clause
128
* @param lineno the starting line number of the catch clause
130
public Object createCatch(String varName, Object catchCond, Object stmts,
133
if (catchCond == null)
134
catchCond = new Node(TokenStream.PRIMARY,
135
new Integer(TokenStream.TRUE));
136
Node result = new Node(TokenStream.CATCH, (Node)createName(varName),
137
(Node)catchCond, (Node)stmts);
138
result.setDatum(new Integer(lineno));
145
public Object createThrow(Object expr, int lineno) {
146
return new Node(TokenStream.THROW, (Node)expr, new Integer(lineno));
152
public Object createReturn(Object expr, int lineno) {
154
? new Node(TokenStream.RETURN, new Integer(lineno))
155
: new Node(TokenStream.RETURN, (Node)expr, new Integer(lineno));
161
public Object createLabel(String label, int lineno) {
162
Node result = new Node(TokenStream.LABEL, new Integer(lineno));
163
Node name = new Node(TokenStream.NAME, label);
164
result.addChildToBack(name);
169
* Break (possibly labeled)
171
public Object createBreak(String label, int lineno) {
172
Node result = new Node(TokenStream.BREAK, new Integer(lineno));
176
Node name = new Node(TokenStream.NAME, label);
177
result.addChildToBack(name);
183
* Continue (possibly labeled)
185
public Object createContinue(String label, int lineno) {
186
Node result = new Node(TokenStream.CONTINUE, new Integer(lineno));
190
Node name = new Node(TokenStream.NAME, label);
191
result.addChildToBack(name);
198
* Creates the empty statement block
199
* Must make subsequent calls to add statements to the node
201
public Object createBlock(int lineno) {
202
return new Node(TokenStream.BLOCK, new Integer(lineno));
205
public Object createFunctionNode(String name, Object args,
210
return new FunctionNode(name, (Node) args, (Node) statements);
213
public Object createFunction(String name, Object args, Object statements,
214
String sourceName, int baseLineno,
215
int endLineno, Object source,
218
FunctionNode f = (FunctionNode) createFunctionNode(name, args,
220
f.setFunctionType(isExpr ? FunctionNode.FUNCTION_EXPRESSION
221
: FunctionNode.FUNCTION_STATEMENT);
222
f.putProp(Node.SOURCENAME_PROP, sourceName);
223
f.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno));
224
f.putProp(Node.END_LINENO_PROP, new Integer(endLineno));
226
f.putProp(Node.SOURCE_PROP, source);
227
Node result = new Node(TokenStream.FUNCTION, name);
228
result.putProp(Node.FUNCTION_PROP, f);
232
public void setFunctionExpressionStatement(Object o) {
234
FunctionNode f = (FunctionNode) n.getProp(Node.FUNCTION_PROP);
235
f.setFunctionType(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
239
* Add a child to the back of the given node. This function
240
* breaks the Factory abstraction, but it removes a requirement
241
* from implementors of Node.
243
public void addChildToBack(Object parent, Object child) {
244
((Node)parent).addChildToBack((Node)child);
250
public Object createWhile(Object cond, Object body, int lineno) {
251
// Just add a GOTO to the condition in the do..while
252
Node result = (Node) createDoWhile(body, cond, lineno);
253
Node condTarget = (Node) result.getProp(Node.CONTINUE_PROP);
254
Node GOTO = new Node(TokenStream.GOTO);
255
GOTO.putProp(Node.TARGET_PROP, condTarget);
256
result.addChildToFront(GOTO);
263
public Object createDoWhile(Object body, Object cond, int lineno) {
264
Node result = new Node(TokenStream.LOOP, new Integer(lineno));
265
Node bodyTarget = new Node(TokenStream.TARGET);
266
Node condTarget = new Node(TokenStream.TARGET);
267
Node IFEQ = new Node(TokenStream.IFEQ, (Node)cond);
268
IFEQ.putProp(Node.TARGET_PROP, bodyTarget);
269
Node breakTarget = new Node(TokenStream.TARGET);
271
result.addChildToBack(bodyTarget);
272
result.addChildrenToBack((Node)body);
273
result.addChildToBack(condTarget);
274
result.addChildToBack(IFEQ);
275
result.addChildToBack(breakTarget);
277
result.putProp(Node.BREAK_PROP, breakTarget);
278
result.putProp(Node.CONTINUE_PROP, condTarget);
286
public Object createFor(Object init, Object test, Object incr,
287
Object body, int lineno)
289
if (((Node) test).getType() == TokenStream.VOID) {
290
test = new Node(TokenStream.PRIMARY,
291
new Integer(TokenStream.TRUE));
293
Node result = (Node)createWhile(test, body, lineno);
294
Node initNode = (Node) init;
295
if (initNode.getType() != TokenStream.VOID) {
296
if (initNode.getType() != TokenStream.VAR)
297
initNode = new Node(TokenStream.POP, initNode);
298
result.addChildToFront(initNode);
300
Node condTarget = (Node)result.getProp(Node.CONTINUE_PROP);
301
Node incrTarget = new Node(TokenStream.TARGET);
302
result.addChildBefore(incrTarget, condTarget);
303
if (((Node) incr).getType() != TokenStream.VOID) {
304
incr = createUnary(TokenStream.POP, incr);
305
result.addChildAfter((Node)incr, incrTarget);
307
result.putProp(Node.CONTINUE_PROP, incrTarget);
315
public Object createForIn(Object lhs, Object obj, Object body, int lineno) {
317
Node lhsNode = (Node) lhs;
318
Node objNode = (Node) obj;
319
int type = lhsNode.getType();
321
Node lvalue = lhsNode;
324
case TokenStream.NAME:
325
case TokenStream.GETPROP:
326
case TokenStream.GETELEM:
329
case TokenStream.VAR:
331
* check that there was only one variable given.
332
* we can't do this in the parser, because then the
333
* parser would have to know something about the
334
* 'init' node of the for-in loop.
336
Node lastChild = lhsNode.getLastChild();
337
if (lhsNode.getFirstChild() != lastChild) {
338
reportError("msg.mult.index");
340
lvalue = new Node(TokenStream.NAME, lastChild.getString());
344
reportError("msg.bad.for.in.lhs");
348
Node init = new Node(TokenStream.ENUMINIT, objNode);
349
Node next = new Node(TokenStream.ENUMNEXT);
350
next.putProp(Node.ENUM_PROP, init);
351
Node temp = createNewTemp(next);
352
Node cond = new Node(TokenStream.EQOP, new Integer(TokenStream.NE));
353
cond.addChildToBack(temp);
354
cond.addChildToBack(new Node(TokenStream.PRIMARY,
355
new Integer(TokenStream.NULL)));
356
Node newBody = new Node(TokenStream.BLOCK);
357
Node assign = (Node) createAssignment(TokenStream.NOP, lvalue,
358
createUseTemp(temp), null,
360
newBody.addChildToBack(new Node(TokenStream.POP, assign));
361
newBody.addChildToBack((Node) body);
362
Node result = (Node) createWhile(cond, newBody, lineno);
364
result.addChildToFront(init);
365
if (type == TokenStream.VAR)
366
result.addChildToFront(lhsNode);
368
Node done = new Node(TokenStream.ENUMDONE);
369
done.putProp(Node.ENUM_PROP, init);
370
result.addChildToBack(done);
378
* The IRFactory tries to express as much as possible in the tree;
379
* the responsibilities remaining for Codegen are to add the Java
380
* handlers: (Either (but not both) of TARGET and FINALLY might not
383
* - a catch handler for javascript exceptions that unwraps the
384
* exception onto the stack and GOTOes to the catch target -
385
* TARGET_PROP in the try node.
387
* - a finally handler that catches any exception, stores it to a
388
* temporary, and JSRs to the finally target - FINALLY_PROP in the
389
* try node - before re-throwing the exception.
391
* ... and a goto to GOTO around these handlers.
393
public Object createTryCatchFinally(Object tryblock, Object catchblocks,
394
Object finallyblock, int lineno)
396
Node trynode = (Node)tryblock;
399
if (trynode.getType() == TokenStream.BLOCK && !trynode.hasChildren())
402
Node pn = new Node(TokenStream.TRY, trynode, new Integer(lineno));
403
Node catchNodes = (Node)catchblocks;
404
boolean hasCatch = catchNodes.hasChildren();
405
boolean hasFinally = false;
406
Node finallyNode = null;
407
Node finallyTarget = null;
408
if (finallyblock != null) {
409
finallyNode = (Node)finallyblock;
410
hasFinally = (finallyNode.getType() != TokenStream.BLOCK
411
|| finallyNode.hasChildren());
413
// make a TARGET for the finally that the tcf node knows about
414
finallyTarget = new Node(TokenStream.TARGET);
415
pn.putProp(Node.FINALLY_PROP, finallyTarget);
417
// add jsr finally to the try block
418
Node jsrFinally = new Node(TokenStream.JSR);
419
jsrFinally.putProp(Node.TARGET_PROP, finallyTarget);
420
pn.addChildToBack(jsrFinally);
425
if (!hasFinally && !hasCatch) // bc finally might be an empty block...
428
Node endTarget = new Node(TokenStream.TARGET);
429
Node GOTOToEnd = new Node(TokenStream.GOTO);
430
GOTOToEnd.putProp(Node.TARGET_PROP, endTarget);
431
pn.addChildToBack(GOTOToEnd);
440
} catch (e: e instanceof Object) {
454
if (e instanceof Object) {
467
// make a TARGET for the catch that the tcf node knows about
468
Node catchTarget = new Node(TokenStream.TARGET);
469
pn.putProp(Node.TARGET_PROP, catchTarget);
471
pn.addChildToBack(catchTarget);
473
// get the exception object and store it in a temp
474
Node exn = createNewLocal(new Node(TokenStream.VOID));
475
pn.addChildToBack(new Node(TokenStream.POP, exn));
477
Node endCatch = new Node(TokenStream.TARGET);
479
// add [jsr finally?] goto end to each catch block
480
// expects catchNode children to be (cond block) pairs.
481
Node cb = catchNodes.getFirstChild();
483
Node catchStmt = new Node(TokenStream.BLOCK);
484
int catchLineNo = ((Integer)cb.getDatum()).intValue();
486
Node name = cb.getFirstChild();
487
Node cond = name.getNextSibling();
488
Node catchBlock = cond.getNextSibling();
489
cb.removeChild(name);
490
cb.removeChild(cond);
491
cb.removeChild(catchBlock);
493
Node newScope = createNewLocal(new Node(TokenStream.NEWSCOPE));
494
Node initScope = new Node(TokenStream.SETPROP, newScope,
495
new Node(TokenStream.STRING,
497
createUseLocal(exn));
498
catchStmt.addChildToBack(new Node(TokenStream.POP, initScope));
500
catchBlock.addChildToBack(new Node(TokenStream.LEAVEWITH));
501
Node GOTOToEndCatch = new Node(TokenStream.GOTO);
502
GOTOToEndCatch.putProp(Node.TARGET_PROP, endCatch);
503
catchBlock.addChildToBack(GOTOToEndCatch);
505
Node ifStmt = (Node) createIf(cond, catchBlock, null, catchLineNo);
506
// Try..catch produces "with" code in order to limit
507
// the scope of the exception object.
508
// OPT: We should be able to figure out the correct
509
// scoping at compile-time and avoid the
511
Node withStmt = (Node) createWith(createUseLocal(newScope),
512
ifStmt, catchLineNo);
513
catchStmt.addChildToBack(withStmt);
515
pn.addChildToBack(catchStmt);
518
cb = cb.getNextSibling();
521
// Generate code to rethrow if no catch clause was executed
522
Node rethrow = new Node(TokenStream.THROW, createUseLocal(exn));
523
pn.addChildToBack(rethrow);
525
pn.addChildToBack(endCatch);
526
// add a JSR finally if needed
528
Node jsrFinally = new Node(TokenStream.JSR);
529
jsrFinally.putProp(Node.TARGET_PROP, finallyTarget);
530
pn.addChildToBack(jsrFinally);
531
Node GOTO = new Node(TokenStream.GOTO);
532
GOTO.putProp(Node.TARGET_PROP, endTarget);
533
pn.addChildToBack(GOTO);
538
pn.addChildToBack(finallyTarget);
539
Node returnTemp = createNewLocal(new Node(TokenStream.VOID));
540
Node popAndMake = new Node(TokenStream.POP, returnTemp);
541
pn.addChildToBack(popAndMake);
542
pn.addChildToBack(finallyNode);
543
Node ret = createUseLocal(returnTemp);
545
// add the magic prop that makes it output a RET
546
ret.putProp(Node.TARGET_PROP, Boolean.TRUE);
547
pn.addChildToBack(ret);
549
pn.addChildToBack(endTarget);
554
* Throw, Return, Label, Break and Continue are defined in ASTFactory.
560
public Object createWith(Object obj, Object body, int lineno) {
561
Node result = new Node(TokenStream.BLOCK, new Integer(lineno));
562
result.addChildToBack(new Node(TokenStream.ENTERWITH, (Node)obj));
563
Node bodyNode = new Node(TokenStream.WITH, (Node) body,
564
new Integer(lineno));
565
result.addChildrenToBack(bodyNode);
566
result.addChildToBack(new Node(TokenStream.LEAVEWITH));
572
* <BR>createArrayLiteral rewrites its argument as array creation
573
* plus a series of array element entries, so later compiler
574
* stages don't need to know about array literals.
576
public Object createArrayLiteral(Object obj) {
579
array = result = new Node(TokenStream.NEW,
580
new Node(TokenStream.NAME, "Array"));
581
Node temp = createNewTemp(result);
584
java.util.Enumeration children = ((Node) obj).getChildIterator();
588
while (children.hasMoreElements()) {
589
elem = (Node) children.nextElement();
590
if (elem.getType() == TokenStream.PRIMARY &&
591
elem.getInt() == TokenStream.UNDEFINED)
596
Node addelem = new Node(TokenStream.SETELEM, createUseTemp(temp),
597
new Node(TokenStream.NUMBER,
601
result = new Node(TokenStream.COMMA, result, addelem);
605
* If the version is 120, then new Array(4) means create a new
606
* array with 4 as the first element. In this case, we might
607
* need to explicitly check against trailing undefined
608
* elements in the array literal, and set the length manually
609
* if these occur. Otherwise, we can add an argument to the
610
* node specifying new Array() to provide the array length.
611
* (Which will make Array optimizations involving allocating a
612
* Java array to back the javascript array work better.)
614
if (Context.getContext().getLanguageVersion() == Context.VERSION_1_2) {
615
/* When last array element is empty, we need to set the
616
* length explicitly, because we can't depend on SETELEM
617
* to do it for us - because empty [,,] array elements
618
* never set anything at all. */
620
elem.getType() == TokenStream.PRIMARY &&
621
elem.getInt() == TokenStream.UNDEFINED)
623
Node setlength = new Node(TokenStream.SETPROP,
625
new Node(TokenStream.STRING,
627
new Node(TokenStream.NUMBER,
629
result = new Node(TokenStream.COMMA, result, setlength);
632
array.addChildToBack(new Node(TokenStream.NUMBER,
635
return new Node(TokenStream.COMMA, result, createUseTemp(temp));
640
* <BR> createObjectLiteral rewrites its argument as object
641
* creation plus object property entries, so later compiler
642
* stages don't need to know about object literals.
644
public Object createObjectLiteral(Object obj) {
645
Node result = new Node(TokenStream.NEW, new Node(TokenStream.NAME,
647
Node temp = createNewTemp(result);
650
java.util.Enumeration children = ((Node) obj).getChildIterator();
652
while (children.hasMoreElements()) {
653
Node elem = (Node)children.nextElement();
655
int op = (elem.getType() == TokenStream.NAME)
656
? TokenStream.SETPROP
657
: TokenStream.SETELEM;
658
Node addelem = new Node(op, createUseTemp(temp),
659
elem, (Node)children.nextElement());
660
result = new Node(TokenStream.COMMA, result, addelem);
662
return new Node(TokenStream.COMMA, result, createUseTemp(temp));
666
* Regular expressions
668
public Object createRegExp(String string, String flags) {
669
return flags.length() == 0
670
? new Node(TokenStream.OBJECT,
671
new Node(TokenStream.STRING, string))
672
: new Node(TokenStream.OBJECT,
673
new Node(TokenStream.STRING, string),
674
new Node(TokenStream.STRING, flags));
680
public Object createIf(Object cond, Object ifTrue, Object ifFalse,
683
Node result = new Node(TokenStream.BLOCK, new Integer(lineno));
684
Node ifNotTarget = new Node(TokenStream.TARGET);
685
Node IFNE = new Node(TokenStream.IFNE, (Node) cond);
686
IFNE.putProp(Node.TARGET_PROP, ifNotTarget);
688
result.addChildToBack(IFNE);
689
result.addChildrenToBack((Node)ifTrue);
691
if (ifFalse != null) {
692
Node GOTOToEnd = new Node(TokenStream.GOTO);
693
Node endTarget = new Node(TokenStream.TARGET);
694
GOTOToEnd.putProp(Node.TARGET_PROP, endTarget);
695
result.addChildToBack(GOTOToEnd);
696
result.addChildToBack(ifNotTarget);
697
result.addChildrenToBack((Node)ifFalse);
698
result.addChildToBack(endTarget);
700
result.addChildToBack(ifNotTarget);
706
public Object createTernary(Object cond, Object ifTrue, Object ifFalse) {
707
return createIf(cond, ifTrue, ifFalse, -1);
713
public Object createUnary(int nodeType, Object child) {
714
Node childNode = (Node) child;
715
if (nodeType == TokenStream.DELPROP) {
716
int childType = childNode.getType();
719
if (childType == TokenStream.NAME) {
720
// Transform Delete(Name "a")
721
// to Delete(Bind("a"), String("a"))
722
childNode.setType(TokenStream.BINDNAME);
724
right = childNode.cloneNode();
725
right.setType(TokenStream.STRING);
726
} else if (childType == TokenStream.GETPROP ||
727
childType == TokenStream.GETELEM)
729
left = childNode.getFirstChild();
730
right = childNode.getLastChild();
731
childNode.removeChild(left);
732
childNode.removeChild(right);
734
return new Node(TokenStream.PRIMARY,
735
new Integer(TokenStream.TRUE));
737
return new Node(nodeType, left, right);
739
return new Node(nodeType, childNode);
742
public Object createUnary(int nodeType, int nodeOp, Object child) {
743
Node childNode = (Node) child;
744
int childType = childNode.getType();
745
if (nodeOp == TokenStream.TYPEOF &&
746
childType == TokenStream.NAME)
748
childNode.setType(TokenStream.TYPEOF);
752
if (nodeType == TokenStream.INC || nodeType == TokenStream.DEC) {
754
if (!hasSideEffects(childNode)
755
&& (nodeOp == TokenStream.POST)
756
&& (childType == TokenStream.NAME
757
|| childType == TokenStream.GETPROP
758
|| childType == TokenStream.GETELEM))
760
// if it's not a LHS type, createAssignment (below) will throw
762
return new Node(nodeType, childNode);
766
* Transform INC/DEC ops to +=1, -=1,
767
* expecting later optimization of all +/-=1 cases to INC, DEC.
769
// we have to use Double for now, because
770
// 0.0 and 1.0 are stored as dconst_[01],
771
// and using a Float creates a stack mismatch.
772
Node rhs = (Node) createNumber(new Double(1.0));
774
return createAssignment(nodeType == TokenStream.INC
779
ScriptRuntime.NumberClass,
780
nodeOp == TokenStream.POST);
783
Node result = new Node(nodeType, new Integer(nodeOp));
784
result.addChildToBack((Node)child);
791
public Object createBinary(int nodeType, Object left, Object right) {
795
case TokenStream.DOT:
796
nodeType = TokenStream.GETPROP;
797
Node idNode = (Node) right;
798
idNode.setType(TokenStream.STRING);
799
String id = idNode.getString();
800
if (id.equals("__proto__") || id.equals("__parent__")) {
801
Node result = new Node(nodeType, (Node) left);
802
result.putProp(Node.SPECIAL_PROP_PROP, id);
808
// OPT: could optimize to GETPROP iff string can't be a number
809
nodeType = TokenStream.GETELEM;
812
case TokenStream.AND:
813
temp = createNewTemp((Node) left);
814
return createTernary(temp, right, createUseTemp(temp));
817
temp = createNewTemp((Node) left);
818
return createTernary(temp, createUseTemp(temp), right);
821
return new Node(nodeType, (Node)left, (Node)right);
824
public Object createBinary(int nodeType, int nodeOp, Object left,
827
if (nodeType == TokenStream.ASSIGN) {
828
return createAssignment(nodeOp, (Node) left, (Node) right,
831
return new Node(nodeType, (Node) left, (Node) right,
832
new Integer(nodeOp));
835
public Object createAssignment(int nodeOp, Node left, Node right,
836
Class convert, boolean postfix)
838
int nodeType = left.getType();
842
case TokenStream.NAME:
843
return createSetName(nodeOp, left, right, convert, postfix);
845
case TokenStream.GETPROP:
846
idString = (String) left.getProp(Node.SPECIAL_PROP_PROP);
847
if (idString != null)
848
id = new Node(TokenStream.STRING, idString);
850
case TokenStream.GETELEM:
852
id = left.getLastChild();
853
return createSetProp(nodeType, nodeOp, left.getFirstChild(),
854
id, right, convert, postfix);
856
// TODO: This should be a ReferenceError--but that's a runtime
857
// exception. Should we compile an exception into the code?
858
reportError("msg.bad.lhs.assign");
863
private Node createConvert(Class toType, Node expr) {
866
Node result = new Node(TokenStream.CONVERT, expr);
867
result.putProp(Node.TYPE_PROP, ScriptRuntime.NumberClass);
871
private Object createSetName(int nodeOp, Node left, Node right,
872
Class convert, boolean postfix)
874
if (nodeOp == TokenStream.NOP) {
875
left.setType(TokenStream.BINDNAME);
876
return new Node(TokenStream.SETNAME, left, right);
879
String s = left.getString();
881
if (s.equals("__proto__") || s.equals("__parent__")) {
882
Node result = new Node(TokenStream.SETPROP, left, right);
883
result.putProp(Node.SPECIAL_PROP_PROP, s);
887
Node opLeft = new Node(TokenStream.NAME, s);
889
opLeft = createConvert(convert, opLeft);
891
opLeft = createNewTemp(opLeft);
892
Node op = new Node(nodeOp, opLeft, right);
894
Node lvalueLeft = new Node(TokenStream.BINDNAME, s);
895
Node result = new Node(TokenStream.SETNAME, lvalueLeft, op);
897
result = new Node(TokenStream.COMMA, result,
898
createUseTemp(opLeft));
903
public Node createNewTemp(Node n) {
904
int type = n.getType();
905
if (type == TokenStream.STRING || type == TokenStream.NUMBER) {
906
// Optimization: clone these values rather than storing
907
// and loading from a temp
910
Node result = new Node(TokenStream.NEWTEMP, n);
914
public Node createUseTemp(Node newTemp) {
915
int type = newTemp.getType();
916
if (type == TokenStream.NEWTEMP) {
917
Node result = new Node(TokenStream.USETEMP);
918
result.putProp(Node.TEMP_PROP, newTemp);
919
Integer n = (Integer) newTemp.getProp(Node.USES_PROP);
923
if (n.intValue() < Integer.MAX_VALUE)
924
n = new Integer(n.intValue() + 1);
926
newTemp.putProp(Node.USES_PROP, n);
929
return newTemp.cloneNode();
932
public Node createNewLocal(Node n) {
933
Node result = new Node(TokenStream.NEWLOCAL, n);
937
public Node createUseLocal(Node newLocal) {
938
int type = newLocal.getType();
939
if (type == TokenStream.NEWLOCAL) {
940
Node result = new Node(TokenStream.USELOCAL);
941
result.putProp(Node.LOCAL_PROP, newLocal);
944
return newLocal.cloneNode(); // what's this path for ?
947
public static boolean hasSideEffects(Node exprTree) {
948
switch (exprTree.getType()) {
949
case TokenStream.INC:
950
case TokenStream.DEC:
951
case TokenStream.SETPROP:
952
case TokenStream.SETELEM:
953
case TokenStream.SETNAME:
954
case TokenStream.CALL:
955
case TokenStream.NEW:
958
Node child = exprTree.getFirstChild();
959
while (child != null) {
960
if (hasSideEffects(child))
963
child = child.getNextSibling();
970
private Node createSetProp(int nodeType, int nodeOp, Node obj, Node id,
971
Node expr, Class convert, boolean postfix)
973
int type = nodeType == TokenStream.GETPROP
974
? TokenStream.SETPROP
975
: TokenStream.SETELEM;
977
Object datum = id.getDatum();
978
if (type == TokenStream.SETPROP && datum != null &&
979
datum instanceof String)
981
String s = (String) datum;
982
if (s.equals("__proto__") || s.equals("__parent__")) {
983
Node result = new Node(type, obj, expr);
984
result.putProp(Node.SPECIAL_PROP_PROP, s);
989
if (nodeOp == TokenStream.NOP)
990
return new Node(type, obj, id, expr);
992
* If the RHS expression could modify the LHS we have
993
* to construct a temporary to hold the LHS context
994
* prior to running the expression. Ditto, if the id
995
* expression has side-effects.
997
* XXX If the hasSideEffects tests take too long, we
998
* could make this an optimizer-only transform
999
* and always do the temp assignment otherwise.
1002
Node tmp1, tmp2, opLeft;
1003
if (hasSideEffects(expr)
1004
|| hasSideEffects(id)
1005
|| (obj.getType() != TokenStream.NAME)) {
1006
tmp1 = createNewTemp(obj);
1007
Node useTmp1 = createUseTemp(tmp1);
1009
tmp2 = createNewTemp(id);
1010
Node useTmp2 = createUseTemp(tmp2);
1012
opLeft = new Node(nodeType, useTmp1, useTmp2);
1014
tmp1 = obj.cloneNode();
1015
tmp2 = id.cloneNode();
1016
opLeft = new Node(nodeType, obj, id);
1019
if (convert != null)
1020
opLeft = createConvert(convert, opLeft);
1022
opLeft = createNewTemp(opLeft);
1023
Node op = new Node(nodeOp, opLeft, expr);
1025
Node result = new Node(type, tmp1, tmp2, op);
1027
result = new Node(TokenStream.COMMA, result,
1028
createUseTemp(opLeft));
1034
private void reportError(String msgResource) {
1037
throw NativeGlobal.constructError(
1038
Context.getContext(), "SyntaxError",
1039
ScriptRuntime.getMessage0(msgResource),
1042
String message = Context.getMessage0(msgResource);
1043
Context.reportError(message, ts.getSourceName(), ts.getLineno(),
1044
ts.getLine(), ts.getOffset());
1048
// Only needed to get file/line information. Could create an interface
1049
// that TokenStream implements if we want to make the connection less
1051
private TokenStream ts;
1053
// Only needed to pass to the Erorr exception constructors
1054
private Scriptable scope;
1
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3
* The contents of this file are subject to the Netscape Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/NPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is Rhino code, released
16
* The Initial Developer of the Original Code is Netscape
17
* Communications Corporation. Portions created by Netscape are
18
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
28
* Alternatively, the contents of this file may be used under the
29
* terms of the GNU Public License (the "GPL"), in which case the
30
* provisions of the GPL are applicable instead of those above.
31
* If you wish to allow use of your version of this file only
32
* under the terms of the GPL and not to allow others to use your
33
* version of this file under the NPL, indicate your decision by
34
* deleting the provisions above and replace them with the notice
35
* and other provisions required by the GPL. If you do not delete
36
* the provisions above, a recipient may use your version of this
37
* file under either the NPL or the GPL.
40
package org.mozilla.javascript;
43
* This class allows the creation of nodes, and follows the Factory pattern.
51
IRFactory(Parser parser)
56
ScriptOrFnNode createScript()
58
return new ScriptOrFnNode(Token.SCRIPT);
62
* Script (for associating file/url names with toplevel scripts.)
64
void initScript(ScriptOrFnNode scriptNode, Node body)
66
Node children = body.getFirstChild();
67
if (children != null) { scriptNode.addChildrenToBack(children); }
73
Node createLeaf(int nodeType)
75
return new Node(nodeType);
78
Node createLeaf(int nodeType, int nodeOp)
80
return new Node(nodeType, nodeOp);
84
* Statement leaf nodes.
87
Node createSwitch(Node expr, int lineno)
90
// The switch will be rewritten from:
93
// case test1: statements1;
95
// default: statementsDefault;
97
// case testN: statementsN;
104
// case test1: goto label1;
106
// case testN: goto labelN;
108
// goto labelDefault;
113
// statementsDefault;
120
// where inside switch each "break;" without label will be replaced
121
// by "goto breakLabel".
123
// If the original switch does not have the default label, then
124
// the transformed code would contain after the switch instead of
125
// goto labelDefault;
126
// the following goto:
130
Node.Jump switchNode = new Node.Jump(Token.SWITCH, expr, lineno);
131
Node block = new Node(Token.BLOCK, switchNode);
136
* If caseExpression argument is null it indicate default label.
138
void addSwitchCase(Node switchBlock, Node caseExpression, Node statements)
140
if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
141
Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
142
if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
144
Node gotoTarget = Node.newTarget();
145
if (caseExpression != null) {
146
Node.Jump caseNode = new Node.Jump(Token.CASE, caseExpression);
147
caseNode.target = gotoTarget;
148
switchNode.addChildToBack(caseNode);
150
switchNode.setDefault(gotoTarget);
152
switchBlock.addChildToBack(gotoTarget);
153
switchBlock.addChildToBack(statements);
156
void closeSwitch(Node switchBlock)
158
if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
159
Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
160
if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
162
Node switchBreakTarget = Node.newTarget();
163
// switchNode.target is only used by NodeTransformer
164
// to detect switch end
165
switchNode.target = switchBreakTarget;
167
Node defaultTarget = switchNode.getDefault();
168
if (defaultTarget == null) {
169
defaultTarget = switchBreakTarget;
172
switchBlock.addChildAfter(makeJump(Token.GOTO, defaultTarget),
174
switchBlock.addChildToBack(switchBreakTarget);
177
Node createVariables(int lineno)
179
return new Node(Token.VAR, lineno);
182
Node createExprStatement(Node expr, int lineno)
185
if (parser.insideFunction()) {
186
type = Token.EXPR_VOID;
188
type = Token.EXPR_RESULT;
190
return new Node(type, expr, lineno);
193
Node createExprStatementNoReturn(Node expr, int lineno)
195
return new Node(Token.EXPR_VOID, expr, lineno);
198
Node createDefaultNamespace(Node expr, int lineno)
200
// default xml namespace requires activation
201
setRequiresActivation();
202
Node n = createUnary(Token.DEFAULTNAMESPACE, expr);
203
Node result = createExprStatement(n, lineno);
210
Node createName(String name)
212
checkActivationName(name, Token.NAME);
213
return Node.newString(Token.NAME, name);
217
* String (for literals)
219
Node createString(String string)
221
return Node.newString(string);
225
* Number (for literals)
227
Node createNumber(double number)
229
return Node.newNumber(number);
233
* Catch clause of try/catch/finally
234
* @param varName the name of the variable to bind to the exception
235
* @param catchCond the condition under which to catch the exception.
236
* May be null if no condition is given.
237
* @param stmts the statements in the catch clause
238
* @param lineno the starting line number of the catch clause
240
Node createCatch(String varName, Node catchCond, Node stmts, int lineno)
242
if (catchCond == null) {
243
catchCond = new Node(Token.EMPTY);
245
return new Node(Token.CATCH, createName(varName),
246
catchCond, stmts, lineno);
252
Node createThrow(Node expr, int lineno)
254
return new Node(Token.THROW, expr, lineno);
260
Node createReturn(Node expr, int lineno)
263
? new Node(Token.RETURN, lineno)
264
: new Node(Token.RETURN, expr, lineno);
270
Node createLabel(int lineno)
272
return new Node.Jump(Token.LABEL, lineno);
275
Node getLabelLoop(Node label)
277
return ((Node.Jump)label).getLoop();
283
Node createLabeledStatement(Node labelArg, Node statement)
285
Node.Jump label = (Node.Jump)labelArg;
287
// Make a target and put it _after_ the statement
288
// node. And in the LABEL node, so breaks get the
291
Node breakTarget = Node.newTarget();
292
Node block = new Node(Token.BLOCK, label, statement, breakTarget);
293
label.target = breakTarget;
299
* Break (possibly labeled)
301
Node createBreak(Node breakStatement, int lineno)
303
Node.Jump n = new Node.Jump(Token.BREAK, lineno);
304
Node.Jump jumpStatement;
305
int t = breakStatement.getType();
306
if (t == Token.LOOP || t == Token.LABEL) {
307
jumpStatement = (Node.Jump)breakStatement;
308
} else if (t == Token.BLOCK
309
&& breakStatement.getFirstChild().getType() == Token.SWITCH)
311
jumpStatement = (Node.Jump)breakStatement.getFirstChild();
315
n.setJumpStatement(jumpStatement);
320
* Continue (possibly labeled)
322
Node createContinue(Node loop, int lineno)
324
if (loop.getType() != Token.LOOP) Kit.codeBug();
325
Node.Jump n = new Node.Jump(Token.CONTINUE, lineno);
326
n.setJumpStatement((Node.Jump)loop);
332
* Creates the empty statement block
333
* Must make subsequent calls to add statements to the node
335
Node createBlock(int lineno)
337
return new Node(Token.BLOCK, lineno);
340
FunctionNode createFunction(String name)
342
return new FunctionNode(name);
345
Node initFunction(FunctionNode fnNode, int functionIndex,
346
Node statements, int functionType)
348
fnNode.itsFunctionType = functionType;
349
fnNode.addChildToBack(statements);
351
int functionCount = fnNode.getFunctionCount();
352
if (functionCount != 0) {
353
// Functions containing other functions require activation objects
354
fnNode.itsNeedsActivation = true;
355
for (int i = 0; i != functionCount; ++i) {
356
FunctionNode fn = fnNode.getFunctionNode(i);
357
// nested function expression statements overrides var
358
if (fn.getFunctionType()
359
== FunctionNode.FUNCTION_EXPRESSION_STATEMENT)
361
String name = fn.getFunctionName();
362
if (name != null && name.length() != 0) {
363
fnNode.removeParamOrVar(name);
369
if (functionType == FunctionNode.FUNCTION_EXPRESSION) {
370
String name = fnNode.getFunctionName();
371
if (name != null && name.length() != 0
372
&& !fnNode.hasParamOrVar(name))
374
// A function expression needs to have its name as a
375
// variable (if it isn't already allocated as a variable).
376
// See ECMA Ch. 13. We add code to the beginning of the
377
// function to initialize a local variable of the
378
// function's name to the function value.
380
Node setFn = new Node(Token.EXPR_VOID,
381
new Node(Token.SETVAR, Node.newString(name),
382
new Node(Token.THISFN)));
383
statements.addChildrenToFront(setFn);
387
// Add return to end if needed.
388
Node lastStmt = statements.getLastChild();
389
if (lastStmt == null || lastStmt.getType() != Token.RETURN) {
390
statements.addChildToBack(new Node(Token.RETURN));
393
Node result = Node.newString(Token.FUNCTION,
394
fnNode.getFunctionName());
395
result.putIntProp(Node.FUNCTION_PROP, functionIndex);
400
* Add a child to the back of the given node. This function
401
* breaks the Factory abstraction, but it removes a requirement
402
* from implementors of Node.
404
void addChildToBack(Node parent, Node child)
406
parent.addChildToBack(child);
410
* Create loop node. The parser will later call
411
* createWhile|createDoWhile|createFor|createForIn
412
* to finish loop generation.
414
Node createLoopNode(Node loopLabel, int lineno)
416
Node.Jump result = new Node.Jump(Token.LOOP, lineno);
417
if (loopLabel != null) {
418
((Node.Jump)loopLabel).setLoop(result);
426
Node createWhile(Node loop, Node cond, Node body)
428
return createLoop((Node.Jump)loop, LOOP_WHILE, body, cond,
435
Node createDoWhile(Node loop, Node body, Node cond)
437
return createLoop((Node.Jump)loop, LOOP_DO_WHILE, body, cond,
444
Node createFor(Node loop, Node init, Node test, Node incr, Node body)
446
return createLoop((Node.Jump)loop, LOOP_FOR, body, test,
450
private Node createLoop(Node.Jump loop, int loopType, Node body, Node cond,
451
Node init, Node incr)
453
Node bodyTarget = Node.newTarget();
454
Node condTarget = Node.newTarget();
455
if (loopType == LOOP_FOR && cond.getType() == Token.EMPTY) {
456
cond = new Node(Token.TRUE);
458
Node.Jump IFEQ = new Node.Jump(Token.IFEQ, cond);
459
IFEQ.target = bodyTarget;
460
Node breakTarget = Node.newTarget();
462
loop.addChildToBack(bodyTarget);
463
loop.addChildrenToBack(body);
464
if (loopType == LOOP_WHILE || loopType == LOOP_FOR) {
465
// propagate lineno to condition
466
loop.addChildrenToBack(new Node(Token.EMPTY, loop.getLineno()));
468
loop.addChildToBack(condTarget);
469
loop.addChildToBack(IFEQ);
470
loop.addChildToBack(breakTarget);
472
loop.target = breakTarget;
473
Node continueTarget = condTarget;
475
if (loopType == LOOP_WHILE || loopType == LOOP_FOR) {
476
// Just add a GOTO to the condition in the do..while
477
loop.addChildToFront(makeJump(Token.GOTO, condTarget));
479
if (loopType == LOOP_FOR) {
480
if (init.getType() != Token.EMPTY) {
481
if (init.getType() != Token.VAR) {
482
init = new Node(Token.EXPR_VOID, init);
484
loop.addChildToFront(init);
486
Node incrTarget = Node.newTarget();
487
loop.addChildAfter(incrTarget, body);
488
if (incr.getType() != Token.EMPTY) {
489
incr = new Node(Token.EXPR_VOID, incr);
490
loop.addChildAfter(incr, incrTarget);
492
continueTarget = incrTarget;
496
loop.setContinue(continueTarget);
505
Node createForIn(Node loop, Node lhs, Node obj, Node body,
509
int type = lhs.getType();
512
if (type == Token.VAR) {
514
* check that there was only one variable given.
515
* we can't do this in the parser, because then the
516
* parser would have to know something about the
517
* 'init' node of the for-in loop.
519
Node lastChild = lhs.getLastChild();
520
if (lhs.getFirstChild() != lastChild) {
521
parser.reportError("msg.mult.index");
523
lvalue = Node.newString(Token.NAME, lastChild.getString());
525
lvalue = makeReference(lhs);
526
if (lvalue == null) {
527
parser.reportError("msg.bad.for.in.lhs");
532
Node localBlock = new Node(Token.LOCAL_BLOCK);
534
int initType = (isForEach) ? Token.ENUM_INIT_VALUES
535
: Token.ENUM_INIT_KEYS;
536
Node init = new Node(initType, obj);
537
init.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
538
Node cond = new Node(Token.ENUM_NEXT);
539
cond.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
540
Node id = new Node(Token.ENUM_ID);
541
id.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
543
Node newBody = new Node(Token.BLOCK);
544
Node assign = simpleAssignment(lvalue, id);
545
newBody.addChildToBack(new Node(Token.EXPR_VOID, assign));
546
newBody.addChildToBack(body);
548
loop = createWhile(loop, cond, newBody);
549
loop.addChildToFront(init);
550
if (type == Token.VAR)
551
loop.addChildToFront(lhs);
552
localBlock.addChildToBack(loop);
560
* The IRFactory tries to express as much as possible in the tree;
561
* the responsibilities remaining for Codegen are to add the Java
562
* handlers: (Either (but not both) of TARGET and FINALLY might not
565
* - a catch handler for javascript exceptions that unwraps the
566
* exception onto the stack and GOTOes to the catch target
568
* - a finally handler
570
* ... and a goto to GOTO around these handlers.
572
Node createTryCatchFinally(Node tryBlock, Node catchBlocks,
573
Node finallyBlock, int lineno)
575
boolean hasFinally = (finallyBlock != null)
576
&& (finallyBlock.getType() != Token.BLOCK
577
|| finallyBlock.hasChildren());
580
if (tryBlock.getType() == Token.BLOCK && !tryBlock.hasChildren()
586
boolean hasCatch = catchBlocks.hasChildren();
589
if (!hasFinally && !hasCatch) {
590
// bc finally might be an empty block...
595
Node handlerBlock = new Node(Token.LOCAL_BLOCK);
596
Node.Jump pn = new Node.Jump(Token.TRY, tryBlock, lineno);
597
pn.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
600
// jump around catch code
601
Node endCatch = Node.newTarget();
602
pn.addChildToBack(makeJump(Token.GOTO, endCatch));
604
// make a TARGET for the catch that the tcf node knows about
605
Node catchTarget = Node.newTarget();
606
pn.target = catchTarget;
608
pn.addChildToBack(catchTarget);
615
// } catch (e if condition1) {
619
// } catch (e if conditionN) {
631
// with (newCatchScope(e, x)) {
638
// with (newCatchScope(e, x)) {
644
// with (newCatchScope(e, x)) {
651
// If there is no default catch, then the last with block
652
// arround "somethingDefault;" is replaced by "rethrow;"
654
// It is assumed that catch handler generation will store
655
// exeception object in handlerBlock register
657
// Block with local for exception scope objects
658
Node catchScopeBlock = new Node(Token.LOCAL_BLOCK);
660
// expects catchblocks children to be (cond block) pairs.
661
Node cb = catchBlocks.getFirstChild();
662
boolean hasDefault = false;
665
int catchLineNo = cb.getLineno();
667
Node name = cb.getFirstChild();
668
Node cond = name.getNext();
669
Node catchStatement = cond.getNext();
670
cb.removeChild(name);
671
cb.removeChild(cond);
672
cb.removeChild(catchStatement);
674
// Add goto to the catch statement to jump out of catch
675
// but prefix it with LEAVEWITH since try..catch produces
676
// "with"code in order to limit the scope of the exception
678
catchStatement.addChildToBack(new Node(Token.LEAVEWITH));
679
catchStatement.addChildToBack(makeJump(Token.GOTO, endCatch));
681
// Create condition "if" when present
683
if (cond.getType() == Token.EMPTY) {
684
condStmt = catchStatement;
687
condStmt = createIf(cond, catchStatement, null,
691
// Generate code to create the scope object and store
692
// it in catchScopeBlock register
693
Node catchScope = new Node(Token.CATCH_SCOPE, name,
694
createUseLocal(handlerBlock));
695
catchScope.putProp(Node.LOCAL_BLOCK_PROP, catchScopeBlock);
696
catchScope.putIntProp(Node.CATCH_SCOPE_PROP, scopeIndex);
697
catchScopeBlock.addChildToBack(catchScope);
699
// Add with statement based on catch scope object
700
catchScopeBlock.addChildToBack(
701
createWith(createUseLocal(catchScopeBlock), condStmt,
708
pn.addChildToBack(catchScopeBlock);
710
// Generate code to rethrow if no catch clause was executed
711
Node rethrow = new Node(Token.RETHROW);
712
rethrow.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
713
pn.addChildToBack(rethrow);
716
pn.addChildToBack(endCatch);
720
Node finallyTarget = Node.newTarget();
721
pn.setFinally(finallyTarget);
723
// add jsr finally to the try block
724
pn.addChildToBack(makeJump(Token.JSR, finallyTarget));
726
// jump around finally code
727
Node finallyEnd = Node.newTarget();
728
pn.addChildToBack(makeJump(Token.GOTO, finallyEnd));
730
pn.addChildToBack(finallyTarget);
731
Node fBlock = new Node(Token.FINALLY, finallyBlock);
732
fBlock.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
733
pn.addChildToBack(fBlock);
735
pn.addChildToBack(finallyEnd);
737
handlerBlock.addChildToBack(pn);
742
* Throw, Return, Label, Break and Continue are defined in ASTFactory.
748
Node createWith(Node obj, Node body, int lineno)
750
setRequiresActivation();
751
Node result = new Node(Token.BLOCK, lineno);
752
result.addChildToBack(new Node(Token.ENTERWITH, obj));
753
Node bodyNode = new Node(Token.WITH, body, lineno);
754
result.addChildrenToBack(bodyNode);
755
result.addChildToBack(new Node(Token.LEAVEWITH));
762
public Node createDotQuery (Node obj, Node body, int lineno)
764
setRequiresActivation();
765
Node result = new Node(Token.DOTQUERY, obj, body, lineno);
769
Node createArrayLiteral(ObjArray elems, int skipCount)
771
int length = elems.size();
772
int[] skipIndexes = null;
773
if (skipCount != 0) {
774
skipIndexes = new int[skipCount];
776
Node array = new Node(Token.ARRAYLIT);
777
for (int i = 0, j = 0; i != length; ++i) {
778
Node elem = (Node)elems.get(i);
780
array.addChildToBack(elem);
786
if (skipCount != 0) {
787
array.putProp(Node.SKIP_INDEXES_PROP, skipIndexes);
794
* <BR> createObjectLiteral rewrites its argument as object
795
* creation plus object property entries, so later compiler
796
* stages don't need to know about object literals.
798
Node createObjectLiteral(ObjArray elems)
800
int size = elems.size() / 2;
801
Node object = new Node(Token.OBJECTLIT);
804
properties = ScriptRuntime.emptyArgs;
806
properties = new Object[size];
807
for (int i = 0; i != size; ++i) {
808
properties[i] = elems.get(2 * i);
809
Node value = (Node)elems.get(2 * i + 1);
810
object.addChildToBack(value);
813
object.putProp(Node.OBJECT_IDS_PROP, properties);
818
* Regular expressions
820
Node createRegExp(int regexpIndex)
822
Node n = new Node(Token.REGEXP);
823
n.putIntProp(Node.REGEXP_PROP, regexpIndex);
830
Node createIf(Node cond, Node ifTrue, Node ifFalse, int lineno)
832
int condStatus = isAlwaysDefinedBoolean(cond);
833
if (condStatus == ALWAYS_TRUE_BOOLEAN) {
835
} else if (condStatus == ALWAYS_FALSE_BOOLEAN) {
836
if (ifFalse != null) {
839
// Replace if (false) xxx by empty block
840
return new Node(Token.BLOCK, lineno);
843
Node result = new Node(Token.BLOCK, lineno);
844
Node ifNotTarget = Node.newTarget();
845
Node.Jump IFNE = new Node.Jump(Token.IFNE, cond);
846
IFNE.target = ifNotTarget;
848
result.addChildToBack(IFNE);
849
result.addChildrenToBack(ifTrue);
851
if (ifFalse != null) {
852
Node endTarget = Node.newTarget();
853
result.addChildToBack(makeJump(Token.GOTO, endTarget));
854
result.addChildToBack(ifNotTarget);
855
result.addChildrenToBack(ifFalse);
856
result.addChildToBack(endTarget);
858
result.addChildToBack(ifNotTarget);
864
Node createCondExpr(Node cond, Node ifTrue, Node ifFalse)
866
int condStatus = isAlwaysDefinedBoolean(cond);
867
if (condStatus == ALWAYS_TRUE_BOOLEAN) {
869
} else if (condStatus == ALWAYS_FALSE_BOOLEAN) {
872
return new Node(Token.HOOK, cond, ifTrue, ifFalse);
878
Node createUnary(int nodeType, Node child)
880
int childType = child.getType();
882
case Token.DELPROP: {
884
if (childType == Token.NAME) {
885
// Transform Delete(Name "a")
886
// to Delete(Bind("a"), String("a"))
887
child.setType(Token.BINDNAME);
889
Node right = Node.newString(child.getString());
890
n = new Node(nodeType, left, right);
891
} else if (childType == Token.GETPROP ||
892
childType == Token.GETELEM)
894
Node left = child.getFirstChild();
895
Node right = child.getLastChild();
896
child.removeChild(left);
897
child.removeChild(right);
898
n = new Node(nodeType, left, right);
899
} else if (childType == Token.GET_REF) {
900
Node ref = child.getFirstChild();
901
child.removeChild(ref);
902
n = new Node(Token.DEL_REF, ref);
904
n = new Node(Token.TRUE);
909
if (childType == Token.NAME) {
910
child.setType(Token.TYPEOFNAME);
915
if (childType == Token.NUMBER) {
916
int value = ScriptRuntime.toInt32(child.getDouble());
917
child.setDouble(~value);
922
if (childType == Token.NUMBER) {
923
child.setDouble(-child.getDouble());
928
int status = isAlwaysDefinedBoolean(child);
931
if (status == ALWAYS_TRUE_BOOLEAN) {
936
if (childType == Token.TRUE || childType == Token.FALSE) {
940
return new Node(type);
945
return new Node(nodeType, child);
948
Node createCallOrNew(int nodeType, Node child)
950
int type = Node.NON_SPECIALCALL;
951
if (child.getType() == Token.NAME) {
952
String name = child.getString();
953
if (name.equals("eval")) {
954
type = Node.SPECIALCALL_EVAL;
955
} else if (name.equals("With")) {
956
type = Node.SPECIALCALL_WITH;
958
} else if (child.getType() == Token.GETPROP) {
959
String name = child.getLastChild().getString();
960
if (name.equals("eval")) {
961
type = Node.SPECIALCALL_EVAL;
964
Node node = new Node(nodeType, child);
965
if (type != Node.NON_SPECIALCALL) {
966
// Calls to these functions require activation objects.
967
setRequiresActivation();
968
node.putIntProp(Node.SPECIALCALL_PROP, type);
973
Node createIncDec(int nodeType, boolean post, Node child)
975
child = makeReference(child);
978
if (nodeType == Token.DEC) {
979
msg = "msg.bad.decr";
981
msg = "msg.bad.incr";
983
parser.reportError(msg);
987
int childType = child.getType();
993
case Token.GET_REF: {
994
Node n = new Node(nodeType, child);
995
int incrDecrMask = 0;
996
if (nodeType == Token.DEC) {
997
incrDecrMask |= Node.DECR_FLAG;
1000
incrDecrMask |= Node.POST_FLAG;
1002
n.putIntProp(Node.INCRDECR_PROP, incrDecrMask);
1006
throw Kit.codeBug();
1009
Node createPropertyGet(Node target, String namespace, String name,
1010
int memberTypeFlags)
1012
if (namespace == null && memberTypeFlags == 0) {
1013
if (target == null) {
1014
return createName(name);
1016
checkActivationName(name, Token.GETPROP);
1017
if (ScriptRuntime.isSpecialProperty(name)) {
1018
Node ref = new Node(Token.REF_SPECIAL, target);
1019
ref.putProp(Node.NAME_PROP, name);
1020
return new Node(Token.GET_REF, ref);
1022
return new Node(Token.GETPROP, target, createString(name));
1024
Node elem = createString(name);
1025
memberTypeFlags |= Node.PROPERTY_FLAG;
1026
return createMemberRefGet(target, namespace, elem, memberTypeFlags);
1029
Node createElementGet(Node target, String namespace, Node elem,
1030
int memberTypeFlags)
1032
// OPT: could optimize to createPropertyGet
1033
// iff elem is string that can not be number
1034
if (namespace == null && memberTypeFlags == 0) {
1035
// stand-alone [aaa] as primary expression is array literal
1036
// declaration and should not come here!
1037
if (target == null) throw Kit.codeBug();
1038
return new Node(Token.GETELEM, target, elem);
1040
return createMemberRefGet(target, namespace, elem, memberTypeFlags);
1043
private Node createMemberRefGet(Node target, String namespace, Node elem,
1044
int memberTypeFlags)
1047
if (namespace != null) {
1048
// See 11.1.2 in ECMA 357
1049
if (namespace.equals("*")) {
1050
nsNode = new Node(Token.NULL);
1052
nsNode = createName(namespace);
1056
if (target == null) {
1057
if (namespace == null) {
1058
ref = new Node(Token.REF_NAME, elem);
1060
ref = new Node(Token.REF_NS_NAME, nsNode, elem);
1063
if (namespace == null) {
1064
ref = new Node(Token.REF_MEMBER, target, elem);
1066
ref = new Node(Token.REF_NS_MEMBER, target, nsNode, elem);
1069
if (memberTypeFlags != 0) {
1070
ref.putIntProp(Node.MEMBER_TYPE_PROP, memberTypeFlags);
1072
return new Node(Token.GET_REF, ref);
1078
Node createBinary(int nodeType, Node left, Node right)
1083
// numerical addition and string concatenation
1084
if (left.type == Token.STRING) {
1086
if (right.type == Token.STRING) {
1087
s2 = right.getString();
1088
} else if (right.type == Token.NUMBER) {
1089
s2 = ScriptRuntime.numberToString(right.getDouble(), 10);
1093
String s1 = left.getString();
1094
left.setString(s1.concat(s2));
1096
} else if (left.type == Token.NUMBER) {
1097
if (right.type == Token.NUMBER) {
1098
left.setDouble(left.getDouble() + right.getDouble());
1100
} else if (right.type == Token.STRING) {
1102
s1 = ScriptRuntime.numberToString(left.getDouble(), 10);
1103
s2 = right.getString();
1104
right.setString(s1.concat(s2));
1108
// can't do anything if we don't know both types - since
1109
// 0 + object is supposed to call toString on the object and do
1110
// string concantenation rather than addition
1114
// numerical subtraction
1115
if (left.type == Token.NUMBER) {
1116
double ld = left.getDouble();
1117
if (right.type == Token.NUMBER) {
1119
left.setDouble(ld - right.getDouble());
1121
} else if (ld == 0.0) {
1122
// first 0: 0-x -> -x
1123
return new Node(Token.NEG, right);
1125
} else if (right.type == Token.NUMBER) {
1126
if (right.getDouble() == 0.0) {
1127
//second 0: x - 0 -> +x
1128
// can not make simply x because x - 0 must be number
1129
return new Node(Token.POS, left);
1135
// numerical multiplication
1136
if (left.type == Token.NUMBER) {
1137
double ld = left.getDouble();
1138
if (right.type == Token.NUMBER) {
1140
left.setDouble(ld * right.getDouble());
1142
} else if (ld == 1.0) {
1143
// first 1: 1 * x -> +x
1144
return new Node(Token.POS, right);
1146
} else if (right.type == Token.NUMBER) {
1147
if (right.getDouble() == 1.0) {
1148
//second 1: x * 1 -> +x
1149
// can not make simply x because x - 0 must be number
1150
return new Node(Token.POS, left);
1153
// can't do x*0: Infinity * 0 gives NaN, not 0
1158
if (right.type == Token.NUMBER) {
1159
double rd = right.getDouble();
1160
if (left.type == Token.NUMBER) {
1161
// both constants -- just divide, trust Java to handle x/0
1162
left.setDouble(left.getDouble() / rd);
1164
} else if (rd == 1.0) {
1165
// second 1: x/1 -> +x
1166
// not simply x to force number convertion
1167
return new Node(Token.POS, left);
1173
int leftStatus = isAlwaysDefinedBoolean(left);
1174
if (leftStatus == ALWAYS_FALSE_BOOLEAN) {
1175
// if the first one is false, replace with FALSE
1176
return new Node(Token.FALSE);
1177
} else if (leftStatus == ALWAYS_TRUE_BOOLEAN) {
1178
// if first is true, set to second
1181
int rightStatus = isAlwaysDefinedBoolean(right);
1182
if (rightStatus == ALWAYS_FALSE_BOOLEAN) {
1183
// if the second one is false, replace with FALSE
1184
if (!hasSideEffects(left)) {
1185
return new Node(Token.FALSE);
1187
} else if (rightStatus == ALWAYS_TRUE_BOOLEAN) {
1188
// if second is true, set to first
1195
int leftStatus = isAlwaysDefinedBoolean(left);
1196
if (leftStatus == ALWAYS_TRUE_BOOLEAN) {
1197
// if the first one is true, replace with TRUE
1198
return new Node(Token.TRUE);
1199
} else if (leftStatus == ALWAYS_FALSE_BOOLEAN) {
1200
// if first is false, set to second
1203
int rightStatus = isAlwaysDefinedBoolean(right);
1204
if (rightStatus == ALWAYS_TRUE_BOOLEAN) {
1205
// if the second one is true, replace with TRUE
1206
if (!hasSideEffects(left)) {
1207
return new Node(Token.TRUE);
1209
} else if (rightStatus == ALWAYS_FALSE_BOOLEAN) {
1210
// if second is false, set to first
1217
return new Node(nodeType, left, right);
1220
private Node simpleAssignment(Node left, Node right)
1222
int nodeType = left.getType();
1225
left.setType(Token.BINDNAME);
1226
return new Node(Token.SETNAME, left, right);
1229
case Token.GETELEM: {
1230
Node obj = left.getFirstChild();
1231
Node id = left.getLastChild();
1233
if (nodeType == Token.GETPROP) {
1234
type = Token.SETPROP;
1236
type = Token.SETELEM;
1238
return new Node(type, obj, id, right);
1240
case Token.GET_REF: {
1241
Node ref = left.getFirstChild();
1242
return new Node(Token.SET_REF, ref, right);
1246
throw Kit.codeBug();
1249
Node createAssignment(int assignType, Node left, Node right)
1251
left = makeReference(left);
1253
parser.reportError("msg.bad.assign.left");
1258
switch (assignType) {
1260
return simpleAssignment(left, right);
1261
case Token.ASSIGN_BITOR: assignOp = Token.BITOR; break;
1262
case Token.ASSIGN_BITXOR: assignOp = Token.BITXOR; break;
1263
case Token.ASSIGN_BITAND: assignOp = Token.BITAND; break;
1264
case Token.ASSIGN_LSH: assignOp = Token.LSH; break;
1265
case Token.ASSIGN_RSH: assignOp = Token.RSH; break;
1266
case Token.ASSIGN_URSH: assignOp = Token.URSH; break;
1267
case Token.ASSIGN_ADD: assignOp = Token.ADD; break;
1268
case Token.ASSIGN_SUB: assignOp = Token.SUB; break;
1269
case Token.ASSIGN_MUL: assignOp = Token.MUL; break;
1270
case Token.ASSIGN_DIV: assignOp = Token.DIV; break;
1271
case Token.ASSIGN_MOD: assignOp = Token.MOD; break;
1272
default: throw Kit.codeBug();
1275
int nodeType = left.getType();
1278
String s = left.getString();
1280
Node opLeft = Node.newString(Token.NAME, s);
1281
Node op = new Node(assignOp, opLeft, right);
1282
Node lvalueLeft = Node.newString(Token.BINDNAME, s);
1283
return new Node(Token.SETNAME, lvalueLeft, op);
1286
case Token.GETELEM: {
1287
Node obj = left.getFirstChild();
1288
Node id = left.getLastChild();
1290
int type = nodeType == Token.GETPROP
1294
Node opLeft = new Node(Token.USE_STACK);
1295
Node op = new Node(assignOp, opLeft, right);
1296
return new Node(type, obj, id, op);
1298
case Token.GET_REF: {
1299
Node ref = left.getFirstChild();
1300
Node opLeft = new Node(Token.USE_STACK);
1301
Node op = new Node(assignOp, opLeft, right);
1302
return new Node(Token.SET_REF_OP, ref, op);
1306
throw Kit.codeBug();
1309
Node createUseLocal(Node localBlock)
1311
if (Token.LOCAL_BLOCK != localBlock.getType()) throw Kit.codeBug();
1312
Node result = new Node(Token.LOCAL_LOAD);
1313
result.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
1317
private Node.Jump makeJump(int type, Node target)
1319
Node.Jump n = new Node.Jump(type);
1324
private Node makeReference(Node node)
1326
int type = node.getType();
1334
node.setType(Token.REF_CALL);
1335
return new Node(Token.GET_REF, node);
1337
// Signal caller to report error
1341
// Check if Node always mean true or false in boolean context
1342
private static int isAlwaysDefinedBoolean(Node node)
1344
switch (node.getType()) {
1347
return ALWAYS_FALSE_BOOLEAN;
1349
return ALWAYS_TRUE_BOOLEAN;
1350
case Token.NUMBER: {
1351
double num = node.getDouble();
1352
if (num == num && num != 0.0) {
1353
return ALWAYS_TRUE_BOOLEAN;
1355
return ALWAYS_FALSE_BOOLEAN;
1362
private static boolean hasSideEffects(Node exprTree)
1364
switch (exprTree.getType()) {
1374
Node child = exprTree.getFirstChild();
1375
while (child != null) {
1376
if (hasSideEffects(child))
1378
child = child.getNext();
1385
private void checkActivationName(String name, int token)
1387
if (parser.insideFunction()) {
1388
boolean activation = false;
1389
if ("arguments".equals(name)
1390
|| (parser.compilerEnv.activationNames != null
1391
&& parser.compilerEnv.activationNames.containsKey(name)))
1394
} else if ("length".equals(name)) {
1395
if (token == Token.GETPROP
1396
&& parser.compilerEnv.getLanguageVersion()
1397
== Context.VERSION_1_2)
1399
// Use of "length" in 1.2 requires an activation object.
1404
setRequiresActivation();
1409
private void setRequiresActivation()
1411
if (parser.insideFunction()) {
1412
((FunctionNode)parser.currentScriptOrFn).itsNeedsActivation = true;
1416
private Parser parser;
1418
private static final int LOOP_DO_WHILE = 0;
1419
private static final int LOOP_WHILE = 1;
1420
private static final int LOOP_FOR = 2;
1422
private static final int ALWAYS_TRUE_BOOLEAN = 1;
1423
private static final int ALWAYS_FALSE_BOOLEAN = -1;