OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 package com.google.dart.compiler.backend.js.analysis; | |
6 | |
7 import org.mozilla.javascript.Token; | |
8 import org.mozilla.javascript.ast.Assignment; | |
9 import org.mozilla.javascript.ast.AstNode; | |
10 import org.mozilla.javascript.ast.ExpressionStatement; | |
11 import org.mozilla.javascript.ast.FunctionCall; | |
12 import org.mozilla.javascript.ast.FunctionNode; | |
13 import org.mozilla.javascript.ast.Name; | |
14 import org.mozilla.javascript.ast.NodeVisitor; | |
15 import org.mozilla.javascript.ast.VariableDeclaration; | |
16 import org.mozilla.javascript.ast.VariableInitializer; | |
17 | |
18 import java.util.ArrayList; | |
19 import java.util.Collection; | |
20 import java.util.Deque; | |
21 import java.util.Iterator; | |
22 import java.util.LinkedList; | |
23 import java.util.List; | |
24 import java.util.Map; | |
25 import java.util.Map.Entry; | |
26 import java.util.Set; | |
27 | |
28 /** | |
29 * Indexes the top level declarations and expressions contained in a JavaScript
AST tree. Where top | |
30 * level expressions can be global variables, method definitions or invocations.
Note that method | |
31 * bodies are not processed. | |
32 */ | |
33 class TopLevelElementIndexer implements NodeVisitor { | |
34 /** | |
35 * | |
36 */ | |
37 private static final String RUN_ENTRY_METHOD_NAME = "RunEntry"; | |
38 | |
39 /** | |
40 * Collects {@link Name} {@link AstNode}s. | |
41 */ | |
42 static class NameLocator implements NodeVisitor { | |
43 private static final int PROTOTYPE_DEFAULT_INDEX = -1; | |
44 private final Deque<String> identifiers = new LinkedList<String>(); | |
45 private int prototypeIndex = PROTOTYPE_DEFAULT_INDEX; | |
46 | |
47 public String getEnclosingTypeName() { | |
48 return identifiers.getFirst(); | |
49 } | |
50 | |
51 public String getName() { | |
52 return identifiers.getLast(); | |
53 } | |
54 | |
55 public Collection<String> getPossibleNames() { | |
56 List<String> names = new ArrayList<String>(2); | |
57 if (hasPrototypeInName()) { | |
58 names.add(identifiers.getLast()); | |
59 } | |
60 names.add(getQualifiedName()); | |
61 return names; | |
62 } | |
63 | |
64 public String getQualifiedName() { | |
65 StringBuffer sb = new StringBuffer(); | |
66 Iterator<String> iterator = identifiers.iterator(); | |
67 while (iterator.hasNext()) { | |
68 sb.append(iterator.next()); | |
69 if (iterator.hasNext()) { | |
70 sb.append("."); | |
71 } | |
72 } | |
73 return sb.toString(); | |
74 } | |
75 | |
76 public boolean hasPrototypeInName() { | |
77 return prototypeIndex != PROTOTYPE_DEFAULT_INDEX; | |
78 } | |
79 | |
80 @Override | |
81 public boolean visit(AstNode node) { | |
82 if (node.getType() == Token.NAME) { | |
83 Name name = (Name) node; | |
84 String identifier = name.getIdentifier(); | |
85 if ("prototype".equals(identifier)) { | |
86 prototypeIndex = identifiers.size(); | |
87 } | |
88 identifiers.add(identifier); | |
89 } | |
90 return true; | |
91 } | |
92 } | |
93 | |
94 public static void printGlobals(List<AstNode> globals) { | |
95 System.out.println("Globals"); | |
96 for (AstNode global : globals) { | |
97 System.out.println("--------"); | |
98 System.out.println(global.toSource()); | |
99 System.out.println(); | |
100 } | |
101 } | |
102 | |
103 public static void printNamesToElements(Map<String, List<JavascriptElement>> n
amesToElements) { | |
104 long rttCost = 0; | |
105 long namedCost = 0; | |
106 long ctorCost = 0; | |
107 | |
108 Set<Entry<String, List<JavascriptElement>>> entrySet = namesToElements.entry
Set(); | |
109 for (Entry<String, List<JavascriptElement>> entry : entrySet) { | |
110 System.out.println("--------"); | |
111 System.out.println("Name: " + entry.getKey()); | |
112 for (JavascriptElement javascriptElement : entry.getValue()) { | |
113 AstNode node = javascriptElement.getNode(); | |
114 if (node == null) { | |
115 continue; | |
116 } | |
117 | |
118 System.out.println("Type: " + Token.typeToName(node.getType())); | |
119 if (javascriptElement.getInheritsElement() != null) { | |
120 System.out.println("Inherits: " + javascriptElement.getInheritsElement
()); | |
121 } | |
122 | |
123 try { | |
124 System.out.println(node.toSource()); | |
125 } catch (Exception e) { | |
126 System.out.println("Failed to print node source code"); | |
127 } | |
128 | |
129 String name = javascriptElement.getName(); | |
130 if (name.endsWith("$named")) { | |
131 namedCost += node.getLength(); | |
132 } else if (name.endsWith("$addTo") || name.endsWith("$lookupRTT") | |
133 || name.endsWith("$RTTimplements")) { | |
134 rttCost += node.getLength(); | |
135 } else if (name.endsWith("$Constructor") || name.endsWith("$Initializer"
)) { | |
136 ctorCost += node.getLength(); | |
137 } | |
138 } | |
139 System.out.println(); | |
140 } | |
141 | |
142 System.out.println(ctorCost + | |
143 " characters worth of $Constructor and $Initializer methode declarations
"); | |
144 System.out.println(namedCost + " characters worth of $named methods declarat
ions"); | |
145 System.out.println(rttCost + " characters worth of RTT method declarations")
; | |
146 } | |
147 | |
148 private final List<AstNode> entryPoints = new ArrayList<AstNode>(); | |
149 private final List<AstNode> globals; | |
150 | |
151 private final Map<String, List<JavascriptElement>> namesToElements; | |
152 | |
153 public TopLevelElementIndexer(Map<String, List<JavascriptElement>> namesToElem
ents, | |
154 List<AstNode> globals) { | |
155 this.globals = globals; | |
156 this.namesToElements = namesToElements; | |
157 } | |
158 | |
159 private void addElement(String identifier, JavascriptElement javascriptElement
) { | |
160 List<JavascriptElement> list = namesToElements.get(identifier); | |
161 if (list == null) { | |
162 list = new ArrayList<JavascriptElement>(1); | |
163 namesToElements.put(identifier, list); | |
164 } | |
165 list.add(javascriptElement); | |
166 } | |
167 | |
168 /** | |
169 * Processes an AST node that does not define a method. If the node is an inv
ocation to a | |
170 * RunEntry method or an inherits method update the entry points and element i
nheritance | |
171 * hierarchy accordingly. Otherwise we just add this node to the globals bloc
k and continue. | |
172 */ | |
173 private void processGlobal(AstNode node) { | |
174 if (node.getType() == Token.EXPR_RESULT) { | |
175 ExpressionStatement expressionStatement = (ExpressionStatement) node; | |
176 AstNode expression = expressionStatement.getExpression(); | |
177 if (expression.getType() == Token.CALL) { | |
178 FunctionCall functionCall = (FunctionCall) expression; | |
179 AstNode target = functionCall.getTarget(); | |
180 if (target.getType() == Token.NAME) { | |
181 Name targetName = (Name) target; | |
182 if (RUN_ENTRY_METHOD_NAME.equals(targetName.getIdentifier())) { | |
183 // This is a call to an entry point so add it to the known set of en
try points. | |
184 entryPoints.add(node); | |
185 return; | |
186 } else if ("$inherits".equals(targetName.getIdentifier())) { | |
187 // This is an inherits call so update the element's inheritance hier
archy. | |
188 List<AstNode> arguments = functionCall.getArguments(); | |
189 assert (arguments.size() == 2); | |
190 assert (arguments.get(0).getType() == Token.NAME); | |
191 assert (arguments.get(1).getType() == Token.NAME); | |
192 Name subtype = (Name) arguments.get(0); | |
193 Name supertype = (Name) arguments.get(1); | |
194 List<JavascriptElement> subTypeFunctions = namesToElements.get(subty
pe.getIdentifier()); | |
195 assert (subTypeFunctions != null && subTypeFunctions.size() == 1); | |
196 JavascriptElement subtypeFunction = subTypeFunctions.get(0); | |
197 subtypeFunction.setInheritsNode(node); | |
198 subtypeFunction.setInherits(namesToElements.get(supertype.getIdentif
ier()).get(0)); | |
199 return; | |
200 } | |
201 } | |
202 } | |
203 } | |
204 | |
205 globals.add(node); | |
206 } | |
207 | |
208 public List<AstNode> getEntryPoints() { | |
209 return entryPoints; | |
210 } | |
211 | |
212 @Override | |
213 public boolean visit(AstNode node) { | |
214 if (node == node.getAstRoot()) { | |
215 return true; | |
216 } | |
217 | |
218 switch (node.getType()) { | |
219 case Token.EXPR_VOID: | |
220 case Token.EXPR_RESULT: | |
221 ExpressionStatement expressionStatement = (ExpressionStatement) node; | |
222 AstNode expression = expressionStatement.getExpression(); | |
223 if (expression.getType() == Token.ASSIGN) { | |
224 Assignment assignment = (Assignment) expression; | |
225 NameLocator nameLocator = new NameLocator(); | |
226 assignment.getLeft().visit(nameLocator); | |
227 String enclosingTypeName = nameLocator.getEnclosingTypeName(); | |
228 JavascriptElement enclosingElement = null; | |
229 if (enclosingTypeName != null) { | |
230 List<JavascriptElement> list = namesToElements.get(enclosingTypeName
); | |
231 if (list == null) { | |
232 // TODO: Assume that this is a native object for now, we could hav
e a whitelist of | |
233 // native objects if we wanted to. | |
234 enclosingElement = | |
235 new JavascriptElement(null, false, enclosingTypeName, nameLoca
tor.getName(), | |
236 null); | |
237 addElement(enclosingTypeName, enclosingElement); | |
238 } else { | |
239 assert (list != null && list.size() == 1); | |
240 enclosingElement = list.get(0); | |
241 } | |
242 } | |
243 | |
244 JavascriptElement javascriptElement = | |
245 new JavascriptElement(enclosingElement, nameLocator.hasPrototypeIn
Name(), | |
246 nameLocator.getQualifiedName(), nameLocator.getName(), node); | |
247 for (String identifier : nameLocator.getPossibleNames()) { | |
248 addElement(identifier, javascriptElement); | |
249 } | |
250 } else { | |
251 processGlobal(node); | |
252 } | |
253 break; | |
254 | |
255 case Token.FUNCTION: | |
256 FunctionNode functionNode = (FunctionNode) node; | |
257 String name = functionNode.getName(); | |
258 if (name != null && !name.isEmpty()) { | |
259 addElement(name, new JavascriptElement(null, false, name, name, node))
; | |
260 } | |
261 break; | |
262 | |
263 case Token.VAR: | |
264 VariableDeclaration variableDeclaration = (VariableDeclaration) node; | |
265 List<VariableInitializer> variables = variableDeclaration.getVariables()
; | |
266 for (VariableInitializer variable : variables) { | |
267 AstNode target = variable.getTarget(); | |
268 if (target.getType() == Token.NAME) { | |
269 Name variableName = (Name) target; | |
270 addElement(variableName.getIdentifier(), new JavascriptElement(null,
false, | |
271 variableName.getIdentifier(), variableName.getIdentifier(), node
)); | |
272 } | |
273 } | |
274 break; | |
275 | |
276 default: | |
277 processGlobal(node); | |
278 break; | |
279 } | |
280 | |
281 return false; | |
282 } | |
283 } | |
OLD | NEW |