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 */ 25 26 package jdk.nashorn.internal.codegen; 27 28 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 29 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC; 30 31 import java.util.Arrays; 32 import java.util.EnumSet; 33 import jdk.nashorn.internal.codegen.types.Type; 34 import jdk.nashorn.internal.ir.Symbol; 35 import jdk.nashorn.internal.runtime.ScriptObject; 36 import jdk.nashorn.internal.runtime.UnwarrantedOptimismException; 37 import jdk.nashorn.internal.runtime.options.Options; 38 39 /** 40 * A scope call or get operation that can be shared by several call sites. This generates a static 41 * method that wraps the invokedynamic instructions to get or call scope variables. 42 * The reason for this is to reduce memory footprint and initial linking overhead of huge scripts. 43 * 44 * <p>Static methods generated by this class expect three parameters in addition to the parameters of the 45 * function call: The current scope object, the depth of the target scope relative to the scope argument, 46 * and the program point in case the target operation is optimistic.</p> 47 * 48 * <p>Optimistic operations are called with program point <code>0</code>. If an <code>UnwarrentedOptimismException</code> 49 * is thrown, it is caught by the shared call method and rethrown with the program point of the invoking call site.</p> 50 * 51 * <p>Shared scope calls are not used if the scope contains a <code>with</code> statement or a call to 52 * <code>eval</code>.</p> 53 */ 54 class SharedScopeCall { 55 56 /** 57 * Threshold for using shared scope function calls. 58 */ 59 public static final int SHARED_CALL_THRESHOLD = 60 Options.getIntProperty("nashorn.shared.scope.call.threshold", 5); 61 /** 62 * Threshold for using shared scope variable getter. This is higher than for calls as lower values 63 * degrade performance on many scripts. 64 */ 65 public static final int SHARED_GET_THRESHOLD = 66 Options.getIntProperty("nashorn.shared.scope.get.threshold", 100); 67 68 private static final CompilerConstants.Call REPLACE_PROGRAM_POINT = virtualCallNoLookup( 69 UnwarrantedOptimismException.class, "replaceProgramPoint", 70 UnwarrantedOptimismException.class, int.class); 71 72 /** Number of fixed parameters */ 73 private static final int FIXED_PARAM_COUNT = 3; 74 75 private final Type valueType; 76 private final Symbol symbol; 77 private final Type returnType; 78 private final Type[] paramTypes; 79 private final int flags; 80 private final boolean isCall; 81 private final boolean isOptimistic; 82 private CompileUnit compileUnit; 83 private String methodName; 84 private String staticSignature; 85 86 /** 87 * Constructor. 88 * 89 * @param symbol the symbol 90 * @param valueType the type of the value 91 * @param returnType the return type 92 * @param paramTypes the function parameter types 93 * @param flags the callsite flags 94 * @param isOptimistic whether target call is optimistic and we need to handle UnwarrentedOptimismException 95 */ 96 SharedScopeCall(final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, 97 final int flags, final boolean isOptimistic) { 98 this.symbol = symbol; 99 this.valueType = valueType; 100 this.returnType = returnType; 101 this.paramTypes = paramTypes; 102 this.flags = flags; 103 this.isCall = paramTypes != null; // If paramTypes is not null this is a call, otherwise it's just a get. 104 this.isOptimistic = isOptimistic; 105 } 106 107 @Override 108 public int hashCode() { 109 return symbol.hashCode() ^ returnType.hashCode() ^ Arrays.hashCode(paramTypes) ^ flags ^ Boolean.hashCode(isOptimistic); 110 } 111 112 @Override 113 public boolean equals(final Object obj) { 114 if (obj instanceof SharedScopeCall) { 115 final SharedScopeCall c = (SharedScopeCall) obj; 116 return symbol.equals(c.symbol) 117 && flags == c.flags 118 && returnType.equals(c.returnType) 119 && Arrays.equals(paramTypes, c.paramTypes) 120 && isOptimistic == c.isOptimistic; 121 } 122 return false; 123 } 124 125 /** 126 * Set the compile unit and method name. 127 * @param compileUnit the compile unit 128 * @param methodName the method name 129 */ 130 protected void setClassAndName(final CompileUnit compileUnit, final String methodName) { 131 this.compileUnit = compileUnit; 132 this.methodName = methodName; 133 } 134 135 /** 136 * Generate the invoke instruction for this shared scope call. 137 * @param method the method emitter 138 */ 139 public void generateInvoke(final MethodEmitter method) { 140 method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature()); 141 } 142 143 /** 144 * Generate the method that implements the scope get or call. 145 */ 146 protected void generateScopeCall() { 147 final ClassEmitter classEmitter = compileUnit.getClassEmitter(); 148 final EnumSet<ClassEmitter.Flag> methodFlags = EnumSet.of(ClassEmitter.Flag.STATIC); 149 150 // This method expects two fixed parameters in addition to any parameters that may be 151 // passed on to the function: A ScriptObject representing the caller's current scope object, 152 // and an int specifying the distance to the target scope containing the symbol we want to 153 // access, or -1 if this is not known at compile time (e.g. because of a "with" or "eval"). 154 155 final MethodEmitter method = classEmitter.method(methodFlags, methodName, getStaticSignature()); 156 method.begin(); 157 158 // Load correct scope by calling getProto(int) on the scope argument with the supplied depth argument 159 method.load(Type.OBJECT, 0); 160 method.load(Type.INT, 1); 161 method.invoke(ScriptObject.GET_PROTO_DEPTH); 162 163 assert !isCall || valueType.isObject(); // Callables are always loaded as object 164 165 // Labels for catch of UnsupportedOptimismException 166 final Label beginTry; 167 final Label endTry; 168 final Label catchLabel; 169 170 if(isOptimistic) { 171 beginTry = new Label("begin_try"); 172 endTry = new Label("end_try"); 173 catchLabel = new Label("catch_label"); 174 method.label(beginTry); 175 method._try(beginTry, endTry, catchLabel, UnwarrantedOptimismException.class, false); 176 } else { 177 beginTry = endTry = catchLabel = null; 178 } 179 180 // If this is an optimistic get we set the optimistic flag but don't set the program point, 181 // which implies a program point of 0. If optimism fails we'll replace it with the actual 182 // program point which caller supplied as third argument. 183 final int getFlags = isOptimistic && !isCall ? flags | CALLSITE_OPTIMISTIC : flags; 184 method.dynamicGet(valueType, symbol.getName(), getFlags, isCall, false); 185 186 // If this is a get we're done, otherwise call the value as function. 187 if (isCall) { 188 method.convert(Type.OBJECT); 189 // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly. 190 method.loadUndefined(Type.OBJECT); 191 int slot = FIXED_PARAM_COUNT; 192 for (final Type type : paramTypes) { 193 method.load(type, slot); 194 slot += type.getSlots(); 195 } 196 197 // Same as above, set optimistic flag but leave program point as 0. 198 final int callFlags = isOptimistic ? flags | CALLSITE_OPTIMISTIC : flags; 199 200 method.dynamicCall(returnType, 2 + paramTypes.length, callFlags, symbol.getName()); 201 202 } 203 204 if (isOptimistic) { 205 method.label(endTry); 206 } 207 208 method._return(returnType); 209 210 if (isOptimistic) { 211 // We caught a UnwarrantedOptimismException, replace 0 program point with actual program point 212 method._catch(catchLabel); 213 method.load(Type.INT, 2); 214 method.invoke(REPLACE_PROGRAM_POINT); 215 method.athrow(); 216 } 217 218 method.end(); 219 } 220 221 private String getStaticSignature() { 222 if (staticSignature == null) { 223 if (paramTypes == null) { 224 staticSignature = Type.getMethodDescriptor(returnType, Type.typeFor(ScriptObject.class), Type.INT, Type.INT); 225 } else { 226 final Type[] params = new Type[paramTypes.length + FIXED_PARAM_COUNT]; 227 params[0] = Type.typeFor(ScriptObject.class); 228 params[1] = Type.INT; 229 params[2] = Type.INT; 230 System.arraycopy(paramTypes, 0, params, FIXED_PARAM_COUNT, paramTypes.length); 231 staticSignature = Type.getMethodDescriptor(returnType, params); 232 } 233 } 234 return staticSignature; 235 } 236 237 @Override 238 public String toString() { 239 return methodName + " " + staticSignature; 240 } 241 242 }