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.runtime;
  27 
  28 import java.lang.invoke.MethodHandle;
  29 import java.lang.invoke.MethodType;
  30 import java.util.Collection;
  31 import java.util.List;
  32 
  33 /**
  34  * This is a subclass that represents a script function that may not be regenerated.
  35  * This is used for example for bound functions and builtins.
  36  */
  37 final class FinalScriptFunctionData extends ScriptFunctionData {
  38 
  39     // documentation key for this function, may be null
  40     private String docKey;
  41 
  42     private static final long serialVersionUID = -930632846167768864L;
  43 
  44     /**
  45      * Constructor - used for bind
  46      *
  47      * @param name      name
  48      * @param arity     arity
  49      * @param functions precompiled code
  50      * @param flags     {@link ScriptFunctionData} flags
  51      */
  52     FinalScriptFunctionData(final String name, final int arity, final List<CompiledFunction> functions, final int flags) {
  53         super(name, arity, flags);
  54         code.addAll(functions);
  55         assert !needsCallee();
  56     }
  57 
  58     /**
  59      * Constructor - used from ScriptFunction. This assumes that we have code already for the
  60      * method (typically a native method) and possibly specializations.
  61      *
  62      * @param name  name
  63      * @param mh    method handle for generic version of method
  64      * @param specs specializations
  65      * @param flags {@link ScriptFunctionData} flags
  66      */
  67     FinalScriptFunctionData(final String name, final MethodHandle mh, final Specialization[] specs, final int flags) {
  68         super(name, methodHandleArity(mh), flags);
  69 
  70         addInvoker(mh);
  71         if (specs != null) {
  72             for (final Specialization spec : specs) {
  73                 addInvoker(spec.getMethodHandle(), spec);
  74             }
  75         }
  76     }
  77 
  78     @Override
  79     String getDocumentationKey() {
  80         return docKey;
  81     }
  82 
  83     @Override
  84     void setDocumentationKey(final String docKey) {
  85         this.docKey = docKey;
  86     }
  87 
  88     @Override
  89     String getDocumentation() {
  90         final String doc = docKey != null?
  91             FunctionDocumentation.getDoc(docKey) : null;
  92         return doc != null? doc : super.getDocumentation();
  93     }
  94 
  95     @Override
  96     protected boolean needsCallee() {
  97         final boolean needsCallee = code.getFirst().needsCallee();
  98         assert allNeedCallee(needsCallee);
  99         return needsCallee;
 100     }
 101 
 102     private boolean allNeedCallee(final boolean needCallee) {
 103         for (final CompiledFunction inv : code) {
 104             if(inv.needsCallee() != needCallee) {
 105                 return false;
 106             }
 107         }
 108         return true;
 109     }
 110 
 111     @Override
 112     CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden, final boolean linkLogicOkay) {
 113         assert isValidCallSite(callSiteType) : callSiteType;
 114 
 115         CompiledFunction best = null;
 116         for (final CompiledFunction candidate: code) {
 117             if (!linkLogicOkay && candidate.hasLinkLogic()) {
 118                 // Skip! Version with no link logic is desired, but this one has link logic!
 119                 continue;
 120             }
 121 
 122             if (!forbidden.contains(candidate) && candidate.betterThanFinal(best, callSiteType)) {
 123                 best = candidate;
 124             }
 125         }
 126 
 127         return best;
 128     }
 129 
 130     @Override
 131     MethodType getGenericType() {
 132         // We need to ask the code for its generic type. We can't just rely on this function data's arity, as it's not
 133         // actually correct for lots of built-ins. E.g. ECMAScript 5.1 section 15.5.3.2 prescribes that
 134         // Script.fromCharCode([char0[, char1[, ...]]]) has a declared arity of 1 even though it's a variable arity
 135         // method.
 136         int max = 0;
 137         for(final CompiledFunction fn: code) {
 138             final MethodType t = fn.type();
 139             if(ScriptFunctionData.isVarArg(t)) {
 140                 // 2 for (callee, this, args[])
 141                 return MethodType.genericMethodType(2, true);
 142             }
 143             final int paramCount = t.parameterCount() - (ScriptFunctionData.needsCallee(t) ? 1 : 0);
 144             if(paramCount > max) {
 145                 max = paramCount;
 146             }
 147         }
 148         // +1 for callee
 149         return MethodType.genericMethodType(max + 1);
 150     }
 151 
 152     private CompiledFunction addInvoker(final MethodHandle mh, final Specialization specialization) {
 153         assert !needsCallee(mh);
 154 
 155         final CompiledFunction invoker;
 156         if (isConstructor(mh)) {
 157             // only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor
 158             // is too conservative a check. However, isConstructor(mh) always implies isConstructor param
 159             assert isConstructor();
 160             invoker = CompiledFunction.createBuiltInConstructor(mh);
 161         } else {
 162             invoker = new CompiledFunction(mh, null, specialization);
 163         }
 164         code.add(invoker);
 165 
 166         return invoker;
 167     }
 168 
 169     private CompiledFunction addInvoker(final MethodHandle mh) {
 170         return addInvoker(mh, null);
 171     }
 172 
 173     private static int methodHandleArity(final MethodHandle mh) {
 174         if (isVarArg(mh)) {
 175             return MAX_ARITY;
 176         }
 177 
 178         //drop self, callee and boolean constructor flag to get real arity
 179         return mh.type().parameterCount() - 1 - (needsCallee(mh) ? 1 : 0) - (isConstructor(mh) ? 1 : 0);
 180     }
 181 
 182     private static boolean isConstructor(final MethodHandle mh) {
 183         return mh.type().parameterCount() >= 1 && mh.type().parameterType(0) == boolean.class;
 184     }
 185 
 186 }