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 } |