All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.painless.Locals Maven / Gradle / Ivy

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.elasticsearch.painless;

import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.MethodKey;
import org.elasticsearch.painless.Definition.Type;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Tracks user defined methods and variables across compilation phases.
 */
public final class Locals {
    
    /** Reserved word: params map parameter */
    public static final String PARAMS = "params";
    /** Reserved word: Lucene scorer parameter */
    public static final String SCORER = "#scorer";
    /** Reserved word: _value variable for aggregations */
    public static final String VALUE  = "_value";
    /** Reserved word: _score variable for search scripts */
    public static final String SCORE  = "_score";
    /** Reserved word: ctx map for executable scripts */
    public static final String CTX    = "ctx";
    /** Reserved word: loop counter */
    public static final String LOOP   = "#loop";
    /** Reserved word: unused */
    public static final String THIS   = "#this";
    /** Reserved word: unused */
    public static final String DOC    = "doc";
    
    /** Map of always reserved keywords */
    public static final Set KEYWORDS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
            THIS,PARAMS,SCORER,DOC,VALUE,SCORE,CTX,LOOP
    )));
    
    /** Creates a new local variable scope (e.g. loop) inside the current scope */
    public static Locals newLocalScope(Locals currentScope) {
        return new Locals(currentScope);
    }
    
    /** 
     * Creates a new lambda scope inside the current scope
     * 

* This is just like {@link #newFunctionScope}, except the captured parameters are made read-only. */ public static Locals newLambdaScope(Locals programScope, Type returnType, List parameters, int captureCount, int maxLoopCounter) { Locals locals = new Locals(programScope, returnType); for (int i = 0; i < parameters.size(); i++) { Parameter parameter = parameters.get(i); // TODO: allow non-captures to be r/w: // boolean isCapture = i < captureCount; // currently, this cannot be allowed, as we swap in real types, // but that can prevent a store of a different type... boolean isCapture = true; locals.addVariable(parameter.location, parameter.type, parameter.name, isCapture); } // Loop counter to catch infinite loops. Internal use only. if (maxLoopCounter > 0) { locals.defineVariable(null, Definition.INT_TYPE, LOOP, true); } return locals; } /** Creates a new function scope inside the current scope */ public static Locals newFunctionScope(Locals programScope, Type returnType, List parameters, int maxLoopCounter) { Locals locals = new Locals(programScope, returnType); for (Parameter parameter : parameters) { locals.addVariable(parameter.location, parameter.type, parameter.name, false); } // Loop counter to catch infinite loops. Internal use only. if (maxLoopCounter > 0) { locals.defineVariable(null, Definition.INT_TYPE, LOOP, true); } return locals; } /** Creates a new main method scope */ public static Locals newMainMethodScope(Locals programScope, boolean usesScore, boolean usesCtx, int maxLoopCounter) { Locals locals = new Locals(programScope, Definition.OBJECT_TYPE); // This reference. Internal use only. locals.defineVariable(null, Definition.getType("Object"), THIS, true); // Input map of variables passed to the script. locals.defineVariable(null, Definition.getType("Map"), PARAMS, true); // Scorer parameter passed to the script. Internal use only. locals.defineVariable(null, Definition.DEF_TYPE, SCORER, true); // Doc parameter passed to the script. TODO: Currently working as a Map, we can do better? locals.defineVariable(null, Definition.getType("Map"), DOC, true); // Aggregation _value parameter passed to the script. locals.defineVariable(null, Definition.DEF_TYPE, VALUE, true); // Shortcut variables. // Document's score as a read-only double. if (usesScore) { locals.defineVariable(null, Definition.DOUBLE_TYPE, SCORE, true); } // The ctx map set by executable scripts as a read-only map. if (usesCtx) { locals.defineVariable(null, Definition.getType("Map"), CTX, true); } // Loop counter to catch infinite loops. Internal use only. if (maxLoopCounter > 0) { locals.defineVariable(null, Definition.INT_TYPE, LOOP, true); } return locals; } /** Creates a new program scope: the list of methods. It is the parent for all methods */ public static Locals newProgramScope(Collection methods) { Locals locals = new Locals(null, null); for (Method method : methods) { locals.addMethod(method); } return locals; } /** Checks if a variable exists or not, in this scope or any parents. */ public boolean hasVariable(String name) { Variable variable = lookupVariable(null, name); if (variable != null) { return true; } if (parent != null) { return parent.hasVariable(name); } return false; } /** Accesses a variable. This will throw IAE if the variable does not exist */ public Variable getVariable(Location location, String name) { Variable variable = lookupVariable(location, name); if (variable != null) { return variable; } if (parent != null) { return parent.getVariable(location, name); } throw location.createError(new IllegalArgumentException("Variable [" + name + "] is not defined.")); } /** Looks up a method. Returns null if the method does not exist. */ public Method getMethod(MethodKey key) { Method method = lookupMethod(key); if (method != null) { return method; } if (parent != null) { return parent.getMethod(key); } return null; } /** Creates a new variable. Throws IAE if the variable has already been defined (even in a parent) or reserved. */ public Variable addVariable(Location location, Type type, String name, boolean readonly) { if (hasVariable(name)) { throw location.createError(new IllegalArgumentException("Variable [" + name + "] is already defined.")); } if (KEYWORDS.contains(name)) { throw location.createError(new IllegalArgumentException("Variable [" + name + "] is reserved.")); } return defineVariable(location, type, name, readonly); } /** Return type of this scope (e.g. int, if inside a function that returns int) */ public Type getReturnType() { return returnType; } /** Returns the top-level program scope. */ public Locals getProgramScope() { Locals locals = this; while (locals.getParent() != null) { locals = locals.getParent(); } return locals; } ///// private impl // parent scope private final Locals parent; // return type of this scope private final Type returnType; // next slot number to assign private int nextSlotNumber; // variable name -> variable private Map variables; // method name+arity -> methods private Map methods; /** * Create a new Locals */ private Locals(Locals parent) { this(parent, parent.getReturnType()); } /** * Create a new Locals with specified return type */ private Locals(Locals parent, Type returnType) { this.parent = parent; this.returnType = returnType; if (parent == null) { this.nextSlotNumber = 0; } else { this.nextSlotNumber = parent.getNextSlot(); } } /** Returns the parent scope */ private Locals getParent() { return parent; } /** Looks up a variable at this scope only. Returns null if the variable does not exist. */ private Variable lookupVariable(Location location, String name) { if (variables == null) { return null; } return variables.get(name); } /** Looks up a method at this scope only. Returns null if the method does not exist. */ private Method lookupMethod(MethodKey key) { if (methods == null) { return null; } return methods.get(key); } /** Defines a variable at this scope internally. */ private Variable defineVariable(Location location, Type type, String name, boolean readonly) { if (variables == null) { variables = new HashMap<>(); } Variable variable = new Variable(location, name, type, getNextSlot(), readonly); variables.put(name, variable); // TODO: check result nextSlotNumber += type.type.getSize(); return variable; } private void addMethod(Method method) { if (methods == null) { methods = new HashMap<>(); } methods.put(new MethodKey(method.name, method.arguments.size()), method); // TODO: check result } private int getNextSlot() { return nextSlotNumber; } public static final class Variable { public final Location location; public final String name; public final Type type; public final boolean readonly; private final int slot; public Variable(Location location, String name, Type type, int slot, boolean readonly) { this.location = location; this.name = name; this.type = type; this.slot = slot; this.readonly = readonly; } public int getSlot() { return slot; } } public static final class Parameter { public final Location location; public final String name; public final Type type; public Parameter(Location location, String name, Type type) { this.location = location; this.name = name; this.type = type; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy