src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java

Print this page




  86     /**
  87      * It is usually a good gamble whever we detect a runtime callsite with a double
  88      * (or java.lang.Number instance) to specialize the parameter to an integer, if the
  89      * parameter in question can be represented as one. The double typically only exists
  90      * because the compiler doesn't know any better than "a number type" and conservatively
  91      * picks doubles when it can't prove that an integer addition wouldn't overflow
  92      */
  93     private static final MethodHandle ENSURE_INT = findOwnMH("ensureInt", int.class, Object.class);
  94 
  95     /**
  96      * Constructor - public as scripts use it
  97      *
  98      * @param functionNode       functionNode that represents this function code
  99      * @param installer          installer for code regeneration versions of this function
 100      * @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor
 101      * @param allocatorMap       allocator map to seed instances with, when constructing
 102      */
 103     public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, final String allocatorClassName, final PropertyMap allocatorMap) {
 104         super(functionName(functionNode),
 105               functionNode.getParameters().size(),
 106               functionNode.isStrict(),
 107               false,
 108               true);
 109 
 110         this.functionNode       = functionNode;
 111         this.source             = functionNode.getSource();
 112         this.token              = tokenFor(functionNode);
 113         this.installer          = installer;
 114         this.allocatorClassName = allocatorClassName;
 115         this.allocatorMap       = allocatorMap;
 116     }
 117 
 118     @Override
 119     String toSource() {
 120         if (source != null && token != 0) {
 121             return source.getString(Token.descPosition(token), Token.descLength(token));
 122         }
 123 
 124         return "function " + (name == null ? "" : name) + "() { [native code] }";
 125     }
 126 
 127     @Override
 128     public String toString() {
 129         final StringBuilder sb = new StringBuilder();
 130 
 131         if (source != null) {
 132             sb.append(source.getName())
 133                 .append(':')
 134                 .append(functionNode.getLineNumber())
 135                 .append(' ');

 136         }
 137 
 138         return sb.toString() + super.toString();
 139     }
 140 
 141     private static String functionName(final FunctionNode fn) {
 142         if (fn.isAnonymous()) {
 143             return "";
 144         } else {
 145             final FunctionNode.Kind kind = fn.getKind();
 146             if (kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) {
 147                 final String name = NameCodec.decode(fn.getIdent().getName());
 148                 return name.substring(4); // 4 is "get " or "set "
 149             } else {
 150                 return fn.getIdent().getName();
 151             }
 152         }
 153     }
 154 
 155     private static long tokenFor(final FunctionNode fn) {
 156         final int  position   = Token.descPosition(fn.getFirstToken());
 157         final int  length     = Token.descPosition(fn.getLastToken()) - position + Token.descLength(fn.getLastToken());
 158 
 159         return Token.toDesc(TokenType.FUNCTION, position, length);
 160     }
 161 














 162     @Override
 163     ScriptObject allocate(final PropertyMap map) {
 164         try {
 165             ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try
 166             return allocator == null ? null : (ScriptObject)allocator.invokeExact(map);
 167         } catch (final RuntimeException | Error e) {
 168             throw e;
 169         } catch (final Throwable t) {
 170             throw new RuntimeException(t);
 171         }
 172     }
 173 
 174     private void ensureHasAllocator() throws ClassNotFoundException {
 175         if (allocator == null && allocatorClassName != null) {
 176             this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
 177         }
 178     }
 179 
 180     @Override
 181     PropertyMap getAllocatorMap() {
 182         return allocatorMap;
 183     }
 184 
 185     @Override
 186     protected synchronized void ensureCodeGenerated() {
 187          if (!code.isEmpty()) {
 188              return; // nothing to do, we have code, at least some.
 189          }
 190 
 191          if (functionNode.isLazy()) {


 192              Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
 193              final Compiler compiler = new Compiler(installer);
 194              functionNode = compiler.compile(functionNode);
 195              assert !functionNode.isLazy();
 196              compiler.install(functionNode);



 197 
 198              /*
 199               * We don't need to update any flags - varArgs and needsCallee are instrincic
 200               * in the function world we need to get a destination node from the compile instead
 201               * and replace it with our function node. TODO
 202               */
 203          }
 204 


 205          /*
 206           * We can't get to this program point unless we have bytecode, either from
 207           * eager compilation or from running a lazy compile on the lines above
 208           */
 209 
 210          assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
 211 
 212          // code exists - look it up and add it into the automatically sorted invoker list
 213          addCode(functionNode);
 214 
 215          if (! functionNode.canSpecialize()) {
 216              // allow GC to claim IR stuff that is not needed anymore
 217              functionNode = null;
 218              installer = null;
 219          }
 220     }
 221 
 222     private MethodHandle addCode(final FunctionNode fn) {
 223         return addCode(fn, null, null, null);
 224     }




  86     /**
  87      * It is usually a good gamble whever we detect a runtime callsite with a double
  88      * (or java.lang.Number instance) to specialize the parameter to an integer, if the
  89      * parameter in question can be represented as one. The double typically only exists
  90      * because the compiler doesn't know any better than "a number type" and conservatively
  91      * picks doubles when it can't prove that an integer addition wouldn't overflow
  92      */
  93     private static final MethodHandle ENSURE_INT = findOwnMH("ensureInt", int.class, Object.class);
  94 
  95     /**
  96      * Constructor - public as scripts use it
  97      *
  98      * @param functionNode       functionNode that represents this function code
  99      * @param installer          installer for code regeneration versions of this function
 100      * @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor
 101      * @param allocatorMap       allocator map to seed instances with, when constructing
 102      */
 103     public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, final String allocatorClassName, final PropertyMap allocatorMap) {
 104         super(functionName(functionNode),
 105               functionNode.getParameters().size(),
 106               getFlags(functionNode));


 107 
 108         this.functionNode       = functionNode;
 109         this.source             = functionNode.getSource();
 110         this.token              = tokenFor(functionNode);
 111         this.installer          = installer;
 112         this.allocatorClassName = allocatorClassName;
 113         this.allocatorMap       = allocatorMap;
 114     }
 115 
 116     @Override
 117     String toSource() {
 118         if (source != null && token != 0) {
 119             return source.getString(Token.descPosition(token), Token.descLength(token));
 120         }
 121 
 122         return "function " + (name == null ? "" : name) + "() { [native code] }";
 123     }
 124 
 125     @Override
 126     public String toString() {
 127         final StringBuilder sb = new StringBuilder();
 128 
 129         if (source != null) {
 130             sb.append(source.getName());
 131             if (functionNode != null) {
 132                 sb.append(':').append(functionNode.getLineNumber());
 133             }
 134             sb.append(' ');
 135         }
 136 
 137         return sb.toString() + super.toString();
 138     }
 139 
 140     private static String functionName(final FunctionNode fn) {
 141         if (fn.isAnonymous()) {
 142             return "";
 143         } else {
 144             final FunctionNode.Kind kind = fn.getKind();
 145             if (kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) {
 146                 final String name = NameCodec.decode(fn.getIdent().getName());
 147                 return name.substring(4); // 4 is "get " or "set "
 148             } else {
 149                 return fn.getIdent().getName();
 150             }
 151         }
 152     }
 153 
 154     private static long tokenFor(final FunctionNode fn) {
 155         final int  position   = Token.descPosition(fn.getFirstToken());
 156         final int  length     = Token.descPosition(fn.getLastToken()) - position + Token.descLength(fn.getLastToken());
 157 
 158         return Token.toDesc(TokenType.FUNCTION, position, length);
 159     }
 160 
 161     private static int getFlags(final FunctionNode functionNode) {
 162         int flags = IS_CONSTRUCTOR;
 163         if (functionNode.isStrict()) {
 164             flags |= IS_STRICT;
 165         }
 166         if (functionNode.needsCallee()) {
 167             flags |= NEEDS_CALLEE;
 168         }
 169         if (functionNode.usesThis() || functionNode.hasEval()) {
 170             flags |= USES_THIS;
 171         }
 172         return flags;
 173     }
 174 
 175     @Override
 176     ScriptObject allocate(final PropertyMap map) {
 177         try {
 178             ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try
 179             return allocator == null ? null : (ScriptObject)allocator.invokeExact(map);
 180         } catch (final RuntimeException | Error e) {
 181             throw e;
 182         } catch (final Throwable t) {
 183             throw new RuntimeException(t);
 184         }
 185     }
 186 
 187     private void ensureHasAllocator() throws ClassNotFoundException {
 188         if (allocator == null && allocatorClassName != null) {
 189             this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
 190         }
 191     }
 192 
 193     @Override
 194     PropertyMap getAllocatorMap() {
 195         return allocatorMap;
 196     }
 197 





 198 
 199     @Override
 200     protected void ensureCompiled() {
 201         if (functionNode != null && functionNode.isLazy()) {
 202             Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
 203             final Compiler compiler = new Compiler(installer);
 204             functionNode = compiler.compile(functionNode);
 205             assert !functionNode.isLazy();
 206             compiler.install(functionNode);
 207             flags = getFlags(functionNode);
 208         }
 209     }
 210 
 211     @Override
 212     protected synchronized void ensureCodeGenerated() {
 213         if (!code.isEmpty()) {
 214             return; // nothing to do, we have code, at least some.

 215         }
 216 
 217         ensureCompiled();
 218 
 219         /*
 220          * We can't get to this program point unless we have bytecode, either from
 221          * eager compilation or from running a lazy compile on the lines above
 222          */
 223 
 224         assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
 225 
 226         // code exists - look it up and add it into the automatically sorted invoker list
 227         addCode(functionNode);
 228 
 229         if (! functionNode.canSpecialize()) {
 230             // allow GC to claim IR stuff that is not needed anymore
 231             functionNode = null;
 232             installer = null;
 233         }
 234     }
 235 
 236     private MethodHandle addCode(final FunctionNode fn) {
 237         return addCode(fn, null, null, null);
 238     }