Java源码示例:jdk.nashorn.internal.ir.FunctionNode
示例1
/**
* This has to run before fix assignment types, store any type specializations for
* parameters, then turn them into objects for the generic version of this method.
*
* @param functionNode functionNode
*/
private FunctionNode finalizeParameters(final FunctionNode functionNode) {
final List<IdentNode> newParams = new ArrayList<>();
final boolean isVarArg = functionNode.isVarArg();
final Block body = functionNode.getBody();
for (final IdentNode param : functionNode.getParameters()) {
final Symbol paramSymbol = body.getExistingSymbol(param.getName());
assert paramSymbol != null;
assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
newParams.add(param.setSymbol(paramSymbol));
// parameters should not be slots for a function that uses variable arity signature
if (isVarArg) {
paramSymbol.setNeedsSlot(false);
}
}
return functionNode.setParameters(lc, newParams);
}
示例2
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
return false;
}
// If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
// this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
// need for the callee.
if (!functionNode.needsCallee()) {
functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
}
// Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope and none of
// its blocks create a scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope
// earlier than this phase.
if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
}
return true;
}
示例3
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
final FunctionNode finalizedFunction;
if (isUnparsedFunction(functionNode)) {
finalizedFunction = functionNode;
} else {
finalizedFunction =
markProgramBlock(
removeUnusedSlots(
createSyntheticInitializers(
finalizeParameters(
lc.applyTopFlags(functionNode))))
.setThisProperties(lc, thisProperties.pop().size()));
}
return finalizedFunction;
}
示例4
/**
* Constructor - public as scripts use it
*
* @param functionNode functionNode that represents this function code
* @param installer installer for code regeneration versions of this function
* @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor
* @param allocatorMap allocator map to seed instances with, when constructing
*/
public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, final String allocatorClassName, final PropertyMap allocatorMap) {
super(functionNode.isAnonymous() ?
"" :
functionNode.getIdent().getName(),
functionNode.getParameters().size(),
functionNode.isStrict(),
false,
true);
this.functionNode = functionNode;
this.source = functionNode.getSource();
this.token = tokenFor(functionNode);
this.installer = installer;
this.allocatorClassName = allocatorClassName;
this.allocatorMap = allocatorMap;
}
示例5
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
return false;
}
// If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
// this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
// need for the callee.
if (!functionNode.needsCallee()) {
functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
}
// Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope and none of
// its blocks create a scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope
// earlier than this phase.
if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
}
return true;
}
示例6
/**
* When we eliminate dead code, we must preserve var declarations as they are scoped to the whole
* function. This method gathers var nodes from code passed to it, removing their initializers.
*
* @param deadCodeRoot the root node of eliminated dead code
* @param statements a list that will be receiving the var nodes from the dead code, with their
* initializers removed.
*/
static void extractVarNodesFromDeadCode(final Node deadCodeRoot, final List<Statement> statements) {
deadCodeRoot.accept(new SimpleNodeVisitor() {
@Override
public boolean enterVarNode(final VarNode varNode) {
statements.add(varNode.setInit(null));
return false;
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
// Don't descend into nested functions
return false;
}
});
}
示例7
/**
* Sets the AST to cache in this function
* @param astToCache the new AST to cache
*/
public void setCachedAst(final FunctionNode astToCache) {
assert astToCache.getId() == functionNodeId; // same function
assert !(cachedAst instanceof SerializedAst); // Can't overwrite serialized AST
final boolean isSplit = astToCache.isSplit();
// If we're caching a split function, we're doing it in the eager pass, hence there can be no other
// cached representation already. In other words, isSplit implies cachedAst == null.
assert !isSplit || cachedAst == null; //
final FunctionNode symbolClonedAst = cloneSymbols(astToCache);
final Reference<FunctionNode> ref = new SoftReference<>(symbolClonedAst);
cachedAst = ref;
// Asynchronously serialize split functions.
if (isSplit) {
astSerializerExecutorService.execute(() -> {
cachedAst = new SerializedAst(symbolClonedAst, ref);
});
}
}
示例8
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
start(functionNode, false);
if (functionNode.isLazy()) {
return false;
}
//an outermost function in our lexical context that is not a program (runScript)
//is possible - it is a function being compiled lazily
if (functionNode.isDeclared()) {
final Iterator<Block> blocks = lc.getBlocks();
if (blocks.hasNext()) {
defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR);
}
}
returnTypes.push(functionNode.getReturnType());
pushLocalsFunction();
return true;
}
示例9
private boolean hasApplies(final FunctionNode functionNode) {
try {
functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public boolean enterFunctionNode(final FunctionNode fn) {
return fn == functionNode;
}
@Override
public boolean enterCallNode(final CallNode callNode) {
if (isApply(callNode)) {
throw HAS_APPLIES;
}
return true;
}
});
} catch (final AppliesFoundException e) {
return true;
}
log.fine("There are no applies in ", DebugLogger.quote(functionNode.getName()), " - nothing to do.");
return false; // no applies
}
示例10
@Override
public Node leaveBlock(final Block block) {
//now we have committed the entire statement list to the block, but we need to truncate
//whatever is after the last terminal. block append won't append past it
if (lc.isFunctionBody()) {
final FunctionNode currentFunction = lc.getCurrentFunction();
final boolean isProgram = currentFunction.isProgram();
final Statement last = lc.getLastStatement();
final ReturnNode returnNode = new ReturnNode(
last == null ? currentFunction.getLineNumber() : last.getLineNumber(), //TODO?
currentFunction.getToken(),
currentFunction.getFinish(),
isProgram ?
compilerConstant(RETURN) :
LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED));
returnNode.accept(this);
}
return block;
}
示例11
MethodType getCallSiteType(final FunctionNode functionNode) {
assert this.functionNodeId == functionNode.getId();
final Type[] types = paramTypes;
MethodType mt = MethodType.methodType(returnType.getTypeClass());
if (needsCallee) {
mt = mt.appendParameterTypes(ScriptFunction.class);
}
mt = mt.appendParameterTypes(Object.class); //this
for (final Type type : types) {
if (type == null) {
return null; // not all parameter information is supplied
}
mt = mt.appendParameterTypes(type.getTypeClass());
}
return mt;
}
示例12
/**
* When we eliminate dead code, we must preserve var declarations as they are scoped to the whole
* function. This method gathers var nodes from code passed to it, removing their initializers.
*
* @param deadCodeRoot the root node of eliminated dead code
* @param statements a list that will be receiving the var nodes from the dead code, with their
* initializers removed.
*/
static void extractVarNodesFromDeadCode(final Node deadCodeRoot, final List<Statement> statements) {
deadCodeRoot.accept(new SimpleNodeVisitor() {
@Override
public boolean enterVarNode(final VarNode varNode) {
statements.add(varNode.setInit(null));
return false;
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
// Don't descend into nested functions
return false;
}
});
}
示例13
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
try {
if(emittedMethods.add(functionNode.getName())) {
method.end(); // wrap up this method
unit = lc.popCompileUnit(functionNode.getCompileUnit());
method = lc.popMethodEmitter(method);
LOG.info("=== END ", functionNode.getName());
}
final FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.EMITTED);
newFunctionObject(newFunctionNode, functionNode);
return newFunctionNode;
} catch (final Throwable t) {
Context.printStackTrace(t);
final VerifyError e = new VerifyError("Code generation bug in \"" + functionNode.getName() + "\": likely stack misaligned: " + t + " " + functionNode.getSource().getName());
e.initCause(t);
throw e;
}
}
示例14
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;
}
示例15
private boolean hasApplies(final FunctionNode functionNode) {
try {
functionNode.accept(new SimpleNodeVisitor() {
@Override
public boolean enterFunctionNode(final FunctionNode fn) {
return fn == functionNode;
}
@Override
public boolean enterCallNode(final CallNode callNode) {
if (isApply(callNode)) {
throw HAS_APPLIES;
}
return true;
}
});
} catch (final AppliesFoundException e) {
return true;
}
log.fine("There are no applies in ", DebugLogger.quote(functionNode.getName()), " - nothing to do.");
return false; // no applies
}
示例16
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
if (functionNode == topFunction) {
// the function being weighted; descend into its statements
return true;
}
// just a reference to inner function from outer function
weight += FUNC_EXPR_WEIGHT;
return false;
}
示例17
/**
* 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));
}
示例18
/**
* Constructor.
*
* @param functionNode the function node
* @param invalidatedProgramPoints invalidated program points
*/
public FunctionInitializer(final FunctionNode functionNode, final Map<Integer, Type> invalidatedProgramPoints) {
this.className = functionNode.getCompileUnit().getUnitClassName();
this.methodType = new FunctionSignature(functionNode).getMethodType();
this.flags = functionNode.getFlags();
this.invalidatedProgramPoints = invalidatedProgramPoints;
final CompileUnit cu = functionNode.getCompileUnit();
if (cu != null) {
this.code = cu.getCode();
}
assert className != null;
}
示例19
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
start(functionNode, false);
thisProperties.push(new HashSet<String>());
// Every function has a body, even the ones skipped on reparse (they have an empty one). We're
// asserting this as even for those, enterBlock() must be invoked to correctly process symbols that
// are used in them.
assert functionNode.getBody() != null;
return true;
}
示例20
private FunctionNode markProgramBlock(final FunctionNode functionNode) {
if (isOnDemand || !functionNode.isProgram()) {
return functionNode;
}
return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
}
示例21
/**
* Execute parse and return the resulting function node.
* Errors will be thrown and the error manager will contain information
* if parsing should fail
*
* This should be used to create one and only one function node
*
* @param scriptName name for the script, given to the parsed FunctionNode
* @param startPos start position in source
* @param len length of parse
* @param allowPropertyFunction if true, "get" and "set" are allowed as first tokens of the program, followed by
* a property getter or setter function. This is used when reparsing a function that can potentially be defined as a
* property getter or setter in an object literal.
*
* @return function node resulting from successful parse
*/
public FunctionNode parse(final String scriptName, final int startPos, final int len, final boolean allowPropertyFunction) {
final boolean isTimingEnabled = env.isTimingEnabled();
final long t0 = isTimingEnabled ? System.nanoTime() : 0L;
log.info(this, " begin for '", scriptName, "'");
try {
stream = new TokenStream();
lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, reparsedFunction != null);
lexer.line = lexer.pendingLine = lineOffset + 1;
line = lineOffset;
// Set up first token (skips opening EOL.)
k = -1;
next();
// Begin parse.
return program(scriptName, allowPropertyFunction);
} catch (final Exception e) {
handleParseException(e);
return null;
} finally {
final String end = this + " end '" + scriptName + "'";
if (isTimingEnabled) {
env._timing.accumulateTime(toString(), System.nanoTime() - t0);
log.info(end, "' in ", Timing.toMillisPrint(System.nanoTime() - t0), " ms");
} else {
log.info(end);
}
}
}
示例22
/**
* Try to do the apply to call transformation
* @return true if successful, false otherwise
*/
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
FunctionNode newFunctionNode = functionNode;
final String functionName = newFunctionNode.getName();
if (changed.contains(newFunctionNode.getId())) {
newFunctionNode = newFunctionNode.clearFlag(lc, FunctionNode.USES_ARGUMENTS).
setFlag(lc, FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION).
setParameters(lc, explodedArguments.peek());
if (log.isEnabled()) {
log.info("Success: ",
massageURL(newFunctionNode.getSource().getURL()),
'.',
functionName,
"' id=",
newFunctionNode.getId(),
" params=",
callSiteTypes.peek());
}
}
callSiteTypes.pop();
explodedArguments.pop();
return newFunctionNode;
}
示例23
private boolean isDynamicScopeBoundary(final LexicalContextNode node) {
if (node instanceof Block) {
// Block's immediate parent is a with node. Note we aren't testing for a WithNode, as that'd capture
// processing of WithNode.expression too, but it should be unaffected.
return !isEmpty() && peek() instanceof WithNode;
} else if (node instanceof FunctionNode) {
// Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new
// variable into the function's scope), and it isn't strict (as evals in strict functions get an
// isolated scope).
return isFunctionDynamicScope((FunctionNode)node);
}
return false;
}
示例24
private Compiler(
final Context context,
final CodeInstaller installer,
final Source source,
final ErrorManager errors,
final boolean isStrict,
final boolean isOnDemand,
final RecompilableScriptFunctionData compiledFunction,
final TypeMap types,
final Map<Integer, Type> invalidatedProgramPoints,
final Object typeInformationFile,
final int[] continuationEntryPoints,
final ScriptObject runtimeScope) {
this.context = context;
this.env = context.getEnv();
this.installer = installer;
this.constantData = new ConstantData();
this.compileUnits = CompileUnit.createCompileUnitSet();
this.bytecode = new LinkedHashMap<>();
this.log = initLogger(context);
this.source = source;
this.errors = errors;
this.sourceName = FunctionNode.getSourceName(source);
this.onDemand = isOnDemand;
this.compiledFunction = compiledFunction;
this.types = types;
this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
this.typeInformationFile = typeInformationFile;
this.continuationEntryPoints = continuationEntryPoints == null ? null: continuationEntryPoints.clone();
this.typeEvaluator = new TypeEvaluator(this, runtimeScope);
this.firstCompileUnitName = firstCompileUnitName();
this.strict = isStrict;
this.optimistic = env._optimistic_types;
}
示例25
FunctionNode reparse() {
final FunctionNode cachedFunction = getCachedAst();
if (cachedFunction != null) {
assert cachedFunction.isCached();
return cachedFunction;
}
final int descPosition = Token.descPosition(token);
final Context context = Context.getContextTrusted();
final Parser parser = new Parser(
context.getEnv(),
source,
new Context.ThrowErrorManager(),
isStrict(),
// source starts at line 0, so even though lineNumber is the correct declaration line, back off
// one to make it exclusive
lineNumber - 1,
context.getLogger(Parser.class));
if (getFunctionFlag(FunctionNode.IS_ANONYMOUS)) {
parser.setFunctionName(functionName);
}
parser.setReparsedFunction(this);
final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition,
Token.descLength(token), isPropertyAccessor());
// Parser generates a program AST even if we're recompiling a single function, so when we are only
// recompiling a single function, extract it from the program.
return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName);
}
示例26
@Override
public boolean enterBlock(final Block block) {
final FunctionNode function = lc.getCurrentFunction();
if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) {
new ExpressionStatement(function.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
}
return true;
}
示例27
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
if (compiler.useOptimisticTypes()) {
return transformFunction(fn, new OptimisticTypesCalculator(compiler));
}
return fn;
}
示例28
private MethodHandle addCode(final FunctionNode fn, final MethodType runtimeType, final MethodHandle guard, final MethodHandle fallback) {
final MethodType targetType = new FunctionSignature(fn).getMethodType();
MethodHandle target =
MH.findStatic(
LOOKUP,
fn.getCompileUnit().getCode(),
fn.getName(),
targetType);
/*
* For any integer argument. a double that is representable as an integer is OK.
* otherwise the guard would have failed. in that case introduce a filter that
* casts the double to an integer, which we know will preserve all precision.
*/
for (int i = 0; i < targetType.parameterCount(); i++) {
if (targetType.parameterType(i) == int.class) {
//representable as int
target = MH.filterArguments(target, i, ENSURE_INT);
}
}
MethodHandle mh = target;
if (guard != null) {
mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback);
}
final CompiledFunction cf = new CompiledFunction(runtimeType == null ? targetType : runtimeType, mh);
code.add(cf);
return cf.getInvoker();
}
示例29
private void logRecompile(final String reason, final FunctionNode fn, final MethodType type, final Map<Integer, Type> ipp) {
if (log.isEnabled()) {
log.info(reason, DebugLogger.quote(fn.getName()), " signature: ", type);
log.indent();
for (final String str : toStringInvalidations(ipp)) {
log.fine(str);
}
log.unindent();
}
}
示例30
@Override
public boolean enterBlock(final Block block) {
final FunctionNode function = lc.getCurrentFunction();
if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) {
new ExpressionStatement(function.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
}
return true;
}