Move binding member field generation to Painless semantic pass (#47739)
This adds an SField node that operates similarly to SFunction as a top level node meant only for use in an SClass node. Member fields are generated for both class bindings and instance bindings using the new SField node during the semantic pass, and information is no longer passed through Globals for this during the write pass.
This commit is contained in:
parent
8f86469d3f
commit
076d3073b5
|
@ -28,8 +28,6 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class Globals {
|
public class Globals {
|
||||||
private final Map<String,Constant> constantInitializers = new HashMap<>();
|
private final Map<String,Constant> constantInitializers = new HashMap<>();
|
||||||
private final Map<String,Class<?>> classBindings = new HashMap<>();
|
|
||||||
private final Map<Object,String> instanceBindings = new HashMap<>();
|
|
||||||
private final BitSet statements;
|
private final BitSet statements;
|
||||||
|
|
||||||
/** Create a new Globals from the set of statement boundaries */
|
/** Create a new Globals from the set of statement boundaries */
|
||||||
|
@ -44,34 +42,11 @@ public class Globals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds a new class binding to be written as a local variable */
|
|
||||||
public String addClassBinding(Class<?> type) {
|
|
||||||
String name = "$class_binding$" + classBindings.size();
|
|
||||||
classBindings.put(name, type);
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Adds a new binding to be written as a local variable */
|
|
||||||
public String addInstanceBinding(Object instance) {
|
|
||||||
return instanceBindings.computeIfAbsent(instance, key -> "$instance_binding$" + instanceBindings.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the current initializers */
|
/** Returns the current initializers */
|
||||||
public Map<String,Constant> getConstantInitializers() {
|
public Map<String,Constant> getConstantInitializers() {
|
||||||
return constantInitializers;
|
return constantInitializers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the current bindings */
|
|
||||||
public Map<String,Class<?>> getClassBindings() {
|
|
||||||
return classBindings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the current bindings */
|
|
||||||
public Map<Object,String> getInstanceBindings() {
|
|
||||||
return instanceBindings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the set of statement boundaries */
|
/** Returns the set of statement boundaries */
|
||||||
public BitSet getStatements() {
|
public BitSet getStatements() {
|
||||||
return statements;
|
return statements;
|
||||||
|
|
|
@ -40,6 +40,9 @@ import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE;
|
import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE;
|
||||||
|
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
||||||
|
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||||
|
import static org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a user-defined call.
|
* Represents a user-defined call.
|
||||||
|
@ -54,6 +57,7 @@ public final class ECallLocal extends AExpression {
|
||||||
private PainlessClassBinding classBinding = null;
|
private PainlessClassBinding classBinding = null;
|
||||||
private int classBindingOffset = 0;
|
private int classBindingOffset = 0;
|
||||||
private PainlessInstanceBinding instanceBinding = null;
|
private PainlessInstanceBinding instanceBinding = null;
|
||||||
|
private String bindingName = null;
|
||||||
|
|
||||||
public ECallLocal(Location location, String name, List<AExpression> arguments) {
|
public ECallLocal(Location location, String name, List<AExpression> arguments) {
|
||||||
super(location);
|
super(location);
|
||||||
|
@ -138,9 +142,15 @@ public final class ECallLocal extends AExpression {
|
||||||
} else if (classBinding != null) {
|
} else if (classBinding != null) {
|
||||||
typeParameters = new ArrayList<>(classBinding.typeParameters);
|
typeParameters = new ArrayList<>(classBinding.typeParameters);
|
||||||
actual = classBinding.returnType;
|
actual = classBinding.returnType;
|
||||||
|
bindingName = scriptRoot.getNextSyntheticName("class_binding");
|
||||||
|
scriptRoot.getClassNode().addField(new SField(location,
|
||||||
|
ACC_PRIVATE, bindingName, classBinding.javaConstructor.getDeclaringClass(), null));
|
||||||
} else if (instanceBinding != null) {
|
} else if (instanceBinding != null) {
|
||||||
typeParameters = new ArrayList<>(instanceBinding.typeParameters);
|
typeParameters = new ArrayList<>(instanceBinding.typeParameters);
|
||||||
actual = instanceBinding.returnType;
|
actual = instanceBinding.returnType;
|
||||||
|
bindingName = scriptRoot.getNextSyntheticName("instance_binding");
|
||||||
|
scriptRoot.getClassNode().addField(new SField(location,
|
||||||
|
ACC_STATIC | ACC_PUBLIC, bindingName, instanceBinding.targetInstance.getClass(), instanceBinding.targetInstance));
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Illegal tree structure.");
|
throw new IllegalStateException("Illegal tree structure.");
|
||||||
}
|
}
|
||||||
|
@ -178,14 +188,13 @@ public final class ECallLocal extends AExpression {
|
||||||
methodWriter.invokeStatic(Type.getType(importedMethod.targetClass),
|
methodWriter.invokeStatic(Type.getType(importedMethod.targetClass),
|
||||||
new Method(importedMethod.javaMethod.getName(), importedMethod.methodType.toMethodDescriptorString()));
|
new Method(importedMethod.javaMethod.getName(), importedMethod.methodType.toMethodDescriptorString()));
|
||||||
} else if (classBinding != null) {
|
} else if (classBinding != null) {
|
||||||
String name = globals.addClassBinding(classBinding.javaConstructor.getDeclaringClass());
|
|
||||||
Type type = Type.getType(classBinding.javaConstructor.getDeclaringClass());
|
Type type = Type.getType(classBinding.javaConstructor.getDeclaringClass());
|
||||||
int javaConstructorParameterCount = classBinding.javaConstructor.getParameterCount() - classBindingOffset;
|
int javaConstructorParameterCount = classBinding.javaConstructor.getParameterCount() - classBindingOffset;
|
||||||
|
|
||||||
Label nonNull = new Label();
|
Label nonNull = new Label();
|
||||||
|
|
||||||
methodWriter.loadThis();
|
methodWriter.loadThis();
|
||||||
methodWriter.getField(CLASS_TYPE, name, type);
|
methodWriter.getField(CLASS_TYPE, bindingName, type);
|
||||||
methodWriter.ifNonNull(nonNull);
|
methodWriter.ifNonNull(nonNull);
|
||||||
methodWriter.loadThis();
|
methodWriter.loadThis();
|
||||||
methodWriter.newInstance(type);
|
methodWriter.newInstance(type);
|
||||||
|
@ -200,11 +209,11 @@ public final class ECallLocal extends AExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
methodWriter.invokeConstructor(type, Method.getMethod(classBinding.javaConstructor));
|
methodWriter.invokeConstructor(type, Method.getMethod(classBinding.javaConstructor));
|
||||||
methodWriter.putField(CLASS_TYPE, name, type);
|
methodWriter.putField(CLASS_TYPE, bindingName, type);
|
||||||
|
|
||||||
methodWriter.mark(nonNull);
|
methodWriter.mark(nonNull);
|
||||||
methodWriter.loadThis();
|
methodWriter.loadThis();
|
||||||
methodWriter.getField(CLASS_TYPE, name, type);
|
methodWriter.getField(CLASS_TYPE, bindingName, type);
|
||||||
|
|
||||||
for (int argument = 0; argument < classBinding.javaMethod.getParameterCount(); ++argument) {
|
for (int argument = 0; argument < classBinding.javaMethod.getParameterCount(); ++argument) {
|
||||||
arguments.get(argument + javaConstructorParameterCount).write(classWriter, methodWriter, globals);
|
arguments.get(argument + javaConstructorParameterCount).write(classWriter, methodWriter, globals);
|
||||||
|
@ -212,11 +221,10 @@ public final class ECallLocal extends AExpression {
|
||||||
|
|
||||||
methodWriter.invokeVirtual(type, Method.getMethod(classBinding.javaMethod));
|
methodWriter.invokeVirtual(type, Method.getMethod(classBinding.javaMethod));
|
||||||
} else if (instanceBinding != null) {
|
} else if (instanceBinding != null) {
|
||||||
String name = globals.addInstanceBinding(instanceBinding.targetInstance);
|
|
||||||
Type type = Type.getType(instanceBinding.targetInstance.getClass());
|
Type type = Type.getType(instanceBinding.targetInstance.getClass());
|
||||||
|
|
||||||
methodWriter.loadThis();
|
methodWriter.loadThis();
|
||||||
methodWriter.getStatic(CLASS_TYPE, name, type);
|
methodWriter.getStatic(CLASS_TYPE, bindingName, type);
|
||||||
|
|
||||||
for (int argument = 0; argument < instanceBinding.javaMethod.getParameterCount(); ++argument) {
|
for (int argument = 0; argument < instanceBinding.javaMethod.getParameterCount(); ++argument) {
|
||||||
arguments.get(argument).write(classWriter, methodWriter, globals);
|
arguments.get(argument).write(classWriter, methodWriter, globals);
|
||||||
|
|
|
@ -83,6 +83,7 @@ public final class SClass extends AStatement {
|
||||||
private final String name;
|
private final String name;
|
||||||
private final Printer debugStream;
|
private final Printer debugStream;
|
||||||
private final List<SFunction> functions = new ArrayList<>();
|
private final List<SFunction> functions = new ArrayList<>();
|
||||||
|
private final List<SField> fields = new ArrayList<>();
|
||||||
private final Globals globals;
|
private final Globals globals;
|
||||||
private final List<AStatement> statements;
|
private final List<AStatement> statements;
|
||||||
|
|
||||||
|
@ -112,6 +113,10 @@ public final class SClass extends AStatement {
|
||||||
functions.add(function);
|
functions.add(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addField(SField field) {
|
||||||
|
fields.add(field);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeSettings(CompilerSettings settings) {
|
public void storeSettings(CompilerSettings settings) {
|
||||||
for (SFunction function : functions) {
|
for (SFunction function : functions) {
|
||||||
|
@ -289,6 +294,11 @@ public final class SClass extends AStatement {
|
||||||
function.write(classWriter, globals);
|
function.write(classWriter, globals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write all fields:
|
||||||
|
for (SField field : fields) {
|
||||||
|
field.write(classWriter);
|
||||||
|
}
|
||||||
|
|
||||||
// Write the constants
|
// Write the constants
|
||||||
if (false == globals.getConstantInitializers().isEmpty()) {
|
if (false == globals.getConstantInitializers().isEmpty()) {
|
||||||
Collection<Constant> inits = globals.getConstantInitializers().values();
|
Collection<Constant> inits = globals.getConstantInitializers().values();
|
||||||
|
@ -315,20 +325,6 @@ public final class SClass extends AStatement {
|
||||||
clinit.endMethod();
|
clinit.endMethod();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write class binding variables
|
|
||||||
for (Map.Entry<String, Class<?>> classBinding : globals.getClassBindings().entrySet()) {
|
|
||||||
String name = classBinding.getKey();
|
|
||||||
String descriptor = Type.getType(classBinding.getValue()).getDescriptor();
|
|
||||||
classVisitor.visitField(Opcodes.ACC_PRIVATE, name, descriptor, null, null).visitEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write instance binding variables
|
|
||||||
for (Map.Entry<Object, String> instanceBinding : globals.getInstanceBindings().entrySet()) {
|
|
||||||
String name = instanceBinding.getValue();
|
|
||||||
String descriptor = Type.getType(instanceBinding.getKey().getClass()).getDescriptor();
|
|
||||||
classVisitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, name, descriptor, null, null).visitEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write any needsVarName methods for used variables
|
// Write any needsVarName methods for used variables
|
||||||
for (org.objectweb.asm.commons.Method needsMethod : scriptClassInfo.getNeedsMethods()) {
|
for (org.objectweb.asm.commons.Method needsMethod : scriptClassInfo.getNeedsMethods()) {
|
||||||
String name = needsMethod.getName();
|
String name = needsMethod.getName();
|
||||||
|
@ -349,8 +345,10 @@ public final class SClass extends AStatement {
|
||||||
Map<String, Object> statics = new HashMap<>();
|
Map<String, Object> statics = new HashMap<>();
|
||||||
statics.put("$FUNCTIONS", table.getFunctionTable());
|
statics.put("$FUNCTIONS", table.getFunctionTable());
|
||||||
|
|
||||||
for (Map.Entry<Object, String> instanceBinding : globals.getInstanceBindings().entrySet()) {
|
for (SField field : fields) {
|
||||||
statics.put(instanceBinding.getValue(), instanceBinding.getKey());
|
if (field.getInstance() != null) {
|
||||||
|
statics.put(field.getName(), field.getInstance());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return statics;
|
return statics;
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* 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.node;
|
||||||
|
|
||||||
|
import org.elasticsearch.painless.ClassWriter;
|
||||||
|
import org.elasticsearch.painless.CompilerSettings;
|
||||||
|
import org.elasticsearch.painless.Globals;
|
||||||
|
import org.elasticsearch.painless.Locals;
|
||||||
|
import org.elasticsearch.painless.Location;
|
||||||
|
import org.elasticsearch.painless.MethodWriter;
|
||||||
|
import org.elasticsearch.painless.ScriptRoot;
|
||||||
|
import org.objectweb.asm.Type;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a member field for its parent class (internal only).
|
||||||
|
*/
|
||||||
|
public class SField extends ANode {
|
||||||
|
|
||||||
|
private final int access;
|
||||||
|
private final String name;
|
||||||
|
private final Class<?> type;
|
||||||
|
private final Object instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard constructor.
|
||||||
|
* @param location original location in the source
|
||||||
|
* @param access asm constants for field modifiers
|
||||||
|
* @param name name of the field
|
||||||
|
* @param type type of the field
|
||||||
|
* @param instance initial value for the field
|
||||||
|
*/
|
||||||
|
public SField(Location location, int access, String name, Class<?> type, Object instance) {
|
||||||
|
super(location);
|
||||||
|
|
||||||
|
this.access = access;
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
this.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void storeSettings(CompilerSettings settings) {
|
||||||
|
throw createError(new UnsupportedOperationException("unexpected node"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void extractVariables(Set<String> variables) {
|
||||||
|
throw createError(new UnsupportedOperationException("unexpected node"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void analyze(ScriptRoot scriptRoot, Locals locals) {
|
||||||
|
throw createError(new UnsupportedOperationException("unexpected node"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(ClassWriter classWriter, MethodWriter methodWriter, Globals globals) {
|
||||||
|
throw createError(new UnsupportedOperationException("unexpected node"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(ClassWriter classWriter) {
|
||||||
|
classWriter.getClassVisitor().visitField(access, name, Type.getType(type).getDescriptor(), null, null).visitEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return singleLineToString(name, type);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue