SharedScopeCall.java revision 1141:7c1cff3cae2e
1145256Sjkoshy/* 2145256Sjkoshy * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3145256Sjkoshy * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4145256Sjkoshy * 5145256Sjkoshy * This code is free software; you can redistribute it and/or modify it 6145256Sjkoshy * under the terms of the GNU General Public License version 2 only, as 7145256Sjkoshy * published by the Free Software Foundation. Oracle designates this 8145256Sjkoshy * particular file as subject to the "Classpath" exception as provided 9145256Sjkoshy * by Oracle in the LICENSE file that accompanied this code. 10145256Sjkoshy * 11145256Sjkoshy * This code is distributed in the hope that it will be useful, but WITHOUT 12145256Sjkoshy * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13145256Sjkoshy * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14145256Sjkoshy * version 2 for more details (a copy is included in the LICENSE file that 15145256Sjkoshy * accompanied this code). 16145256Sjkoshy * 17145256Sjkoshy * You should have received a copy of the GNU General Public License version 18145256Sjkoshy * 2 along with this work; if not, write to the Free Software Foundation, 19145256Sjkoshy * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20145256Sjkoshy * 21145256Sjkoshy * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22145256Sjkoshy * or visit www.oracle.com if you need additional information or have any 23145256Sjkoshy * questions. 24145256Sjkoshy */ 25145256Sjkoshy 26145256Sjkoshypackage jdk.nashorn.internal.codegen; 27145256Sjkoshy 28145256Sjkoshyimport static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC; 29145256Sjkoshy 30145256Sjkoshyimport java.util.Arrays; 31145256Sjkoshyimport java.util.EnumSet; 32145256Sjkoshyimport jdk.nashorn.internal.codegen.types.Type; 33145338Smarcelimport jdk.nashorn.internal.ir.Symbol; 34145256Sjkoshyimport jdk.nashorn.internal.runtime.ScriptObject; 35145256Sjkoshy 36145256Sjkoshy/** 37145256Sjkoshy * A scope call or get operation that can be shared by several callsites. This generates a static 38147191Sjkoshy * method that wraps the invokedynamic instructions to get or call scope variables. 39145256Sjkoshy * The rationale for this is that initial linking of invokedynamic callsites is expensive, 40147191Sjkoshy * so by sharing them we can reduce startup overhead and allow very large scripts to run that otherwise wouldn't. 41145256Sjkoshy * 42145256Sjkoshy * <p>Static methods generated by this class expect two parameters in addition to the parameters of the 43145256Sjkoshy * function call: The current scope object and the depth of the target scope relative to the scope argument 44145256Sjkoshy * for when this is known at compile-time (fast-scope access).</p> 45145256Sjkoshy * 46145256Sjkoshy * <p>The second argument may be -1 for non-fast-scope symbols, in which case the scope chain is checked 47145256Sjkoshy * for each call. This may cause callsite invalidation when the shared method is used from different 48145256Sjkoshy * scopes, but such sharing of non-fast scope calls may still be necessary for very large scripts.</p> 49145256Sjkoshy * 50145256Sjkoshy * <p>Scope calls must not be shared between normal callsites and callsites contained in a <tt>with</tt> 51145256Sjkoshy * statement as this condition is not handled by current guards and will cause a runtime error.</p> 52145256Sjkoshy */ 53145256Sjkoshyclass SharedScopeCall { 54145256Sjkoshy 55145256Sjkoshy /** Threshold for using shared scope calls with fast scope access. */ 56145256Sjkoshy public static final int FAST_SCOPE_CALL_THRESHOLD = 4; 57145256Sjkoshy /** Threshold for using shared scope calls with slow scope access. */ 58145256Sjkoshy public static final int SLOW_SCOPE_CALL_THRESHOLD = 500; 59145256Sjkoshy /** Threshold for using shared scope gets with fast scope access. */ 60145256Sjkoshy public static final int FAST_SCOPE_GET_THRESHOLD = 200; 61145256Sjkoshy 62145256Sjkoshy final Type valueType; 63145256Sjkoshy final Symbol symbol; 64145256Sjkoshy final Type returnType; 65145256Sjkoshy final Type[] paramTypes; 66145256Sjkoshy final int flags; 67145256Sjkoshy final boolean isCall; 68145256Sjkoshy private CompileUnit compileUnit; 69145256Sjkoshy private String methodName; 70145256Sjkoshy private String staticSignature; 71145256Sjkoshy 72145256Sjkoshy /** 73145256Sjkoshy * Constructor. 74145256Sjkoshy * 75145256Sjkoshy * @param symbol the symbol 76145256Sjkoshy * @param valueType the type of the value 77145256Sjkoshy * @param returnType the return type 78145256Sjkoshy * @param paramTypes the function parameter types 79145256Sjkoshy * @param flags the callsite flags 80145256Sjkoshy */ 81145256Sjkoshy SharedScopeCall(final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) { 82145256Sjkoshy this.symbol = symbol; 83145256Sjkoshy this.valueType = valueType; 84145256Sjkoshy this.returnType = returnType; 85145256Sjkoshy this.paramTypes = paramTypes; 86145256Sjkoshy assert (flags & CALLSITE_OPTIMISTIC) == 0; 87145256Sjkoshy this.flags = flags; 88145256Sjkoshy // If paramTypes is not null this is a call, otherwise it's just a get. 89145256Sjkoshy this.isCall = paramTypes != null; 90145256Sjkoshy } 91145256Sjkoshy 92145256Sjkoshy @Override 93145256Sjkoshy public int hashCode() { 94145256Sjkoshy return symbol.hashCode() ^ returnType.hashCode() ^ Arrays.hashCode(paramTypes) ^ flags; 95145256Sjkoshy } 96145256Sjkoshy 97145256Sjkoshy @Override 98145256Sjkoshy public boolean equals(final Object obj) { 99145256Sjkoshy if (obj instanceof SharedScopeCall) { 100145774Sjkoshy final SharedScopeCall c = (SharedScopeCall) obj; 101145774Sjkoshy return symbol.equals(c.symbol) 102151205Sjkoshy && flags == c.flags 103151205Sjkoshy && returnType.equals(c.returnType) 104151205Sjkoshy && Arrays.equals(paramTypes, c.paramTypes); 105145256Sjkoshy } 106151205Sjkoshy return false; 107151205Sjkoshy } 108151205Sjkoshy 109145774Sjkoshy /** 110145774Sjkoshy * Set the compile unit and method name. 111151205Sjkoshy * @param compileUnit the compile unit 112151205Sjkoshy * @param methodName the method name 113151205Sjkoshy */ 114151205Sjkoshy protected void setClassAndName(final CompileUnit compileUnit, final String methodName) { 115151205Sjkoshy this.compileUnit = compileUnit; 116151205Sjkoshy this.methodName = methodName; 117151205Sjkoshy } 118151205Sjkoshy 119151205Sjkoshy /** 120151205Sjkoshy * Generate the invoke instruction for this shared scope call. 121151205Sjkoshy * @param method the method emitter 122151205Sjkoshy * @return the method emitter 123151205Sjkoshy */ 124145774Sjkoshy public MethodEmitter generateInvoke(final MethodEmitter method) { 125145774Sjkoshy return method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature()); 126145774Sjkoshy } 127145774Sjkoshy 128145774Sjkoshy /** 129145774Sjkoshy * Generate the method that implements the scope get or call. 130145774Sjkoshy */ 131145774Sjkoshy protected void generateScopeCall() { 132145774Sjkoshy final ClassEmitter classEmitter = compileUnit.getClassEmitter(); 133145774Sjkoshy final EnumSet<ClassEmitter.Flag> methodFlags = EnumSet.of(ClassEmitter.Flag.STATIC); 134145774Sjkoshy 135145774Sjkoshy // This method expects two fixed parameters in addition to any parameters that may be 136145774Sjkoshy // passed on to the function: A ScriptObject representing the caller's current scope object, 137145774Sjkoshy // and an int specifying the distance to the target scope containing the symbol we want to 138145774Sjkoshy // access, or -1 if this is not known at compile time (e.g. because of a "with" or "eval"). 139147191Sjkoshy 140147191Sjkoshy final MethodEmitter method = classEmitter.method(methodFlags, methodName, getStaticSignature()); 141147191Sjkoshy method.begin(); 142147191Sjkoshy 143147191Sjkoshy // Load correct scope by calling getProto() on the scope argument as often as specified 144145774Sjkoshy // by the second argument. 145145774Sjkoshy final Label parentLoopStart = new Label("parent_loop_start"); 146145774Sjkoshy final Label parentLoopDone = new Label("parent_loop_done"); 147145774Sjkoshy method.load(Type.OBJECT, 0); 148145774Sjkoshy method.label(parentLoopStart); 149145774Sjkoshy method.load(Type.INT, 1); 150145774Sjkoshy method.iinc(1, -1); 151151205Sjkoshy method.ifle(parentLoopDone); 152151205Sjkoshy method.invoke(ScriptObject.GET_PROTO); 153151205Sjkoshy method._goto(parentLoopStart); 154151205Sjkoshy method.label(parentLoopDone); 155151205Sjkoshy 156145256Sjkoshy assert !isCall || valueType.isObject(); // Callables are always objects 157145256Sjkoshy // If flags are optimistic, but we're doing a call, remove optimistic flags from the getter, as they obviously 158145256Sjkoshy // only apply to the call. 159145256Sjkoshy method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall, false); 160145256Sjkoshy 161145256Sjkoshy // If this is a get we're done, otherwise call the value as function. 162145256Sjkoshy if (isCall) { 163145256Sjkoshy method.convert(Type.OBJECT); 164145256Sjkoshy // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly. 165145256Sjkoshy method.loadUndefined(Type.OBJECT); 166145256Sjkoshy int slot = 2; 167145256Sjkoshy for (final Type type : paramTypes) { 168145256Sjkoshy method.load(type, slot); 169145256Sjkoshy slot += type.getSlots(); 170145256Sjkoshy } 171145256Sjkoshy // Shared scope calls disabled in optimistic world. TODO is this right? 172145256Sjkoshy method.dynamicCall(returnType, 2 + paramTypes.length, flags); 173145256Sjkoshy } 174145256Sjkoshy 175145256Sjkoshy method._return(returnType); 176145256Sjkoshy method.end(); 177145256Sjkoshy } 178145256Sjkoshy 179145256Sjkoshy private String getStaticSignature() { 180145256Sjkoshy if (staticSignature == null) { 181145256Sjkoshy if (paramTypes == null) { 182145256Sjkoshy staticSignature = Type.getMethodDescriptor(returnType, Type.typeFor(ScriptObject.class), Type.INT); 183145256Sjkoshy } else { 184145256Sjkoshy final Type[] params = new Type[paramTypes.length + 2]; 185145256Sjkoshy params[0] = Type.typeFor(ScriptObject.class); 186145256Sjkoshy params[1] = Type.INT; 187145256Sjkoshy System.arraycopy(paramTypes, 0, params, 2, paramTypes.length); 188145256Sjkoshy staticSignature = Type.getMethodDescriptor(returnType, params); 189145256Sjkoshy } 190145256Sjkoshy } 191145256Sjkoshy return staticSignature; 192145256Sjkoshy } 193145256Sjkoshy 194145256Sjkoshy @Override 195145256Sjkoshy public String toString() { 196145256Sjkoshy return methodName + " " + staticSignature; 197145256Sjkoshy } 198145256Sjkoshy 199145256Sjkoshy} 200145256Sjkoshy