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 {
|
||||
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;
|
||||
|
||||
/** 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 */
|
||||
public Map<String,Constant> getConstantInitializers() {
|
||||
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 */
|
||||
public BitSet getStatements() {
|
||||
return statements;
|
||||
|
|
|
@ -40,6 +40,9 @@ import java.util.Objects;
|
|||
import java.util.Set;
|
||||
|
||||
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.
|
||||
|
@ -54,6 +57,7 @@ public final class ECallLocal extends AExpression {
|
|||
private PainlessClassBinding classBinding = null;
|
||||
private int classBindingOffset = 0;
|
||||
private PainlessInstanceBinding instanceBinding = null;
|
||||
private String bindingName = null;
|
||||
|
||||
public ECallLocal(Location location, String name, List<AExpression> arguments) {
|
||||
super(location);
|
||||
|
@ -138,9 +142,15 @@ public final class ECallLocal extends AExpression {
|
|||
} else if (classBinding != null) {
|
||||
typeParameters = new ArrayList<>(classBinding.typeParameters);
|
||||
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) {
|
||||
typeParameters = new ArrayList<>(instanceBinding.typeParameters);
|
||||
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 {
|
||||
throw new IllegalStateException("Illegal tree structure.");
|
||||
}
|
||||
|
@ -178,14 +188,13 @@ public final class ECallLocal extends AExpression {
|
|||
methodWriter.invokeStatic(Type.getType(importedMethod.targetClass),
|
||||
new Method(importedMethod.javaMethod.getName(), importedMethod.methodType.toMethodDescriptorString()));
|
||||
} else if (classBinding != null) {
|
||||
String name = globals.addClassBinding(classBinding.javaConstructor.getDeclaringClass());
|
||||
Type type = Type.getType(classBinding.javaConstructor.getDeclaringClass());
|
||||
int javaConstructorParameterCount = classBinding.javaConstructor.getParameterCount() - classBindingOffset;
|
||||
|
||||
Label nonNull = new Label();
|
||||
|
||||
methodWriter.loadThis();
|
||||
methodWriter.getField(CLASS_TYPE, name, type);
|
||||
methodWriter.getField(CLASS_TYPE, bindingName, type);
|
||||
methodWriter.ifNonNull(nonNull);
|
||||
methodWriter.loadThis();
|
||||
methodWriter.newInstance(type);
|
||||
|
@ -200,11 +209,11 @@ public final class ECallLocal extends AExpression {
|
|||
}
|
||||
|
||||
methodWriter.invokeConstructor(type, Method.getMethod(classBinding.javaConstructor));
|
||||
methodWriter.putField(CLASS_TYPE, name, type);
|
||||
methodWriter.putField(CLASS_TYPE, bindingName, type);
|
||||
|
||||
methodWriter.mark(nonNull);
|
||||
methodWriter.loadThis();
|
||||
methodWriter.getField(CLASS_TYPE, name, type);
|
||||
methodWriter.getField(CLASS_TYPE, bindingName, type);
|
||||
|
||||
for (int argument = 0; argument < classBinding.javaMethod.getParameterCount(); ++argument) {
|
||||
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));
|
||||
} else if (instanceBinding != null) {
|
||||
String name = globals.addInstanceBinding(instanceBinding.targetInstance);
|
||||
Type type = Type.getType(instanceBinding.targetInstance.getClass());
|
||||
|
||||
methodWriter.loadThis();
|
||||
methodWriter.getStatic(CLASS_TYPE, name, type);
|
||||
methodWriter.getStatic(CLASS_TYPE, bindingName, type);
|
||||
|
||||
for (int argument = 0; argument < instanceBinding.javaMethod.getParameterCount(); ++argument) {
|
||||
arguments.get(argument).write(classWriter, methodWriter, globals);
|
||||
|
|
|
@ -83,6 +83,7 @@ public final class SClass extends AStatement {
|
|||
private final String name;
|
||||
private final Printer debugStream;
|
||||
private final List<SFunction> functions = new ArrayList<>();
|
||||
private final List<SField> fields = new ArrayList<>();
|
||||
private final Globals globals;
|
||||
private final List<AStatement> statements;
|
||||
|
||||
|
@ -112,6 +113,10 @@ public final class SClass extends AStatement {
|
|||
functions.add(function);
|
||||
}
|
||||
|
||||
void addField(SField field) {
|
||||
fields.add(field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeSettings(CompilerSettings settings) {
|
||||
for (SFunction function : functions) {
|
||||
|
@ -289,6 +294,11 @@ public final class SClass extends AStatement {
|
|||
function.write(classWriter, globals);
|
||||
}
|
||||
|
||||
// Write all fields:
|
||||
for (SField field : fields) {
|
||||
field.write(classWriter);
|
||||
}
|
||||
|
||||
// Write the constants
|
||||
if (false == globals.getConstantInitializers().isEmpty()) {
|
||||
Collection<Constant> inits = globals.getConstantInitializers().values();
|
||||
|
@ -315,20 +325,6 @@ public final class SClass extends AStatement {
|
|||
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
|
||||
for (org.objectweb.asm.commons.Method needsMethod : scriptClassInfo.getNeedsMethods()) {
|
||||
String name = needsMethod.getName();
|
||||
|
@ -349,8 +345,10 @@ public final class SClass extends AStatement {
|
|||
Map<String, Object> statics = new HashMap<>();
|
||||
statics.put("$FUNCTIONS", table.getFunctionTable());
|
||||
|
||||
for (Map.Entry<Object, String> instanceBinding : globals.getInstanceBindings().entrySet()) {
|
||||
statics.put(instanceBinding.getValue(), instanceBinding.getKey());
|
||||
for (SField field : fields) {
|
||||
if (field.getInstance() != null) {
|
||||
statics.put(field.getName(), field.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
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