/** * Class for representing a runtime event, giving less global dependencies than logger. Every {@link * NativeDebug} object keeps a queue of RuntimeEvents that can be explored through the debug API. * * @param <T> class of the value this event wraps */ public class RuntimeEvent<T> { /** Queue size for the runtime event buffer */ public static final int RUNTIME_EVENT_QUEUE_SIZE = Options.getIntProperty("nashorn.runtime.event.queue.size", 1024); private final Level level; private final T value; /** * Constructor * * @param level log level for runtime event to create * @param object object to wrap */ public RuntimeEvent(final Level level, final T object) { this.level = level; this.value = object; } /** * Return the value wrapped in this runtime event * * @return value */ public final T getValue() { return value; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append('[') .append(level) .append("] ") .append(value == null ? "null" : getValueClass().getSimpleName()) .append(" value=") .append(value); return sb.toString(); } /** * Descriptor for this runtime event, must be overridden and implemented, e.g. "RewriteException" * * @return event name */ public final Class<?> getValueClass() { return value.getClass(); } }
static { final DynamicLinkerFactory factory = new DynamicLinkerFactory(); final NashornBeansLinker nashornBeansLinker = new NashornBeansLinker(); factory.setPrioritizedLinkers( new NashornLinker(), new NashornPrimitiveLinker(), new NashornStaticClassLinker(), new BoundCallableLinker(), new JavaSuperAdapterLinker(), new JSObjectLinker(nashornBeansLinker), new BrowserJSObjectLinker(nashornBeansLinker), new ReflectionCheckLinker()); factory.setFallbackLinkers(nashornBeansLinker, new NashornBottomLinker()); factory.setSyncOnRelink(true); factory.setPrelinkFilter( new GuardedInvocationFilter() { @Override public GuardedInvocation filter( final GuardedInvocation inv, final LinkRequest request, final LinkerServices linkerServices) { final CallSiteDescriptor desc = request.getCallSiteDescriptor(); return OptimisticReturnFilters.filterOptimisticReturnValue(inv, desc) .asType(linkerServices, desc.getMethodType()); } }); factory.setAutoConversionStrategy( new MethodTypeConversionStrategy() { @Override public MethodHandle asType(final MethodHandle target, final MethodType newType) { return unboxReturnType(target, newType); } }); factory.setInternalObjectsFilter(NashornBeansLinker.createHiddenObjectFilter()); final int relinkThreshold = Options.getIntProperty( "nashorn.unstable.relink.threshold", NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD); if (relinkThreshold > -1) { factory.setUnstableRelinkThreshold(relinkThreshold); } // Linkers for any additional language runtimes deployed alongside Nashorn will be picked up by // the factory. factory.setClassLoader(Bootstrap.class.getClassLoader()); dynamicLinker = factory.createLinker(); }
/** Split the IR into smaller compile units. */ final class Splitter extends NodeVisitor<LexicalContext> { /** Current compiler. */ private final Compiler compiler; /** IR to be broken down. */ private final FunctionNode outermost; /** Compile unit for the main script. */ private final CompileUnit outermostCompileUnit; /** Cache for calculated block weights. */ private final Map<Node, Long> weightCache = new HashMap<>(); /** Weight threshold for when to start a split. */ public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024); private final DebugLogger log; /** * Constructor. * * @param compiler the compiler * @param functionNode function node to split * @param outermostCompileUnit compile unit for outermost function, if non-lazy this is the * script's compile unit */ public Splitter( final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) { super(new LexicalContext()); this.compiler = compiler; this.outermost = functionNode; this.outermostCompileUnit = outermostCompileUnit; this.log = compiler.getLogger(); } /** * Execute the split. * * @param fn the function to split * @param top whether this is the topmost compiled function (it's either a program, or we're doing * a recompilation). */ FunctionNode split(final FunctionNode fn, final boolean top) { FunctionNode functionNode = fn; log.finest("Initiating split of '", functionNode.getName(), "'"); long weight = WeighNodes.weigh(functionNode); // We know that our LexicalContext is empty outside the call to functionNode.accept(this) below, // so we can pass null to all methods expecting a LexicalContext parameter. assert lc.isEmpty() : "LexicalContext not empty"; if (weight >= SPLIT_THRESHOLD) { log.finest( "Splitting '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD); functionNode = (FunctionNode) functionNode.accept(this); if (functionNode.isSplit()) { // Weight has changed so weigh again, this time using block weight cache weight = WeighNodes.weigh(functionNode, weightCache); functionNode = functionNode.setBody(null, functionNode.getBody().setNeedsScope(null)); } if (weight >= SPLIT_THRESHOLD) { functionNode = functionNode.setBody(null, splitBlock(functionNode.getBody(), functionNode)); functionNode = functionNode.setFlag(null, FunctionNode.IS_SPLIT); weight = WeighNodes.weigh(functionNode.getBody(), weightCache); } } assert functionNode.getCompileUnit() == null : "compile unit already set for " + functionNode.getName(); if (top) { assert outermostCompileUnit != null : "outermost compile unit is null"; functionNode = functionNode.setCompileUnit(null, outermostCompileUnit); outermostCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT); } else { functionNode = functionNode.setCompileUnit(null, findUnit(weight)); } final Block body = functionNode.getBody(); final List<FunctionNode> dc = directChildren(functionNode); final Block newBody = (Block) body.accept( new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override public boolean enterFunctionNode(final FunctionNode nestedFunction) { return dc.contains(nestedFunction); } @Override public Node leaveFunctionNode(final FunctionNode nestedFunction) { final FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit) .split(nestedFunction, false); lc.replace(nestedFunction, split); return split; } }); functionNode = functionNode.setBody(null, newBody); assert functionNode.getCompileUnit() != null; return functionNode.setState(null, CompilationState.SPLIT); } private static List<FunctionNode> directChildren(final FunctionNode functionNode) { final List<FunctionNode> dc = new ArrayList<>(); functionNode.accept( new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override public boolean enterFunctionNode(final FunctionNode child) { if (child == functionNode) { return true; } if (lc.getParentFunction(child) == functionNode) { dc.add(child); } return false; } }); return dc; } /** * Override this logic to look up compile units in a different way * * @param weight weight needed * @return compile unit */ protected CompileUnit findUnit(final long weight) { return compiler.findUnit(weight); } /** * Split a block into sub methods. * * @param block Block or function to split. * @return new weight for the resulting block. */ private Block splitBlock(final Block block, final FunctionNode function) { final List<Statement> splits = new ArrayList<>(); List<Statement> statements = new ArrayList<>(); long statementsWeight = 0; for (final Statement statement : block.getStatements()) { final long weight = WeighNodes.weigh(statement, weightCache); if (statementsWeight + weight >= SPLIT_THRESHOLD || statement.isTerminal()) { if (!statements.isEmpty()) { splits.add(createBlockSplitNode(block, function, statements, statementsWeight)); statements = new ArrayList<>(); statementsWeight = 0; } } if (statement.isTerminal()) { splits.add(statement); } else { statements.add(statement); statementsWeight += weight; } } if (!statements.isEmpty()) { splits.add(createBlockSplitNode(block, function, statements, statementsWeight)); } return block.setStatements(lc, splits); } /** * Create a new split node from statements contained in a parent block. * * @param parent Parent block. * @param statements Statements to include. * @return New split node. */ private SplitNode createBlockSplitNode( final Block parent, final FunctionNode function, final List<Statement> statements, final long weight) { final long token = parent.getToken(); final int finish = parent.getFinish(); final String name = function.uniqueName(SPLIT_PREFIX.symbolName()); final Block newBlock = new Block(token, finish, statements); return new SplitNode(name, newBlock, compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT)); } @Override public boolean enterBlock(final Block block) { if (block.isCatchBlock()) { return false; } final long weight = WeighNodes.weigh(block, weightCache); if (weight < SPLIT_THRESHOLD) { weightCache.put(block, weight); return false; } return true; } @Override public Node leaveBlock(final Block block) { assert !block.isCatchBlock(); Block newBlock = block; // Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have // been split already, so weigh again before splitting. long weight = WeighNodes.weigh(block, weightCache); if (weight >= SPLIT_THRESHOLD) { final FunctionNode currentFunction = lc.getCurrentFunction(); newBlock = splitBlock(block, currentFunction); weight = WeighNodes.weigh(newBlock, weightCache); lc.setFlag(currentFunction, FunctionNode.IS_SPLIT); } weightCache.put(newBlock, weight); return newBlock; } @SuppressWarnings("rawtypes") @Override public Node leaveLiteralNode(final LiteralNode literal) { long weight = WeighNodes.weigh(literal); if (weight < SPLIT_THRESHOLD) { return literal; } final FunctionNode functionNode = lc.getCurrentFunction(); lc.setFlag(functionNode, FunctionNode.IS_SPLIT); if (literal instanceof ArrayLiteralNode) { final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal; final Node[] value = arrayLiteralNode.getValue(); final int[] postsets = arrayLiteralNode.getPostsets(); final List<ArrayUnit> units = new ArrayList<>(); long totalWeight = 0; int lo = 0; for (int i = 0; i < postsets.length; i++) { final int postset = postsets[i]; final Node element = value[postset]; weight = WeighNodes.weigh(element); totalWeight += WeighNodes.AASTORE_WEIGHT + weight; if (totalWeight >= SPLIT_THRESHOLD) { final CompileUnit unit = compiler.findUnit(totalWeight - weight); units.add(new ArrayUnit(unit, lo, i)); lo = i; totalWeight = weight; } } if (lo != postsets.length) { final CompileUnit unit = compiler.findUnit(totalWeight); units.add(new ArrayUnit(unit, lo, postsets.length)); } return arrayLiteralNode.setUnits(lc, units); } return literal; } @Override public boolean enterFunctionNode(final FunctionNode node) { // only go into the function node for this splitter. any subfunctions are rejected return node == outermost; } }