1 /*
2 * Copyright (c) 2010, 2014, 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 static jdk.nashorn.internal.lookup.Lookup.MH;
29
30 import java.io.IOException;
31 import java.io.Serializable;
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import java.util.Collections;
36 import java.util.HashSet;
37 import java.util.Map;
38 import java.util.Set;
39 import jdk.internal.dynalink.support.NameCodec;
40 import jdk.nashorn.internal.codegen.CompileUnit;
41 import jdk.nashorn.internal.codegen.Compiler;
42 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
43 import jdk.nashorn.internal.codegen.CompilerConstants;
44 import jdk.nashorn.internal.codegen.FunctionSignature;
45 import jdk.nashorn.internal.codegen.TypeMap;
46 import jdk.nashorn.internal.codegen.types.Type;
47 import jdk.nashorn.internal.ir.FunctionNode;
48 import jdk.nashorn.internal.ir.LexicalContext;
49 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
50 import jdk.nashorn.internal.objects.Global;
51 import jdk.nashorn.internal.parser.Parser;
52 import jdk.nashorn.internal.parser.Token;
53 import jdk.nashorn.internal.parser.TokenType;
54 import jdk.nashorn.internal.runtime.logging.DebugLogger;
55 import jdk.nashorn.internal.runtime.logging.Loggable;
56 import jdk.nashorn.internal.runtime.logging.Logger;
57 import jdk.nashorn.internal.runtime.options.Options;
58 import jdk.nashorn.internal.scripts.JS;
59
60 /**
61 * This is a subclass that represents a script function that may be regenerated,
62 * for example with specialization based on call site types, or lazily generated.
63 * The common denominator is that it can get new invokers during its lifespan,
64 * unlike {@code FinalScriptFunctionData}
65 */
66 @Logger(name="recompile")
67 public final class RecompilableScriptFunctionData extends ScriptFunctionData implements Loggable {
68 /** Is lazy compilation enabled? TODO: this should be the default */
69 public static final boolean LAZY_COMPILATION = Options.getBooleanProperty("nashorn.lazy");
70
71 /** Prefix used for all recompiled script classes */
72 public static final String RECOMPILATION_PREFIX = "Recompilation$";
73
74 /** Unique function node id for this function node */
75 private final int functionNodeId;
76
77 private final String functionName;
78
79 // TODO: try to eliminate the need for this somehow, either by allowing Source to change its name, allowing a
80 // function to internally replace its Source with one of a different name, or storing this additional field in the
81 // Source object.
82 private final String sourceURL;
83
84 /** The line number where this function begins. */
85 private final int lineNumber;
86
87 /** Source from which FunctionNode was parsed. */
88 private transient Source source;
89
90 /** Allows us to retrieve the method handle for this function once the code is compiled */
91 private MethodLocator methodLocator;
92
93 /** Token of this function within the source. */
94 private final long token;
95
96 /** Allocator map from makeMap() */
97 private final PropertyMap allocatorMap;
98
99 /** Code installer used for all further recompilation/specialization of this ScriptFunction */
100 private transient CodeInstaller<ScriptEnvironment> installer;
101
102 /** Name of class where allocator function resides */
103 private final String allocatorClassName;
104
105 /** lazily generated allocator */
106 private transient MethodHandle allocator;
107
108 private final Map<Integer, RecompilableScriptFunctionData> nestedFunctions;
109
110 /** Id to parent function if one exists */
111 private RecompilableScriptFunctionData parent;
112
113 private final boolean isDeclared;
114 private final boolean isAnonymous;
115 private final boolean needsCallee;
116
117 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
118
119 private transient DebugLogger log;
120
121 private final Map<String, Integer> externalScopeDepths;
122
123 private final Set<String> internalSymbols;
124
125 private static final int GET_SET_PREFIX_LENGTH = "*et ".length();
126
127 private static final long serialVersionUID = 4914839316174633726L;
128
129 /**
130 * Constructor - public as scripts use it
131 *
132 * @param functionNode functionNode that represents this function code
133 * @param installer installer for code regeneration versions of this function
134 * @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor
135 * @param allocatorMap allocator map to seed instances with, when constructing
136 * @param nestedFunctions nested function map
137 * @param sourceURL source URL
138 * @param externalScopeDepths external scope depths
139 * @param internalSymbols internal symbols to method, defined in its scope
140 */
141 public RecompilableScriptFunctionData(
142 final FunctionNode functionNode,
143 final CodeInstaller<ScriptEnvironment> installer,
144 final String allocatorClassName,
145 final PropertyMap allocatorMap,
146 final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
147 final String sourceURL,
148 final Map<String, Integer> externalScopeDepths,
149 final Set<String> internalSymbols) {
150
151 super(functionName(functionNode),
152 Math.min(functionNode.getParameters().size(), MAX_ARITY),
153 getFlags(functionNode));
154
155 this.functionName = functionNode.getName();
156 this.lineNumber = functionNode.getLineNumber();
157 this.isDeclared = functionNode.isDeclared();
158 this.needsCallee = functionNode.needsCallee();
159 this.isAnonymous = functionNode.isAnonymous();
160 this.functionNodeId = functionNode.getId();
161 this.source = functionNode.getSource();
162 this.token = tokenFor(functionNode);
163 this.installer = installer;
164 this.sourceURL = sourceURL;
165 this.allocatorClassName = allocatorClassName;
166 this.allocatorMap = allocatorMap;
167 this.nestedFunctions = nestedFunctions;
168 this.externalScopeDepths = externalScopeDepths;
169 this.internalSymbols = new HashSet<>(internalSymbols);
170
171 for (final RecompilableScriptFunctionData nfn : nestedFunctions.values()) {
172 assert nfn.getParent() == null;
173 nfn.setParent(this);
174 }
175
176 createLogger();
177 }
178
179 @Override
180 public DebugLogger getLogger() {
181 return log;
182 }
183
184 @Override
185 public DebugLogger initLogger(final Context ctxt) {
186 return ctxt.getLogger(this.getClass());
187 }
188
189 /**
190 * Check if a symbol is internally defined in a function. For example
191 * if "undefined" is internally defined in the outermost program function,
192 * it has not been reassigned or overridden and can be optimized
193 *
194 * @param symbolName symbol name
195 * @return true if symbol is internal to this ScriptFunction
196 */
197
198 public boolean hasInternalSymbol(final String symbolName) {
199 return internalSymbols.contains(symbolName);
200 }
201
202 /**
203 * Return the external symbol table
204 * @param symbolName symbol name
205 * @return the external symbol table with proto depths
206 */
207 public int getExternalSymbolDepth(final String symbolName) {
208 final Map<String, Integer> map = externalScopeDepths;
209 if (map == null) {
210 return -1;
211 }
212 final Integer depth = map.get(symbolName);
213 if (depth == null) {
214 return -1;
215 }
216 return depth;
217 }
218
219 /**
220 * Get the parent of this RecompilableScriptFunctionData. If we are
221 * a nested function, we have a parent. Note that "null" return value
222 * can also mean that we have a parent but it is unknown, so this can
223 * only be used for conservative assumptions.
224 * @return parent data, or null if non exists and also null IF UNKNOWN.
225 */
226 public RecompilableScriptFunctionData getParent() {
227 return parent;
228 }
229
230 void setParent(final RecompilableScriptFunctionData parent) {
231 this.parent = parent;
232 }
233
234 @Override
235 String toSource() {
236 if (source != null && token != 0) {
237 return source.getString(Token.descPosition(token), Token.descLength(token));
238 }
239
240 return "function " + (name == null ? "" : name) + "() { [native code] }";
241 }
242
243 public void setCodeAndSource(final Map<String, Class<?>> code, final Source source) {
244 this.source = source;
245 if (methodLocator != null) {
246 methodLocator.setClass(code.get(methodLocator.getClassName()));
247 }
248 }
249
250 @Override
251 public String toString() {
252 return super.toString() + '@' + functionNodeId;
253 }
254
255 @Override
256 public String toStringVerbose() {
257 final StringBuilder sb = new StringBuilder();
258
259 sb.append("fnId=").append(functionNodeId).append(' ');
260
261 if (source != null) {
262 sb.append(source.getName())
263 .append(':')
264 .append(lineNumber)
265 .append(' ');
266 }
267
268 return sb.toString() + super.toString();
269 }
270
271 @Override
272 public String getFunctionName() {
273 return functionName;
274 }
275
276 @Override
277 public boolean inDynamicContext() {
278 return (flags & IN_DYNAMIC_CONTEXT) != 0;
279 }
280
281 private static String functionName(final FunctionNode fn) {
282 if (fn.isAnonymous()) {
283 return "";
284 }
285 final FunctionNode.Kind kind = fn.getKind();
286 if (kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) {
287 final String name = NameCodec.decode(fn.getIdent().getName());
288 return name.substring(GET_SET_PREFIX_LENGTH);
289 }
290 return fn.getIdent().getName();
291 }
292
293 private static long tokenFor(final FunctionNode fn) {
294 final int position = Token.descPosition(fn.getFirstToken());
295 final long lastToken = Token.withDelimiter(fn.getLastToken());
296 // EOL uses length field to store the line number
297 final int length = Token.descPosition(lastToken) - position + (Token.descType(lastToken) == TokenType.EOL ? 0 : Token.descLength(lastToken));
298
299 return Token.toDesc(TokenType.FUNCTION, position, length);
300 }
301
302 private static int getFlags(final FunctionNode functionNode) {
303 int flags = IS_CONSTRUCTOR;
304 if (functionNode.isStrict()) {
305 flags |= IS_STRICT;
306 }
307 if (functionNode.needsCallee()) {
308 flags |= NEEDS_CALLEE;
309 }
310 if (functionNode.usesThis() || functionNode.hasEval()) {
311 flags |= USES_THIS;
312 }
313 if (functionNode.isVarArg()) {
314 flags |= IS_VARIABLE_ARITY;
315 }
316 if (functionNode.inDynamicContext()) {
317 flags |= IN_DYNAMIC_CONTEXT;
318 }
319 return flags;
320 }
321
322 @Override
323 PropertyMap getAllocatorMap() {
324 return allocatorMap;
325 }
326
327 @Override
328 ScriptObject allocate(final PropertyMap map) {
329 try {
330 ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try
331 return allocator == null ? null : (ScriptObject)allocator.invokeExact(map);
332 } catch (final RuntimeException | Error e) {
333 throw e;
334 } catch (final Throwable t) {
335 throw new RuntimeException(t);
336 }
337 }
338
339 private void ensureHasAllocator() throws ClassNotFoundException {
340 if (allocator == null && allocatorClassName != null) {
341 this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
342 }
343 }
344
345 FunctionNode reparse() {
346 final boolean isProgram = functionNodeId == FunctionNode.FIRST_FUNCTION_ID;
347 // NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node
348 final int descPosition = Token.descPosition(token);
349 final Context context = Context.getContextTrusted();
350 final Parser parser = new Parser(
351 context.getEnv(),
352 source,
353 new Context.ThrowErrorManager(),
354 isStrict(),
355 functionNodeId - (isProgram ? 0 : 1),
356 lineNumber - 1,
357 context.getLogger(Parser.class)); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive
358
359 if (isAnonymous) {
360 parser.setFunctionName(functionName);
361 }
362
363 final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, Token.descLength(token), true);
364 // Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a
365 // single function, extract it from the program.
366 return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName).setSourceURL(null, sourceURL);
367 }
368
369 TypeMap typeMap(final MethodType fnCallSiteType) {
370 if (fnCallSiteType == null) {
371 return null;
372 }
373
374 if (CompiledFunction.isVarArgsType(fnCallSiteType)) {
375 return null;
376 }
377
378 return new TypeMap(functionNodeId, explicitParams(fnCallSiteType), needsCallee());
379 }
380
381 private static ScriptObject newLocals(final ScriptObject runtimeScope) {
382 final ScriptObject locals = Global.newEmptyInstance();
383 locals.setProto(runtimeScope);
384 return locals;
385 }
386
387 private Compiler getCompiler(final FunctionNode fn, final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
388 return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null);
389 }
390
391 Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType, final ScriptObject runtimeScope, final Map<Integer, Type> ipp, final int[] cep) {
392 final Context context = Context.getContextTrusted();
393 return new Compiler(
394 context,
395 context.getEnv(),
396 installer,
397 functionNode.getSource(), // source
398 functionNode.getSourceURL(),
399 isStrict() | functionNode.isStrict(), // is strict
400 true, // is on demand
401 this, // compiledFunction, i.e. this RecompilableScriptFunctionData
402 typeMap(actualCallSiteType), // type map
403 ipp, // invalidated program points
404 cep, // continuation entry points
405 runtimeScope); // runtime scope
406 }
407
408 private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
409 // We're creating an empty script object for holding local variables. AssignSymbols will populate it with
410 // explicit Undefined values for undefined local variables (see AssignSymbols#defineSymbol() and
411 // CompilationEnvironment#declareLocalSymbol()).
412
413 if (log.isEnabled()) {
414 log.info("Type specialization of '", functionName, "' signature: ", actualCallSiteType);
415 }
416
417 final FunctionNode fn = reparse();
418 return getCompiler(fn, actualCallSiteType, runtimeScope).compile(fn, CompilationPhases.COMPILE_ALL);
419 }
420
421 private MethodType explicitParams(final MethodType callSiteType) {
422 if (CompiledFunction.isVarArgsType(callSiteType)) {
423 return null;
424 }
425
426 final MethodType noCalleeThisType = callSiteType.dropParameterTypes(0, 2); // (callee, this) is always in call site type
427 final int callSiteParamCount = noCalleeThisType.parameterCount();
428
429 // Widen parameters of reference types to Object as we currently don't care for specialization among reference
430 // types. E.g. call site saying (ScriptFunction, Object, String) should still link to (ScriptFunction, Object, Object)
431 final Class<?>[] paramTypes = noCalleeThisType.parameterArray();
432 boolean changed = false;
433 for (int i = 0; i < paramTypes.length; ++i) {
434 final Class<?> paramType = paramTypes[i];
435 if (!(paramType.isPrimitive() || paramType == Object.class)) {
436 paramTypes[i] = Object.class;
437 changed = true;
438 }
439 }
440 final MethodType generalized = changed ? MethodType.methodType(noCalleeThisType.returnType(), paramTypes) : noCalleeThisType;
441
442 if (callSiteParamCount < getArity()) {
443 return generalized.appendParameterTypes(Collections.<Class<?>>nCopies(getArity() - callSiteParamCount, Object.class));
444 }
445 return generalized;
446 }
447
448 private FunctionNode extractFunctionFromScript(final FunctionNode script) {
449 final Set<FunctionNode> fns = new HashSet<>();
450 script.getBody().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
451 @Override
452 public boolean enterFunctionNode(final FunctionNode fn) {
453 fns.add(fn);
454 return false;
455 }
456 });
457 assert fns.size() == 1 : "got back more than one method in recompilation";
458 final FunctionNode f = fns.iterator().next();
459 assert f.getId() == functionNodeId;
460 if (!isDeclared && f.isDeclared()) {
461 return f.clearFlag(null, FunctionNode.IS_DECLARED);
462 }
463 return f;
464 }
465
466 MethodHandle lookup(final FunctionNode fn) {
467 final MethodType type = new FunctionSignature(fn).getMethodType();
468 log.info("Looking up ", DebugLogger.quote(fn.getName()), " type=", type);
469 return lookupWithExplicitType(fn, new FunctionSignature(fn).getMethodType());
470 }
471
472 MethodHandle lookupWithExplicitType(final FunctionNode fn, final MethodType targetType) {
473 return lookupCodeMethod(fn.getCompileUnit(), targetType);
474 }
475
476 private MethodHandle lookupCodeMethod(final CompileUnit compileUnit, final MethodType targetType) {
477 return MH.findStatic(LOOKUP, compileUnit.getCode(), functionName, targetType);
478 }
479
480 /**
481 * Initializes this function data with the eagerly generated version of the code. This method can only be invoked
482 * by the compiler internals in Nashorn and is public for implementation reasons only. Attempting to invoke it
483 * externally will result in an exception.
484 * @param functionNode the functionNode belonging to this data
485 */
486 public void initializeCode(final FunctionNode functionNode) {
487 // Since the method is public, we double-check that we aren't invoked with an inappropriate compile unit.
488 if(!(code.isEmpty() && functionNode.getCompileUnit().isInitializing(this, functionNode))) {
489 throw new IllegalStateException(functionNode.getName() + " id=" + functionNode.getId());
490 }
491 addCode(functionNode);
492 methodLocator = new MethodLocator(functionNode);
493 }
494
495 private CompiledFunction addCode(final MethodHandle target, final int fnFlags) {
496 final CompiledFunction cfn = new CompiledFunction(target, this, fnFlags);
497 code.add(cfn);
498 return cfn;
499 }
500
501 private CompiledFunction addCode(final FunctionNode fn) {
502 return addCode(lookup(fn), fn.getFlags());
503 }
504
505 /**
506 * Add code with specific call site type. It will adapt the type of the looked up method handle to fit the call site
507 * type. This is necessary because even if we request a specialization that takes an "int" parameter, we might end
508 * up getting one that takes a "double" etc. because of internal function logic causes widening (e.g. assignment of
509 * a wider value to the parameter variable). However, we use the method handle type for matching subsequent lookups
510 * for the same specialization, so we must adapt the handle to the expected type.
511 * @param fn the function
512 * @param callSiteType the call site type
513 * @return the compiled function object, with its type matching that of the call site type.
514 */
515 private CompiledFunction addCode(final FunctionNode fn, final MethodType callSiteType) {
516 if (fn.isVarArg()) {
517 return addCode(fn);
518 }
519
520 final MethodHandle handle = lookup(fn);
521 final MethodType fromType = handle.type();
522 MethodType toType = needsCallee(fromType) ? callSiteType.changeParameterType(0, ScriptFunction.class) : callSiteType.dropParameterTypes(0, 1);
523 toType = toType.changeReturnType(fromType.returnType());
524
525 final int toCount = toType.parameterCount();
526 final int fromCount = fromType.parameterCount();
527 final int minCount = Math.min(fromCount, toCount);
528 for(int i = 0; i < minCount; ++i) {
529 final Class<?> fromParam = fromType.parameterType(i);
530 final Class<?> toParam = toType.parameterType(i);
531 // If method has an Object parameter, but call site had String, preserve it as Object. No need to narrow it
532 // artificially. Note that this is related to how CompiledFunction.matchesCallSite() works, specifically
533 // the fact that various reference types compare to equal (see "fnType.isEquivalentTo(csType)" there).
534 if (fromParam != toParam && !fromParam.isPrimitive() && !toParam.isPrimitive()) {
535 assert fromParam.isAssignableFrom(toParam);
536 toType = toType.changeParameterType(i, fromParam);
537 }
538 }
539 if (fromCount > toCount) {
540 toType = toType.appendParameterTypes(fromType.parameterList().subList(toCount, fromCount));
541 } else if (fromCount < toCount) {
542 toType = toType.dropParameterTypes(fromCount, toCount);
543 }
544
545 return addCode(lookup(fn).asType(toType), fn.getFlags());
546 }
547
548
549 @Override
550 CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
551 synchronized (code) {
552 CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
553 if (existingBest == null) {
554 if(code.isEmpty() && methodLocator != null) {
555 // This is a deserialized object, reconnect from method handle
556 existingBest = addCode(methodLocator.getMethodHandle(), methodLocator.getFunctionFlags());
557 } else {
558 existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
559 }
560 }
561
562 assert existingBest != null;
563 //we are calling a vararg method with real args
564 boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
565
566 //if the best one is an apply to call, it has to match the callsite exactly
567 //or we need to regenerate
568 if (existingBest.isApplyToCall()) {
569 final CompiledFunction best = code.lookupExactApplyToCall(callSiteType);
570 if (best != null) {
571 return best;
572 }
573 applyToCall = true;
574 }
575
576 if (applyToCall) {
577 final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope);
578 if (fn.hasOptimisticApplyToCall()) { //did the specialization work
579 existingBest = addCode(fn, callSiteType);
580 }
581 }
582
583 return existingBest;
584 }
585 }
586
587 @Override
588 boolean isRecompilable() {
589 return true;
590 }
591
592 @Override
593 public boolean needsCallee() {
594 return needsCallee;
595 }
596
597 @Override
598 MethodType getGenericType() {
599 // 2 is for (callee, this)
600 if (isVariableArity()) {
601 return MethodType.genericMethodType(2, true);
602 }
603 return MethodType.genericMethodType(2 + getArity());
604 }
605
606 /**
607 * Return a script function data based on a function id, either this function if
608 * the id matches or a nested function based on functionId. This goes down into
609 * nested functions until all leaves are exhausted.
610 *
611 * @param functionId function id
612 * @return script function data or null if invalid id
613 */
614 public RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
615 if (functionId == functionNodeId) {
616 return this;
617 }
618 RecompilableScriptFunctionData data;
619
620 data = nestedFunctions == null ? null : nestedFunctions.get(functionId);
621 if (data != null) {
622 return data;
623 }
624 for (final RecompilableScriptFunctionData ndata : nestedFunctions.values()) {
625 data = ndata.getScriptFunctionData(functionId);
626 if (data != null) {
627 return data;
628 }
629 }
630 return null;
631 }
632
633 /**
634 * Get the uppermost parent, the program, for this data
635 * @return program
636 */
637 public RecompilableScriptFunctionData getProgram() {
638 RecompilableScriptFunctionData program = this;
639 while (true) {
640 final RecompilableScriptFunctionData p = program.getParent();
641 if (p == null) {
642 return program;
643 }
644 program = p;
645 }
646 }
647
648 /**
649 * Check whether a certain name is a global symbol, i.e. only exists as defined
650 * in outermost scope and not shadowed by being parameter or assignment in inner
651 * scopes
652 *
653 * @param functionNode function node to check
654 * @param symbolName symbol name
655 * @return true if global symbol
656 */
657 public boolean isGlobalSymbol(final FunctionNode functionNode, final String symbolName) {
658 RecompilableScriptFunctionData data = getScriptFunctionData(functionNode.getId());
659 assert data != null;
660
661 do {
662 if (data.hasInternalSymbol(symbolName)) {
663 return false;
664 }
665 data = data.getParent();
666 } while(data != null);
667
668 return true;
669 }
670
671 /**
672 * Helper class that allows us to retrieve the method handle for this function once it has been generated.
673 */
674 private static class MethodLocator implements Serializable {
675 private transient Class<?> clazz;
676 private final String className;
677 private final String methodName;
678 private final MethodType methodType;
679 private final int functionFlags;
680
681 private static final long serialVersionUID = -5420835725902966692L;
682
683 MethodLocator(final FunctionNode functionNode) {
684 this.className = functionNode.getCompileUnit().getUnitClassName();
685 this.methodName = functionNode.getName();
686 this.methodType = new FunctionSignature(functionNode).getMethodType();
687 this.functionFlags = functionNode.getFlags();
688
689 assert className != null;
690 assert methodName != null;
691 }
692
693 void setClass(final Class<?> clazz) {
694 if (!JS.class.isAssignableFrom(clazz)) {
695 throw new IllegalArgumentException();
696 }
697 this.clazz = clazz;
698 }
699
700 String getClassName() {
701 return className;
702 }
703
704 MethodHandle getMethodHandle() {
705 return MH.findStatic(LOOKUP, clazz, methodName, methodType);
706 }
707
708 int getFunctionFlags() {
709 return functionFlags;
710 }
711 }
712
713 private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
714 in.defaultReadObject();
715 createLogger();
716 }
717
718 private void createLogger() {
719 log = initLogger(Context.getContextTrusted());
720 }
721 }
--- EOF ---