RewriteException.java revision 953:221a84ef44c0
1193326Sed/* 2193326Sed * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3193326Sed * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4193326Sed * 5193326Sed * This code is free software; you can redistribute it and/or modify it 6193326Sed * under the terms of the GNU General Public License version 2 only, as 7193326Sed * published by the Free Software Foundation. Oracle designates this 8193326Sed * particular file as subject to the "Classpath" exception as provided 9193326Sed * by Oracle in the LICENSE file that accompanied this code. 10193326Sed * 11193326Sed * This code is distributed in the hope that it will be useful, but WITHOUT 12193326Sed * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13193326Sed * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14193326Sed * version 2 for more details (a copy is included in the LICENSE file that 15193326Sed * accompanied this code). 16193326Sed * 17193326Sed * You should have received a copy of the GNU General Public License version 18193326Sed * 2 along with this work; if not, write to the Free Software Foundation, 19193326Sed * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20193326Sed * 21193326Sed * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22193326Sed * or visit www.oracle.com if you need additional information or have any 23193326Sed * questions. 24193326Sed */ 25193326Sedpackage jdk.nashorn.internal.runtime; 26193326Sed 27193326Sedimport static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; 28193326Sedimport static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; 29193326Sedimport static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 30193326Sed 31198893Srdivackyimport java.io.NotSerializableException; 32198893Srdivackyimport java.io.ObjectInputStream; 33251662Sdimimport java.io.ObjectOutputStream; 34251662Sdimimport java.lang.invoke.CallSite; 35251662Sdimimport java.lang.invoke.ConstantCallSite; 36251662Sdimimport java.lang.invoke.MethodHandle; 37251662Sdimimport java.lang.invoke.MethodHandles; 38251662Sdimimport java.lang.invoke.MethodHandles.Lookup; 39251662Sdimimport java.lang.invoke.MethodType; 40251662Sdimimport java.lang.reflect.Array; 41251662Sdimimport java.util.Arrays; 42251662Sdimimport jdk.nashorn.internal.codegen.CompilerConstants; 43251662Sdimimport jdk.nashorn.internal.codegen.CompilerConstants.Call; 44251662Sdimimport jdk.nashorn.internal.codegen.types.Type; 45251662Sdimimport jdk.nashorn.internal.lookup.MethodHandleFactory; 46251662Sdimimport jdk.nashorn.internal.lookup.MethodHandleFunctionality; 47251662Sdimimport jdk.nashorn.internal.objects.Global; 48251662Sdim 49251662Sdim/** 50251662Sdim * Used to signal to the linker to relink the callee 51251662Sdim */ 52251662Sdim@SuppressWarnings("serial") 53251662Sdimpublic final class RewriteException extends Exception { 54251662Sdim private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality(); 55251662Sdim 56251662Sdim // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly 57251662Sdim // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations). 58251662Sdim private ScriptObject runtimeScope; 59251662Sdim 60251662Sdim // Contents of bytecode slots 61251662Sdim private Object[] byteCodeSlots; 62251662Sdim 63251662Sdim private final int[] previousContinuationEntryPoints; 64193326Sed 65251662Sdim /** Call for getting the contents of the bytecode slots in the exception */ 66251662Sdim public static final Call GET_BYTECODE_SLOTS = virtualCallNoLookup(RewriteException.class, "getByteCodeSlots", Object[].class); 67251662Sdim /** Call for getting the program point in the exception */ 68251662Sdim public static final Call GET_PROGRAM_POINT = virtualCallNoLookup(RewriteException.class, "getProgramPoint", int.class); 69251662Sdim /** Call for getting the return value for the exception */ 70251662Sdim public static final Call GET_RETURN_VALUE = virtualCallNoLookup(RewriteException.class, "getReturnValueDestructive", Object.class); 71251662Sdim /** Call for the populate array bootstrap */ 72251662Sdim public static final Call BOOTSTRAP = staticCallNoLookup(RewriteException.class, "populateArrayBootstrap", CallSite.class, Lookup.class, String.class, MethodType.class, int.class); 73251662Sdim 74251662Sdim /** Call for populating an array with local variable state */ 75193326Sed private static final Call POPULATE_ARRAY = staticCall(MethodHandles.lookup(), RewriteException.class, "populateArray", Object[].class, Object[].class, int.class, Object[].class); 76193326Sed 77193326Sed /** Call for converting an array to a long array. */ 78193326Sed public static final Call TO_LONG_ARRAY = staticCallNoLookup(RewriteException.class, "toLongArray", long[].class, Object.class, RewriteException.class); 79193326Sed /** Call for converting an array to a double array. */ 80199482Srdivacky public static final Call TO_DOUBLE_ARRAY = staticCallNoLookup(RewriteException.class, "toDoubleArray", double[].class, Object.class, RewriteException.class); 81199482Srdivacky /** Call for converting an array to an object array. */ 82199482Srdivacky public static final Call TO_OBJECT_ARRAY = staticCallNoLookup(RewriteException.class, "toObjectArray", Object[].class, Object.class, RewriteException.class); 83199482Srdivacky /** Call for converting an object to null if it can't be represented as an instance of a class. */ 84199482Srdivacky public static final Call INSTANCE_OR_NULL = staticCallNoLookup(RewriteException.class, "instanceOrNull", Object.class, Object.class, Class.class); 85199482Srdivacky /** Call for asserting the length of an array. */ 86199482Srdivacky public static final Call ASSERT_ARRAY_LENGTH = staticCallNoLookup(RewriteException.class, "assertArrayLength", void.class, Object[].class, int.class); 87199482Srdivacky 88199482Srdivacky private RewriteException( 89199482Srdivacky final UnwarrantedOptimismException e, 90221345Sdim final Object[] byteCodeSlots, 91199482Srdivacky final String[] byteCodeSymbolNames, 92199482Srdivacky final int[] previousContinuationEntryPoints) { 93199482Srdivacky super("", e, false, Context.DEBUG); 94199482Srdivacky this.byteCodeSlots = byteCodeSlots; 95199482Srdivacky this.runtimeScope = mergeSlotsWithScope(byteCodeSlots, byteCodeSymbolNames); 96199482Srdivacky this.previousContinuationEntryPoints = previousContinuationEntryPoints; 97199482Srdivacky } 98199482Srdivacky 99199482Srdivacky /** 100199482Srdivacky * Constructor for a rewrite exception thrown from an optimistic function. 101199482Srdivacky * @param e the {@link UnwarrantedOptimismException} that triggered this exception. 102221345Sdim * @param byteCodeSlots contents of local variable slots at the time of rewrite at the program point 103199482Srdivacky * @param byteCodeSymbolNames the names of the variables in the {@code byteCodeSlots} parameter. The array might 104193326Sed * have less elements, and some elements might be unnamed (the name can be null). The information is provided in an 105193326Sed * effort to assist evaluation of expressions for their types by the compiler doing the deoptimizing recompilation, 106198954Srdivacky * and can thus be incomplete - the more complete it is, the more expressions can be evaluated by the compiler, and 107199482Srdivacky * the more unnecessary deoptimizing compilations can be avoided. 108199482Srdivacky * @return a new rewrite exception 109199482Srdivacky */ 110198954Srdivacky public static RewriteException create(final UnwarrantedOptimismException e, 111199482Srdivacky final Object[] byteCodeSlots, 112199482Srdivacky final String[] byteCodeSymbolNames) { 113199482Srdivacky return create(e, byteCodeSlots, byteCodeSymbolNames, null); 114199482Srdivacky } 115199482Srdivacky 116199482Srdivacky /** 117199482Srdivacky * Constructor for a rewrite exception thrown from a rest-of method. 118199482Srdivacky * @param e the {@link UnwarrantedOptimismException} that triggered this exception. 119199482Srdivacky * @param byteCodeSlots contents of local variable slots at the time of rewrite at the program point 120193326Sed * @param byteCodeSymbolNames the names of the variables in the {@code byteCodeSlots} parameter. The array might 121199482Srdivacky * have less elements, and some elements might be unnamed (the name can be null). The information is provided in an 122199482Srdivacky * effort to assist evaluation of expressions for their types by the compiler doing the deoptimizing recompilation, 123199482Srdivacky * and can thus be incomplete - the more complete it is, the more expressions can be evaluated by the compiler, and 124199482Srdivacky * the more unnecessary deoptimizing compilations can be avoided. 125199482Srdivacky * @param previousContinuationEntryPoints an array of continuation entry points that were already executed during 126199482Srdivacky * one logical invocation of the function (a rest-of triggering a rest-of triggering a...) 127199482Srdivacky * @return a new rewrite exception 128199482Srdivacky */ 129199482Srdivacky public static RewriteException create(final UnwarrantedOptimismException e, 130199482Srdivacky final Object[] byteCodeSlots, 131199482Srdivacky final String[] byteCodeSymbolNames, 132199482Srdivacky final int[] previousContinuationEntryPoints) { 133199482Srdivacky return new RewriteException(e, byteCodeSlots, byteCodeSymbolNames, previousContinuationEntryPoints); 134199482Srdivacky } 135199482Srdivacky 136199482Srdivacky /** 137199482Srdivacky * Bootstrap method for populate array 138199482Srdivacky * @param lookup lookup 139199482Srdivacky * @param name name (ignored) 140199482Srdivacky * @param type method type for signature 141199482Srdivacky * @param startIndex start index to start writing to 142199482Srdivacky * @return callsite to array populator (constant) 143199482Srdivacky */ 144199482Srdivacky public static CallSite populateArrayBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type, final int startIndex) { 145199482Srdivacky MethodHandle mh = POPULATE_ARRAY.methodHandle(); 146199482Srdivacky mh = MH.insertArguments(mh, 1, startIndex); 147199482Srdivacky mh = MH.asCollector(mh, Object[].class, type.parameterCount() - 1); 148199482Srdivacky mh = MH.asType(mh, type); 149199482Srdivacky return new ConstantCallSite(mh); 150199482Srdivacky } 151199482Srdivacky 152199482Srdivacky private static ScriptObject mergeSlotsWithScope(final Object[] byteCodeSlots, final String[] byteCodeSymbolNames) { 153199482Srdivacky final ScriptObject locals = Global.newEmptyInstance(); 154199482Srdivacky final int l = Math.min(byteCodeSlots.length, byteCodeSymbolNames.length); 155199482Srdivacky ScriptObject runtimeScope = null; 156199482Srdivacky final String scopeName = CompilerConstants.SCOPE.symbolName(); 157199482Srdivacky for(int i = 0; i < l; ++i) { 158199482Srdivacky final String name = byteCodeSymbolNames[i]; 159199482Srdivacky final Object value = byteCodeSlots[i]; 160199482Srdivacky if(scopeName.equals(name)) { 161199482Srdivacky assert runtimeScope == null; 162199482Srdivacky runtimeScope = (ScriptObject)value; 163199482Srdivacky } else if(name != null) { 164199482Srdivacky locals.set(name, value, true); 165199482Srdivacky } 166199482Srdivacky } 167199482Srdivacky locals.setProto(runtimeScope); 168199482Srdivacky return locals; 169199482Srdivacky } 170199482Srdivacky 171199482Srdivacky /** 172199482Srdivacky * Array populator used for saving the local variable state into the array contained in the 173199482Srdivacky * RewriteException 174199482Srdivacky * @param arrayToBePopluated array to be populated 175199482Srdivacky * @param startIndex start index to write to 176199482Srdivacky * @param items items with which to populate the array 177199482Srdivacky * @return the populated array - same array object 178199482Srdivacky */ 179199482Srdivacky public static Object[] populateArray(final Object[] arrayToBePopluated, final int startIndex, final Object[] items) { 180199482Srdivacky System.arraycopy(items, 0, arrayToBePopluated, startIndex, items.length); 181199482Srdivacky return arrayToBePopluated; 182199482Srdivacky } 183199482Srdivacky 184193326Sed /** 185199482Srdivacky * Continuation handler calls this method when a local variable carried over into the continuation is expected to be 186193326Sed * a long array in the continued method. Normally, it will also be a long array in the original (interrupted by 187199482Srdivacky * deoptimization) method, but it can actually be an int array that underwent widening in the new code version. 188199482Srdivacky * @param obj the object that has to be converted into a long array 189199482Srdivacky * @param e the exception being processed 190199482Srdivacky * @return a long array 191199482Srdivacky */ 192199482Srdivacky public static long[] toLongArray(final Object obj, final RewriteException e) { 193199482Srdivacky if(obj instanceof long[]) { 194198954Srdivacky return (long[])obj; 195199482Srdivacky } 196199482Srdivacky 197199482Srdivacky assert obj instanceof int[]; 198199482Srdivacky 199199482Srdivacky final int[] in = (int[])obj; 200199482Srdivacky final long[] out = new long[in.length]; 201199482Srdivacky for(int i = 0; i < in.length; ++i) { 202199482Srdivacky out[i] = in[i]; 203199482Srdivacky } 204199482Srdivacky return e.replaceByteCodeValue(in, out); 205199482Srdivacky } 206199482Srdivacky 207199482Srdivacky /** 208199482Srdivacky * Continuation handler calls this method when a local variable carried over into the continuation is expected to be 209199482Srdivacky * a double array in the continued method. Normally, it will also be a double array in the original (interrupted by 210199482Srdivacky * deoptimization) method, but it can actually be an int or long array that underwent widening in the new code version. 211199482Srdivacky * @param obj the object that has to be converted into a double array 212199482Srdivacky * @param e the exception being processed 213199482Srdivacky * @return a double array 214199482Srdivacky */ 215199482Srdivacky public static double[] toDoubleArray(final Object obj, final RewriteException e) { 216199482Srdivacky if(obj instanceof double[]) { 217199482Srdivacky return (double[])obj; 218199482Srdivacky } 219198954Srdivacky 220199482Srdivacky assert obj instanceof int[] || obj instanceof long[]; 221199482Srdivacky 222199482Srdivacky final int l = Array.getLength(obj); 223199482Srdivacky final double[] out = new double[l]; 224199482Srdivacky for(int i = 0; i < l; ++i) { 225193326Sed out[i] = Array.getDouble(obj, i); 226199482Srdivacky } 227199482Srdivacky return e.replaceByteCodeValue(obj, out); 228199482Srdivacky } 229199482Srdivacky 230199482Srdivacky /** 231199482Srdivacky * Continuation handler calls this method when a local variable carried over into the continuation is expected to be 232193326Sed * an Object array in the continued method. Normally, it will also be an Object array in the original (interrupted by 233199482Srdivacky * deoptimization) method, but it can actually be an int, long, or double array that underwent widening in the new 234199482Srdivacky * code version. 235199482Srdivacky * @param obj the object that has to be converted into an Object array 236198954Srdivacky * @param e the exception being processed 237199482Srdivacky * @return an Object array 238198954Srdivacky */ 239199482Srdivacky public static Object[] toObjectArray(final Object obj, final RewriteException e) { 240199482Srdivacky if(obj instanceof Object[]) { 241199482Srdivacky return (Object[])obj; 242198954Srdivacky } 243199482Srdivacky 244199482Srdivacky assert obj instanceof int[] || obj instanceof long[] || obj instanceof double[] : obj + " is " + obj.getClass().getName(); 245199482Srdivacky 246199482Srdivacky final int l = Array.getLength(obj); 247199482Srdivacky final Object[] out = new Object[l]; 248199482Srdivacky for(int i = 0; i < l; ++i) { 249199482Srdivacky out[i] = Array.get(obj, i); 250198954Srdivacky } 251198954Srdivacky return e.replaceByteCodeValue(obj, out); 252198954Srdivacky } 253198954Srdivacky 254198954Srdivacky /** 255193326Sed * Continuation handler calls this method when a local variable carried over into the continuation is expected to 256193326Sed * have a certain type, but the value can have a different type coming from the deoptimized method as it was a dead 257199512Srdivacky * store. If we had precise liveness analysis, we wouldn't need this. 258199512Srdivacky * @param obj the object inspected for being of a particular type 259199990Srdivacky * @param clazz the type the object must belong to 260199990Srdivacky * @return the object if it belongs to the type, or null otherwise 261199990Srdivacky */ 262207619Srdivacky public static Object instanceOrNull(final Object obj, final Class<?> clazz) { 263193326Sed return clazz.isInstance(obj) ? obj : null; 264199990Srdivacky } 265193326Sed 266207619Srdivacky /** 267193326Sed * Asserts the length of an array. Invoked from continuation handler only when running with assertions enabled. 268207619Srdivacky * The array can, in fact, have more elements than asserted, but they must all have Undefined as their value. The 269207619Srdivacky * method does not test for the array having less elements than asserted, as those would already have caused an 270207619Srdivacky * {@code ArrayIndexOutOfBoundsException} to be thrown as the continuation handler attempts to access the missing 271199990Srdivacky * elements. 272207619Srdivacky * @param arr the array 273207619Srdivacky * @param length the asserted length 274193326Sed */ 275193326Sed public static void assertArrayLength(final Object[] arr, final int length) { 276193326Sed for(int i = arr.length; i-- > length;) { 277210299Sed if(arr[i] != ScriptRuntime.UNDEFINED) { 278210299Sed throw new AssertionError(String.format("Expected array length %d, but it is %d", length, i + 1)); 279193326Sed } 280198954Srdivacky } 281198954Srdivacky } 282199482Srdivacky 283199482Srdivacky private <T> T replaceByteCodeValue(final Object in, final T out) { 284199482Srdivacky for(int i = 0; i < byteCodeSlots.length; ++i) { 285199482Srdivacky if(byteCodeSlots[i] == in) { 286199482Srdivacky byteCodeSlots[i] = out; 287199482Srdivacky } 288199482Srdivacky } 289199482Srdivacky return out; 290199482Srdivacky } 291199482Srdivacky 292199482Srdivacky private UnwarrantedOptimismException getUOE() { 293198954Srdivacky return (UnwarrantedOptimismException)getCause(); 294198954Srdivacky } 295198954Srdivacky /** 296198954Srdivacky * Get return value. This method is destructive, after it is invoked subsequent invocation of either 297199482Srdivacky * {@link #getByteCodeSlots()} or this method will return null. This method is invoked from the generated 298199482Srdivacky * continuation code as the last step before continuing the execution, and we need to make sure we don't hang on to 299199482Srdivacky * either the entry bytecode slot values or the return value and prevent them from being garbage collected. 300199482Srdivacky * @return return value 301199482Srdivacky */ 302198954Srdivacky public Object getReturnValueDestructive() { 303199482Srdivacky assert byteCodeSlots != null; 304199482Srdivacky byteCodeSlots = null; 305199482Srdivacky runtimeScope = null; 306199482Srdivacky return getUOE().getReturnValueDestructive(); 307199482Srdivacky } 308199482Srdivacky 309199482Srdivacky Object getReturnValueNonDestructive() { 310199482Srdivacky return getUOE().getReturnValueNonDestructive(); 311199482Srdivacky } 312199482Srdivacky 313199482Srdivacky /** 314199482Srdivacky * Get return type 315198954Srdivacky * @return return type 316199482Srdivacky */ 317199482Srdivacky public Type getReturnType() { 318199482Srdivacky return getUOE().getReturnType(); 319199482Srdivacky } 320199482Srdivacky 321199482Srdivacky /** 322199482Srdivacky * Get the program point. 323199482Srdivacky * @return program point. 324199482Srdivacky */ 325198954Srdivacky public int getProgramPoint() { 326199482Srdivacky return getUOE().getProgramPoint(); 327199482Srdivacky } 328199482Srdivacky 329199482Srdivacky /** 330199482Srdivacky * Get the bytecode slot contents. 331199482Srdivacky * @return bytecode slot contents. 332199482Srdivacky */ 333199482Srdivacky public Object[] getByteCodeSlots() { 334199482Srdivacky return byteCodeSlots == null ? null : byteCodeSlots.clone(); 335199482Srdivacky } 336199482Srdivacky 337199482Srdivacky /** 338199482Srdivacky * @return an array of continuation entry points that were already executed during one logical invocation of the 339199482Srdivacky * function (a rest-of triggering a rest-of triggering a...) 340199482Srdivacky */ 341199482Srdivacky public int[] getPreviousContinuationEntryPoints() { 342199482Srdivacky return previousContinuationEntryPoints == null ? null : previousContinuationEntryPoints.clone(); 343199482Srdivacky } 344199482Srdivacky 345199482Srdivacky /** 346199482Srdivacky * Returns the runtime scope that was in effect when the exception was thrown. 347199482Srdivacky * @return the runtime scope. 348199482Srdivacky */ 349199482Srdivacky public ScriptObject getRuntimeScope() { 350199482Srdivacky return runtimeScope; 351199482Srdivacky } 352199482Srdivacky 353199482Srdivacky private static String stringify(final Object returnValue) { 354199482Srdivacky if (returnValue == null) { 355199482Srdivacky return "null"; 356199482Srdivacky } 357199482Srdivacky String str = returnValue.toString(); 358199482Srdivacky if (returnValue instanceof String) { 359199482Srdivacky str = '\'' + str + '\''; 360199482Srdivacky } else if (returnValue instanceof Double) { 361199482Srdivacky str = str + 'd'; 362199482Srdivacky } else if (returnValue instanceof Long) { 363199482Srdivacky str = str + 'l'; 364199482Srdivacky } 365199482Srdivacky return str; 366199482Srdivacky } 367199482Srdivacky 368199482Srdivacky @Override 369199482Srdivacky public String getMessage() { 370199482Srdivacky return getMessage(false); 371199482Srdivacky } 372199482Srdivacky 373199482Srdivacky /** 374199482Srdivacky * Short toString function for message 375199482Srdivacky * @return short message 376199482Srdivacky */ 377199482Srdivacky public String getMessageShort() { 378199482Srdivacky return getMessage(true); 379199482Srdivacky } 380199482Srdivacky 381199482Srdivacky private String getMessage(final boolean isShort) { 382199482Srdivacky final StringBuilder sb = new StringBuilder(); 383199482Srdivacky 384199482Srdivacky //program point 385199482Srdivacky sb.append("[pp="). 386199482Srdivacky append(getProgramPoint()). 387199482Srdivacky append(", "); 388199482Srdivacky 389199482Srdivacky //slot contents 390199482Srdivacky if (!isShort) { 391199482Srdivacky final Object[] slots = byteCodeSlots; 392199482Srdivacky if (slots != null) { 393199482Srdivacky sb.append("slots="). 394199482Srdivacky append(Arrays.asList(slots)). 395199482Srdivacky append(", "); 396199482Srdivacky } 397199482Srdivacky } 398199482Srdivacky 399199482Srdivacky //return type 400199482Srdivacky sb.append("type="). 401199482Srdivacky append(getReturnType()). 402199482Srdivacky append(", "); 403199482Srdivacky 404199482Srdivacky //return value 405199482Srdivacky sb.append("value="). 406199482Srdivacky append(stringify(getReturnValueNonDestructive())). 407199482Srdivacky append(")]"); 408199482Srdivacky 409199482Srdivacky return sb.toString(); 410199482Srdivacky } 411199482Srdivacky 412199482Srdivacky private void writeObject(final ObjectOutputStream out) throws NotSerializableException { 413199482Srdivacky throw new NotSerializableException(getClass().getName()); 414199482Srdivacky } 415199482Srdivacky 416199482Srdivacky private void readObject(final ObjectInputStream in) throws NotSerializableException { 417199482Srdivacky throw new NotSerializableException(getClass().getName()); 418199482Srdivacky } 419199482Srdivacky} 420199482Srdivacky