# HG changeset patch # User sundar # Date 1390227714 -19800 # Node ID 1d2d88e478ea2c7a885ffe5afbec0a3950afd5fa # Parent ea3dda90768c0279f2ab2ac0f9ccf904aebd3f1e 8032068: implement @sourceURL and #sourceURL directives Reviewed-by: hannesw, lagergren diff -r ea3dda90768c -r 1d2d88e478ea nashorn/src/jdk/nashorn/internal/codegen/Compiler.java --- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Fri Jan 17 20:09:47 2014 +0530 +++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Mon Jan 20 19:51:54 2014 +0530 @@ -85,6 +85,8 @@ private Source source; + private String sourceName; + private final Map bytecode; private final Set compileUnits; @@ -267,6 +269,7 @@ append('$'). append(safeSourceName(functionNode.getSource())); this.source = functionNode.getSource(); + this.sourceName = functionNode.getSourceName(); this.scriptName = sb.toString(); } @@ -573,7 +576,7 @@ } private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) { - final ClassEmitter classEmitter = new ClassEmitter(env, source.getName(), unitClassName, strict); + final ClassEmitter classEmitter = new ClassEmitter(env, sourceName, unitClassName, strict); final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); classEmitter.begin(); diff -r ea3dda90768c -r 1d2d88e478ea nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java --- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Fri Jan 17 20:09:47 2014 +0530 +++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Mon Jan 20 19:51:54 2014 +0530 @@ -29,6 +29,7 @@ import java.util.EnumSet; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.Compiler; @@ -138,6 +139,9 @@ /** Function flags. */ private final int flags; + /** //@ sourceURL or //# sourceURL for program function nodes */ + private final String sourceURL; + private final int lineNumber; /** Is anonymous function flag. */ @@ -223,6 +227,7 @@ * @param parameters parameter list * @param kind kind of function as in {@link FunctionNode.Kind} * @param flags initial flags + * @param sourceURL sourceURL specified in script (optional) */ public FunctionNode( final Source source, @@ -235,7 +240,8 @@ final String name, final List parameters, final FunctionNode.Kind kind, - final int flags) { + final int flags, + final String sourceURL) { super(token, finish); this.source = source; @@ -250,6 +256,7 @@ this.compilationState = EnumSet.of(CompilationState.INITIALIZED); this.declaredSymbols = new HashSet<>(); this.flags = flags; + this.sourceURL = sourceURL; this.compileUnit = null; this.body = null; this.snapshot = null; @@ -260,6 +267,7 @@ final FunctionNode functionNode, final long lastToken, final int flags, + final String sourceURL, final String name, final Type returnType, final CompileUnit compileUnit, @@ -271,6 +279,7 @@ super(functionNode); this.lineNumber = functionNode.lineNumber; this.flags = flags; + this.sourceURL = sourceURL; this.name = name; this.returnType = returnType; this.compileUnit = compileUnit; @@ -308,6 +317,38 @@ } /** + * get source name - sourceURL or name derived from Source. + * + * @return name for the script source + */ + public String getSourceName() { + return (sourceURL != null)? sourceURL : source.getName(); + } + + /** + * get the sourceURL + * @return the sourceURL + */ + public String getSourceURL() { + return sourceURL; + } + + /** + * Set the sourceURL + * + * @param lc lexical context + * @param newSourceURL source url string to set + * @return function node or a new one if state was changed + */ + public FunctionNode setSourceURL(final LexicalContext lc, final String newSourceURL) { + if (Objects.equals(sourceURL, newSourceURL)) { + return this; + } + + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, newSourceURL, name, returnType, compileUnit, compilationState, body, parameters, null, hints)); + } + + /** * Returns the line number. * @return the line number. */ @@ -335,7 +376,7 @@ if (this.snapshot == null) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, null, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, null, hints)); } /** @@ -351,7 +392,7 @@ if (isProgram() || parameters.isEmpty()) { return this; //never specialize anything that won't be recompiled } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, this, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, this, hints)); } /** @@ -409,7 +450,7 @@ } final EnumSet newState = EnumSet.copyOf(this.compilationState); newState.add(state); - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, newState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, newState, body, parameters, snapshot, hints)); } /** @@ -430,7 +471,7 @@ if (this.hints == hints) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** @@ -483,7 +524,7 @@ if (this.flags == flags) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } @Override @@ -593,7 +634,7 @@ if(this.body == body) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** @@ -688,7 +729,7 @@ if (this.lastToken == lastToken) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** @@ -710,7 +751,7 @@ if (this.name.equals(name)) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** @@ -760,7 +801,7 @@ if (this.parameters == parameters) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** @@ -825,6 +866,7 @@ this, lastToken, flags, + sourceURL, name, type, compileUnit, @@ -863,7 +905,7 @@ if (this.compileUnit == compileUnit) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** diff -r ea3dda90768c -r 1d2d88e478ea nashorn/src/jdk/nashorn/internal/parser/AbstractParser.java --- a/nashorn/src/jdk/nashorn/internal/parser/AbstractParser.java Fri Jan 17 20:09:47 2014 +0530 +++ b/nashorn/src/jdk/nashorn/internal/parser/AbstractParser.java Mon Jan 20 19:51:54 2014 +0530 @@ -26,6 +26,7 @@ package jdk.nashorn.internal.parser; import static jdk.nashorn.internal.parser.TokenType.COMMENT; +import static jdk.nashorn.internal.parser.TokenType.DIRECTIVE_COMMENT; import static jdk.nashorn.internal.parser.TokenType.EOF; import static jdk.nashorn.internal.parser.TokenType.EOL; import static jdk.nashorn.internal.parser.TokenType.IDENT; @@ -84,6 +85,9 @@ /** Is this parser running under strict mode? */ protected boolean isStrictMode; + /** //@ sourceURL or //# sourceURL */ + protected String sourceURL; + /** * Construct a parser. * @@ -156,17 +160,38 @@ protected final TokenType nextOrEOL() { do { nextToken(); - } while (type == COMMENT); + if (type == DIRECTIVE_COMMENT) { + checkDirectiveComment(); + } + } while (type == COMMENT || type == DIRECTIVE_COMMENT); return type; } + // sourceURL= after directive comment + private static final String SOURCE_URL_PREFIX = "sourceURL="; + + // currently only @sourceURL=foo supported + private void checkDirectiveComment() { + // if already set, ignore this one + if (sourceURL != null) { + return; + } + + final String comment = (String) lexer.getValueOf(token, isStrictMode); + final int len = comment.length(); + // 4 characters for directive comment marker //@\s or //#\s + if (len > 4 && comment.substring(4).startsWith(SOURCE_URL_PREFIX)) { + sourceURL = comment.substring(4 + SOURCE_URL_PREFIX.length()); + } + } + /** * Seek next token. * * @return tokenType of next token. */ - private final TokenType nextToken() { + private TokenType nextToken() { // Capture last token tokenType. last = type; if (type != EOF) { diff -r ea3dda90768c -r 1d2d88e478ea nashorn/src/jdk/nashorn/internal/parser/Lexer.java --- a/nashorn/src/jdk/nashorn/internal/parser/Lexer.java Fri Jan 17 20:09:47 2014 +0530 +++ b/nashorn/src/jdk/nashorn/internal/parser/Lexer.java Mon Jan 20 19:51:54 2014 +0530 @@ -27,6 +27,7 @@ import static jdk.nashorn.internal.parser.TokenType.ADD; import static jdk.nashorn.internal.parser.TokenType.COMMENT; +import static jdk.nashorn.internal.parser.TokenType.DIRECTIVE_COMMENT; import static jdk.nashorn.internal.parser.TokenType.DECIMAL; import static jdk.nashorn.internal.parser.TokenType.EOF; import static jdk.nashorn.internal.parser.TokenType.EOL; @@ -434,12 +435,18 @@ if (ch1 == '/') { // Skip over //. skip(2); + + boolean directiveComment = false; + if ((ch0 == '#' || ch0 == '@') && (ch1 == ' ')) { + directiveComment = true; + } + // Scan for EOL. while (!atEOF() && !isEOL(ch0)) { skip(1); } // Did detect a comment. - add(COMMENT, start); + add(directiveComment? DIRECTIVE_COMMENT : COMMENT, start); return true; } else if (ch1 == '*') { // Skip over /*. @@ -1623,6 +1630,8 @@ return valueOfPattern(start, len); // RegexToken::LexerToken case XML: return valueOfXML(start, len); // XMLToken::LexerToken + case DIRECTIVE_COMMENT: + return source.getString(start, len); default: break; } diff -r ea3dda90768c -r 1d2d88e478ea nashorn/src/jdk/nashorn/internal/parser/Parser.java --- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java Fri Jan 17 20:09:47 2014 +0530 +++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java Mon Jan 20 19:51:54 2014 +0530 @@ -421,7 +421,8 @@ name, parameters, kind, - flags); + flags, + sourceURL); lc.push(functionNode); // Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the @@ -640,6 +641,10 @@ script = restoreFunctionNode(script, token); //commit code script = script.setBody(lc, script.getBody().setNeedsScope(lc)); + // user may have directive comment to set sourceURL + if (sourceURL != null) { + script = script.setSourceURL(lc, sourceURL); + } return script; } diff -r ea3dda90768c -r 1d2d88e478ea nashorn/src/jdk/nashorn/internal/parser/TokenType.java --- a/nashorn/src/jdk/nashorn/internal/parser/TokenType.java Fri Jan 17 20:09:47 2014 +0530 +++ b/nashorn/src/jdk/nashorn/internal/parser/TokenType.java Mon Jan 20 19:51:54 2014 +0530 @@ -41,10 +41,14 @@ */ @SuppressWarnings("javadoc") public enum TokenType { - ERROR (SPECIAL, null), - EOF (SPECIAL, null), - EOL (SPECIAL, null), - COMMENT (SPECIAL, null), + ERROR (SPECIAL, null), + EOF (SPECIAL, null), + EOL (SPECIAL, null), + COMMENT (SPECIAL, null), + // comments of the form //@ foo=bar or //# foo=bar + // These comments are treated as special instructions + // to the lexer, parser or codegenerator. + DIRECTIVE_COMMENT (SPECIAL, null), NOT (UNARY, "!", 14, false), NE (BINARY, "!=", 9, true), diff -r ea3dda90768c -r 1d2d88e478ea nashorn/test/script/basic/JDK-8032068.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8032068.js Mon Jan 20 19:51:54 2014 +0530 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8032068: implement @sourceURL and #sourceURL directives. + * + * @test + * @run + */ + + +try { + Function("throw new Error();\n//# sourceURL=foo.js")(); +} catch (e) { + print(e.stack.replace(/\\/g, '/')); +} + +try { + eval("function g() { throw Error('x');\n } g();\n//# sourceURL=bar.js"); +} catch (e) { + print(e.stack.replace(/\\/g, '/')); +} + +// check @sourceURL for compatibility +try { + Function("throw new Error();\n//@ sourceURL=foo2.js")(); +} catch (e) { + print(e.stack.replace(/\\/g, '/')); +} + +try { + eval("function g() { throw Error('x');\n } g();\n//@ sourceURL=bar2.js"); +} catch (e) { + print(e.stack.replace(/\\/g, '/')); +} + diff -r ea3dda90768c -r 1d2d88e478ea nashorn/test/script/basic/JDK-8032068.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8032068.js.EXPECTED Mon Jan 20 19:51:54 2014 +0530 @@ -0,0 +1,14 @@ +Error + at (foo.js:2) + at (test/script/basic/JDK-8032068.js:33) +Error: x + at g (bar.js:1) + at (bar.js:2) + at (test/script/basic/JDK-8032068.js:39) +Error + at (foo2.js:2) + at (test/script/basic/JDK-8032068.js:46) +Error: x + at g (bar2.js:1) + at (bar2.js:2) + at (test/script/basic/JDK-8032068.js:52)