172 173 /** 174 * Define symbols for all variable declarations at the top of the function scope. This way we can get around 175 * problems like 176 * 177 * while (true) { 178 * break; 179 * if (true) { 180 * var s; 181 * } 182 * } 183 * 184 * to an arbitrary nesting depth. 185 * 186 * see NASHORN-73 187 * 188 * @param functionNode the FunctionNode we are entering 189 * @param body the body of the FunctionNode we are entering 190 */ 191 private void acceptDeclarations(final FunctionNode functionNode, final Block body) { 192 // This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers. 193 body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 194 @Override 195 protected boolean enterDefault(final Node node) { 196 // Don't bother visiting expressions; var is a statement, it can't be inside an expression. 197 // This will also prevent visiting nested functions (as FunctionNode is an expression). 198 return !(node instanceof Expression); 199 } 200 201 @Override 202 public Node leaveVarNode(final VarNode varNode) { 203 if (varNode.isStatement()) { 204 final IdentNode ident = varNode.getName(); 205 final Block block = varNode.isBlockScoped() ? getLexicalContext().getCurrentBlock() : body; 206 final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags()); 207 if (varNode.isFunctionDeclaration()) { 208 symbol.setIsFunctionDeclaration(); 209 } 210 return varNode.setName(ident.setSymbol(symbol)); 211 } 212 return varNode; 213 } 214 }); 215 } 216 217 private IdentNode compilerConstantIdentifier(final CompilerConstants cc) { 218 return createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc)); 219 } 220 221 /** 222 * Creates an ident node for an implicit identifier within the function (one not declared in the script source 223 * code). These identifiers are defined with function's token and finish. 224 * @param name the name of the identifier 225 * @return an ident node representing the implicit identifier. 226 */ 227 private IdentNode createImplicitIdentifier(final String name) { 228 final FunctionNode fn = lc.getCurrentFunction(); 229 return new IdentNode(fn.getToken(), fn.getFinish(), name); 230 } 231 232 private Symbol createSymbol(final String name, final int flags) { 233 if ((flags & Symbol.KINDMASK) == IS_GLOBAL) { 1029 } else if (node instanceof Block) { 1030 if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) { 1031 // We reached the block that defines the symbol without reaching either the function boundary, or a 1032 // WithNode. The symbol need not be scoped. 1033 return false; 1034 } 1035 previousWasBlock = true; 1036 } else { 1037 previousWasBlock = false; 1038 } 1039 } 1040 throw new AssertionError(); 1041 } 1042 1043 private static boolean isSplitArray(final LexicalContextNode expr) { 1044 if(!(expr instanceof ArrayLiteralNode)) { 1045 return false; 1046 } 1047 final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits(); 1048 return !(units == null || units.isEmpty()); 1049 } 1050 1051 private void throwParserException(final String message, final Node origin) { 1052 if (origin == null) { 1053 throw new ParserException(message); 1054 } 1055 final Source source = compiler.getSource(); 1056 final long token = origin.getToken(); 1057 final int line = source.getLine(origin.getStart()); 1058 final int column = source.getColumn(origin.getStart()); 1059 final String formatted = ErrorManager.format(message, source, line, column, token); 1060 throw new ParserException(JSErrorType.SYNTAX_ERROR, formatted, source, line, column, token); 1061 } 1062 } | 172 173 /** 174 * Define symbols for all variable declarations at the top of the function scope. This way we can get around 175 * problems like 176 * 177 * while (true) { 178 * break; 179 * if (true) { 180 * var s; 181 * } 182 * } 183 * 184 * to an arbitrary nesting depth. 185 * 186 * see NASHORN-73 187 * 188 * @param functionNode the FunctionNode we are entering 189 * @param body the body of the FunctionNode we are entering 190 */ 191 private void acceptDeclarations(final FunctionNode functionNode, final Block body) { 192 // This visitor will assign symbol to all declared variables. 193 body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 194 @Override 195 protected boolean enterDefault(final Node node) { 196 // Don't bother visiting expressions; var is a statement, it can't be inside an expression. 197 // This will also prevent visiting nested functions (as FunctionNode is an expression). 198 return !(node instanceof Expression); 199 } 200 201 @Override 202 public Node leaveVarNode(final VarNode varNode) { 203 final IdentNode ident = varNode.getName(); 204 final boolean blockScoped = varNode.isBlockScoped(); 205 if (blockScoped && lc.inUnprotectedSwitchContext()) { 206 throwUnprotectedSwitchError(varNode); 207 } 208 final Block block = blockScoped ? lc.getCurrentBlock() : body; 209 final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags()); 210 if (varNode.isFunctionDeclaration()) { 211 symbol.setIsFunctionDeclaration(); 212 } 213 return varNode.setName(ident.setSymbol(symbol)); 214 } 215 }); 216 } 217 218 private IdentNode compilerConstantIdentifier(final CompilerConstants cc) { 219 return createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc)); 220 } 221 222 /** 223 * Creates an ident node for an implicit identifier within the function (one not declared in the script source 224 * code). These identifiers are defined with function's token and finish. 225 * @param name the name of the identifier 226 * @return an ident node representing the implicit identifier. 227 */ 228 private IdentNode createImplicitIdentifier(final String name) { 229 final FunctionNode fn = lc.getCurrentFunction(); 230 return new IdentNode(fn.getToken(), fn.getFinish(), name); 231 } 232 233 private Symbol createSymbol(final String name, final int flags) { 234 if ((flags & Symbol.KINDMASK) == IS_GLOBAL) { 1030 } else if (node instanceof Block) { 1031 if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) { 1032 // We reached the block that defines the symbol without reaching either the function boundary, or a 1033 // WithNode. The symbol need not be scoped. 1034 return false; 1035 } 1036 previousWasBlock = true; 1037 } else { 1038 previousWasBlock = false; 1039 } 1040 } 1041 throw new AssertionError(); 1042 } 1043 1044 private static boolean isSplitArray(final LexicalContextNode expr) { 1045 if(!(expr instanceof ArrayLiteralNode)) { 1046 return false; 1047 } 1048 final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits(); 1049 return !(units == null || units.isEmpty()); 1050 } 1051 1052 private void throwUnprotectedSwitchError(final VarNode varNode) { 1053 // Block scoped declarations in switch statements without explicit blocks should be declared 1054 // in a common block that contains all the case clauses. We cannot support this without a 1055 // fundamental rewrite of how switch statements are handled (case nodes contain blocks and are 1056 // directly contained by switch node). As a temporary solution we throw a reference error here. 1057 final String msg = ECMAErrors.getMessage("syntax.error.unprotected.switch.declaration", varNode.isLet() ? "let" : "const"); 1058 throwParserException(msg, varNode); 1059 } 1060 1061 private void throwParserException(final String message, final Node origin) { 1062 if (origin == null) { 1063 throw new ParserException(message); 1064 } 1065 final Source source = compiler.getSource(); 1066 final long token = origin.getToken(); 1067 final int line = source.getLine(origin.getStart()); 1068 final int column = source.getColumn(origin.getStart()); 1069 final String formatted = ErrorManager.format(message, source, line, column, token); 1070 throw new ParserException(JSErrorType.SYNTAX_ERROR, formatted, source, line, column, token); 1071 } 1072 } |