RewriteException.java revision 1033:c1f651636d9c
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 */ 25package jdk.nashorn.internal.runtime; 26 27import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; 28import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; 29import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 30 31import java.io.NotSerializableException; 32import java.io.ObjectInputStream; 33import java.io.ObjectOutputStream; 34import java.lang.invoke.CallSite; 35import java.lang.invoke.ConstantCallSite; 36import java.lang.invoke.MethodHandle; 37import java.lang.invoke.MethodHandles; 38import java.lang.invoke.MethodHandles.Lookup; 39import java.lang.invoke.MethodType; 40import java.lang.reflect.Array; 41import java.util.Arrays; 42import jdk.nashorn.internal.codegen.CompilerConstants; 43import jdk.nashorn.internal.codegen.CompilerConstants.Call; 44import jdk.nashorn.internal.codegen.types.Type; 45import jdk.nashorn.internal.lookup.MethodHandleFactory; 46import jdk.nashorn.internal.lookup.MethodHandleFunctionality; 47import jdk.nashorn.internal.objects.Global; 48import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 49 50/** 51 * Used to signal to the linker to relink the callee 52 */ 53@SuppressWarnings("serial") 54public final class RewriteException extends Exception { 55 private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality(); 56 57 // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly 58 // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations). 59 private ScriptObject runtimeScope; 60 61 // Contents of bytecode slots 62 private Object[] byteCodeSlots; 63 64 private final int[] previousContinuationEntryPoints; 65 66 /** Call for getting the contents of the bytecode slots in the exception */ 67 public static final Call GET_BYTECODE_SLOTS = virtualCallNoLookup(RewriteException.class, "getByteCodeSlots", Object[].class); 68 /** Call for getting the program point in the exception */ 69 public static final Call GET_PROGRAM_POINT = virtualCallNoLookup(RewriteException.class, "getProgramPoint", int.class); 70 /** Call for getting the return value for the exception */ 71 public static final Call GET_RETURN_VALUE = virtualCallNoLookup(RewriteException.class, "getReturnValueDestructive", Object.class); 72 /** Call for the populate array bootstrap */ 73 public static final Call BOOTSTRAP = staticCallNoLookup(RewriteException.class, "populateArrayBootstrap", CallSite.class, Lookup.class, String.class, MethodType.class, int.class); 74 75 /** Call for populating an array with local variable state */ 76 private static final Call POPULATE_ARRAY = staticCall(MethodHandles.lookup(), RewriteException.class, "populateArray", Object[].class, Object[].class, int.class, Object[].class); 77 78 /** Call for converting an array to a long array. */ 79 public static final Call TO_LONG_ARRAY = staticCallNoLookup(RewriteException.class, "toLongArray", long[].class, Object.class, RewriteException.class); 80 /** Call for converting an array to a double array. */ 81 public static final Call TO_DOUBLE_ARRAY = staticCallNoLookup(RewriteException.class, "toDoubleArray", double[].class, Object.class, RewriteException.class); 82 /** Call for converting an array to an object array. */ 83 public static final Call TO_OBJECT_ARRAY = staticCallNoLookup(RewriteException.class, "toObjectArray", Object[].class, Object.class, RewriteException.class); 84 /** Call for converting an object to null if it can't be represented as an instance of a class. */ 85 public static final Call INSTANCE_OR_NULL = staticCallNoLookup(RewriteException.class, "instanceOrNull", Object.class, Object.class, Class.class); 86 /** Call for asserting the length of an array. */ 87 public static final Call ASSERT_ARRAY_LENGTH = staticCallNoLookup(RewriteException.class, "assertArrayLength", void.class, Object[].class, int.class); 88 89 private RewriteException( 90 final UnwarrantedOptimismException e, 91 final Object[] byteCodeSlots, 92 final String[] byteCodeSymbolNames, 93 final int[] previousContinuationEntryPoints) { 94 super("", e, false, Context.DEBUG); 95 this.byteCodeSlots = byteCodeSlots; 96 this.runtimeScope = mergeSlotsWithScope(byteCodeSlots, byteCodeSymbolNames); 97 this.previousContinuationEntryPoints = previousContinuationEntryPoints; 98 } 99 100 /** 101 * Constructor for a rewrite exception thrown from an optimistic function. 102 * @param e the {@link UnwarrantedOptimismException} that triggered this exception. 103 * @param byteCodeSlots contents of local variable slots at the time of rewrite at the program point 104 * @param byteCodeSymbolNames the names of the variables in the {@code byteCodeSlots} parameter. The array might 105 * have less elements, and some elements might be unnamed (the name can be null). The information is provided in an 106 * effort to assist evaluation of expressions for their types by the compiler doing the deoptimizing recompilation, 107 * and can thus be incomplete - the more complete it is, the more expressions can be evaluated by the compiler, and 108 * the more unnecessary deoptimizing compilations can be avoided. 109 * @return a new rewrite exception 110 */ 111 public static RewriteException create(final UnwarrantedOptimismException e, 112 final Object[] byteCodeSlots, 113 final String[] byteCodeSymbolNames) { 114 return create(e, byteCodeSlots, byteCodeSymbolNames, null); 115 } 116 117 /** 118 * Constructor for a rewrite exception thrown from a rest-of method. 119 * @param e the {@link UnwarrantedOptimismException} that triggered this exception. 120 * @param byteCodeSlots contents of local variable slots at the time of rewrite at the program point 121 * @param byteCodeSymbolNames the names of the variables in the {@code byteCodeSlots} parameter. The array might 122 * have less elements, and some elements might be unnamed (the name can be null). The information is provided in an 123 * effort to assist evaluation of expressions for their types by the compiler doing the deoptimizing recompilation, 124 * and can thus be incomplete - the more complete it is, the more expressions can be evaluated by the compiler, and 125 * the more unnecessary deoptimizing compilations can be avoided. 126 * @param previousContinuationEntryPoints an array of continuation entry points that were already executed during 127 * one logical invocation of the function (a rest-of triggering a rest-of triggering a...) 128 * @return a new rewrite exception 129 */ 130 public static RewriteException create(final UnwarrantedOptimismException e, 131 final Object[] byteCodeSlots, 132 final String[] byteCodeSymbolNames, 133 final int[] previousContinuationEntryPoints) { 134 return new RewriteException(e, byteCodeSlots, byteCodeSymbolNames, previousContinuationEntryPoints); 135 } 136 137 /** 138 * Bootstrap method for populate array 139 * @param lookup lookup 140 * @param name name (ignored) 141 * @param type method type for signature 142 * @param startIndex start index to start writing to 143 * @return callsite to array populator (constant) 144 */ 145 public static CallSite populateArrayBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type, final int startIndex) { 146 MethodHandle mh = POPULATE_ARRAY.methodHandle(); 147 mh = MH.insertArguments(mh, 1, startIndex); 148 mh = MH.asCollector(mh, Object[].class, type.parameterCount() - 1); 149 mh = MH.asType(mh, type); 150 return new ConstantCallSite(mh); 151 } 152 153 private static ScriptObject mergeSlotsWithScope(final Object[] byteCodeSlots, final String[] byteCodeSymbolNames) { 154 final ScriptObject locals = Global.newEmptyInstance(); 155 final int l = Math.min(byteCodeSlots.length, byteCodeSymbolNames.length); 156 ScriptObject runtimeScope = null; 157 final String scopeName = CompilerConstants.SCOPE.symbolName(); 158 for(int i = 0; i < l; ++i) { 159 final String name = byteCodeSymbolNames[i]; 160 final Object value = byteCodeSlots[i]; 161 if(scopeName.equals(name)) { 162 assert runtimeScope == null; 163 runtimeScope = (ScriptObject)value; 164 } else if(name != null) { 165 locals.set(name, value, NashornCallSiteDescriptor.CALLSITE_STRICT); 166 } 167 } 168 locals.setProto(runtimeScope); 169 return locals; 170 } 171 172 /** 173 * Array populator used for saving the local variable state into the array contained in the 174 * RewriteException 175 * @param arrayToBePopluated array to be populated 176 * @param startIndex start index to write to 177 * @param items items with which to populate the array 178 * @return the populated array - same array object 179 */ 180 public static Object[] populateArray(final Object[] arrayToBePopluated, final int startIndex, final Object[] items) { 181 System.arraycopy(items, 0, arrayToBePopluated, startIndex, items.length); 182 return arrayToBePopluated; 183 } 184 185 /** 186 * Continuation handler calls this method when a local variable carried over into the continuation is expected to be 187 * a long array in the continued method. Normally, it will also be a long array in the original (interrupted by 188 * deoptimization) method, but it can actually be an int array that underwent widening in the new code version. 189 * @param obj the object that has to be converted into a long array 190 * @param e the exception being processed 191 * @return a long array 192 */ 193 public static long[] toLongArray(final Object obj, final RewriteException e) { 194 if(obj instanceof long[]) { 195 return (long[])obj; 196 } 197 198 assert obj instanceof int[]; 199 200 final int[] in = (int[])obj; 201 final long[] out = new long[in.length]; 202 for(int i = 0; i < in.length; ++i) { 203 out[i] = in[i]; 204 } 205 return e.replaceByteCodeValue(in, out); 206 } 207 208 /** 209 * Continuation handler calls this method when a local variable carried over into the continuation is expected to be 210 * a double array in the continued method. Normally, it will also be a double array in the original (interrupted by 211 * deoptimization) method, but it can actually be an int or long array that underwent widening in the new code version. 212 * @param obj the object that has to be converted into a double array 213 * @param e the exception being processed 214 * @return a double array 215 */ 216 public static double[] toDoubleArray(final Object obj, final RewriteException e) { 217 if(obj instanceof double[]) { 218 return (double[])obj; 219 } 220 221 assert obj instanceof int[] || obj instanceof long[]; 222 223 final int l = Array.getLength(obj); 224 final double[] out = new double[l]; 225 for(int i = 0; i < l; ++i) { 226 out[i] = Array.getDouble(obj, i); 227 } 228 return e.replaceByteCodeValue(obj, out); 229 } 230 231 /** 232 * Continuation handler calls this method when a local variable carried over into the continuation is expected to be 233 * an Object array in the continued method. Normally, it will also be an Object array in the original (interrupted by 234 * deoptimization) method, but it can actually be an int, long, or double array that underwent widening in the new 235 * code version. 236 * @param obj the object that has to be converted into an Object array 237 * @param e the exception being processed 238 * @return an Object array 239 */ 240 public static Object[] toObjectArray(final Object obj, final RewriteException e) { 241 if(obj instanceof Object[]) { 242 return (Object[])obj; 243 } 244 245 assert obj instanceof int[] || obj instanceof long[] || obj instanceof double[] : obj + " is " + obj.getClass().getName(); 246 247 final int l = Array.getLength(obj); 248 final Object[] out = new Object[l]; 249 for(int i = 0; i < l; ++i) { 250 out[i] = Array.get(obj, i); 251 } 252 return e.replaceByteCodeValue(obj, out); 253 } 254 255 /** 256 * Continuation handler calls this method when a local variable carried over into the continuation is expected to 257 * have a certain type, but the value can have a different type coming from the deoptimized method as it was a dead 258 * store. If we had precise liveness analysis, we wouldn't need this. 259 * @param obj the object inspected for being of a particular type 260 * @param clazz the type the object must belong to 261 * @return the object if it belongs to the type, or null otherwise 262 */ 263 public static Object instanceOrNull(final Object obj, final Class<?> clazz) { 264 return clazz.isInstance(obj) ? obj : null; 265 } 266 267 /** 268 * Asserts the length of an array. Invoked from continuation handler only when running with assertions enabled. 269 * The array can, in fact, have more elements than asserted, but they must all have Undefined as their value. The 270 * method does not test for the array having less elements than asserted, as those would already have caused an 271 * {@code ArrayIndexOutOfBoundsException} to be thrown as the continuation handler attempts to access the missing 272 * elements. 273 * @param arr the array 274 * @param length the asserted length 275 */ 276 public static void assertArrayLength(final Object[] arr, final int length) { 277 for(int i = arr.length; i-- > length;) { 278 if(arr[i] != ScriptRuntime.UNDEFINED) { 279 throw new AssertionError(String.format("Expected array length %d, but it is %d", length, i + 1)); 280 } 281 } 282 } 283 284 private <T> T replaceByteCodeValue(final Object in, final T out) { 285 for(int i = 0; i < byteCodeSlots.length; ++i) { 286 if(byteCodeSlots[i] == in) { 287 byteCodeSlots[i] = out; 288 } 289 } 290 return out; 291 } 292 293 private UnwarrantedOptimismException getUOE() { 294 return (UnwarrantedOptimismException)getCause(); 295 } 296 /** 297 * Get return value. This method is destructive, after it is invoked subsequent invocation of either 298 * {@link #getByteCodeSlots()} or this method will return null. This method is invoked from the generated 299 * continuation code as the last step before continuing the execution, and we need to make sure we don't hang on to 300 * either the entry bytecode slot values or the return value and prevent them from being garbage collected. 301 * @return return value 302 */ 303 public Object getReturnValueDestructive() { 304 assert byteCodeSlots != null; 305 byteCodeSlots = null; 306 runtimeScope = null; 307 return getUOE().getReturnValueDestructive(); 308 } 309 310 Object getReturnValueNonDestructive() { 311 return getUOE().getReturnValueNonDestructive(); 312 } 313 314 /** 315 * Get return type 316 * @return return type 317 */ 318 public Type getReturnType() { 319 return getUOE().getReturnType(); 320 } 321 322 /** 323 * Get the program point. 324 * @return program point. 325 */ 326 public int getProgramPoint() { 327 return getUOE().getProgramPoint(); 328 } 329 330 /** 331 * Get the bytecode slot contents. 332 * @return bytecode slot contents. 333 */ 334 public Object[] getByteCodeSlots() { 335 return byteCodeSlots == null ? null : byteCodeSlots.clone(); 336 } 337 338 /** 339 * @return an array of continuation entry points that were already executed during one logical invocation of the 340 * function (a rest-of triggering a rest-of triggering a...) 341 */ 342 public int[] getPreviousContinuationEntryPoints() { 343 return previousContinuationEntryPoints == null ? null : previousContinuationEntryPoints.clone(); 344 } 345 346 /** 347 * Returns the runtime scope that was in effect when the exception was thrown. 348 * @return the runtime scope. 349 */ 350 public ScriptObject getRuntimeScope() { 351 return runtimeScope; 352 } 353 354 private static String stringify(final Object returnValue) { 355 if (returnValue == null) { 356 return "null"; 357 } 358 String str = returnValue.toString(); 359 if (returnValue instanceof String) { 360 str = '\'' + str + '\''; 361 } else if (returnValue instanceof Double) { 362 str = str + 'd'; 363 } else if (returnValue instanceof Long) { 364 str = str + 'l'; 365 } 366 return str; 367 } 368 369 @Override 370 public String getMessage() { 371 return getMessage(false); 372 } 373 374 /** 375 * Short toString function for message 376 * @return short message 377 */ 378 public String getMessageShort() { 379 return getMessage(true); 380 } 381 382 private String getMessage(final boolean isShort) { 383 final StringBuilder sb = new StringBuilder(); 384 385 //program point 386 sb.append("[pp="). 387 append(getProgramPoint()). 388 append(", "); 389 390 //slot contents 391 if (!isShort) { 392 final Object[] slots = byteCodeSlots; 393 if (slots != null) { 394 sb.append("slots="). 395 append(Arrays.asList(slots)). 396 append(", "); 397 } 398 } 399 400 //return type 401 sb.append("type="). 402 append(getReturnType()). 403 append(", "); 404 405 //return value 406 sb.append("value="). 407 append(stringify(getReturnValueNonDestructive())). 408 append(")]"); 409 410 return sb.toString(); 411 } 412 413 private void writeObject(final ObjectOutputStream out) throws NotSerializableException { 414 throw new NotSerializableException(getClass().getName()); 415 } 416 417 private void readObject(final ObjectInputStream in) throws NotSerializableException { 418 throw new NotSerializableException(getClass().getName()); 419 } 420} 421