| 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 |