Tue, 12 Mar 2013 15:30:53 +0100
8009718: Lazy execution architecture continued - ScriptFunctionData is either final or recompilable. Moved ScriptFunctionData creation logic away from runtime to compile time. Prepared for method generation/specialization. Got rid of ScriptFunctionImplTrampoline whose semantics could be done as part of the relinking anyway. Merge with the lookup package change.
Reviewed-by: attila, jlaskey
1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package jdk.nashorn.internal.parser;
28 import static jdk.nashorn.internal.parser.TokenType.COLON;
29 import static jdk.nashorn.internal.parser.TokenType.COMMARIGHT;
30 import static jdk.nashorn.internal.parser.TokenType.EOF;
31 import static jdk.nashorn.internal.parser.TokenType.ESCSTRING;
32 import static jdk.nashorn.internal.parser.TokenType.RBRACE;
33 import static jdk.nashorn.internal.parser.TokenType.RBRACKET;
34 import static jdk.nashorn.internal.parser.TokenType.STRING;
36 import java.util.ArrayList;
37 import java.util.List;
38 import jdk.nashorn.internal.ir.LiteralNode;
39 import jdk.nashorn.internal.ir.Node;
40 import jdk.nashorn.internal.ir.ObjectNode;
41 import jdk.nashorn.internal.ir.PropertyNode;
42 import jdk.nashorn.internal.ir.UnaryNode;
43 import jdk.nashorn.internal.runtime.ErrorManager;
44 import jdk.nashorn.internal.runtime.Source;
46 /**
47 * Parses JSON text and returns the corresponding IR node. This is derived from the objectLiteral production of the main parser.
48 *
49 * See: 15.12.1.2 The JSON Syntactic Grammar
50 */
51 public class JSONParser extends AbstractParser {
53 /**
54 * Constructor
55 * @param source the source
56 * @param errors the error manager
57 * @param strict are we in strict mode
58 */
59 public JSONParser(final Source source, final ErrorManager errors, final boolean strict) {
60 super(source, errors, strict);
61 }
63 /**
64 * Implementation of the Quote(value) operation as defined in the ECMA script spec
65 * It wraps a String value in double quotes and escapes characters within in
66 *
67 * @param value string to quote
68 *
69 * @return quoted and escaped string
70 */
71 public static String quote(final String value) {
73 final StringBuilder product = new StringBuilder();
75 product.append("\"");
77 for (final char ch : value.toCharArray()) {
78 // TODO: should use a table?
79 switch (ch) {
80 case '\\':
81 product.append("\\\\");
82 break;
83 case '"':
84 product.append("\\\"");
85 break;
86 case '\b':
87 product.append("\\b");
88 break;
89 case '\f':
90 product.append("\\f");
91 break;
92 case '\n':
93 product.append("\\n");
94 break;
95 case '\r':
96 product.append("\\r");
97 break;
98 case '\t':
99 product.append("\\t");
100 break;
101 default:
102 if (ch < ' ') {
103 product.append(Lexer.unicodeEscape(ch));
104 break;
105 }
107 product.append(ch);
108 break;
109 }
110 }
112 product.append("\"");
114 return product.toString();
115 }
117 /**
118 * Public parsed method - start lexing a new token stream for
119 * a JSON script
120 *
121 * @return the JSON literal
122 */
123 public Node parse() {
124 stream = new TokenStream();
126 lexer = new Lexer(source, stream) {
128 @Override
129 protected boolean skipComments() {
130 return false;
131 }
133 @Override
134 protected boolean isStringDelimiter(final char ch) {
135 return ch == '\"';
136 }
138 @Override
139 protected boolean isWhitespace(final char ch) {
140 return Lexer.isJsonWhitespace(ch);
141 }
143 @Override
144 protected boolean isEOL(final char ch) {
145 return Lexer.isJsonEOL(ch);
146 }
147 };
149 k = -1;
151 next();
153 final Node resultNode = jsonLiteral();
154 expect(EOF);
156 return resultNode;
157 }
159 @SuppressWarnings("fallthrough")
160 private LiteralNode<?> getStringLiteral() {
161 final LiteralNode<?> literal = getLiteral();
162 final String str = (String)literal.getValue();
164 for (int i = 0; i < str.length(); i++) {
165 final char ch = str.charAt(i);
166 switch (ch) {
167 default:
168 if (ch > 0x001f) {
169 break;
170 }
171 case '"':
172 case '\\':
173 error(AbstractParser.message("unexpected.token", str));
174 break;
175 }
176 }
178 return literal;
179 }
181 /**
182 * Parse a JSON literal from the token stream
183 * @return the JSON literal as a Node
184 */
185 private Node jsonLiteral() {
186 final long literalToken = token;
188 switch (type) {
189 case STRING:
190 return getStringLiteral();
191 case ESCSTRING:
192 case DECIMAL:
193 case FLOATING:
194 return getLiteral();
195 case FALSE:
196 next();
197 return LiteralNode.newInstance(source, literalToken, finish, false);
198 case TRUE:
199 next();
200 return LiteralNode.newInstance(source, literalToken, finish, true);
201 case NULL:
202 next();
203 return LiteralNode.newInstance(source, literalToken, finish);
204 case LBRACKET:
205 return arrayLiteral();
206 case LBRACE:
207 return objectLiteral();
208 /*
209 * A.8.1 JSON Lexical Grammar
210 *
211 * JSONNumber :: See 15.12.1.1
212 * -opt DecimalIntegerLiteral JSONFractionopt ExponentPartopt
213 */
214 case SUB:
215 next();
217 final long realToken = token;
218 final Object value = getValue();
220 if (value instanceof Number) {
221 next();
222 return new UnaryNode(source, literalToken, LiteralNode.newInstance(source, realToken, finish, (Number)value));
223 }
225 error(AbstractParser.message("expected", "number", type.getNameOrType()));
226 break;
227 default:
228 break;
229 }
231 error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
232 return null;
233 }
235 /**
236 * Parse an array literal from the token stream
237 * @return the array literal as a Node
238 */
239 private Node arrayLiteral() {
240 // Unlike JavaScript array literals, elison is not permitted in JSON.
242 // Capture LBRACKET token.
243 final long arrayToken = token;
244 // LBRACKET tested in caller.
245 next();
247 Node result = null;
248 // Prepare to accummulating elements.
249 final List<Node> elements = new ArrayList<>();
251 loop:
252 while (true) {
253 switch (type) {
254 case RBRACKET:
255 next();
256 result = LiteralNode.newInstance(source, arrayToken, finish, elements);
257 break loop;
259 case COMMARIGHT:
260 next();
261 break;
263 default:
264 // Add expression element.
265 elements.add(jsonLiteral());
266 // Comma between array elements is mandatory in JSON.
267 if (type != COMMARIGHT && type != RBRACKET) {
268 error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
269 }
270 break;
271 }
272 }
274 return result;
275 }
277 /**
278 * Parse an object literal from the token stream
279 * @return the object literal as a Node
280 */
281 private Node objectLiteral() {
282 // Capture LBRACE token.
283 final long objectToken = token;
284 // LBRACE tested in caller.
285 next();
287 // Prepare to accumulate elements.
288 final List<Node> elements = new ArrayList<>();
290 // Create a block for the object literal.
291 loop:
292 while (true) {
293 switch (type) {
294 case RBRACE:
295 next();
296 break loop;
298 case COMMARIGHT:
299 next();
300 break;
302 default:
303 // Get and add the next property.
304 final Node property = propertyAssignment();
305 elements.add(property);
307 // Comma between property assigments is mandatory in JSON.
308 if (type != RBRACE && type != COMMARIGHT) {
309 error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
310 }
311 break;
312 }
313 }
315 // Construct new object literal.
316 return new ObjectNode(source, objectToken, finish, elements);
317 }
319 /**
320 * Parse a property assignment from the token stream
321 * @return the property assignment as a Node
322 */
323 private Node propertyAssignment() {
324 // Capture firstToken.
325 final long propertyToken = token;
326 LiteralNode<?> name = null;
328 if (type == STRING) {
329 name = getStringLiteral();
330 } else if (type == ESCSTRING) {
331 name = getLiteral();
332 }
334 if (name != null) {
335 expect(COLON);
336 final Node value = jsonLiteral();
337 return new PropertyNode(source, propertyToken, value.getFinish(), name, value);
338 }
340 // Raise an error.
341 error(AbstractParser.message("expected", "string", type.getNameOrType()));
343 return null;
344 }
346 }