/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.elasticsearch.painless; import java.util.Objects; /** * Represents a location in script code (name of script + character offset) */ public final class Location { private final String sourceName; private final int offset; /** * Create a new Location * @param sourceName script's name * @param offset character offset of script element */ public Location(String sourceName, int offset) { this.sourceName = Objects.requireNonNull(sourceName); this.offset = offset; } /** * Return the script's name */ public String getSourceName() { return sourceName; } /** * Return the character offset */ public int getOffset() { return offset; } /** * Augments an exception with this location's information. */ public RuntimeException createError(RuntimeException exception) { StackTraceElement element = new StackTraceElement(WriterConstants.CLASS_NAME, "compile", sourceName, offset + 1); StackTraceElement[] oldStack = exception.getStackTrace(); StackTraceElement[] newStack = new StackTraceElement[oldStack.length + 1]; System.arraycopy(oldStack, 0, newStack, 1, oldStack.length); newStack[0] = element; exception.setStackTrace(newStack); assert exception.getStackTrace().length == newStack.length : "non-writeable stacktrace for exception: " + exception.getClass(); return exception; } // This maximum length is theoretically 65535 bytes, but as it's CESU-8 encoded we don't know how large it is in bytes, so be safe private static final int MAX_NAME_LENGTH = 256; /** Computes the file name (mostly important for stacktraces) */ public static String computeSourceName(String scriptName, String source) { StringBuilder fileName = new StringBuilder(); if (scriptName.equals(PainlessScriptEngine.INLINE_NAME)) { // its an anonymous script, include at least a portion of the source to help identify which one it is // but don't create stacktraces with filenames that contain newlines or huge names. // truncate to the first newline int limit = source.indexOf('\n'); if (limit >= 0) { int limit2 = source.indexOf('\r'); if (limit2 >= 0) { limit = Math.min(limit, limit2); } } else { limit = source.length(); } // truncate to our limit limit = Math.min(limit, MAX_NAME_LENGTH); fileName.append(source, 0, limit); // if we truncated, make it obvious if (limit != source.length()) { fileName.append(" ..."); } fileName.append(" @ <inline script>"); } else { // its a named script, just use the name // but don't trust this has a reasonable length! if (scriptName.length() > MAX_NAME_LENGTH) { fileName.append(scriptName, 0, MAX_NAME_LENGTH); fileName.append(" ..."); } else { fileName.append(scriptName); } } return fileName.toString(); } }