private ScriptFunction compileImpl(final Source source, final Global newGlobal) throws ScriptException { final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != newGlobal); try { if (globalChanged) { Context.setGlobal(newGlobal); } return nashornContext.compileScript(source, newGlobal); } catch (final Exception e) { throwAsScriptException(e, newGlobal); throw new AssertionError("should not reach here"); } finally { if (globalChanged) { Context.setGlobal(oldGlobal); } } }
/** * Construct a parser. * * @param env script environment * @param source source to parse * @param errors error manager * @param strict parser created with strict mode enabled. * @param lineOffset line offset to start counting lines from * @param log debug logger if one is needed */ public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset, final DebugLogger log) { super(source, errors, strict, lineOffset); this.env = env; this.namespace = new Namespace(env.getNamespace()); this.scripting = env._scripting; if (this.scripting) { this.lineInfoReceiver = new Lexer.LineInfoReceiver() { @Override public void lineInfo(final int receiverLine, final int receiverLinePosition) { // update the parser maintained line information Parser.this.line = receiverLine; Parser.this.linePosition = receiverLinePosition; } }; } else { // non-scripting mode script can't have multi-line literals this.lineInfoReceiver = null; } this.log = log == null ? DebugLogger.DISABLED_LOGGER : log; }
/** * Return a string representation of a token. * @param source Token source. * @param token Token descriptor. * @param verbose True to include details. * @return String representation. */ public static String toString(final Source source, final long token, final boolean verbose) { final TokenType type = Token.descType(token); String result; if (source != null && type.getKind() == LITERAL) { result = source.getString(token); } else { result = type.getNameOrType(); } if (verbose) { final int position = Token.descPosition(token); final int length = Token.descLength(token); result += " (" + position + ", " + length + ")"; } return result; }
/** * Sets the source and namespace for this function. It can only set a non-null source and namespace for a function * that currently has both a null source and a null namespace. This is used to re-set the source and namespace for * a deserialized function node. * @param source the source for the function. * @param namespace the namespace for the function * @return a new function node with the set source and namespace * @throws IllegalArgumentException if the specified source or namespace is null * @throws IllegalStateException if the function already has either a source or namespace set. */ public FunctionNode initializeDeserialized(final Source source, final Namespace namespace) { if (source == null || namespace == null) { throw new IllegalArgumentException(); } else if (this.source == source && this.namespace == namespace) { return this; } else if (this.source != null || this.namespace != null) { throw new IllegalStateException(); } return new FunctionNode( this, lastToken, endParserState, flags, name, returnType, compileUnit, compilationState, body, parameters, thisProperties, rootClass, source, namespace); }
@Override public String toString() { final StringBuffer sb = new StringBuffer(); sb.append("[ "); for (int i = 0; i < sp; i++) { final Object node = stack[i]; sb.append(node.getClass().getSimpleName()); sb.append('@'); sb.append(Debug.id(node)); sb.append(':'); if (node instanceof FunctionNode) { final FunctionNode fn = (FunctionNode)node; final Source source = fn.getSource(); String src = source.toString(); if (src.contains(File.pathSeparator)) { src = src.substring(src.lastIndexOf(File.pathSeparator)); } src += ' '; src += fn.getLineNumber(); sb.append(src); } sb.append(' '); } sb.append(" ==> ]"); return sb.toString(); }
/** * Retrieves an opaque descriptor for the persistence location for a given function. It should be passed * to {@link #load(Object)} and {@link #store(Object, Map)} methods. * @param source the source where the function comes from * @param functionId the unique ID number of the function within the source * @param paramTypes the types of the function parameters (as persistence is per parameter type * specialization). * @return an opaque descriptor for the persistence location. Can be null if persistence is disabled. */ public static Object getLocationDescriptor(final Source source, final int functionId, final Type[] paramTypes) { if(cacheDir == null) { return null; } final StringBuilder b = new StringBuilder(48); // Base64-encode the digest of the source, and append the function id. b.append(source.getDigest()).append('-').append(functionId); // Finally, if this is a parameter-type specialized version of the function, add the parameter types // to the file name. if(paramTypes != null && paramTypes.length > 0) { b.append('-'); for(final Type t: paramTypes) { b.append(Type.getShortSignatureDescriptor(t)); } } return new LocationDescriptor(new File(cacheDir, b.toString())); }
private static String safeSourceName(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source) { String baseName = new File(source.getName()).getName(); final int index = baseName.lastIndexOf(".js"); if (index != -1) { baseName = baseName.substring(0, index); } baseName = baseName.replace('.', '_').replace('-', '_'); if (!env._loader_per_compile) { baseName = baseName + installer.getUniqueScriptId(); } // ASM's bytecode verifier does not allow JVM allowed safe escapes using '\' as escape char. // While ASM accepts such escapes for method names, field names, it enforces Java identifier // for class names. Workaround that ASM bug here by replacing JVM 'dangerous' chars with '_' // rather than safe encoding using '\'. final String mangled = env._verify_code? replaceDangerChars(baseName) : NameCodec.encode(baseName); return mangled != null ? mangled : baseName; }
/** * Construct a parser. * * @param env script environment * @param source source to parse * @param errors error manager * @param strict parser created with strict mode enabled. * @param lineOffset line offset to start counting lines from * @param log debug logger if one is needed */ public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset, final DebugLogger log) { super(source, errors, strict, lineOffset); this.lc = new ParserContext(); this.defaultNames = new ArrayDeque<>(); this.env = env; this.namespace = new Namespace(env.getNamespace()); this.scripting = env._scripting; if (this.scripting) { this.lineInfoReceiver = new Lexer.LineInfoReceiver() { @Override public void lineInfo(final int receiverLine, final int receiverLinePosition) { // update the parser maintained line information Parser.this.line = receiverLine; Parser.this.linePosition = receiverLinePosition; } }; } else { // non-scripting mode script can't have multi-line literals this.lineInfoReceiver = null; } this.log = log == null ? DebugLogger.DISABLED_LOGGER : log; }
/** * Sets the source and namespace for this function. It can only set a non-null source and namespace for a function * that currently has both a null source and a null namespace. This is used to re-set the source and namespace for * a deserialized function node. * @param source the source for the function. * @param namespace the namespace for the function * @return a new function node with the set source and namespace * @throws IllegalArgumentException if the specified source or namespace is null * @throws IllegalStateException if the function already has either a source or namespace set. */ public FunctionNode initializeDeserialized(final Source source, final Namespace namespace) { if (source == null || namespace == null) { throw new IllegalArgumentException(); } else if (this.source == source && this.namespace == namespace) { return this; } else if (this.source != null || this.namespace != null) { throw new IllegalStateException(); } return new FunctionNode( this, lastToken, endParserState, flags, name, returnType, compileUnit, body, parameters, thisProperties, rootClass, source, namespace); }
/** * Creates a compiler for an on-demand compilation job. * * @param installer code installer * @param source source to compile * @param isStrict is this a strict compilation * @param compiledFunction compiled function, if any * @param types parameter and return value type information, if any is known * @param invalidatedProgramPoints invalidated program points for recompilation * @param typeInformationFile descriptor of the location where type information is persisted * @param continuationEntryPoints continuation entry points for restof method * @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator} * @return a new compiler */ public static Compiler forOnDemandCompilation( final CodeInstaller installer, final Source source, final boolean isStrict, final RecompilableScriptFunctionData compiledFunction, final TypeMap types, final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile, final int[] continuationEntryPoints, final ScriptObject runtimeScope) { final Context context = installer.getContext(); return new Compiler(context, installer, source, context.getErrorManager(), isStrict, true, compiledFunction, types, invalidatedProgramPoints, typeInformationFile, continuationEntryPoints, runtimeScope); }
private static Source makeSource(final Reader reader, final ScriptContext ctxt) throws ScriptException { try { return sourceFor(getScriptName(ctxt), reader); } catch (final IOException e) { throw new ScriptException(e); } }
private CompiledScript asCompiledScript(final Source source) throws ScriptException { final Context.MultiGlobalCompiledScript mgcs; final ScriptFunction func; final Global oldGlobal = Context.getGlobal(); final Global newGlobal = getNashornGlobalFrom(context); final boolean globalChanged = (oldGlobal != newGlobal); try { if (globalChanged) { Context.setGlobal(newGlobal); } mgcs = nashornContext.compileScript(source); func = mgcs.getFunction(newGlobal); } catch (final Exception e) { throwAsScriptException(e, newGlobal); throw new AssertionError("should not reach here"); } finally { if (globalChanged) { Context.setGlobal(oldGlobal); } } return new CompiledScript() { @Override public Object eval(final ScriptContext ctxt) throws ScriptException { final Global globalObject = getNashornGlobalFrom(ctxt); // Are we running the script in the same global in which it was compiled? if (func.getScope() == globalObject) { return evalImpl(func, ctxt, globalObject); } // different global return evalImpl(mgcs, ctxt, globalObject); } @Override public ScriptEngine getEngine() { return NashornScriptEngine.this; } }; }
/** * Construct a parser. * * @param source Source to parse. * @param errors Error reporting manager. * @param strict True if we are in strict mode * @param lineOffset Offset from which lines should be counted */ protected AbstractParser(final Source source, final ErrorManager errors, final boolean strict, final int lineOffset) { this.source = source; this.errors = errors; this.k = -1; this.token = Token.toDesc(EOL, 0, 1); this.type = EOL; this.last = EOL; this.isStrictMode = strict; this.lineOffset = lineOffset; }
/** * Constructor * * @param source the source * @param start start position in source from which to start lexing * @param len length of source segment to lex * @param stream token stream to lex * @param scripting are we in scripting mode * @param pauseOnFunctionBody if true, lexer will return from {@link #lexify()} when it encounters a * function body. This is used with the feature where the parser is skipping nested function bodies to * avoid reading ahead unnecessarily when we skip the function bodies. */ public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting, final boolean pauseOnFunctionBody) { super(source.getContent(), 1, start, len); this.source = source; this.stream = stream; this.scripting = scripting; this.nested = false; this.pendingLine = 1; this.last = EOL; this.pauseOnFunctionBody = pauseOnFunctionBody; }
private void location(final Node node) { if (includeLocation) { objectStart("loc"); // source name final Source src = lc.getCurrentFunction().getSource(); property("source", src.getName()); comma(); // start position objectStart("start"); final int start = node.getStart(); property("line", src.getLine(start)); comma(); property("column", src.getColumn(start)); objectEnd(); comma(); // end position objectStart("end"); final int end = node.getFinish(); property("line", src.getLine(end)); comma(); property("column", src.getColumn(end)); objectEnd(); // end 'loc' objectEnd(); comma(); } }
/** * Constructor * * @param source the source * @param lineNumber line number * @param token token * @param finish finish * @param firstToken first token of the function node (including the function declaration) * @param namespace the namespace * @param ident the identifier * @param name the name of the function * @param parameters parameter list * @param kind kind of function as in {@link FunctionNode.Kind} * @param flags initial flags */ public FunctionNode( final Source source, final int lineNumber, final long token, final int finish, final long firstToken, final Namespace namespace, final IdentNode ident, final String name, final List<IdentNode> parameters, final FunctionNode.Kind kind, final int flags) { super(token, finish); this.source = source; this.lineNumber = lineNumber; this.ident = ident; this.name = name; this.kind = kind; this.parameters = parameters; this.firstToken = firstToken; this.lastToken = token; this.namespace = namespace; this.compilationState = EnumSet.of(CompilationState.INITIALIZED); this.flags = flags; this.compileUnit = null; this.body = null; this.thisProperties = 0; this.rootClass = null; this.endParserState = null; }
private FunctionNode( final FunctionNode functionNode, final long lastToken, final Object endParserState, final int flags, final String name, final Type returnType, final CompileUnit compileUnit, final EnumSet<CompilationState> compilationState, final Block body, final List<IdentNode> parameters, final int thisProperties, final Class<?> rootClass, final Source source, Namespace namespace) { super(functionNode); this.endParserState = endParserState; this.lineNumber = functionNode.lineNumber; this.flags = flags; this.name = name; this.returnType = returnType; this.compileUnit = compileUnit; this.lastToken = lastToken; this.compilationState = compilationState; this.body = body; this.parameters = parameters; this.thisProperties = thisProperties; this.rootClass = rootClass; this.source = source; this.namespace = namespace; // the fields below never change - they are final and assigned in constructor this.ident = functionNode.ident; this.kind = functionNode.kind; this.firstToken = functionNode.firstToken; }
private void throwParserException(final String message, final Node origin) { if (origin == null) { throw new ParserException(message); } final Source source = compiler.getSource(); final long token = origin.getToken(); final int line = source.getLine(origin.getStart()); final int column = source.getColumn(origin.getStart()); final String formatted = ErrorManager.format(message, source, line, column, token); throw new ParserException(JSErrorType.SYNTAX_ERROR, formatted, source, line, column, token); }
/** * Calculate a synthetic eval location for a node for the stacktrace, for example src#17<eval> * @param node a node * @return eval location */ private String evalLocation(final IdentNode node) { final Source source = lc.getCurrentFunction().getSource(); final int pos = node.position(); return new StringBuilder(). append(source.getName()). append('#'). append(source.getLine(pos)). append(':'). append(source.getColumn(pos)). append("<eval>"). toString(); }
/** * Define the static fields common in all scripts. * @param strictMode Should we generate this method in strict mode */ private void defineCommonStatics(final boolean strictMode) { // source - used to store the source data (text) for this script. Shared across // compile units. Set externally by the compiler. field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class); // constants - used to the constants array for this script. Shared across // compile units. Set externally by the compiler. field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class); // strictMode - was this script compiled in strict mode. Set externally by the compiler. field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode); }