You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by am...@apache.org on 2021/03/17 10:13:06 UTC
[ignite-3] branch main updated: IGNITE-13618: Provide generated and
reflection-based class (de)serializers. (#35)
This is an automated email from the ASF dual-hosted git repository.
amashenkov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new d5b50d7 IGNITE-13618: Provide generated and reflection-based class (de)serializers. (#35)
d5b50d7 is described below
commit d5b50d758df68131a5fcee40ca6e0da86cf6209e
Author: Andrew V. Mashenkov <AM...@users.noreply.github.com>
AuthorDate: Wed Mar 17 13:12:58 2021 +0300
IGNITE-13618: Provide generated and reflection-based class (de)serializers. (#35)
---
check-rules/pmd-rules.xml | 30 +-
modules/README.md | 12 +-
modules/bytecode/README.md | 6 +
pom.xml => modules/bytecode/pom.xml | 46 +-
.../java/com/facebook/presto/bytecode/Access.java | 85 +++
.../bytecode/AddFakeLineNumberClassVisitor.java | 157 ++++
.../presto/bytecode/AnnotationDefinition.java | 211 ++++++
.../com/facebook/presto/bytecode/ArrayOpCode.java | 124 +++
.../presto/bytecode/ByteCodeTooLargeException.java | 23 +
.../facebook/presto/bytecode/BytecodeBlock.java | 837 +++++++++++++++++++++
.../com/facebook/presto/bytecode/BytecodeNode.java | 26 +
.../facebook/presto/bytecode/BytecodeUtils.java | 118 +++
.../facebook/presto/bytecode/BytecodeVisitor.java | 327 ++++++++
.../facebook/presto/bytecode/ClassDefinition.java | 281 +++++++
.../facebook/presto/bytecode/ClassGenerator.java | 204 +++++
.../com/facebook/presto/bytecode/ClassInfo.java | 169 +++++
.../facebook/presto/bytecode/ClassInfoLoader.java | 147 ++++
.../java/com/facebook/presto/bytecode/Comment.java | 50 ++
.../presto/bytecode/CompilationException.java | 23 +
.../presto/bytecode/DumpBytecodeVisitor.java | 601 +++++++++++++++
.../presto/bytecode/DynamicClassLoader.java | 144 ++++
.../facebook/presto/bytecode/FieldDefinition.java | 106 +++
.../facebook/presto/bytecode/MethodDefinition.java | 284 +++++++
.../presto/bytecode/MethodGenerationContext.java | 122 +++
.../java/com/facebook/presto/bytecode/OpCode.java | 268 +++++++
.../com/facebook/presto/bytecode/Parameter.java | 33 +
.../presto/bytecode/ParameterizedType.java | 285 +++++++
.../java/com/facebook/presto/bytecode/Scope.java | 102 +++
.../facebook/presto/bytecode/SmartClassWriter.java | 54 ++
.../com/facebook/presto/bytecode/Variable.java | 89 +++
.../presto/bytecode/control/CaseStatement.java | 72 ++
.../presto/bytecode/control/DoWhileLoop.java | 106 +++
.../presto/bytecode/control/FlowControl.java | 22 +
.../facebook/presto/bytecode/control/ForLoop.java | 155 ++++
.../presto/bytecode/control/IfStatement.java | 124 +++
.../presto/bytecode/control/SwitchStatement.java | 171 +++++
.../facebook/presto/bytecode/control/TryCatch.java | 97 +++
.../presto/bytecode/control/WhileLoop.java | 100 +++
.../facebook/presto/bytecode/debug/DebugNode.java | 21 +
.../presto/bytecode/debug/LineNumberNode.java | 59 ++
.../presto/bytecode/debug/LocalVariableNode.java | 62 ++
.../bytecode/expression/AndBytecodeExpression.java | 64 ++
.../expression/ArithmeticBytecodeExpression.java | 202 +++++
.../expression/ArrayLengthBytecodeExpression.java | 55 ++
.../bytecode/expression/BytecodeExpression.java | 221 ++++++
.../bytecode/expression/BytecodeExpressions.java | 623 +++++++++++++++
.../expression/CastBytecodeExpression.java | 328 ++++++++
.../expression/ComparisonBytecodeExpression.java | 313 ++++++++
.../expression/ConstantBytecodeExpression.java | 68 ++
.../expression/GetElementBytecodeExpression.java | 63 ++
.../expression/GetFieldBytecodeExpression.java | 109 +++
.../expression/InlineIfBytecodeExpression.java | 69 ++
.../expression/InstanceOfBytecodeExpression.java | 62 ++
.../expression/InvokeBytecodeExpression.java | 115 +++
.../InvokeDynamicBytecodeExpression.java | 84 +++
.../expression/NegateBytecodeExpression.java | 66 ++
.../expression/NewArrayBytecodeExpression.java | 99 +++
.../expression/NewInstanceBytecodeExpression.java | 67 ++
.../bytecode/expression/NotBytecodeExpression.java | 63 ++
.../bytecode/expression/OrBytecodeExpression.java | 64 ++
.../bytecode/expression/PopBytecodeExpression.java | 49 ++
.../expression/ReturnBytecodeExpression.java | 82 ++
.../SetArrayElementBytecodeExpression.java | 68 ++
.../expression/SetFieldBytecodeExpression.java | 129 ++++
.../presto/bytecode/instruction/Constant.java | 542 +++++++++++++
.../bytecode/instruction/FieldInstruction.java | 156 ++++
.../bytecode/instruction/InstructionNode.java | 21 +
.../bytecode/instruction/InvokeInstruction.java | 405 ++++++++++
.../bytecode/instruction/JumpInstruction.java | 137 ++++
.../presto/bytecode/instruction/LabelNode.java | 70 ++
.../bytecode/instruction/TypeInstruction.java | 109 +++
.../bytecode/instruction/VariableInstruction.java | 126 ++++
.../presto/bytecode/TestBytecodeUtils.java | 29 +
.../presto/bytecode/TestClassGenerator.java | 97 +++
.../expression/BytecodeExpressionAssertions.java | 148 ++++
.../TestArithmeticBytecodeExpression.java | 136 ++++
.../expression/TestArrayBytecodeExpressions.java | 155 ++++
.../expression/TestCastBytecodeExpression.java | 164 ++++
.../TestComparisonBytecodeExpression.java | 179 +++++
.../expression/TestConstantBytecodeExpression.java | 75 ++
.../expression/TestGetFieldBytecodeExpression.java | 41 +
.../expression/TestInlineIfBytecodeExpression.java | 32 +
.../expression/TestInvokeBytecodeExpression.java | 55 ++
.../TestInvokeDynamicBytecodeExpression.java | 56 ++
.../expression/TestLogicalBytecodeExpression.java | 51 ++
.../TestNewInstanceBytecodeExpression.java | 34 +
.../expression/TestPopBytecodeExpression.java | 46 ++
.../expression/TestSetFieldBytecodeExpression.java | 85 +++
.../TestSetVariableBytecodeExpression.java | 47 ++
modules/cli-common/pom.xml | 1 +
modules/schema/README.md | 49 ++
modules/schema/pom.xml | 84 +++
.../ignite/internal/schema/AssemblyException.java | 32 +
.../org/apache/ignite/internal/schema/Bitmask.java | 87 +++
.../ignite/internal/schema/ByteBufferTuple.java | 91 +++
.../org/apache/ignite/internal/schema/Column.java | 137 ++++
.../org/apache/ignite/internal/schema/Columns.java | 279 +++++++
.../ignite/internal/schema/ExpandableByteBuf.java | 253 +++++++
.../internal/schema/InvalidTypeException.java | 30 +
.../apache/ignite/internal/schema/NativeType.java | 142 ++++
.../ignite/internal/schema/NativeTypeSpec.java | 178 +++++
.../org/apache/ignite/internal/schema/README.md | 87 +++
.../ignite/internal/schema/SchemaDescriptor.java | 87 +++
.../org/apache/ignite/internal/schema/Tuple.java | 429 +++++++++++
.../ignite/internal/schema/TupleAssembler.java | 453 +++++++++++
.../schema/marshaller/AbstractSerializer.java | 108 +++
.../internal/schema/marshaller/BinaryMode.java | 90 +++
.../internal/schema/marshaller/MarshallerUtil.java | 115 +++
.../schema/marshaller/SerializationException.java | 42 ++
.../internal/schema/marshaller/Serializer.java | 49 ++
.../schema/marshaller/SerializerFactory.java | 52 ++
.../marshaller/asm/AsmSerializerGenerator.java | 458 +++++++++++
.../asm/IdentityMarshallerCodeGenerator.java | 77 ++
.../marshaller/asm/MarshallerCodeGenerator.java | 71 ++
.../asm/ObjectMarshallerCodeGenerator.java | 182 +++++
.../asm/TupleColumnAccessCodeGenerator.java | 139 ++++
.../marshaller/reflection/FieldAccessor.java | 643 ++++++++++++++++
.../marshaller/reflection/JavaSerializer.java | 157 ++++
.../reflection/JavaSerializerFactory.java | 32 +
.../schema/marshaller/reflection/Marshaller.java | 153 ++++
.../ignite/internal/schema/package-info.java | 22 +
.../org/apache/ignite/internal/util/Factory.java | 32 +
.../apache/ignite/internal/util/ObjectFactory.java | 54 ++
.../java/org/apache/ignite/internal/util/Pair.java | 57 ++
.../benchmarks/SerializerBenchmarkTest.java | 206 +++++
.../apache/ignite/internal/schema/ColumnTest.java | 48 ++
.../apache/ignite/internal/schema/ColumnsTest.java | 443 +++++++++++
.../internal/schema/ExpandableByteBufTest.java | 150 ++++
.../ignite/internal/schema/NativeTypeTest.java | 71 ++
.../internal/schema/SchemaDescriptorTest.java | 51 ++
.../apache/ignite/internal/schema/TestUtils.java | 127 ++++
.../apache/ignite/internal/schema/TupleTest.java | 320 ++++++++
.../schema/marshaller/JavaSerializerTest.java | 675 +++++++++++++++++
.../marshaller/reflection/FieldAccessorTest.java | 417 ++++++++++
parent/pom.xml | 42 ++
pom.xml | 2 +
136 files changed, 19485 insertions(+), 31 deletions(-)
diff --git a/check-rules/pmd-rules.xml b/check-rules/pmd-rules.xml
index 7cee12f..3d2e458 100644
--- a/check-rules/pmd-rules.xml
+++ b/check-rules/pmd-rules.xml
@@ -20,21 +20,27 @@
<ruleset name="Default Maven PMD Plugin Ruleset"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
+ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
PMD Ruleset for Apache Ignite
</description>
- <rule ref="category/java/errorprone.xml/EmptyFinallyBlock" />
- <rule ref="category/java/errorprone.xml/AvoidCatchingNPE" />
- <rule ref="category/java/errorprone.xml/BrokenNullCheck" />
- <rule ref="category/java/errorprone.xml/EmptySwitchStatements" />
- <rule ref="category/java/errorprone.xml/EmptyStatementNotInLoop" />
- <rule ref="category/java/errorprone.xml/EmptySynchronizedBlock" />
- <rule ref="category/java/errorprone.xml/UselessOperationOnImmutable" />
-
- <rule ref="category/java/bestpractices.xml/UnusedLocalVariable" />
- <rule ref="category/java/bestpractices.xml/UnusedPrivateField" />
- <rule ref="category/java/bestpractices.xml/UnusedPrivateMethod" />
+ <rule ref="category/java/errorprone.xml/EmptyFinallyBlock"/>
+ <rule ref="category/java/errorprone.xml/AvoidCatchingNPE"/>
+ <rule ref="category/java/errorprone.xml/BrokenNullCheck"/>
+ <rule ref="category/java/errorprone.xml/EmptySwitchStatements"/>
+ <rule ref="category/java/errorprone.xml/EmptyStatementNotInLoop"/>
+ <rule ref="category/java/errorprone.xml/EmptySynchronizedBlock"/>
+ <rule ref="category/java/errorprone.xml/UselessOperationOnImmutable"/>
+
+ <rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/>
+ <rule ref="category/java/bestpractices.xml/UnusedPrivateField"/>
+ <!--
+ UnusedPrivateMethod has a known bug which leads to false positive triggering if
+ method signature contains generic parameter and\or descendant class is passed as parameter.
+ See for details: https://github.com/pmd/pmd/issues/770
+ -->
+ <!-- <rule ref="category/java/bestpractices.xml/UnusedPrivateMethod" />-->
+
</ruleset>
diff --git a/modules/README.md b/modules/README.md
index 6b3f13b..091b7f0 100644
--- a/modules/README.md
+++ b/modules/README.md
@@ -10,10 +10,12 @@ We prohibit cyclic dependencies between modules in order to simplify JIGSAW migr
Module Name | Description
----------- | -----------
-[network](network/README.md)|Networking module: group membership and message passing
-[configuration-annotation-processor](configuration-annotation-processor/README.md)|Tooling for generating Ignite configuration model classes from configuration schema definition
+[bytecode](bytecode/README.md)|Ignite Bytecode module.
+[cli](cli/README.md)|Ignite CLI implementation
+[cli-common](cli-common/README.md)|Shared interfaces definitions for pluggable CLIng
[configuration](configuration/README.md)|Ignite configuration classes and configuration management framework
-[runner](runner/README.md)|Ignite server node runner. The module that wires up the Ignite components and handles node lifecycle
+[configuration-annotation-processor](configuration-annotation-processor/README.md)|Tooling for generating Ignite configuration model classes from configuration schema definition
+[network](network/README.md)|Networking module: group membership and message passi
[rest](rest/README.md)|REST management endpoint bindings and command handlers
-[cli-common](cli-common/README.md)|Shared interfaces definitions for pluggable CLI
-[cli](cli/README.md)|Ignite CLI implementation
+[runner](runner/README.md)|Ignite server node runner. The module that wires up the Ignite components and handles node lifecycle
+[schema](schema/README.md)|Ignite schema API implementation and schema management classes.
\ No newline at end of file
diff --git a/modules/bytecode/README.md b/modules/bytecode/README.md
new file mode 100644
index 0000000..3a178c8
--- /dev/null
+++ b/modules/bytecode/README.md
@@ -0,0 +1,6 @@
+# Apache Ignite Bytecode module
+Fork of [PrestoDB Bytecode module (ver 0.243)](https://github.com/prestodb/presto/tree/0.243/presto-bytecode).
+* Removed unnecessary guava dependency.
+* Tests migrated from TestNG to JUnit 5.
+
+This module provides a convenient thin wrapper around [ASM](https://asm.ow2.io/) library to generate classes at runtime.
\ No newline at end of file
diff --git a/pom.xml b/modules/bytecode/pom.xml
similarity index 58%
copy from pom.xml
copy to modules/bytecode/pom.xml
index 86a51b8..d475a49 100644
--- a/pom.xml
+++ b/modules/bytecode/pom.xml
@@ -26,20 +26,38 @@
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-parent</artifactId>
<version>1</version>
- <relativePath>parent</relativePath>
+ <relativePath>../../parent/pom.xml</relativePath>
</parent>
- <artifactId>apache-ignite</artifactId>
+ <artifactId>ignite-bytecode</artifactId>
<version>3.0.0-SNAPSHOT</version>
- <packaging>pom</packaging>
-
- <modules>
- <module>modules/cli</module>
- <module>modules/cli-common</module>
- <module>modules/configuration</module>
- <module>modules/configuration-annotation-processor</module>
- <module>modules/rest</module>
- <module>modules/runner</module>
- <module>modules/network</module>
- </modules>
-</project>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jetbrains</groupId>
+ <artifactId>annotations</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-tree</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-util</artifactId>
+ </dependency>
+
+ <!-- Test dependencies -->
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Access.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Access.java
new file mode 100644
index 0000000..1b531c5
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Access.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import java.util.EnumSet;
+import java.util.List;
+
+import static java.util.Locale.ENGLISH;
+import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
+import static org.objectweb.asm.Opcodes.ACC_ANNOTATION;
+import static org.objectweb.asm.Opcodes.ACC_BRIDGE;
+import static org.objectweb.asm.Opcodes.ACC_ENUM;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
+import static org.objectweb.asm.Opcodes.ACC_NATIVE;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.ACC_STRICT;
+import static org.objectweb.asm.Opcodes.ACC_SUPER;
+import static org.objectweb.asm.Opcodes.ACC_SYNCHRONIZED;
+import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
+import static org.objectweb.asm.Opcodes.ACC_TRANSIENT;
+import static org.objectweb.asm.Opcodes.ACC_VARARGS;
+import static org.objectweb.asm.Opcodes.ACC_VOLATILE;
+
+public enum Access {
+ PUBLIC(ACC_PUBLIC),
+ PRIVATE(ACC_PRIVATE),
+ PROTECTED(ACC_PROTECTED),
+ STATIC(ACC_STATIC),
+ FINAL(ACC_FINAL),
+ SUPER(ACC_SUPER),
+ SYNCHRONIZED(ACC_SYNCHRONIZED),
+ VOLATILE(ACC_VOLATILE),
+ BRIDGE(ACC_BRIDGE),
+ VARARGS(ACC_VARARGS),
+ TRANSIENT(ACC_TRANSIENT),
+ NATIVE(ACC_NATIVE),
+ INTERFACE(ACC_INTERFACE),
+ ABSTRACT(ACC_ABSTRACT),
+ STRICT(ACC_STRICT),
+ SYNTHETIC(ACC_SYNTHETIC),
+ ANNOTATION(ACC_ANNOTATION),
+ ENUM(ACC_ENUM);
+
+ private final int modifier;
+
+ Access(int modifier) {
+ this.modifier = modifier;
+ }
+
+ public int getModifier() {
+ return modifier;
+ }
+
+ @Override
+ public String toString() {
+ return name().toLowerCase(ENGLISH);
+ }
+
+ public static EnumSet<Access> a(Access... access) {
+ return EnumSet.copyOf(List.of(access));
+ }
+
+ public static int toAccessModifier(Iterable<Access> accesses) {
+ int modifier = 0;
+ for (Access access : accesses) {
+ modifier += access.getModifier();
+ }
+ return modifier;
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/AddFakeLineNumberClassVisitor.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/AddFakeLineNumberClassVisitor.java
new file mode 100644
index 0000000..5d79270
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/AddFakeLineNumberClassVisitor.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import static org.objectweb.asm.Opcodes.ASM5;
+
+class AddFakeLineNumberClassVisitor
+ extends ClassVisitor
+{
+ int methodCount;
+
+ AddFakeLineNumberClassVisitor(ClassVisitor cv)
+ {
+ super(ASM5, cv);
+ super.visitSource("FakeSource.java", null);
+ }
+
+ @Override
+ public void visitSource(String source, String debug)
+ {
+ super.visitSource(source, debug);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
+ {
+ MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions);
+ methodCount++;
+ return new AddFakeLineNumberMethodVisitor(methodVisitor, 1000 * methodCount);
+ }
+
+ private static class AddFakeLineNumberMethodVisitor
+ extends MethodVisitor
+ {
+ private int count;
+
+ AddFakeLineNumberMethodVisitor(MethodVisitor mv, int startLineNumber)
+ {
+ super(ASM5, mv);
+ this.count = startLineNumber;
+ }
+
+ private void addFakeLineNumber()
+ {
+ Label label = new Label();
+ mv.visitLabel(label);
+ mv.visitLineNumber(++count, label);
+ }
+
+ @Override
+ public void visitInsn(int opcode)
+ {
+ addFakeLineNumber();
+ super.visitInsn(opcode);
+ }
+
+ @Override
+ public void visitIntInsn(int opcode, int operand)
+ {
+ addFakeLineNumber();
+ super.visitIntInsn(opcode, operand);
+ }
+
+ @Override
+ public void visitVarInsn(int opcode, int var)
+ {
+ addFakeLineNumber();
+ super.visitVarInsn(opcode, var);
+ }
+
+ @Override
+ public void visitTypeInsn(int opcode, String type)
+ {
+ addFakeLineNumber();
+ super.visitTypeInsn(opcode, type);
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc)
+ {
+ addFakeLineNumber();
+ super.visitFieldInsn(opcode, owner, name, desc);
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf)
+ {
+ addFakeLineNumber();
+ super.visitMethodInsn(opcode, owner, name, desc, itf);
+ }
+
+ @Override
+ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs)
+ {
+ addFakeLineNumber();
+ super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+ }
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label)
+ {
+ addFakeLineNumber();
+ super.visitJumpInsn(opcode, label);
+ }
+
+ @Override
+ public void visitLdcInsn(Object cst)
+ {
+ addFakeLineNumber();
+ super.visitLdcInsn(cst);
+ }
+
+ @Override
+ public void visitIincInsn(int var, int increment)
+ {
+ addFakeLineNumber();
+ super.visitIincInsn(var, increment);
+ }
+
+ @Override
+ public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels)
+ {
+ addFakeLineNumber();
+ super.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+
+ @Override
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels)
+ {
+ addFakeLineNumber();
+ super.visitLookupSwitchInsn(dflt, keys, labels);
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims)
+ {
+ addFakeLineNumber();
+ super.visitMultiANewArrayInsn(desc, dims);
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/AnnotationDefinition.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/AnnotationDefinition.java
new file mode 100644
index 0000000..6a1d41d
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/AnnotationDefinition.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.util.Objects.requireNonNull;
+
+public class AnnotationDefinition {
+ private static final Set<Class<?>> ALLOWED_TYPES = Set.of(
+ Byte.class,
+ Character.class,
+ Double.class,
+ Float.class,
+ Integer.class,
+ Long.class,
+ Short.class,
+ Void.class,
+ String.class,
+ Class.class,
+ ParameterizedType.class,
+ AnnotationDefinition.class,
+ Enum.class);
+
+ private final ParameterizedType type;
+ private final Map<String, Object> values = new LinkedHashMap<>();
+
+ public AnnotationDefinition(Class<?> type) {
+ this.type = type(type);
+ }
+
+ public AnnotationDefinition(ParameterizedType type) {
+ this.type = type;
+ }
+
+ public AnnotationDefinition setValue(String name, Byte value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, Boolean value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, Character value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, Short value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, Integer value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, Long value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, Float value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, Double value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, String value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, Class<?> value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, ParameterizedType value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, AnnotationDefinition value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, Enum<?> value) {
+ return setValueInternal(name, value);
+ }
+
+ public AnnotationDefinition setValue(String name, List<?> value) {
+ return setValueInternal(name, value);
+ }
+
+ private AnnotationDefinition setValueInternal(String name, Object value) {
+ requireNonNull(name, "name is null");
+ requireNonNull(value, "value is null");
+
+ isValidType(value);
+
+ values.put(name, value);
+ return this;
+ }
+
+ public ParameterizedType getType() {
+ return type;
+ }
+
+ public Map<String, Object> getValues() {
+ // todo we need an unmodifiable view
+ return values;
+ }
+
+ @SuppressWarnings("OverlyStrongTypeCast")
+ private static void isValidType(Object value) {
+ if (value instanceof List) {
+ // todo verify list contains single type
+ for (Object v : (List<Object>)value) {
+ checkArgument(ALLOWED_TYPES.contains(v.getClass()), "List contains invalid type %s", v.getClass());
+ if (v instanceof List) {
+ isValidType(value);
+ }
+ }
+ }
+ else {
+ checkArgument(ALLOWED_TYPES.contains(value.getClass()), "Invalid value type %s", value.getClass());
+ }
+ }
+
+ public void visitClassAnnotation(ClassVisitor visitor) {
+ AnnotationVisitor annotationVisitor = visitor.visitAnnotation(type.getType(), true);
+ visit(annotationVisitor);
+ annotationVisitor.visitEnd();
+ }
+
+ public void visitFieldAnnotation(FieldVisitor visitor) {
+ AnnotationVisitor annotationVisitor = visitor.visitAnnotation(type.getType(), true);
+ visit(annotationVisitor);
+ annotationVisitor.visitEnd();
+ }
+
+ public void visitMethodAnnotation(MethodVisitor visitor) {
+ AnnotationVisitor annotationVisitor = visitor.visitAnnotation(type.getType(), true);
+ visit(annotationVisitor);
+ annotationVisitor.visitEnd();
+ }
+
+ public void visitParameterAnnotation(int parameterIndex, MethodVisitor visitor) {
+ AnnotationVisitor annotationVisitor = visitor.visitParameterAnnotation(parameterIndex, type.getType(), true);
+ visit(annotationVisitor);
+ annotationVisitor.visitEnd();
+ }
+
+ private void visit(AnnotationVisitor visitor) {
+ for (Entry<String, Object> entry : values.entrySet()) {
+ String name = entry.getKey();
+ Object value = entry.getValue();
+ visit(visitor, name, value);
+ }
+ }
+
+ @SuppressWarnings("OverlyStrongTypeCast")
+ private static void visit(AnnotationVisitor visitor, String name, Object value) {
+ if (value instanceof AnnotationDefinition) {
+ AnnotationDefinition annotation = (AnnotationDefinition)value;
+ AnnotationVisitor annotationVisitor = visitor.visitAnnotation(name, annotation.type.getType());
+ annotation.visit(annotationVisitor);
+ }
+ else if (value instanceof Enum) {
+ Enum<?> enumConstant = (Enum<?>)value;
+ visitor.visitEnum(name, type(enumConstant.getDeclaringClass()).getClassName(), enumConstant.name());
+ }
+ else if (value instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType)value;
+ visitor.visit(name, Type.getType(parameterizedType.getType()));
+ }
+ else if (value instanceof Class) {
+ Class<?> clazz = (Class<?>)value;
+ visitor.visit(name, Type.getType(clazz));
+ }
+ else if (value instanceof List) {
+ AnnotationVisitor arrayVisitor = visitor.visitArray(name);
+ for (Object element : (List<?>)value) {
+ visit(arrayVisitor, null, element);
+ }
+ arrayVisitor.visitEnd();
+ }
+ else {
+ visitor.visit(name, value);
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ArrayOpCode.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ArrayOpCode.java
new file mode 100644
index 0000000..b11f831
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ArrayOpCode.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.jetbrains.annotations.Nullable;
+
+import static com.facebook.presto.bytecode.ArrayOpCode.AType.T_BOOLEAN;
+import static com.facebook.presto.bytecode.ArrayOpCode.AType.T_BYTE;
+import static com.facebook.presto.bytecode.ArrayOpCode.AType.T_CHAR;
+import static com.facebook.presto.bytecode.ArrayOpCode.AType.T_DOUBLE;
+import static com.facebook.presto.bytecode.ArrayOpCode.AType.T_FLOAT;
+import static com.facebook.presto.bytecode.ArrayOpCode.AType.T_INT;
+import static com.facebook.presto.bytecode.ArrayOpCode.AType.T_LONG;
+import static com.facebook.presto.bytecode.ArrayOpCode.AType.T_SHORT;
+import static com.facebook.presto.bytecode.OpCode.AALOAD;
+import static com.facebook.presto.bytecode.OpCode.AASTORE;
+import static com.facebook.presto.bytecode.OpCode.BALOAD;
+import static com.facebook.presto.bytecode.OpCode.BASTORE;
+import static com.facebook.presto.bytecode.OpCode.CALOAD;
+import static com.facebook.presto.bytecode.OpCode.CASTORE;
+import static com.facebook.presto.bytecode.OpCode.DALOAD;
+import static com.facebook.presto.bytecode.OpCode.DASTORE;
+import static com.facebook.presto.bytecode.OpCode.FALOAD;
+import static com.facebook.presto.bytecode.OpCode.FASTORE;
+import static com.facebook.presto.bytecode.OpCode.IALOAD;
+import static com.facebook.presto.bytecode.OpCode.IASTORE;
+import static com.facebook.presto.bytecode.OpCode.LALOAD;
+import static com.facebook.presto.bytecode.OpCode.LASTORE;
+import static com.facebook.presto.bytecode.OpCode.SALOAD;
+import static com.facebook.presto.bytecode.OpCode.SASTORE;
+import static java.util.Arrays.stream;
+import static java.util.Objects.requireNonNull;
+
+public enum ArrayOpCode {
+ NOT_PRIMITIVE(null, AALOAD, AASTORE, null),
+ BYTE(byte.class, BALOAD, BASTORE, T_BYTE),
+ BOOLEAN(boolean.class, BALOAD, BASTORE, T_BOOLEAN),
+ CHAR(char.class, CALOAD, CASTORE, T_CHAR),
+ SHORT(short.class, SALOAD, SASTORE, T_SHORT),
+ INT(int.class, IALOAD, IASTORE, T_INT),
+ LONG(long.class, LALOAD, LASTORE, T_LONG),
+ FLOAT(float.class, FALOAD, FASTORE, T_FLOAT),
+ DOUBLE(double.class, DALOAD, DASTORE, T_DOUBLE);
+
+ private final OpCode load;
+ private final OpCode store;
+ private final AType atype;
+ private final Class<?> type;
+
+ private static final Map<Class<?>, ArrayOpCode> arrayOpCodeMap = initializeArrayOpCodeMap();
+
+ ArrayOpCode(@Nullable Class<?> clazz, OpCode load, OpCode store, @Nullable AType atype) {
+ this.type = clazz;
+ this.load = requireNonNull(load, "load is null");
+ this.store = requireNonNull(store, "store is null");
+ this.atype = atype;
+ }
+
+ public OpCode getLoad() {
+ return load;
+ }
+
+ public OpCode getStore() {
+ return store;
+ }
+
+ public int getAtype() {
+ return atype.getType();
+ }
+
+ public Class<?> getType() {
+ return type;
+ }
+
+ public static ArrayOpCode getArrayOpCode(ParameterizedType type) {
+ if (!type.isPrimitive()) {
+ return NOT_PRIMITIVE;
+ }
+ ArrayOpCode arrayOpCode = arrayOpCodeMap.get(type.getPrimitiveType());
+ if (arrayOpCode == null) {
+ throw new IllegalArgumentException("unsupported primitive type " + type);
+ }
+ return arrayOpCode;
+ }
+
+ static Map<Class<?>, ArrayOpCode> initializeArrayOpCodeMap() {
+ return stream(values()).filter(o -> o.getType() != null).collect(Collectors.toMap(ArrayOpCode::getType, Function.identity()));
+ }
+
+ enum AType {
+ T_BOOLEAN(4),
+ T_CHAR(5),
+ T_FLOAT(6),
+ T_DOUBLE(7),
+ T_BYTE(8),
+ T_SHORT(9),
+ T_INT(10),
+ T_LONG(11);
+
+ private final int type;
+
+ AType(int type) {
+ this.type = type;
+ }
+
+ int getType() {
+ return type;
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ByteCodeTooLargeException.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ByteCodeTooLargeException.java
new file mode 100644
index 0000000..49c7285
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ByteCodeTooLargeException.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+public class ByteCodeTooLargeException
+ extends RuntimeException
+{
+ public ByteCodeTooLargeException(Exception cause)
+ {
+ super(cause);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeBlock.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeBlock.java
new file mode 100644
index 0000000..06d2b5b
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeBlock.java
@@ -0,0 +1,837 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import com.facebook.presto.bytecode.debug.LineNumberNode;
+import com.facebook.presto.bytecode.instruction.Constant;
+import com.facebook.presto.bytecode.instruction.InvokeInstruction;
+import com.facebook.presto.bytecode.instruction.JumpInstruction;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import com.facebook.presto.bytecode.instruction.TypeInstruction;
+import com.facebook.presto.bytecode.instruction.VariableInstruction;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+
+import static com.facebook.presto.bytecode.Access.STATIC;
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static com.facebook.presto.bytecode.instruction.Constant.loadBoolean;
+import static com.facebook.presto.bytecode.instruction.Constant.loadClass;
+import static com.facebook.presto.bytecode.instruction.Constant.loadDouble;
+import static com.facebook.presto.bytecode.instruction.Constant.loadFloat;
+import static com.facebook.presto.bytecode.instruction.Constant.loadInt;
+import static com.facebook.presto.bytecode.instruction.Constant.loadLong;
+import static com.facebook.presto.bytecode.instruction.Constant.loadNumber;
+import static com.facebook.presto.bytecode.instruction.FieldInstruction.getFieldInstruction;
+import static com.facebook.presto.bytecode.instruction.FieldInstruction.getStaticInstruction;
+import static com.facebook.presto.bytecode.instruction.FieldInstruction.putFieldInstruction;
+import static com.facebook.presto.bytecode.instruction.FieldInstruction.putStaticInstruction;
+import static com.facebook.presto.bytecode.instruction.TypeInstruction.cast;
+import static com.facebook.presto.bytecode.instruction.TypeInstruction.instanceOf;
+import static com.facebook.presto.bytecode.instruction.VariableInstruction.loadVariable;
+import static com.facebook.presto.bytecode.instruction.VariableInstruction.storeVariable;
+
+@SuppressWarnings("UnusedDeclaration")
+public class BytecodeBlock
+ implements BytecodeNode {
+ private final List<BytecodeNode> nodes = new ArrayList<>();
+
+ private String description;
+ private int currentLineNumber = -1;
+
+ public String getDescription() {
+ return description;
+ }
+
+ public BytecodeBlock setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.copyOf(nodes);
+ }
+
+ public BytecodeBlock append(BytecodeNode node) {
+ nodes.add(node);
+ return this;
+ }
+
+ public BytecodeBlock comment(String comment) {
+ nodes.add(new Comment(comment));
+ return this;
+ }
+
+ public BytecodeBlock comment(String comment, Object... args) {
+ nodes.add(new Comment(String.format(comment, args)));
+ return this;
+ }
+
+ public boolean isEmpty() {
+ return nodes.isEmpty();
+ }
+
+ public BytecodeBlock visitLabel(LabelNode label) {
+ nodes.add(label);
+ return this;
+ }
+
+ public BytecodeBlock gotoLabel(LabelNode label) {
+ nodes.add(JumpInstruction.jump(label));
+ return this;
+ }
+
+ public BytecodeBlock ifFalseGoto(LabelNode label) {
+ return ifZeroGoto(label);
+ }
+
+ public BytecodeBlock ifTrueGoto(LabelNode label) {
+ return ifNotZeroGoto(label);
+ }
+
+ public BytecodeBlock ifZeroGoto(LabelNode label) {
+ nodes.add(JumpInstruction.jumpIfEqualZero(label));
+ return this;
+ }
+
+ public BytecodeBlock ifNotZeroGoto(LabelNode label) {
+ nodes.add(JumpInstruction.jumpIfNotEqualZero(label));
+ return this;
+ }
+
+ public BytecodeBlock ifNullGoto(LabelNode label) {
+ nodes.add(JumpInstruction.jumpIfNull(label));
+ return this;
+ }
+
+ public BytecodeBlock ifNotNullGoto(LabelNode label) {
+ nodes.add(JumpInstruction.jumpIfNotNull(label));
+ return this;
+ }
+
+ public BytecodeBlock intAdd() {
+ nodes.add(OpCode.IADD);
+ return this;
+ }
+
+ public BytecodeBlock longAdd() {
+ nodes.add(OpCode.LADD);
+ return this;
+ }
+
+ public BytecodeBlock longCompare() {
+ nodes.add(OpCode.LCMP);
+ return this;
+ }
+
+ /**
+ * Compare two doubles. If either is NaN comparison is -1.
+ */
+ public BytecodeBlock doubleCompareNanLess() {
+ nodes.add(OpCode.DCMPL);
+ return this;
+ }
+
+ /**
+ * Compare two doubles. If either is NaN comparison is 1.
+ */
+ public BytecodeBlock doubleCompareNanGreater() {
+ nodes.add(OpCode.DCMPG);
+ return this;
+ }
+
+ public BytecodeBlock intLeftShift() {
+ nodes.add(OpCode.ISHL);
+ return this;
+ }
+
+ public BytecodeBlock intRightShift() {
+ nodes.add(OpCode.ISHR);
+ return this;
+ }
+
+ public BytecodeBlock longLeftShift() {
+ nodes.add(OpCode.LSHL);
+ return this;
+ }
+
+ public BytecodeBlock longRightShift() {
+ nodes.add(OpCode.LSHR);
+ return this;
+ }
+
+ public BytecodeBlock unsignedIntRightShift() {
+ nodes.add(OpCode.IUSHR);
+ return this;
+ }
+
+ public BytecodeBlock unsignedLongRightShift() {
+ nodes.add(OpCode.LUSHR);
+ return this;
+ }
+
+ public BytecodeBlock intBitAnd() {
+ nodes.add(OpCode.IAND);
+ return this;
+ }
+
+ public BytecodeBlock intBitOr() {
+ nodes.add(OpCode.IOR);
+ return this;
+ }
+
+ public BytecodeBlock intBitXor() {
+ nodes.add(OpCode.IXOR);
+ return this;
+ }
+
+ public BytecodeBlock longBitAnd() {
+ nodes.add(OpCode.LAND);
+ return this;
+ }
+
+ public BytecodeBlock longBitOr() {
+ nodes.add(OpCode.LOR);
+ return this;
+ }
+
+ public BytecodeBlock longBitXor() {
+ nodes.add(OpCode.LXOR);
+ return this;
+ }
+
+ public BytecodeBlock intNegate() {
+ nodes.add(OpCode.INEG);
+ return this;
+ }
+
+ public BytecodeBlock intToLong() {
+ nodes.add(OpCode.I2L);
+ return this;
+ }
+
+ public BytecodeBlock longNegate() {
+ nodes.add(OpCode.LNEG);
+ return this;
+ }
+
+ public BytecodeBlock longToInt() {
+ nodes.add(OpCode.L2I);
+ return this;
+ }
+
+ public BytecodeBlock isInstanceOf(Class<?> type) {
+ nodes.add(instanceOf(type));
+ return this;
+ }
+
+ public BytecodeBlock isInstanceOf(ParameterizedType type) {
+ nodes.add(instanceOf(type));
+ return this;
+ }
+
+ public BytecodeBlock checkCast(Class<?> type) {
+ nodes.add(cast(type));
+ return this;
+ }
+
+ public BytecodeBlock checkCast(ParameterizedType type) {
+ nodes.add(cast(type));
+ return this;
+ }
+
+ public BytecodeBlock invokeStatic(Method method) {
+ nodes.add(InvokeInstruction.invokeStatic(method));
+ return this;
+ }
+
+ public BytecodeBlock invokeStatic(MethodDefinition method) {
+ nodes.add(InvokeInstruction.invokeStatic(method));
+ return this;
+ }
+
+ public BytecodeBlock invokeStatic(Class<?> type, String name, Class<?> returnType, Class<?>... parameterTypes) {
+ nodes.add(InvokeInstruction.invokeStatic(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeStatic(Class<?> type, String name, Class<?> returnType,
+ Collection<Class<?>> parameterTypes) {
+ nodes.add(InvokeInstruction.invokeStatic(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeStatic(ParameterizedType type, String name, ParameterizedType returnType,
+ ParameterizedType... parameterTypes) {
+ nodes.add(InvokeInstruction.invokeStatic(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeStatic(ParameterizedType type, String name, ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes) {
+ nodes.add(InvokeInstruction.invokeStatic(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeVirtual(Method method) {
+ nodes.add(InvokeInstruction.invokeVirtual(method));
+ return this;
+ }
+
+ public BytecodeBlock invokeVirtual(MethodDefinition method) {
+ nodes.add(InvokeInstruction.invokeVirtual(method));
+ return this;
+ }
+
+ public BytecodeBlock invokeVirtual(Class<?> type, String name, Class<?> returnType, Class<?>... parameterTypes) {
+ nodes.add(InvokeInstruction.invokeVirtual(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeVirtual(Class<?> type, String name, Class<?> returnType,
+ Collection<Class<?>> parameterTypes) {
+ nodes.add(InvokeInstruction.invokeVirtual(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeVirtual(ParameterizedType type, String name, ParameterizedType returnType,
+ ParameterizedType... parameterTypes) {
+ nodes.add(InvokeInstruction.invokeVirtual(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeVirtual(ParameterizedType type, String name, ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes) {
+ nodes.add(InvokeInstruction.invokeVirtual(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeInterface(Method method) {
+ nodes.add(InvokeInstruction.invokeInterface(method));
+ return this;
+ }
+
+ public BytecodeBlock invokeInterface(MethodDefinition method) {
+ nodes.add(InvokeInstruction.invokeInterface(method));
+ return this;
+ }
+
+ public BytecodeBlock invokeInterface(Class<?> type, String name, Class<?> returnType, Class<?>... parameterTypes) {
+ nodes.add(InvokeInstruction.invokeInterface(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeInterface(Class<?> type, String name, Class<?> returnType,
+ Collection<Class<?>> parameterTypes) {
+ nodes.add(InvokeInstruction.invokeInterface(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeInterface(ParameterizedType type, String name, ParameterizedType returnType,
+ ParameterizedType... parameterTypes) {
+ nodes.add(InvokeInstruction.invokeInterface(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeInterface(ParameterizedType type, String name, ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes) {
+ nodes.add(InvokeInstruction.invokeInterface(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeConstructor(Constructor<?> constructor) {
+ nodes.add(InvokeInstruction.invokeConstructor(constructor));
+ return this;
+ }
+
+ public BytecodeBlock invokeConstructor(Class<?> type, Class<?>... parameterTypes) {
+ nodes.add(InvokeInstruction.invokeConstructor(type, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeConstructor(Class<?> type, Collection<Class<?>> parameterTypes) {
+ nodes.add(InvokeInstruction.invokeConstructor(type, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeConstructor(ParameterizedType type, ParameterizedType... parameterTypes) {
+ nodes.add(InvokeInstruction.invokeConstructor(type, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeConstructor(ParameterizedType type, Collection<ParameterizedType> parameterTypes) {
+ nodes.add(InvokeInstruction.invokeConstructor(type, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeSpecial(Method method) {
+ nodes.add(InvokeInstruction.invokeSpecial(method));
+ return this;
+ }
+
+ public BytecodeBlock invokeSpecial(MethodDefinition method) {
+ nodes.add(InvokeInstruction.invokeSpecial(method));
+ return this;
+ }
+
+ public BytecodeBlock invokeSpecial(Class<?> type, String name, Class<?> returnType, Class<?>... parameterTypes) {
+ nodes.add(InvokeInstruction.invokeSpecial(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeSpecial(Class<?> type, String name, Class<?> returnType,
+ Collection<Class<?>> parameterTypes) {
+ nodes.add(InvokeInstruction.invokeSpecial(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeSpecial(ParameterizedType type, String name, ParameterizedType returnType,
+ ParameterizedType... parameterTypes) {
+ nodes.add(InvokeInstruction.invokeSpecial(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeSpecial(ParameterizedType type, String name, ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes) {
+ nodes.add(InvokeInstruction.invokeSpecial(type, name, returnType, parameterTypes));
+ return this;
+ }
+
+ public BytecodeBlock invokeDynamic(String name, MethodType methodType, Method bootstrapMethod,
+ Object... defaultBootstrapArguments) {
+ nodes.add(InvokeInstruction.invokeDynamic(name, methodType, bootstrapMethod, defaultBootstrapArguments));
+ return this;
+ }
+
+ public BytecodeNode invokeDynamic(String name,
+ ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes,
+ Method bootstrapMethod,
+ List<Object> bootstrapArgs) {
+ nodes.add(InvokeInstruction.invokeDynamic(name, returnType, parameterTypes, bootstrapMethod, bootstrapArgs));
+ return this;
+ }
+
+ public BytecodeBlock ret(Class<?> type) {
+ if (type == long.class) {
+ retLong();
+ }
+ else if (type == boolean.class) {
+ retBoolean();
+ }
+ else if (type == int.class || type == byte.class || type == char.class || type == short.class) {
+ retInt();
+ }
+ else if (type == float.class) {
+ retFloat();
+ }
+ else if (type == double.class) {
+ retDouble();
+ }
+ else if (type == void.class) {
+ ret();
+ }
+ else if (!type.isPrimitive()) {
+ retObject();
+ }
+ else {
+ throw new IllegalArgumentException("Unsupported type: " + type.getName());
+ }
+
+ return this;
+ }
+
+ public BytecodeBlock ret() {
+ nodes.add(OpCode.RETURN);
+ return this;
+ }
+
+ public BytecodeBlock retObject() {
+ nodes.add(OpCode.ARETURN);
+ return this;
+ }
+
+ public BytecodeBlock retFloat() {
+ nodes.add(OpCode.FRETURN);
+ return this;
+ }
+
+ public BytecodeBlock retDouble() {
+ nodes.add(OpCode.DRETURN);
+ return this;
+ }
+
+ public BytecodeBlock retBoolean() {
+ nodes.add(OpCode.IRETURN);
+ return this;
+ }
+
+ public BytecodeBlock retLong() {
+ nodes.add(OpCode.LRETURN);
+ return this;
+ }
+
+ public BytecodeBlock retInt() {
+ nodes.add(OpCode.IRETURN);
+ return this;
+ }
+
+ public BytecodeBlock throwObject() {
+ nodes.add(OpCode.ATHROW);
+ return this;
+ }
+
+ public BytecodeBlock newObject(Class<?> type) {
+ nodes.add(TypeInstruction.newObject(type));
+ return this;
+ }
+
+ public BytecodeBlock newObject(ParameterizedType type) {
+ nodes.add(TypeInstruction.newObject(type));
+ return this;
+ }
+
+ public BytecodeBlock newArray(Class<?> type) {
+ nodes.add(TypeInstruction.newObjectArray(type));
+ return this;
+ }
+
+ public BytecodeBlock dup() {
+ nodes.add(OpCode.DUP);
+ return this;
+ }
+
+ public BytecodeBlock dup(Class<?> type) {
+ if (type == long.class || type == double.class) {
+ nodes.add(OpCode.DUP2);
+ }
+ else if (type != void.class) {
+ nodes.add(OpCode.DUP);
+ }
+ return this;
+ }
+
+ public BytecodeBlock pop() {
+ nodes.add(OpCode.POP);
+ return this;
+ }
+
+ public BytecodeBlock pop(Class<?> type) {
+ if (type == long.class || type == double.class) {
+ nodes.add(OpCode.POP2);
+ }
+ else if (type != void.class) {
+ nodes.add(OpCode.POP);
+ }
+ return this;
+ }
+
+ public BytecodeBlock pop(ParameterizedType type) {
+ Class<?> primitiveType = type.getPrimitiveType();
+ if (primitiveType == long.class || primitiveType == double.class) {
+ nodes.add(OpCode.POP2);
+ }
+ else if (primitiveType != void.class) {
+ nodes.add(OpCode.POP);
+ }
+ return this;
+ }
+
+ public BytecodeBlock swap() {
+ nodes.add(OpCode.SWAP);
+ return this;
+ }
+
+ //
+ // Fields (non-static)
+ //
+
+ public BytecodeBlock getField(Field field) {
+ return getField(field.getDeclaringClass(), field.getName(), field.getType());
+ }
+
+ public BytecodeBlock getField(FieldDefinition field) {
+ getField(field.getDeclaringClass().getType(), field.getName(), field.getType());
+ return this;
+ }
+
+ public BytecodeBlock getField(Class<?> target, String fieldName, Class<?> fieldType) {
+ getField(type(target), fieldName, type(fieldType));
+ return this;
+ }
+
+ public BytecodeBlock getField(ParameterizedType target, String fieldName, ParameterizedType fieldType) {
+ nodes.add(getFieldInstruction(target, fieldName, fieldType));
+ return this;
+ }
+
+ public BytecodeBlock putField(Field field) {
+ return putField(field.getDeclaringClass(), field.getName(), field.getType());
+ }
+
+ public BytecodeBlock putField(Class<?> target, String fieldName, Class<?> fieldType) {
+ putField(type(target), fieldName, type(fieldType));
+ return this;
+ }
+
+ public BytecodeBlock putField(FieldDefinition field) {
+ checkArgument(!field.getAccess().contains(STATIC), "Field is static: %s", field);
+ putField(field.getDeclaringClass().getType(), field.getName(), field.getType());
+ return this;
+ }
+
+ public BytecodeBlock putField(ParameterizedType target, String fieldName, ParameterizedType fieldType) {
+ nodes.add(putFieldInstruction(target, fieldName, fieldType));
+ return this;
+ }
+
+ //
+ // Static fields
+ //
+
+ public BytecodeBlock getStaticField(FieldDefinition field) {
+ getStaticField(field.getDeclaringClass().getType(), field.getName(), field.getType());
+ return this;
+ }
+
+ public BytecodeBlock getStaticField(Field field) {
+ checkArgument(Modifier.isStatic(field.getModifiers()), "Field is not static: %s", field);
+ getStaticField(type(field.getDeclaringClass()), field.getName(), type(field.getType()));
+ return this;
+ }
+
+ public BytecodeBlock getStaticField(Class<?> target, String fieldName, Class<?> fieldType) {
+ nodes.add(getStaticInstruction(target, fieldName, fieldType));
+ return this;
+ }
+
+ public BytecodeBlock getStaticField(ParameterizedType target, String fieldName, ParameterizedType fieldType) {
+ nodes.add(getStaticInstruction(target, fieldName, fieldType));
+ return this;
+ }
+
+ public BytecodeBlock getStaticField(ParameterizedType target, FieldDefinition field) {
+ nodes.add(getStaticInstruction(target, field.getName(), field.getType()));
+ return this;
+ }
+
+ public BytecodeBlock putStaticField(FieldDefinition field) {
+ putStaticField(field.getDeclaringClass().getType(), field.getName(), field.getType());
+ return this;
+ }
+
+ public BytecodeBlock putStaticField(ParameterizedType target, FieldDefinition field) {
+ checkArgument(field.getAccess().contains(STATIC), "Field is not static: %s", field);
+ putStaticField(target, field.getName(), field.getType());
+ return this;
+ }
+
+ public BytecodeBlock putStaticField(ParameterizedType target, String fieldName, ParameterizedType fieldType) {
+ nodes.add(putStaticInstruction(target, fieldName, fieldType));
+ return this;
+ }
+
+ //
+ // Load constants
+ //
+
+ public BytecodeBlock pushNull() {
+ nodes.add(OpCode.ACONST_NULL);
+ return this;
+ }
+
+ public BytecodeBlock push(Class<?> type) {
+ nodes.add(loadClass(type));
+ return this;
+ }
+
+ public BytecodeBlock push(ParameterizedType type) {
+ nodes.add(loadClass(type));
+ return this;
+ }
+
+ public BytecodeBlock push(String value) {
+ nodes.add(Constant.loadString(value));
+ return this;
+ }
+
+ public BytecodeBlock push(Number value) {
+ nodes.add(loadNumber(value));
+ return this;
+ }
+
+ public BytecodeBlock push(int value) {
+ nodes.add(loadInt(value));
+ return this;
+ }
+
+ public BytecodeBlock push(boolean value) {
+ nodes.add(loadBoolean(value));
+ return this;
+ }
+
+ public BytecodeBlock pushJavaDefault(Class<?> type) {
+ if (type == void.class) {
+ return this;
+ }
+ if (type == boolean.class || type == byte.class || type == char.class || type == short.class || type == int.class) {
+ return push(0);
+ }
+ if (type == long.class) {
+ return push(0L);
+ }
+ if (type == float.class) {
+ return push(0.0f);
+ }
+ if (type == double.class) {
+ return push(0.0d);
+ }
+ return pushNull();
+ }
+
+ public BytecodeBlock initializeVariable(Variable variable) {
+ ParameterizedType type = variable.getType();
+ if (type.getType().length() == 1) {
+ switch (type.getType().charAt(0)) {
+ case 'B':
+ case 'Z':
+ case 'S':
+ case 'C':
+ case 'I':
+ nodes.add(loadInt(0));
+ break;
+ case 'F':
+ nodes.add(loadFloat(0));
+ break;
+ case 'D':
+ nodes.add(loadDouble(0));
+ break;
+ case 'J':
+ nodes.add(loadLong(0));
+ break;
+ default:
+ checkArgument(false, "Unknown type '%s'", variable.getType());
+ }
+ }
+ else {
+ nodes.add(Constant.loadNull());
+ }
+
+ nodes.add(storeVariable(variable));
+
+ return this;
+ }
+
+ public BytecodeBlock getVariable(Variable variable) {
+ nodes.add(loadVariable(variable));
+ return this;
+ }
+
+ public BytecodeBlock putVariable(Variable variable) {
+ nodes.add(storeVariable(variable));
+ return this;
+ }
+
+ public BytecodeBlock putVariable(Variable variable, Class<?> type) {
+ nodes.add(loadClass(type));
+ putVariable(variable);
+ return this;
+ }
+
+ public BytecodeBlock putVariable(Variable variable, ParameterizedType type) {
+ nodes.add(loadClass(type));
+ putVariable(variable);
+ return this;
+ }
+
+ public BytecodeBlock putVariable(Variable variable, String value) {
+ nodes.add(Constant.loadString(value));
+ putVariable(variable);
+ return this;
+ }
+
+ public BytecodeBlock putVariable(Variable variable, Number value) {
+ nodes.add(loadNumber(value));
+ putVariable(variable);
+ return this;
+ }
+
+ public BytecodeBlock putVariable(Variable variable, int value) {
+ nodes.add(loadInt(value));
+ putVariable(variable);
+ return this;
+ }
+
+ public BytecodeBlock putVariable(Variable variable, boolean value) {
+ nodes.add(loadBoolean(value));
+ putVariable(variable);
+ return this;
+ }
+
+ public BytecodeBlock incrementVariable(Variable variable, byte increment) {
+ String type = variable.getType().getClassName();
+ checkArgument(List.of("byte", "short", "int").contains(type), "variable must be an byte, short or int, but is %s", type);
+ nodes.add(VariableInstruction.incrementVariable(variable, increment));
+ return this;
+ }
+
+ public BytecodeBlock getObjectArrayElement() {
+ nodes.add(OpCode.AALOAD);
+ return this;
+ }
+
+ public BytecodeBlock putObjectArrayElement() {
+ nodes.add(OpCode.AASTORE);
+ return this;
+ }
+
+ public BytecodeBlock getIntArrayElement() {
+ nodes.add(OpCode.IALOAD);
+ return this;
+ }
+
+ public BytecodeBlock putIntArrayElement() {
+ nodes.add(OpCode.IASTORE);
+ return this;
+ }
+
+ public BytecodeBlock visitLineNumber(int currentLineNumber) {
+ checkArgument(currentLineNumber >= 0, "currentLineNumber must be positive");
+ if (this.currentLineNumber != currentLineNumber) {
+ nodes.add(new LineNumberNode(currentLineNumber));
+ this.currentLineNumber = currentLineNumber;
+ }
+ return this;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ for (BytecodeNode node : nodes) {
+ node.accept(visitor, generationContext);
+ }
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitBlock(parent, this);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeNode.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeNode.java
new file mode 100644
index 0000000..451d522
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeNode.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+
+public interface BytecodeNode
+{
+ List<BytecodeNode> getChildNodes();
+
+ void accept(MethodVisitor visitor, MethodGenerationContext generationContext);
+
+ <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor);
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeUtils.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeUtils.java
new file mode 100644
index 0000000..66e6a8a
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeUtils.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import java.io.StringWriter;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+import org.jetbrains.annotations.Nullable;
+
+import static java.time.ZoneOffset.UTC;
+
+public final class BytecodeUtils {
+ private static final AtomicLong CLASS_ID = new AtomicLong();
+ private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
+
+ private static final Map<Class<?>, Class<?>> PRIMITIVES_TO_WRAPPERS
+ = Map.of(boolean.class, Boolean.class,
+ byte.class, Byte.class,
+ char.class, Character.class,
+ double.class, Double.class,
+ float.class, Float.class,
+ int.class, Integer.class,
+ long.class, Long.class,
+ short.class, Short.class,
+ void.class, Void.class);
+
+ private BytecodeUtils() {
+ }
+
+ public static ParameterizedType makeClassName(String baseName, Optional<String> suffix) {
+ String className = baseName
+ + "_" + suffix.orElseGet(() -> Instant.now().atZone(UTC).format(TIMESTAMP_FORMAT))
+ + "_" + CLASS_ID.incrementAndGet();
+ String javaClassName = toJavaIdentifierString(className);
+ return ParameterizedType.typeFromJavaClassName("com.facebook.presto.$gen." + javaClassName);
+ }
+
+ public static ParameterizedType makeClassName(String baseName) {
+ return makeClassName(baseName, Optional.empty());
+ }
+
+ public static String toJavaIdentifierString(String className) {
+ // replace invalid characters with '_'
+ return className.codePoints().mapToObj(c -> Character.isJavaIdentifierPart(c) ? c : '_' & 0xFFFF)
+ .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();
+ }
+
+ public static String dumpBytecodeTree(ClassDefinition classDefinition) {
+ StringWriter writer = new StringWriter();
+ new DumpBytecodeVisitor(writer).visitClass(classDefinition);
+ return writer.toString();
+ }
+
+ public static void checkArgument(boolean condition, String errMsg, Object... params) {
+ if (!condition)
+ throw new IllegalArgumentException(String.format(errMsg, params));
+ }
+
+ public static void checkState(boolean expression, @Nullable Object errorMessage) {
+ if (!expression) {
+ throw new IllegalStateException(String.valueOf(errorMessage));
+ }
+ }
+
+ public static void checkState(
+ boolean expression,
+ @Nullable String errorMessageTemplate,
+ Object... errorMessageArgs) {
+ if (!expression) {
+ throw new IllegalStateException(String.format(errorMessageTemplate, errorMessageArgs));
+ }
+ }
+
+ public static <T> Class<T> wrap(Class<T> c) {
+ return c.isPrimitive() ? (Class<T>)PRIMITIVES_TO_WRAPPERS.get(c) : c;
+ }
+
+ public static String repeat(String string, int count) {
+ Objects.requireNonNull(string); // eager for GWT.
+
+ if (count <= 1) {
+ checkArgument(count >= 0, "invalid count: %s", count);
+ return (count == 0) ? "" : string;
+ }
+
+ // IF YOU MODIFY THE CODE HERE, you must update StringsRepeatBenchmark
+ final int len = string.length();
+ final long longSize = (long)len * (long)count;
+ final int size = (int)longSize;
+ if (size != longSize) {
+ throw new ArrayIndexOutOfBoundsException("Required array size too large: " + longSize);
+ }
+
+ final char[] array = new char[size];
+ string.getChars(0, len, array, 0);
+ int n;
+ for (n = len; n < size - n; n <<= 1) {
+ System.arraycopy(array, 0, array, n, n);
+ }
+ System.arraycopy(array, 0, array, n, size - n);
+ return new String(array);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeVisitor.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeVisitor.java
new file mode 100644
index 0000000..37ff572
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/BytecodeVisitor.java
@@ -0,0 +1,327 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import com.facebook.presto.bytecode.control.DoWhileLoop;
+import com.facebook.presto.bytecode.control.FlowControl;
+import com.facebook.presto.bytecode.control.ForLoop;
+import com.facebook.presto.bytecode.control.IfStatement;
+import com.facebook.presto.bytecode.control.SwitchStatement;
+import com.facebook.presto.bytecode.control.TryCatch;
+import com.facebook.presto.bytecode.control.WhileLoop;
+import com.facebook.presto.bytecode.debug.DebugNode;
+import com.facebook.presto.bytecode.debug.LineNumberNode;
+import com.facebook.presto.bytecode.debug.LocalVariableNode;
+import com.facebook.presto.bytecode.expression.BytecodeExpression;
+import com.facebook.presto.bytecode.instruction.Constant;
+import com.facebook.presto.bytecode.instruction.Constant.BooleanConstant;
+import com.facebook.presto.bytecode.instruction.Constant.BoxedBooleanConstant;
+import com.facebook.presto.bytecode.instruction.Constant.BoxedDoubleConstant;
+import com.facebook.presto.bytecode.instruction.Constant.BoxedFloatConstant;
+import com.facebook.presto.bytecode.instruction.Constant.BoxedIntegerConstant;
+import com.facebook.presto.bytecode.instruction.Constant.BoxedLongConstant;
+import com.facebook.presto.bytecode.instruction.Constant.ClassConstant;
+import com.facebook.presto.bytecode.instruction.Constant.DoubleConstant;
+import com.facebook.presto.bytecode.instruction.Constant.FloatConstant;
+import com.facebook.presto.bytecode.instruction.Constant.IntConstant;
+import com.facebook.presto.bytecode.instruction.Constant.LongConstant;
+import com.facebook.presto.bytecode.instruction.Constant.StringConstant;
+import com.facebook.presto.bytecode.instruction.FieldInstruction;
+import com.facebook.presto.bytecode.instruction.FieldInstruction.GetFieldInstruction;
+import com.facebook.presto.bytecode.instruction.FieldInstruction.PutFieldInstruction;
+import com.facebook.presto.bytecode.instruction.InstructionNode;
+import com.facebook.presto.bytecode.instruction.InvokeInstruction;
+import com.facebook.presto.bytecode.instruction.InvokeInstruction.InvokeDynamicInstruction;
+import com.facebook.presto.bytecode.instruction.JumpInstruction;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import com.facebook.presto.bytecode.instruction.VariableInstruction;
+import com.facebook.presto.bytecode.instruction.VariableInstruction.IncrementVariableInstruction;
+import com.facebook.presto.bytecode.instruction.VariableInstruction.LoadVariableInstruction;
+import com.facebook.presto.bytecode.instruction.VariableInstruction.StoreVariableInstruction;
+
+public class BytecodeVisitor<T>
+{
+ public T visitClass(ClassDefinition classDefinition)
+ {
+ for (AnnotationDefinition annotationDefinition : classDefinition.getAnnotations()) {
+ visitAnnotation(classDefinition, annotationDefinition);
+ }
+ for (FieldDefinition fieldDefinition : classDefinition.getFields()) {
+ visitField(classDefinition, fieldDefinition);
+ }
+ for (MethodDefinition methodDefinition : classDefinition.getMethods()) {
+ visitMethod(classDefinition, methodDefinition);
+ }
+ return null;
+ }
+
+ public T visitAnnotation(Object parent, AnnotationDefinition annotationDefinition)
+ {
+ return null;
+ }
+
+ public T visitField(ClassDefinition classDefinition, FieldDefinition fieldDefinition)
+ {
+ for (AnnotationDefinition annotationDefinition : fieldDefinition.getAnnotations()) {
+ visitAnnotation(fieldDefinition, annotationDefinition);
+ }
+ return null;
+ }
+
+ public T visitMethod(ClassDefinition classDefinition, MethodDefinition methodDefinition)
+ {
+ for (AnnotationDefinition annotationDefinition : methodDefinition.getAnnotations()) {
+ visitAnnotation(methodDefinition, annotationDefinition);
+ }
+ methodDefinition.getBody().accept(null, this);
+ return null;
+ }
+
+ public T visitNode(BytecodeNode parent, BytecodeNode node)
+ {
+ for (BytecodeNode child : node.getChildNodes()) {
+ child.accept(node, this);
+ }
+ return null;
+ }
+
+ //
+ // Comment
+ //
+
+ public T visitComment(BytecodeNode parent, Comment node)
+ {
+ return visitNode(parent, node);
+ }
+
+ //
+ // Block
+ //
+
+ public T visitBlock(BytecodeNode parent, BytecodeBlock block)
+ {
+ return visitNode(parent, block);
+ }
+
+ //
+ // Bytecode Expression
+ //
+ public T visitBytecodeExpression(BytecodeNode parent, BytecodeExpression expression)
+ {
+ return visitNode(parent, expression);
+ }
+
+ //
+ // Flow Control
+ //
+
+ public T visitFlowControl(BytecodeNode parent, FlowControl flowControl)
+ {
+ return visitNode(parent, flowControl);
+ }
+
+ public T visitTryCatch(BytecodeNode parent, TryCatch tryCatch)
+ {
+ return visitFlowControl(parent, tryCatch);
+ }
+
+ public T visitIf(BytecodeNode parent, IfStatement ifStatement)
+ {
+ return visitFlowControl(parent, ifStatement);
+ }
+
+ public T visitFor(BytecodeNode parent, ForLoop forLoop)
+ {
+ return visitFlowControl(parent, forLoop);
+ }
+
+ public T visitWhile(BytecodeNode parent, WhileLoop whileLoop)
+ {
+ return visitFlowControl(parent, whileLoop);
+ }
+
+ public T visitDoWhile(BytecodeNode parent, DoWhileLoop doWhileLoop)
+ {
+ return visitFlowControl(parent, doWhileLoop);
+ }
+
+ public T visitSwitch(BytecodeNode parent, SwitchStatement switchStatement)
+ {
+ return visitFlowControl(parent, switchStatement);
+ }
+
+ //
+ // Instructions
+ //
+
+ public T visitInstruction(BytecodeNode parent, InstructionNode node)
+ {
+ return visitNode(parent, node);
+ }
+
+ public T visitLabel(BytecodeNode parent, LabelNode labelNode)
+ {
+ return visitInstruction(parent, labelNode);
+ }
+
+ public T visitJumpInstruction(BytecodeNode parent, JumpInstruction jumpInstruction)
+ {
+ return visitInstruction(parent, jumpInstruction);
+ }
+
+ //
+ // Constants
+ //
+
+ public T visitConstant(BytecodeNode parent, Constant constant)
+ {
+ return visitInstruction(parent, constant);
+ }
+
+ public T visitBoxedBooleanConstant(BytecodeNode parent, BoxedBooleanConstant boxedBooleanConstant)
+ {
+ return visitConstant(parent, boxedBooleanConstant);
+ }
+
+ public T visitBooleanConstant(BytecodeNode parent, BooleanConstant booleanConstant)
+ {
+ return visitConstant(parent, booleanConstant);
+ }
+
+ public T visitIntConstant(BytecodeNode parent, IntConstant intConstant)
+ {
+ return visitConstant(parent, intConstant);
+ }
+
+ public T visitBoxedIntegerConstant(BytecodeNode parent, BoxedIntegerConstant boxedIntegerConstant)
+ {
+ return visitConstant(parent, boxedIntegerConstant);
+ }
+
+ public T visitFloatConstant(BytecodeNode parent, FloatConstant floatConstant)
+ {
+ return visitConstant(parent, floatConstant);
+ }
+
+ public T visitBoxedFloatConstant(BytecodeNode parent, BoxedFloatConstant boxedFloatConstant)
+ {
+ return visitConstant(parent, boxedFloatConstant);
+ }
+
+ public T visitLongConstant(BytecodeNode parent, LongConstant longConstant)
+ {
+ return visitConstant(parent, longConstant);
+ }
+
+ public T visitBoxedLongConstant(BytecodeNode parent, BoxedLongConstant boxedLongConstant)
+ {
+ return visitConstant(parent, boxedLongConstant);
+ }
+
+ public T visitDoubleConstant(BytecodeNode parent, DoubleConstant doubleConstant)
+ {
+ return visitConstant(parent, doubleConstant);
+ }
+
+ public T visitBoxedDoubleConstant(BytecodeNode parent, BoxedDoubleConstant boxedDoubleConstant)
+ {
+ return visitConstant(parent, boxedDoubleConstant);
+ }
+
+ public T visitStringConstant(BytecodeNode parent, StringConstant stringConstant)
+ {
+ return visitConstant(parent, stringConstant);
+ }
+
+ public T visitClassConstant(BytecodeNode parent, ClassConstant classConstant)
+ {
+ return visitConstant(parent, classConstant);
+ }
+
+ //
+ // Local Variable Instructions
+ //
+
+ public T visitVariableInstruction(BytecodeNode parent, VariableInstruction variableInstruction)
+ {
+ return visitInstruction(parent, variableInstruction);
+ }
+
+ public T visitLoadVariable(BytecodeNode parent, LoadVariableInstruction loadVariableInstruction)
+ {
+ return visitVariableInstruction(parent, loadVariableInstruction);
+ }
+
+ public T visitStoreVariable(BytecodeNode parent, StoreVariableInstruction storeVariableInstruction)
+ {
+ return visitVariableInstruction(parent, storeVariableInstruction);
+ }
+
+ public T visitIncrementVariable(BytecodeNode parent, IncrementVariableInstruction incrementVariableInstruction)
+ {
+ return visitVariableInstruction(parent, incrementVariableInstruction);
+ }
+
+ //
+ // Field Instructions
+ //
+
+ public T visitFieldInstruction(BytecodeNode parent, FieldInstruction fieldInstruction)
+ {
+ return visitInstruction(parent, fieldInstruction);
+ }
+
+ public T visitGetField(BytecodeNode parent, GetFieldInstruction getFieldInstruction)
+ {
+ return visitFieldInstruction(parent, getFieldInstruction);
+ }
+
+ public T visitPutField(BytecodeNode parent, PutFieldInstruction putFieldInstruction)
+ {
+ return visitFieldInstruction(parent, putFieldInstruction);
+ }
+
+ //
+ // Invoke
+ //
+
+ public T visitInvoke(BytecodeNode parent, InvokeInstruction invokeInstruction)
+ {
+ return visitInstruction(parent, invokeInstruction);
+ }
+
+ public T visitInvokeDynamic(BytecodeNode parent, InvokeDynamicInstruction invokeDynamicInstruction)
+ {
+ return visitInvoke(parent, invokeDynamicInstruction);
+ }
+
+ //
+ // Debug
+ //
+
+ public T visitDebug(BytecodeNode parent, DebugNode debugNode)
+ {
+ return visitNode(parent, debugNode);
+ }
+
+ public T visitLineNumber(BytecodeNode parent, LineNumberNode lineNumberNode)
+ {
+ return visitDebug(parent, lineNumberNode);
+ }
+
+ public T visitLocalVariable(BytecodeNode parent, LocalVariableNode localVariableNode)
+ {
+ return visitDebug(parent, localVariableNode);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassDefinition.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassDefinition.java
new file mode 100644
index 0000000..319e304
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassDefinition.java
@@ -0,0 +1,281 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.objectweb.asm.ClassVisitor;
+
+import static com.facebook.presto.bytecode.Access.INTERFACE;
+import static com.facebook.presto.bytecode.Access.STATIC;
+import static com.facebook.presto.bytecode.Access.a;
+import static com.facebook.presto.bytecode.Access.toAccessModifier;
+import static java.util.Objects.requireNonNull;
+import static org.objectweb.asm.Opcodes.ACC_SUPER;
+import static org.objectweb.asm.Opcodes.V11;
+
+public class ClassDefinition {
+ private final EnumSet<Access> access;
+ private final ParameterizedType type;
+ private final ParameterizedType superClass;
+ private final List<ParameterizedType> interfaces = new ArrayList<>();
+ private final List<AnnotationDefinition> annotations = new ArrayList<>();
+ private final List<FieldDefinition> fields = new ArrayList<>();
+ private final List<MethodDefinition> methods = new ArrayList<>();
+ private final MethodDefinition classInitializer;
+ private String source;
+ private String debug;
+
+ public ClassDefinition(
+ EnumSet<Access> access,
+ String name,
+ ParameterizedType superClass,
+ ParameterizedType... interfaces) {
+ this(access, new ParameterizedType(name), superClass, interfaces);
+ }
+
+ public ClassDefinition(
+ EnumSet<Access> access,
+ ParameterizedType type,
+ ParameterizedType superClass,
+ ParameterizedType... interfaces) {
+ requireNonNull(access, "access is null");
+ requireNonNull(type, "type is null");
+ requireNonNull(superClass, "superClass is null");
+ requireNonNull(interfaces, "interfaces is null");
+
+ this.access = access;
+ this.type = type;
+ this.superClass = superClass;
+ this.interfaces.addAll(List.of(interfaces));
+
+ classInitializer = new MethodDefinition(this, a(STATIC), "<clinit>", ParameterizedType.type(void.class), List.of());
+ }
+
+ public Set<Access> getAccess() {
+ return Set.copyOf(access);
+ }
+
+ public String getName() {
+ return type.getClassName();
+ }
+
+ public ParameterizedType getType() {
+ return type;
+ }
+
+ public ParameterizedType getSuperClass() {
+ return superClass;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public List<ParameterizedType> getInterfaces() {
+ return List.copyOf(interfaces);
+ }
+
+ public List<AnnotationDefinition> getAnnotations() {
+ return List.copyOf(annotations);
+ }
+
+ public List<FieldDefinition> getFields() {
+ return List.copyOf(fields);
+ }
+
+ public List<MethodDefinition> getMethods() {
+ return List.copyOf(methods);
+ }
+
+ public boolean isInterface() {
+ return access.contains(INTERFACE);
+ }
+
+ public void visit(ClassVisitor visitor) {
+ // Generic signature if super class or any interface is generic
+ String signature = null;
+ if (superClass.isGeneric() || interfaces.stream().anyMatch(ParameterizedType::isGeneric)) {
+ signature = genericClassSignature(superClass, interfaces);
+ }
+
+ String[] interfaces = new String[this.interfaces.size()];
+ for (int i = 0; i < interfaces.length; i++) {
+ interfaces[i] = this.interfaces.get(i).getClassName();
+ }
+ int accessModifier = toAccessModifier(access);
+ visitor.visit(V11, isInterface() ? accessModifier : accessModifier | ACC_SUPER, type.getClassName(), signature, superClass.getClassName(), interfaces);
+
+ // visit source
+ if (source != null) {
+ visitor.visitSource(source, debug);
+ }
+
+ // visit annotations
+ for (AnnotationDefinition annotation : annotations) {
+ annotation.visitClassAnnotation(visitor);
+ }
+
+ // visit fields
+ for (FieldDefinition field : fields) {
+ field.visit(visitor);
+ }
+
+ // visit clinit method
+ if (!isInterface()) {
+ classInitializer.visit(visitor, true);
+ }
+
+ // visit methods
+ for (MethodDefinition method : methods) {
+ method.visit(visitor);
+ }
+
+ // done
+ visitor.visitEnd();
+ }
+
+ public AnnotationDefinition declareAnnotation(Class<?> type) {
+ AnnotationDefinition annotationDefinition = new AnnotationDefinition(type);
+ annotations.add(annotationDefinition);
+ return annotationDefinition;
+ }
+
+ public AnnotationDefinition declareAnnotation(ParameterizedType type) {
+ AnnotationDefinition annotationDefinition = new AnnotationDefinition(type);
+ annotations.add(annotationDefinition);
+ return annotationDefinition;
+ }
+
+ public FieldDefinition declareField(EnumSet<Access> access, String name, Class<?> type) {
+ FieldDefinition fieldDefinition = new FieldDefinition(this, access, name, type);
+ fields.add(fieldDefinition);
+ return fieldDefinition;
+ }
+
+ public ClassDefinition addField(EnumSet<Access> access, String name, Class<?> type) {
+ declareField(access, name, type);
+ return this;
+ }
+
+ public FieldDefinition declareField(EnumSet<Access> access, String name, ParameterizedType type) {
+ FieldDefinition fieldDefinition = new FieldDefinition(this, access, name, type);
+ fields.add(fieldDefinition);
+ return fieldDefinition;
+ }
+
+ public ClassDefinition addField(EnumSet<Access> access, String name, ParameterizedType type) {
+ declareField(access, name, type);
+ return this;
+ }
+
+ public ClassDefinition addField(FieldDefinition field) {
+ fields.add(field);
+ return this;
+ }
+
+ public MethodDefinition getClassInitializer() {
+ if (isInterface()) {
+ throw new IllegalAccessError("Interface does not have class initializer");
+ }
+ return classInitializer;
+ }
+
+ public MethodDefinition declareConstructor(
+ EnumSet<Access> access,
+ Parameter... parameters) {
+ return declareMethod(access, "<init>", ParameterizedType.type(void.class), List.of(parameters));
+ }
+
+ public MethodDefinition declareConstructor(
+ EnumSet<Access> access,
+ Collection<Parameter> parameters) {
+ return declareMethod(access, "<init>", ParameterizedType.type(void.class), List.copyOf(parameters));
+ }
+
+ public ClassDefinition declareDefaultConstructor(EnumSet<Access> access) {
+ MethodDefinition constructor = declareConstructor(access);
+ constructor
+ .getBody()
+ .append(constructor.getThis())
+ .invokeConstructor(superClass)
+ .ret();
+ return this;
+ }
+
+ public ClassDefinition addMethod(MethodDefinition method) {
+ methods.add(method);
+ return this;
+ }
+
+ public ClassDefinition visitSource(String source, String debug) {
+ this.source = source;
+ this.debug = debug;
+ return this;
+ }
+
+ public MethodDefinition declareMethod(
+ EnumSet<Access> access,
+ String name,
+ ParameterizedType returnType,
+ Parameter... parameters) {
+ return declareMethod(access, name, returnType, List.of(parameters));
+ }
+
+ public MethodDefinition declareMethod(
+ EnumSet<Access> access,
+ String name,
+ ParameterizedType returnType,
+ Collection<Parameter> parameters) {
+ MethodDefinition methodDefinition = new MethodDefinition(this, access, name, returnType, parameters);
+ for (MethodDefinition method : methods) {
+ if (name.equals(method.getName()) && method.getParameterTypes().equals(methodDefinition.getParameterTypes())) {
+ throw new IllegalArgumentException("Method with same name and signature already exists: " + name);
+ }
+ }
+ methods.add(methodDefinition);
+ return methodDefinition;
+ }
+
+ public static String genericClassSignature(
+ ParameterizedType classType,
+ ParameterizedType... interfaceTypes) {
+
+ return Stream.concat(Stream.of(classType), Stream.of(interfaceTypes))
+ .map(ParameterizedType::toString).collect(Collectors.joining(""));
+ }
+
+ public static String genericClassSignature(
+ ParameterizedType classType,
+ List<ParameterizedType> interfaceTypes) {
+
+ return Stream.concat(Stream.of(classType), interfaceTypes.stream())
+ .map(ParameterizedType::toString).collect(Collectors.joining(""));
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ClassDefinition");
+ sb.append("{access=").append(access);
+ sb.append(", type=").append(type);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassGenerator.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassGenerator.java
new file mode 100644
index 0000000..aa2389f
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassGenerator.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.lang.invoke.MethodHandle;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassTooLargeException;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodTooLargeException;
+import org.objectweb.asm.util.CheckClassAdapter;
+import org.objectweb.asm.util.Textifier;
+import org.objectweb.asm.util.TraceClassVisitor;
+
+import static com.facebook.presto.bytecode.ClassInfoLoader.createClassInfoLoader;
+import static com.facebook.presto.bytecode.ParameterizedType.typeFromJavaClassName;
+import static java.io.Writer.nullWriter;
+import static java.nio.file.Files.createDirectories;
+import static java.util.Objects.requireNonNull;
+
+public class ClassGenerator
+{
+ private final DynamicClassLoader classLoader;
+ private final boolean fakeLineNumbers;
+ private final boolean runAsmVerifier;
+ private final boolean dumpRawBytecode;
+ private final Writer output;
+ private final Optional<Path> dumpClassPath;
+
+ public static ClassGenerator classGenerator(ClassLoader parentClassLoader)
+ {
+ if (parentClassLoader instanceof DynamicClassLoader)
+ return classGenerator((DynamicClassLoader)parentClassLoader);
+
+ return classGenerator(parentClassLoader, Map.of());
+ }
+
+ public static ClassGenerator classGenerator(ClassLoader parentClassLoader, Map<Long, MethodHandle> callSiteBindings)
+ {
+ return classGenerator(new DynamicClassLoader(parentClassLoader, callSiteBindings));
+ }
+
+ public static ClassGenerator classGenerator(DynamicClassLoader classLoader)
+ {
+ return new ClassGenerator(classLoader, false, false, false, nullWriter(), Optional.empty());
+ }
+
+ private ClassGenerator(
+ DynamicClassLoader classLoader,
+ boolean fakeLineNumbers,
+ boolean runAsmVerifier,
+ boolean dumpRawBytecode,
+ Writer output,
+ Optional<Path> dumpClassPath)
+ {
+ this.classLoader = requireNonNull(classLoader, "classLoader is null");
+ this.fakeLineNumbers = fakeLineNumbers;
+ this.runAsmVerifier = runAsmVerifier;
+ this.dumpRawBytecode = dumpRawBytecode;
+ this.output = requireNonNull(output, "output is null");
+ this.dumpClassPath = requireNonNull(dumpClassPath, "dumpClassPath is null");
+ }
+
+ public ClassGenerator fakeLineNumbers(boolean fakeLineNumbers)
+ {
+ return new ClassGenerator(classLoader, fakeLineNumbers, runAsmVerifier, dumpRawBytecode, output, dumpClassPath);
+ }
+
+ public ClassGenerator runAsmVerifier(boolean runAsmVerifier)
+ {
+ return new ClassGenerator(classLoader, fakeLineNumbers, runAsmVerifier, dumpRawBytecode, output, dumpClassPath);
+ }
+
+ public ClassGenerator dumpRawBytecode(boolean dumpRawBytecode)
+ {
+ return new ClassGenerator(classLoader, fakeLineNumbers, runAsmVerifier, dumpRawBytecode, output, dumpClassPath);
+ }
+
+ public ClassGenerator outputTo(Writer output)
+ {
+ return new ClassGenerator(classLoader, fakeLineNumbers, runAsmVerifier, dumpRawBytecode, output, dumpClassPath);
+ }
+
+ public ClassGenerator dumpClassFilesTo(Path dumpClassPath)
+ {
+ return dumpClassFilesTo(Optional.of(dumpClassPath));
+ }
+
+ public ClassGenerator dumpClassFilesTo(Optional<Path> dumpClassPath)
+ {
+ return new ClassGenerator(classLoader, fakeLineNumbers, runAsmVerifier, dumpRawBytecode, output, dumpClassPath);
+ }
+
+ public <T> Class<? extends T> defineClass(ClassDefinition classDefinition, Class<T> superType)
+ {
+ Map<String, Class<?>> classes = defineClasses(List.of(classDefinition));
+
+ return classes.values().stream().findFirst().get().asSubclass(superType);
+ }
+
+ public Map<String, Class<?>> defineClasses(List<ClassDefinition> classDefinitions)
+ {
+ ClassInfoLoader classInfoLoader = createClassInfoLoader(classDefinitions, classLoader);
+ Map<String, byte[]> bytecodes = new LinkedHashMap<>();
+
+ for (ClassDefinition classDefinition : classDefinitions) {
+ // We call the simpler class writer first to get any errors out using simpler setting.
+ // This helps when we have large queries that can potentially cause COMPUTE_FRAMES
+ // (used by SmartClassWriter for doing more thorough analysis)
+ ClassWriter simpleClassWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ classDefinition.visit(simpleClassWriter);
+ try {
+ simpleClassWriter.toByteArray();
+ }
+ catch (ClassTooLargeException | MethodTooLargeException largeCodeException) {
+ throw new ByteCodeTooLargeException(largeCodeException);
+ }
+ catch (RuntimeException e) {
+ throw new CompilationException("Error compiling class: " + classDefinition.getName(), e);
+ }
+
+ ClassWriter writer = new SmartClassWriter(classInfoLoader);
+
+ try {
+ classDefinition.visit(fakeLineNumbers ? new AddFakeLineNumberClassVisitor(writer) : writer);
+ }
+ catch (IndexOutOfBoundsException | NegativeArraySizeException e) {
+ StringWriter out = new StringWriter();
+ classDefinition.visit(new TraceClassVisitor(null, new Textifier(), new PrintWriter(out)));
+ throw new IllegalArgumentException("Error processing class definition:\n" + out, e);
+ }
+
+ byte[] bytecode;
+ try {
+ bytecode = writer.toByteArray();
+ }
+ catch (ClassTooLargeException | MethodTooLargeException largeCodeException) {
+ throw new ByteCodeTooLargeException(largeCodeException);
+ }
+ catch (RuntimeException e) {
+ throw new CompilationException("Error compiling class: " + classDefinition.getName(), e);
+ }
+
+ bytecodes.put(classDefinition.getType().getJavaClassName(), bytecode);
+
+ if (runAsmVerifier) {
+ ClassReader reader = new ClassReader(bytecode);
+ CheckClassAdapter.verify(reader, classLoader, true, new PrintWriter(output));
+ }
+ }
+
+ dumpClassPath.ifPresent(path -> bytecodes.forEach((className, bytecode) -> {
+ String name = typeFromJavaClassName(className).getClassName() + ".class";
+ Path file = path.resolve(name).toAbsolutePath();
+ try {
+ createDirectories(file.getParent());
+ Files.write(file, bytecode);
+ }
+ catch (IOException e) {
+ throw new UncheckedIOException("Failed to write generated class file: " + file, e);
+ }
+ }));
+
+ if (dumpRawBytecode) {
+ for (byte[] bytecode : bytecodes.values()) {
+ ClassReader classReader = new ClassReader(bytecode);
+ classReader.accept(new TraceClassVisitor(new PrintWriter(output)), ClassReader.EXPAND_FRAMES);
+ }
+ }
+
+ Map<String, Class<?>> classes = classLoader.defineClasses(bytecodes);
+
+ try {
+ for (Class<?> clazz : classes.values()) {
+ Class.forName(clazz.getName(), true, clazz.getClassLoader());
+ }
+ } catch (ClassNotFoundException | VerifyError e) {
+ throw new RuntimeException(e);
+ }
+
+ return classes;
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassInfo.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassInfo.java
new file mode 100644
index 0000000..b9b6f64
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassInfo.java
@@ -0,0 +1,169 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.facebook.presto.bytecode;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkState;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static com.facebook.presto.bytecode.ParameterizedType.typeFromPathName;
+import static java.util.Arrays.stream;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * @author Eugene Kuleshov
+ */
+public class ClassInfo {
+ private final ClassInfoLoader loader;
+ private final ParameterizedType type;
+ private final int access;
+ private final ParameterizedType superClass;
+ private final List<ParameterizedType> interfaces;
+ private final List<MethodNode> methods;
+
+ public ClassInfo(ClassInfoLoader loader, ClassNode classNode) {
+ this(loader,
+ typeFromPathName(classNode.name),
+ classNode.access,
+ classNode.superName == null ? null : typeFromPathName(classNode.superName),
+ classNode.interfaces.stream().map(ParameterizedType::typeFromPathName).collect(Collectors.toList()),
+ classNode.methods);
+ }
+
+ public ClassInfo(ClassInfoLoader loader, Class<?> aClass) {
+ this(loader,
+ type(aClass),
+ aClass.getModifiers(),
+ aClass.getSuperclass() == null ? null : type(aClass.getSuperclass()),
+ stream(aClass.getInterfaces()).map(ParameterizedType::type).collect(Collectors.toList()),
+ null);
+ }
+
+ public ClassInfo(ClassInfoLoader loader, ParameterizedType type, int access, ParameterizedType superClass,
+ Collection<ParameterizedType> interfaces, Collection<MethodNode> methods) {
+ requireNonNull(loader, "loader is null");
+ requireNonNull(type, "type is null");
+ requireNonNull(interfaces, "interfaces is null");
+
+ this.loader = loader;
+ this.type = type;
+ this.access = access;
+ this.superClass = superClass;
+ this.interfaces = List.copyOf(interfaces);
+ if (methods != null) {
+ this.methods = List.copyOf(methods);
+ }
+ else {
+ this.methods = null;
+ }
+ }
+
+ public ParameterizedType getType() {
+ return type;
+ }
+
+ public int getModifiers() {
+ return access;
+ }
+
+ public ClassInfo getSuperclass() {
+ if (superClass == null) {
+ return null;
+ }
+ return loader.loadClassInfo(superClass);
+ }
+
+ public List<ClassInfo> getInterfaces() {
+ if (interfaces == null) {
+ return List.of();
+ }
+ return interfaces.stream().map(loader::loadClassInfo).collect(Collectors.toList());
+ }
+
+ public List<MethodNode> getMethods() {
+ checkState(methods != null, "Methods were not loaded for type %s", type);
+ return methods;
+ }
+
+ boolean isInterface() {
+ return (getModifiers() & Opcodes.ACC_INTERFACE) > 0;
+ }
+
+ private boolean implementsInterface(ClassInfo that) {
+ for (ClassInfo classInfo = this; classInfo != null; classInfo = classInfo.getSuperclass()) {
+ for (ClassInfo anInterface : classInfo.getInterfaces()) {
+ if (anInterface.type.equals(that.type) || anInterface.implementsInterface(that)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isSubclassOf(ClassInfo that) {
+ for (ClassInfo classInfo = this; classInfo != null; classInfo = classInfo.getSuperclass()) {
+ if (classInfo.getSuperclass() != null &&
+ classInfo.getSuperclass().type.equals(that.type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isAssignableFrom(ClassInfo that) {
+ if (this == that) {
+ return true;
+ }
+
+ if (that.isSubclassOf(this)) {
+ return true;
+ }
+
+ if (that.implementsInterface(this)) {
+ return true;
+ }
+
+ if (that.isInterface() && getType().equals(type(Object.class))) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return type.toString();
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassInfoLoader.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassInfoLoader.java
new file mode 100644
index 0000000..75f215c
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassInfoLoader.java
@@ -0,0 +1,147 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.facebook.presto.bytecode;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.util.CheckClassAdapter;
+
+import static com.facebook.presto.bytecode.ParameterizedType.typeFromPathName;
+
+public class ClassInfoLoader {
+ public static ClassInfoLoader createClassInfoLoader(Collection<ClassDefinition> classDefinitions,
+ ClassLoader classLoader) {
+ Map<ParameterizedType, ClassNode> classNodes = new HashMap<>();
+ for (ClassDefinition classDefinition : classDefinitions) {
+ ClassNode classNode = new ClassNode();
+ classDefinition.visit(classNode);
+ classNodes.put(classDefinition.getType(), classNode);
+ }
+ return new ClassInfoLoader(Collections.unmodifiableMap(classNodes), Map.of(), classLoader, true);
+ }
+
+ private final Map<ParameterizedType, ClassNode> classNodes;
+ private final Map<ParameterizedType, byte[]> bytecodes;
+ private final ClassLoader classLoader;
+ private final Map<ParameterizedType, ClassInfo> classInfoCache = new HashMap<>();
+ private final boolean loadMethodNodes;
+
+ public ClassInfoLoader(Map<ParameterizedType, ClassNode> classNodes, Map<ParameterizedType, byte[]> bytecodes,
+ ClassLoader classLoader, boolean loadMethodNodes) {
+ this.classNodes = Map.copyOf(classNodes);
+ this.bytecodes = Map.copyOf(bytecodes);
+ this.classLoader = classLoader;
+ this.loadMethodNodes = loadMethodNodes;
+ }
+
+ public ClassInfo loadClassInfo(ParameterizedType type) {
+ ClassInfo classInfo = classInfoCache.get(type);
+ if (classInfo == null) {
+ classInfo = readClassInfoQuick(type);
+ classInfoCache.put(type, classInfo);
+ }
+ return classInfo;
+ }
+
+ private ClassInfo readClassInfoQuick(ParameterizedType type) {
+ // check for user supplied class node
+ ClassNode classNode = classNodes.get(type);
+ if (classNode != null) {
+ return new ClassInfo(this, classNode);
+ }
+
+ // check for user supplied byte code
+ ClassReader classReader;
+ byte[] bytecode = bytecodes.get(type);
+ if (bytecode != null) {
+ classReader = new ClassReader(bytecode);
+ }
+ else {
+ // load class file from class loader
+ String classFileName = type.getClassName() + ".class";
+ try (InputStream is = classLoader.getResourceAsStream(classFileName)) {
+ classReader = new ClassReader(is);
+ }
+ catch (IOException e) {
+ // check if class is already loaded
+ try {
+ Class<?> aClass = classLoader.loadClass(type.getJavaClassName());
+ return new ClassInfo(this, aClass);
+ }
+ catch (ClassNotFoundException e1) {
+ throw new RuntimeException("Class not found " + type, e);
+ }
+ }
+ }
+
+ if (loadMethodNodes) {
+ // slower version that loads all operations
+ classNode = new ClassNode();
+ classReader.accept(new CheckClassAdapter(classNode, false), ClassReader.SKIP_DEBUG);
+
+ return new ClassInfo(this, classNode);
+ }
+ else {
+ // optimized version
+ int header = classReader.header;
+ int access = classReader.readUnsignedShort(header);
+
+ char[] buf = new char[2048];
+
+ // read super class name
+ int superClassIndex = classReader.getItem(classReader.readUnsignedShort(header + 4));
+ ParameterizedType superClass;
+ if (superClassIndex == 0) {
+ superClass = null;
+ }
+ else {
+ superClass = typeFromPathName(classReader.readUTF8(superClassIndex, buf));
+ }
+
+ // read each interface name
+ int interfaceCount = classReader.readUnsignedShort(header + 6);
+ List<ParameterizedType> interfaces = new ArrayList<>();
+ header += 8;
+ for (int i = 0; i < interfaceCount; ++i) {
+ interfaces.add(typeFromPathName(classReader.readClass(header, buf)));
+ header += 2;
+ }
+ return new ClassInfo(this, type, access, superClass, interfaces, null);
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Comment.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Comment.java
new file mode 100644
index 0000000..761e9a6
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Comment.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import com.facebook.presto.bytecode.instruction.InstructionNode;
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+
+public class Comment
+ implements InstructionNode {
+ protected final String comment;
+
+ public Comment(String comment) {
+ this.comment = comment;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + '{' + comment + '}';
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of();
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitComment(parent, this);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/CompilationException.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/CompilationException.java
new file mode 100644
index 0000000..f1483cd
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/CompilationException.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+public class CompilationException
+ extends RuntimeException
+{
+ public CompilationException(String message, RuntimeException cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/DumpBytecodeVisitor.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/DumpBytecodeVisitor.java
new file mode 100644
index 0000000..bc79f0c
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/DumpBytecodeVisitor.java
@@ -0,0 +1,601 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import com.facebook.presto.bytecode.control.CaseStatement;
+import com.facebook.presto.bytecode.control.DoWhileLoop;
+import com.facebook.presto.bytecode.control.ForLoop;
+import com.facebook.presto.bytecode.control.IfStatement;
+import com.facebook.presto.bytecode.control.SwitchStatement;
+import com.facebook.presto.bytecode.control.TryCatch;
+import com.facebook.presto.bytecode.control.WhileLoop;
+import com.facebook.presto.bytecode.debug.LineNumberNode;
+import com.facebook.presto.bytecode.expression.BytecodeExpression;
+import com.facebook.presto.bytecode.instruction.Constant.BooleanConstant;
+import com.facebook.presto.bytecode.instruction.Constant.BoxedBooleanConstant;
+import com.facebook.presto.bytecode.instruction.Constant.BoxedDoubleConstant;
+import com.facebook.presto.bytecode.instruction.Constant.BoxedFloatConstant;
+import com.facebook.presto.bytecode.instruction.Constant.BoxedIntegerConstant;
+import com.facebook.presto.bytecode.instruction.Constant.BoxedLongConstant;
+import com.facebook.presto.bytecode.instruction.Constant.ClassConstant;
+import com.facebook.presto.bytecode.instruction.Constant.DoubleConstant;
+import com.facebook.presto.bytecode.instruction.Constant.FloatConstant;
+import com.facebook.presto.bytecode.instruction.Constant.IntConstant;
+import com.facebook.presto.bytecode.instruction.Constant.LongConstant;
+import com.facebook.presto.bytecode.instruction.Constant.StringConstant;
+import com.facebook.presto.bytecode.instruction.InvokeInstruction;
+import com.facebook.presto.bytecode.instruction.InvokeInstruction.InvokeDynamicInstruction;
+import com.facebook.presto.bytecode.instruction.JumpInstruction;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import com.facebook.presto.bytecode.instruction.VariableInstruction.IncrementVariableInstruction;
+import com.facebook.presto.bytecode.instruction.VariableInstruction.LoadVariableInstruction;
+import com.facebook.presto.bytecode.instruction.VariableInstruction.StoreVariableInstruction;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.facebook.presto.bytecode.Access.INTERFACE;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.lang.String.format;
+
+public class DumpBytecodeVisitor
+ extends BytecodeVisitor<Void>
+{
+ private final PrintWriter out;
+ private int indentLevel;
+
+ public DumpBytecodeVisitor(Writer out)
+ {
+ this.out = new PrintWriter(out);
+ indentLevel = 0;
+ }
+
+ @Override
+ public Void visitClass(ClassDefinition classDefinition)
+ {
+ // print annotations first
+ for (AnnotationDefinition annotationDefinition : classDefinition.getAnnotations()) {
+ visitAnnotation(classDefinition, annotationDefinition);
+ }
+
+ // print class declaration
+ Line classDeclaration = line().addAll(classDefinition.getAccess());
+ if (!classDefinition.getAccess().contains(INTERFACE)) {
+ classDeclaration.add("class");
+ }
+ classDeclaration.add(classDefinition.getType().getJavaClassName());
+ if (!classDefinition.getSuperClass().equals(type(Object.class))) {
+ classDeclaration.add("extends").add(classDefinition.getSuperClass().getJavaClassName());
+ }
+ if (!classDefinition.getInterfaces().isEmpty()) {
+ classDeclaration.add("implements");
+ for (ParameterizedType interfaceType : classDefinition.getInterfaces()) {
+ classDeclaration.add(interfaceType.getJavaClassName());
+ }
+ }
+ classDeclaration.print();
+
+ // print class body
+ printLine("{");
+ indentLevel++;
+
+ // print fields
+ for (FieldDefinition fieldDefinition : classDefinition.getFields()) {
+ visitField(classDefinition, fieldDefinition);
+ }
+
+ // print methods
+ for (MethodDefinition methodDefinition : classDefinition.getMethods()) {
+ visitMethod(classDefinition, methodDefinition);
+ }
+
+ // print class initializer
+ visitMethod(classDefinition, classDefinition.getClassInitializer());
+
+ indentLevel--;
+ printLine("}");
+ printLine();
+ return null;
+ }
+
+ @Override
+ public Void visitAnnotation(Object parent, AnnotationDefinition annotationDefinition)
+ {
+ printLine("@%s", annotationDefinition.getType().getJavaClassName(), annotationDefinition.getValues());
+ return null;
+ }
+
+ @Override
+ public Void visitField(ClassDefinition classDefinition, FieldDefinition fieldDefinition)
+ {
+ // print annotations first
+ for (AnnotationDefinition annotationDefinition : fieldDefinition.getAnnotations()) {
+ visitAnnotation(fieldDefinition, annotationDefinition);
+ }
+
+ // print field declaration
+ line().addAll(fieldDefinition.getAccess()).add(fieldDefinition.getType().getJavaClassName()).add(fieldDefinition.getName()).add(";").print();
+
+ printLine();
+ return null;
+ }
+
+ @Override
+ public Void visitMethod(ClassDefinition classDefinition, MethodDefinition methodDefinition)
+ {
+ if (methodDefinition.getComment() != null) {
+ printLine("// %s", methodDefinition.getComment());
+ }
+ // print annotations first
+ for (AnnotationDefinition annotationDefinition : methodDefinition.getAnnotations()) {
+ visitAnnotation(methodDefinition, annotationDefinition);
+ }
+
+ // print method declaration
+ printLine(methodDefinition.toSourceString());
+
+ // print body
+ methodDefinition.getBody().accept(null, this);
+
+ printLine();
+ return null;
+ }
+
+ @Override
+ public Void visitComment(BytecodeNode parent, Comment node)
+ {
+ printLine();
+ printLine("// %s", node.getComment());
+ return null;
+ }
+
+ @Override
+ public Void visitBlock(BytecodeNode parent, BytecodeBlock block)
+ {
+ // only indent if we have a block description or more than one child node
+ boolean indented;
+ if (block.getDescription() != null) {
+ line().add(block.getDescription()).add("{").print();
+ indentLevel++;
+ indented = true;
+ }
+ else if (block.getChildNodes().size() > 1) {
+ printLine("{");
+ indentLevel++;
+ indented = true;
+ }
+ else {
+ indented = false;
+ }
+
+ visitBlockContents(block);
+ if (indented) {
+ indentLevel--;
+ printLine("}");
+ }
+
+ return null;
+ }
+
+ private void visitBlockContents(BytecodeBlock block)
+ {
+ for (BytecodeNode node : block.getChildNodes()) {
+ if (node instanceof BytecodeBlock) {
+ BytecodeBlock childBlock = (BytecodeBlock) node;
+ if (childBlock.getDescription() != null) {
+ visitBlock(block, childBlock);
+ }
+ else {
+ visitBlockContents(childBlock);
+ }
+ }
+ else {
+ node.accept(node, this);
+ }
+ }
+ }
+
+ @Override
+ public Void visitBytecodeExpression(BytecodeNode parent, BytecodeExpression expression)
+ {
+ printLine(expression.toString());
+ return null;
+ }
+
+ @Override
+ public Void visitNode(BytecodeNode parent, BytecodeNode node)
+ {
+ printLine(node.toString());
+ super.visitNode(parent, node);
+ return null;
+ }
+
+ @Override
+ public Void visitLabel(BytecodeNode parent, LabelNode labelNode)
+ {
+ printLine("%s:", labelNode.getName());
+ return null;
+ }
+
+ @Override
+ public Void visitJumpInstruction(BytecodeNode parent, JumpInstruction jumpInstruction)
+ {
+ printLine("%s %s", jumpInstruction.getOpCode(), jumpInstruction.getLabel().getName());
+ return null;
+ }
+
+ //
+ // Variable
+ //
+
+ @Override
+ public Void visitLoadVariable(BytecodeNode parent, LoadVariableInstruction loadVariableInstruction)
+ {
+ Variable variable = loadVariableInstruction.getVariable();
+ printLine("load %s", variable.getName());
+ return null;
+ }
+
+ @Override
+ public Void visitStoreVariable(BytecodeNode parent, StoreVariableInstruction storeVariableInstruction)
+ {
+ Variable variable = storeVariableInstruction.getVariable();
+ printLine("store %s)", variable.getName());
+ return null;
+ }
+
+ @Override
+ public Void visitIncrementVariable(BytecodeNode parent, IncrementVariableInstruction incrementVariableInstruction)
+ {
+ Variable variable = incrementVariableInstruction.getVariable();
+ byte increment = incrementVariableInstruction.getIncrement();
+ printLine("increment %s %s", variable.getName(), increment);
+ return null;
+ }
+
+ //
+ // Invoke
+ //
+
+ @Override
+ public Void visitInvoke(BytecodeNode parent, InvokeInstruction invokeInstruction)
+ {
+ printLine("invoke %s.%s%s",
+ invokeInstruction.getTarget().getJavaClassName(),
+ invokeInstruction.getName(),
+ invokeInstruction.getMethodDescription());
+ return null;
+ }
+
+ @Override
+ public Void visitInvokeDynamic(BytecodeNode parent, InvokeDynamicInstruction invokeDynamicInstruction)
+ {
+ printLine("invokeDynamic %s%s %s",
+ invokeDynamicInstruction.getName(),
+ invokeDynamicInstruction.getMethodDescription(),
+ invokeDynamicInstruction.getBootstrapArguments());
+ return null;
+ }
+
+ //
+ // Control Flow
+ //
+
+ @Override
+ public Void visitTryCatch(BytecodeNode parent, TryCatch tryCatch)
+ {
+ if (tryCatch.getComment() != null) {
+ printLine();
+ printLine("// %s", tryCatch.getComment());
+ }
+
+ printLine("try {");
+ indentLevel++;
+ tryCatch.getTryNode().accept(tryCatch, this);
+ indentLevel--;
+ printLine("}");
+
+ printLine("catch (%s) {", tryCatch.getExceptionName());
+ indentLevel++;
+ tryCatch.getCatchNode().accept(tryCatch, this);
+ indentLevel--;
+ printLine("}");
+
+ return null;
+ }
+
+ @Override
+ public Void visitIf(BytecodeNode parent, IfStatement ifStatement)
+ {
+ if (ifStatement.getComment() != null) {
+ printLine();
+ printLine("// %s", ifStatement.getComment());
+ }
+ printLine("if {");
+ indentLevel++;
+ visitNestedNode("condition", ifStatement.condition(), ifStatement);
+ if (!ifStatement.ifTrue().isEmpty()) {
+ visitNestedNode("ifTrue", ifStatement.ifTrue(), ifStatement);
+ }
+ if (!ifStatement.ifFalse().isEmpty()) {
+ visitNestedNode("ifFalse", ifStatement.ifFalse(), ifStatement);
+ }
+ indentLevel--;
+ printLine("}");
+ return null;
+ }
+
+ @Override
+ public Void visitFor(BytecodeNode parent, ForLoop forLoop)
+ {
+ if (forLoop.getComment() != null) {
+ printLine();
+ printLine("// %s", forLoop.getComment());
+ }
+ printLine("for {");
+ indentLevel++;
+ visitNestedNode("initialize", forLoop.initialize(), forLoop);
+ visitNestedNode("condition", forLoop.condition(), forLoop);
+ visitNestedNode("update", forLoop.update(), forLoop);
+ visitNestedNode("body", forLoop.body(), forLoop);
+ indentLevel--;
+ printLine("}");
+ return null;
+ }
+
+ @Override
+ public Void visitWhile(BytecodeNode parent, WhileLoop whileLoop)
+ {
+ if (whileLoop.getComment() != null) {
+ printLine();
+ printLine("// %s", whileLoop.getComment());
+ }
+ printLine("while {");
+ indentLevel++;
+ visitNestedNode("condition", whileLoop.condition(), whileLoop);
+ visitNestedNode("body", whileLoop.body(), whileLoop);
+ indentLevel--;
+ printLine("}");
+ return null;
+ }
+
+ @Override
+ public Void visitDoWhile(BytecodeNode parent, DoWhileLoop doWhileLoop)
+ {
+ if (doWhileLoop.getComment() != null) {
+ printLine();
+ printLine("// %s", doWhileLoop.getComment());
+ }
+ printLine("while {");
+ indentLevel++;
+ visitNestedNode("body", doWhileLoop.body(), doWhileLoop);
+ visitNestedNode("condition", doWhileLoop.condition(), doWhileLoop);
+ indentLevel--;
+ printLine("}");
+ return null;
+ }
+
+ @Override
+ public Void visitSwitch(BytecodeNode parent, SwitchStatement switchStatement)
+ {
+ if (switchStatement.getComment() != null) {
+ printLine();
+ printLine("// %s", switchStatement.getComment());
+ }
+ printLine("switch {");
+ indentLevel++;
+ visitNestedNode("expression", switchStatement.expression(), switchStatement);
+ for (CaseStatement caseStatement : switchStatement.cases()) {
+ visitNestedNode(format("case %s:", caseStatement.getKey()), caseStatement.getBody(), switchStatement);
+ }
+ if (switchStatement.getDefaultBody() != null) {
+ visitNestedNode("default:", switchStatement.getDefaultBody(), switchStatement);
+ }
+ indentLevel--;
+ printLine("}");
+ return null;
+ }
+
+ //
+ // Instructions
+ //
+
+ //
+ // Constants
+ //
+
+ @Override
+ public Void visitBoxedBooleanConstant(BytecodeNode parent, BoxedBooleanConstant boxedBooleanConstant)
+ {
+ printLine("load constant %s", boxedBooleanConstant.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitBooleanConstant(BytecodeNode parent, BooleanConstant booleanConstant)
+ {
+ printLine("load constant %s", booleanConstant.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitIntConstant(BytecodeNode parent, IntConstant intConstant)
+ {
+ printLine("load constant %s", intConstant.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitBoxedIntegerConstant(BytecodeNode parent, BoxedIntegerConstant boxedIntegerConstant)
+ {
+ printLine("load constant new Integer(%s)", boxedIntegerConstant.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitFloatConstant(BytecodeNode parent, FloatConstant floatConstant)
+ {
+ printLine("load constant %sf", floatConstant.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitBoxedFloatConstant(BytecodeNode parent, BoxedFloatConstant boxedFloatConstant)
+ {
+ printLine("load constant new Float(%sf)", boxedFloatConstant.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitLongConstant(BytecodeNode parent, LongConstant longConstant)
+ {
+ printLine("load constant %sL", longConstant.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitBoxedLongConstant(BytecodeNode parent, BoxedLongConstant boxedLongConstant)
+ {
+ printLine("load constant new Long(%sL)", boxedLongConstant.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitDoubleConstant(BytecodeNode parent, DoubleConstant doubleConstant)
+ {
+ printLine("load constant %s", doubleConstant.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitBoxedDoubleConstant(BytecodeNode parent, BoxedDoubleConstant boxedDoubleConstant)
+ {
+ printLine("load constant new Double(%s)", boxedDoubleConstant.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitStringConstant(BytecodeNode parent, StringConstant stringConstant)
+ {
+ printLine("load constant \"%s\"", stringConstant.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitClassConstant(BytecodeNode parent, ClassConstant classConstant)
+ {
+ printLine("load constant %s.class", classConstant.getValue().getJavaClassName());
+ return null;
+ }
+
+ //
+ // Line Number
+ //
+
+ @Override
+ public Void visitLineNumber(BytecodeNode parent, LineNumberNode lineNumberNode)
+ {
+ lineNumber = lineNumberNode.getLineNumber();
+ printLine("LINE %s", lineNumber);
+ return null;
+ }
+
+ //
+ // Print
+ //
+
+ private int lineNumber = -1;
+
+ public void printLine()
+ {
+ out.println(indent(indentLevel));
+ }
+
+ public void printLine(String line)
+ {
+ out.println(format("%s%s", indent(indentLevel), line));
+ }
+
+ public void printLine(String format, Object... args)
+ {
+ String line = format(format, args);
+ out.println(format("%s%s", indent(indentLevel), line));
+ }
+
+ public void printWords(String... words)
+ {
+ String line = String.join(" ", words);
+ out.println(format("%s%s", indent(indentLevel), line));
+ }
+
+ private String indent(int level)
+ {
+ return BytecodeUtils.repeat(" ", level);
+ }
+
+ private void visitNestedNode(String description, BytecodeNode node, BytecodeNode parent)
+ {
+ printLine(description + " {");
+ indentLevel++;
+ node.accept(parent, this);
+ indentLevel--;
+ printLine("}");
+ }
+
+ private Line line()
+ {
+ return new Line();
+ }
+
+ private class Line
+ {
+ private final String separator;
+ private final List<Object> parts = new ArrayList<>();
+
+ private Line()
+ {
+ separator = " ";
+ }
+
+ public Line add(Object element)
+ {
+ parts.add(element);
+ return this;
+ }
+
+ public Line addAll(Collection<?> c)
+ {
+ parts.addAll(c);
+ return this;
+ }
+
+ public void print()
+ {
+ printLine(parts.stream().map(this::toCharSequence).collect(Collectors.joining(separator)));
+ }
+
+ private CharSequence toCharSequence(Object p) {
+ return p instanceof CharSequence ? (CharSequence)p : p.toString();
+ }
+
+ @Override
+ public String toString()
+ {
+ return parts.stream().map(this::toCharSequence).collect(Collectors.joining(separator));
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/DynamicClassLoader.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/DynamicClassLoader.java
new file mode 100644
index 0000000..c33787c
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/DynamicClassLoader.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import java.lang.invoke.MethodHandle;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+
+public class DynamicClassLoader
+ extends ClassLoader
+{
+ private final ConcurrentMap<String, byte[]> pendingClasses = new ConcurrentHashMap<>();
+ private final Map<Long, MethodHandle> callSiteBindings;
+ private final Optional<ClassLoader> overrideClassLoader;
+
+ public DynamicClassLoader(ClassLoader parentClassLoader)
+ {
+ this(parentClassLoader, Map.of());
+ }
+
+ // TODO: this is a hack that should be removed
+ @Deprecated
+ public DynamicClassLoader(ClassLoader overrideClassLoader, ClassLoader parentClassLoader)
+ {
+ super(parentClassLoader);
+ this.callSiteBindings = Map.of();
+ this.overrideClassLoader = Optional.of(overrideClassLoader);
+ }
+
+ public DynamicClassLoader(ClassLoader parentClassLoader, Map<Long, MethodHandle> callSiteBindings)
+ {
+ super(parentClassLoader);
+ this.callSiteBindings = Map.copyOf(callSiteBindings);
+ this.overrideClassLoader = Optional.empty();
+ }
+
+ public Class<?> defineClass(String className, byte[] bytecode)
+ {
+ return defineClass(className, bytecode, 0, bytecode.length);
+ }
+
+ public Map<String, Class<?>> defineClasses(Map<String, byte[]> newClasses)
+ {
+ final Set<String> conflicts = newClasses.keySet().stream().filter(pendingClasses::containsKey).collect(Collectors.toSet());
+
+ checkArgument(conflicts.isEmpty(), "The classes %s have already been defined", conflicts);
+
+ pendingClasses.putAll(newClasses);
+ try {
+ Map<String, Class<?>> classes = new HashMap<>();
+ for (String className : newClasses.keySet()) {
+ try {
+ Class<?> clazz = loadClass(className);
+ classes.put(className, clazz);
+ }
+ catch (ClassNotFoundException e) {
+ // this should never happen
+ throw new RuntimeException(e);
+ }
+ }
+ return classes;
+ }
+ finally {
+ pendingClasses.keySet().removeAll(newClasses.keySet());
+ }
+ }
+
+ public Map<Long, MethodHandle> getCallSiteBindings()
+ {
+ return callSiteBindings;
+ }
+
+ @Override
+ protected Class<?> findClass(String name)
+ throws ClassNotFoundException
+ {
+ byte[] bytecode = pendingClasses.get(name);
+ if (bytecode == null) {
+ throw new ClassNotFoundException(name);
+ }
+
+ return defineClass(name, bytecode);
+ }
+
+ @Override
+ protected Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+ // grab the magic lock
+ synchronized (getClassLoadingLock(name)) {
+ // Check if class is in the loaded classes cache
+ Class<?> cachedClass = findLoadedClass(name);
+ if (cachedClass != null) {
+ return resolveClass(cachedClass, resolve);
+ }
+
+ try {
+ Class<?> clazz = findClass(name);
+ return resolveClass(clazz, resolve);
+ }
+ catch (ClassNotFoundException ignored) {
+ // not a local class
+ }
+
+ if (overrideClassLoader.isPresent()) {
+ try {
+ return resolveClass(overrideClassLoader.get().loadClass(name), resolve);
+ }
+ catch (ClassNotFoundException e) {
+ // not in override loader
+ }
+ }
+
+ Class<?> clazz = getParent().loadClass(name);
+ return resolveClass(clazz, resolve);
+ }
+ }
+
+ private Class<?> resolveClass(Class<?> clazz, boolean resolve)
+ {
+ if (resolve) {
+ resolveClass(clazz);
+ }
+ return clazz;
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/FieldDefinition.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/FieldDefinition.java
new file mode 100644
index 0000000..b8dd132
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/FieldDefinition.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+
+import static com.facebook.presto.bytecode.Access.toAccessModifier;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+
+public class FieldDefinition {
+ private final ClassDefinition declaringClass;
+ private final Set<Access> access;
+ private final String name;
+ private final ParameterizedType type;
+ private final List<AnnotationDefinition> annotations = new ArrayList<>();
+
+ public FieldDefinition(ClassDefinition declaringClass, EnumSet<Access> access, String name,
+ ParameterizedType type) {
+ this.declaringClass = declaringClass;
+ this.access = Collections.unmodifiableSet(access);
+ this.name = name;
+ this.type = type;
+ }
+
+ public FieldDefinition(ClassDefinition declaringClass, EnumSet<Access> access, String name, Class<?> type) {
+ this(declaringClass, access, name, type(type));
+ }
+
+ public ClassDefinition getDeclaringClass() {
+ return declaringClass;
+ }
+
+ public Set<Access> getAccess() {
+ return access;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ParameterizedType getType() {
+ return type;
+ }
+
+ public List<AnnotationDefinition> getAnnotations() {
+ return List.copyOf(annotations);
+ }
+
+ public AnnotationDefinition declareAnnotation(Class<?> type) {
+ AnnotationDefinition annotationDefinition = new AnnotationDefinition(type);
+ annotations.add(annotationDefinition);
+ return annotationDefinition;
+ }
+
+ public AnnotationDefinition declareAnnotation(ParameterizedType type) {
+ AnnotationDefinition annotationDefinition = new AnnotationDefinition(type);
+ annotations.add(annotationDefinition);
+ return annotationDefinition;
+ }
+
+ public void visit(ClassVisitor visitor) {
+ FieldVisitor fieldVisitor = visitor.visitField(toAccessModifier(access),
+ name,
+ type.getType(),
+ type.isPrimitive() ? null : type.getGenericSignature(),
+ null);
+
+ if (fieldVisitor == null) {
+ return;
+ }
+
+ for (AnnotationDefinition annotation : annotations) {
+ annotation.visitFieldAnnotation(fieldVisitor);
+ }
+
+ fieldVisitor.visitEnd();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("FieldDefinition");
+ sb.append("{access=").append(access);
+ sb.append(", name='").append(name).append('\'');
+ sb.append(", type=").append(type);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/MethodDefinition.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/MethodDefinition.java
new file mode 100644
index 0000000..1405765
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/MethodDefinition.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.tree.InsnNode;
+
+import static com.facebook.presto.bytecode.Access.STATIC;
+import static com.facebook.presto.bytecode.Access.toAccessModifier;
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static org.objectweb.asm.Opcodes.RETURN;
+
+@SuppressWarnings("UnusedDeclaration")
+public class MethodDefinition {
+ private final Scope scope;
+ private final ClassDefinition declaringClass;
+ private final EnumSet<Access> access;
+ private final String name;
+ private final List<AnnotationDefinition> annotations = new ArrayList<>();
+ private final ParameterizedType returnType;
+ private final List<Parameter> parameters;
+ private final List<ParameterizedType> parameterTypes;
+ private final List<List<AnnotationDefinition>> parameterAnnotations;
+ private final List<ParameterizedType> exceptions = new ArrayList<>();
+
+ private final BytecodeBlock body;
+ private String comment;
+
+ public MethodDefinition(
+ ClassDefinition declaringClass,
+ EnumSet<Access> access,
+ String name,
+ ParameterizedType returnType,
+ Parameter... parameters) {
+ this(declaringClass, access, name, returnType, List.of(parameters));
+ }
+
+ public MethodDefinition(
+ ClassDefinition declaringClass,
+ EnumSet<Access> access,
+ String name,
+ ParameterizedType returnType,
+ Collection<Parameter> parameters) {
+ checkArgument(parameters.size() <= 254, "Too many parameters for method");
+
+ this.declaringClass = declaringClass;
+ body = new BytecodeBlock();
+
+ this.access = access;
+ this.name = name;
+ this.returnType = returnType != null ? returnType : type(void.class);
+ this.parameters = List.copyOf(parameters);
+ this.parameterTypes = parameters.stream().map(Parameter::getType).collect(Collectors.toList());
+ this.parameterAnnotations = parameters.stream().map(p -> new ArrayList<AnnotationDefinition>()).collect(Collectors.toList());
+ Optional<ParameterizedType> thisType = Optional.empty();
+ if (!declaringClass.isInterface() && !access.contains(STATIC)) {
+ thisType = Optional.of(declaringClass.getType());
+ }
+ scope = new Scope(thisType, parameters);
+ }
+
+ public ClassDefinition getDeclaringClass() {
+ return declaringClass;
+ }
+
+ public List<AnnotationDefinition> getAnnotations() {
+ return List.copyOf(annotations);
+ }
+
+ public List<AnnotationDefinition> getParameterAnnotations(int index) {
+ return List.copyOf(parameterAnnotations.get(index));
+ }
+
+ public EnumSet<Access> getAccess() {
+ return access;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ParameterizedType getReturnType() {
+ return returnType;
+ }
+
+ public List<Parameter> getParameters() {
+ return parameters;
+ }
+
+ public List<ParameterizedType> getParameterTypes() {
+ return parameterTypes;
+ }
+
+ public List<ParameterizedType> getExceptions() {
+ return exceptions;
+ }
+
+ public MethodDefinition addException(Class<? extends Throwable> exceptionClass) {
+ exceptions.add(type(exceptionClass));
+ return this;
+ }
+
+ public MethodDefinition comment(String format, Object... args) {
+ this.comment = String.format(format, args);
+ return this;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public Scope getScope() {
+ return scope;
+ }
+
+ public Variable getThis() {
+ return scope.getThis();
+ }
+
+ public String getMethodDescriptor() {
+ return methodDescription(returnType, parameterTypes);
+ }
+
+ public BytecodeBlock getBody() {
+ if (declaringClass.isInterface()) {
+ throw new IllegalAccessError("Interface does not have method body");
+ }
+ return body;
+ }
+
+ public AnnotationDefinition declareAnnotation(Class<?> type) {
+ AnnotationDefinition annotationDefinition = new AnnotationDefinition(type);
+ annotations.add(annotationDefinition);
+ return annotationDefinition;
+ }
+
+ public AnnotationDefinition declareAnnotation(ParameterizedType type) {
+ AnnotationDefinition annotationDefinition = new AnnotationDefinition(type);
+ annotations.add(annotationDefinition);
+ return annotationDefinition;
+ }
+
+ public AnnotationDefinition declareParameterAnnotation(Class<?> type, int parameterIndex) {
+ AnnotationDefinition annotationDefinition = new AnnotationDefinition(type);
+ parameterAnnotations.get(parameterIndex).add(annotationDefinition);
+ return annotationDefinition;
+ }
+
+ public AnnotationDefinition declareParameterAnnotation(ParameterizedType type, int parameterIndex) {
+ AnnotationDefinition annotationDefinition = new AnnotationDefinition(type);
+ parameterAnnotations.get(parameterIndex).add(annotationDefinition);
+ return annotationDefinition;
+ }
+
+ public void visit(ClassVisitor visitor) {
+ visit(visitor, false);
+ }
+
+ public void visit(ClassVisitor visitor, boolean addReturn) {
+ String[] exceptions = new String[this.exceptions.size()];
+ for (int i = 0; i < exceptions.length; i++) {
+ exceptions[i] = this.exceptions.get(i).getClassName();
+ }
+
+ MethodVisitor methodVisitor = visitor.visitMethod(toAccessModifier(access),
+ name,
+ getMethodDescriptor(),
+ genericMethodSignature(returnType, parameterTypes),
+ exceptions);
+
+ if (methodVisitor == null) {
+ return;
+ }
+
+ // visit method annotations
+ for (AnnotationDefinition annotation : annotations) {
+ annotation.visitMethodAnnotation(methodVisitor);
+ }
+
+ // visit parameter annotations
+ for (int parameterIndex = 0; parameterIndex < parameterAnnotations.size(); parameterIndex++) {
+ List<AnnotationDefinition> parameterAnnotations1 = this.parameterAnnotations.get(parameterIndex);
+ for (AnnotationDefinition parameterAnnotation : parameterAnnotations1) {
+ parameterAnnotation.visitParameterAnnotation(parameterIndex, methodVisitor);
+ }
+ }
+ if (!declaringClass.isInterface()) {
+ // visit code
+ methodVisitor.visitCode();
+
+ // visit instructions
+ MethodGenerationContext generationContext = new MethodGenerationContext(methodVisitor);
+ generationContext.enterScope(scope);
+ body.accept(methodVisitor, generationContext);
+ if (addReturn) {
+ new InsnNode(RETURN).accept(methodVisitor);
+ }
+ generationContext.exitScope(scope);
+ }
+ // done
+ methodVisitor.visitMaxs(-1, -1);
+ methodVisitor.visitEnd();
+ }
+
+ public String toSourceString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(access.stream().map(Access::toString).collect(Collectors.joining(" ")))
+ .append(' ');
+ sb.append(returnType.getJavaClassName()).append(' ');
+ sb.append(name).append('(');
+ sb.append(parameters.stream().map(Parameter::getSourceString).collect(Collectors.joining(", ")))
+ .append(')');
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return toSourceString();
+ }
+
+ public static String methodDescription(Class<?> returnType, Class<?>... parameterTypes) {
+ return methodDescription(returnType, List.of(parameterTypes));
+ }
+
+ public static String methodDescription(Class<?> returnType, List<Class<?>> parameterTypes) {
+ return methodDescription(
+ type(returnType),
+ parameterTypes.stream().map(ParameterizedType::type).collect(Collectors.toList()));
+ }
+
+ public static String methodDescription(
+ ParameterizedType returnType,
+ ParameterizedType... parameterTypes) {
+ return methodDescription(returnType, List.of(parameterTypes));
+ }
+
+ public static String methodDescription(
+ ParameterizedType returnType,
+ List<ParameterizedType> parameterTypes) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ sb.append(parameterTypes.stream().map(ParameterizedType::getType).collect(Collectors.joining("")));
+ sb.append(")");
+ sb.append(returnType.getType());
+ return sb.toString();
+ }
+
+ public static String genericMethodSignature(
+ ParameterizedType returnType,
+ ParameterizedType... parameterTypes) {
+ return genericMethodSignature(returnType, List.of(parameterTypes));
+ }
+
+ public static String genericMethodSignature(
+ ParameterizedType returnType,
+ List<ParameterizedType> parameterTypes) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ sb.append(parameterTypes.stream().map(ParameterizedType::toString).collect(Collectors.joining("")));
+ sb.append(")");
+ sb.append(returnType);
+ return sb.toString();
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/MethodGenerationContext.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/MethodGenerationContext.java
new file mode 100644
index 0000000..e62da9d
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/MethodGenerationContext.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import com.facebook.presto.bytecode.debug.LocalVariableNode;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class MethodGenerationContext {
+ private final MethodVisitor methodVisitor;
+
+ private final Set<Scope> allEnteredScopes = new LinkedHashSet<>();
+ private final Deque<ScopeContext> scopes = new ArrayDeque<>();
+
+ private final Map<Variable, Integer> variableSlots = new HashMap<>();
+ private int nextSlot;
+
+ private int currentLineNumber = -1;
+
+ public MethodGenerationContext(MethodVisitor methodVisitor) {
+ this.methodVisitor = requireNonNull(methodVisitor, "methodVisitor is null");
+ }
+
+ public void enterScope(Scope scope) {
+ requireNonNull(scope, "scope is null");
+
+ checkArgument(!allEnteredScopes.contains(scope), "scope has already been entered");
+ allEnteredScopes.add(scope);
+
+ ScopeContext scopeContext = new ScopeContext(scope);
+ scopes.addLast(scopeContext);
+
+ for (Variable variable : scopeContext.getVariables()) {
+ checkArgument(!"this".equals(variable.getName()) || nextSlot == 0, "The 'this' variable must be in slot 0");
+ variableSlots.put(variable, nextSlot);
+ nextSlot += Type.getType(variable.getType().getType()).getSize();
+ }
+
+ scopeContext.getStartLabel().accept(methodVisitor, this);
+ }
+
+ public void exitScope(Scope scope) {
+ checkArgument(allEnteredScopes.contains(scope), "scope has not been entered");
+ checkArgument(!scopes.isEmpty() && scope == scopes.peekLast().getScope(), "Scope is not top of the stack");
+
+ ScopeContext scopeContext = scopes.removeLast();
+
+ scopeContext.getEndLabel().accept(methodVisitor, this);
+
+ for (Variable variable : scopeContext.getVariables()) {
+ new LocalVariableNode(variable, scopeContext.getStartLabel(), scopeContext.getEndLabel()).accept(methodVisitor, this);
+ }
+
+ variableSlots.keySet().removeAll(scopeContext.getVariables());
+ }
+
+ public int getVariableSlot(Variable variable) {
+ Integer slot = variableSlots.get(variable);
+ checkArgument(slot != null, "Variable '%s' has not been assigned a slot", variable);
+ return slot;
+ }
+
+ public boolean updateLineNumber(int lineNumber) {
+ if (lineNumber == currentLineNumber) {
+ return false;
+ }
+
+ currentLineNumber = lineNumber;
+ return true;
+ }
+
+ private static final class ScopeContext {
+ private final Scope scope;
+ private final List<Variable> variables;
+
+ private final LabelNode startLabel = new LabelNode("VariableStart");
+ private final LabelNode endLabel = new LabelNode("VariableEnd");
+
+ ScopeContext(Scope scope) {
+ this.scope = scope;
+ this.variables = List.copyOf(scope.getVariables());
+ }
+
+ public Scope getScope() {
+ return scope;
+ }
+
+ public List<Variable> getVariables() {
+ return variables;
+ }
+
+ public LabelNode getStartLabel() {
+ return startLabel;
+ }
+
+ public LabelNode getEndLabel() {
+ return endLabel;
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/OpCode.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/OpCode.java
new file mode 100644
index 0000000..5ca541a
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/OpCode.java
@@ -0,0 +1,268 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import com.facebook.presto.bytecode.instruction.InstructionNode;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.objectweb.asm.MethodVisitor;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+
+@SuppressWarnings("SpellCheckingInspection")
+public enum OpCode
+ implements InstructionNode {
+ NOP(0),
+ ACONST_NULL(1),
+ ICONST_M1(2),
+ ICONST_0(3),
+ ICONST_1(4),
+ ICONST_2(5),
+ ICONST_3(6),
+ ICONST_4(7),
+ ICONST_5(8),
+ LCONST_0(9),
+ LCONST_1(10),
+ FCONST_0(11),
+ FCONST_1(12),
+ FCONST_2(13),
+ DCONST_0(14),
+ DCONST_1(15),
+ BIPUSH(16),
+ SIPUSH(17),
+ LDC(18),
+ LDC_W(19),
+ LDC2_W(20),
+ ILOAD(21),
+ LLOAD(22),
+ FLOAD(23),
+ DLOAD(24),
+ ALOAD(25),
+ ILOAD_0(26),
+ ILOAD_1(27),
+ ILOAD_2(28),
+ ILOAD_3(29),
+ LLOAD_0(30),
+ LLOAD_1(31),
+ LLOAD_2(32),
+ LLOAD_3(33),
+ FLOAD_0(34),
+ FLOAD_1(35),
+ FLOAD_2(36),
+ FLOAD_3(37),
+ DLOAD_0(38),
+ DLOAD_1(39),
+ DLOAD_2(40),
+ DLOAD_3(41),
+ ALOAD_0(42),
+ ALOAD_1(43),
+ ALOAD_2(44),
+ ALOAD_3(45),
+ IALOAD(46),
+ LALOAD(47),
+ FALOAD(48),
+ DALOAD(49),
+ AALOAD(50),
+ BALOAD(51),
+ CALOAD(52),
+ SALOAD(53),
+ ISTORE(54),
+ LSTORE(55),
+ FSTORE(56),
+ DSTORE(57),
+ ASTORE(58),
+ ISTORE_0(59),
+ ISTORE_1(60),
+ ISTORE_2(61),
+ ISTORE_3(62),
+ LSTORE_0(63),
+ LSTORE_1(64),
+ LSTORE_2(65),
+ LSTORE_3(66),
+ FSTORE_0(67),
+ FSTORE_1(68),
+ FSTORE_2(69),
+ FSTORE_3(70),
+ DSTORE_0(71),
+ DSTORE_1(72),
+ DSTORE_2(73),
+ DSTORE_3(74),
+ ASTORE_0(75),
+ ASTORE_1(76),
+ ASTORE_2(77),
+ ASTORE_3(78),
+ IASTORE(79),
+ LASTORE(80),
+ FASTORE(81),
+ DASTORE(82),
+ AASTORE(83),
+ BASTORE(84),
+ CASTORE(85),
+ SASTORE(86),
+ POP(87),
+ POP2(88),
+ DUP(89),
+ DUP_X1(90),
+ DUP_X2(91),
+ DUP2(92),
+ DUP2_X1(93),
+ DUP2_X2(94),
+ SWAP(95),
+ IADD(96),
+ LADD(97),
+ FADD(98),
+ DADD(99),
+ ISUB(100),
+ LSUB(101),
+ FSUB(102),
+ DSUB(103),
+ IMUL(104),
+ LMUL(105),
+ FMUL(106),
+ DMUL(107),
+ IDIV(108),
+ LDIV(109),
+ FDIV(110),
+ DDIV(111),
+ IREM(112),
+ LREM(113),
+ FREM(114),
+ DREM(115),
+ INEG(116),
+ LNEG(117),
+ FNEG(118),
+ DNEG(119),
+ ISHL(120),
+ LSHL(121),
+ ISHR(122),
+ LSHR(123),
+ IUSHR(124),
+ LUSHR(125),
+ IAND(126),
+ LAND(127),
+ IOR(128),
+ LOR(129),
+ IXOR(130),
+ LXOR(131),
+ IINC(132),
+ I2L(133),
+ I2F(134),
+ I2D(135),
+ L2I(136),
+ L2F(137),
+ L2D(138),
+ F2I(139),
+ F2L(140),
+ F2D(141),
+ D2I(142),
+ D2L(143),
+ D2F(144),
+ I2B(145),
+ I2C(146),
+ I2S(147),
+ LCMP(148),
+ FCMPL(149),
+ FCMPG(150),
+ DCMPL(151),
+ DCMPG(152),
+ IFEQ(153),
+ IFNE(154),
+ IFLT(155),
+ IFGE(156),
+ IFGT(157),
+ IFLE(158),
+ IF_ICMPEQ(159),
+ IF_ICMPNE(160),
+ IF_ICMPLT(161),
+ IF_ICMPGE(162),
+ IF_ICMPGT(163),
+ IF_ICMPLE(164),
+ IF_ACMPEQ(165),
+ IF_ACMPNE(166),
+ GOTO(167),
+ JSR(168),
+ RET(169),
+ TABLESWITCH(170),
+ LOOKUPSWITCH(171),
+ IRETURN(172),
+ LRETURN(173),
+ FRETURN(174),
+ DRETURN(175),
+ ARETURN(176),
+ RETURN(177),
+ GETSTATIC(178),
+ PUTSTATIC(179),
+ GETFIELD(180),
+ PUTFIELD(181),
+ INVOKEVIRTUAL(182),
+ INVOKESPECIAL(183),
+ INVOKESTATIC(184),
+ INVOKEINTERFACE(185),
+ INVOKEDYNAMIC(186),
+ NEW(187),
+ NEWARRAY(188),
+ ANEWARRAY(189),
+ ARRAYLENGTH(190),
+ ATHROW(191),
+ CHECKCAST(192),
+ INSTANCEOF(193),
+ MONITORENTER(194),
+ MONITOREXIT(195),
+ WIDE(196),
+ MULTIANEWARRAY(197),
+ IFNULL(198),
+ IFNONNULL(199),
+ GOTO_W(200),
+ JSR_W(201);
+
+ private static final Map<Integer, OpCode> OP_CODE_INDEX;
+
+ static {
+ OP_CODE_INDEX = Arrays.stream(values()).collect(Collectors.toMap(OpCode::getOpCode, Function.identity()));
+ }
+
+ public static OpCode getOpCode(int opCode) {
+ OpCode value = OP_CODE_INDEX.get(opCode);
+ checkArgument(value != null, "Unknown opCode %s", opCode);
+ return value;
+ }
+
+ private final int opCode;
+
+ OpCode(int opCode) {
+ this.opCode = opCode;
+ }
+
+ public int getOpCode() {
+ return opCode;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ visitor.visitInsn(opCode);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of();
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitInstruction(parent, this);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Parameter.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Parameter.java
new file mode 100644
index 0000000..a87220f
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Parameter.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+public class Parameter
+ extends Variable {
+ public static Parameter arg(String name, Class<?> type) {
+ return new Parameter(name, ParameterizedType.type(type));
+ }
+
+ public static Parameter arg(String name, ParameterizedType type) {
+ return new Parameter(name, type);
+ }
+
+ Parameter(String name, ParameterizedType type) {
+ super(name, type);
+ }
+
+ String getSourceString() {
+ return getType().getJavaClassName() + " " + getName();
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ParameterizedType.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ParameterizedType.java
new file mode 100644
index 0000000..9092e36
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ParameterizedType.java
@@ -0,0 +1,285 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.jetbrains.annotations.Nullable;
+import org.objectweb.asm.Type;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class ParameterizedType {
+ public static ParameterizedType typeFromJavaClassName(String className) {
+ requireNonNull(className, "type is null");
+ return new ParameterizedType(className.replace('.', '/'));
+ }
+
+ public static ParameterizedType typeFromPathName(String className) {
+ requireNonNull(className, "type is null");
+ return new ParameterizedType(className);
+ }
+
+ public static ParameterizedType type(Type type) {
+ requireNonNull(type, "type is null");
+ return new ParameterizedType(type.getInternalName());
+ }
+
+ public static ParameterizedType type(Class<?> type) {
+ requireNonNull(type, "type is null");
+ return new ParameterizedType(type);
+ }
+
+ public static ParameterizedType type(Class<?> type, Class<?>... parameters) {
+ requireNonNull(type, "type is null");
+ return new ParameterizedType(type, parameters);
+ }
+
+ public static ParameterizedType type(Class<?> type, ParameterizedType... parameters) {
+ requireNonNull(type, "type is null");
+ return new ParameterizedType(type, parameters);
+ }
+
+ private final String type;
+ private final String className;
+ private final String simpleName;
+ private final List<String> parameters;
+
+ private final boolean isInterface;
+ @Nullable
+ private final Class<?> primitiveType;
+ @Nullable
+ private final ParameterizedType arrayComponentType;
+
+ public ParameterizedType(String className) {
+ requireNonNull(className, "className is null");
+ checkArgument(!className.contains("."), "Invalid class name %s", className);
+ checkArgument(!className.endsWith(";"), "Invalid class name %s", className);
+
+ this.className = className;
+ this.simpleName = className.substring(className.lastIndexOf("/") + 1);
+ this.type = "L" + className + ";";
+ this.parameters = List.of();
+
+ this.isInterface = false;
+ this.primitiveType = null;
+ this.arrayComponentType = null;
+ }
+
+ private ParameterizedType(Class<?> type) {
+ requireNonNull(type, "type is null");
+ this.type = toInternalIdentifier(type);
+ this.className = getPathName(type);
+ this.simpleName = type.getSimpleName();
+ this.parameters = List.of();
+
+ this.isInterface = type.isInterface();
+ this.primitiveType = type.isPrimitive() ? type : null;
+ this.arrayComponentType = type.isArray() ? type(type.getComponentType()) : null;
+ }
+
+ private ParameterizedType(Class<?> type, Class<?>... parameters) {
+ requireNonNull(type, "type is null");
+ this.type = toInternalIdentifier(type);
+ this.className = getPathName(type);
+ this.simpleName = type.getSimpleName();
+
+ this.parameters = Arrays.stream(parameters).map(ParameterizedType::toInternalIdentifier).collect(Collectors.toList());
+
+ this.isInterface = type.isInterface();
+ this.primitiveType = type.isPrimitive() ? type : null;
+ this.arrayComponentType = type.isArray() ? type(type.getComponentType()) : null;
+ }
+
+ private ParameterizedType(Class<?> type, ParameterizedType... parameters) {
+ requireNonNull(type, "type is null");
+ this.type = toInternalIdentifier(type);
+ this.className = getPathName(type);
+ this.simpleName = type.getSimpleName();
+
+ this.parameters = Arrays.stream(parameters).map(ParameterizedType::toString).collect(Collectors.toList());
+
+ this.isInterface = type.isInterface();
+ this.primitiveType = type.isPrimitive() ? type : null;
+ this.arrayComponentType = type.isArray() ? type(type.getComponentType()) : null;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public String getJavaClassName() {
+ return className.replace('/', '.');
+ }
+
+ public String getSimpleName() {
+ return simpleName;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public Type getAsmType() {
+ return Type.getObjectType(className);
+ }
+
+ public String getGenericSignature() {
+ StringBuilder sb = new StringBuilder();
+ if (primitiveType != null || arrayComponentType != null) {
+ return type;
+ }
+ sb.append('L').append(className);
+ if (!parameters.isEmpty()) {
+ sb.append("<");
+ for (String parameterType : parameters) {
+ sb.append(parameterType);
+ }
+ sb.append(">");
+ }
+ sb.append(";");
+ return sb.toString();
+ }
+
+ public boolean isGeneric() {
+ return !parameters.isEmpty();
+ }
+
+ public boolean isInterface() {
+ return isInterface;
+ }
+
+ @Nullable
+ public Class<?> getPrimitiveType() {
+ return primitiveType;
+ }
+
+ public boolean isPrimitive() {
+ return primitiveType != null;
+ }
+
+ @Nullable
+ public ParameterizedType getArrayComponentType() {
+ return arrayComponentType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ParameterizedType that = (ParameterizedType)o;
+
+ if (!type.equals(that.type)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return getGenericSignature();
+ }
+
+ public static String getPathName(Class<?> n) {
+ return n.getName().replace('.', '/');
+ }
+
+ private static String toInternalIdentifier(Class<?> n) {
+ if (n.isArray()) {
+ n = n.getComponentType();
+ if (n.isPrimitive()) {
+ if (n == Byte.TYPE) {
+ return "[B";
+ }
+ else if (n == Boolean.TYPE) {
+ return "[Z";
+ }
+ else if (n == Short.TYPE) {
+ return "[S";
+ }
+ else if (n == Character.TYPE) {
+ return "[C";
+ }
+ else if (n == Integer.TYPE) {
+ return "[I";
+ }
+ else if (n == Float.TYPE) {
+ return "[F";
+ }
+ else if (n == Double.TYPE) {
+ return "[D";
+ }
+ else if (n == Long.TYPE) {
+ return "[J";
+ }
+ else {
+ throw new RuntimeException("Unrecognized type in compiler: " + n.getName());
+ }
+ }
+ else {
+ return "[" + toInternalIdentifier(n);
+ }
+ }
+ else {
+ if (n.isPrimitive()) {
+ if (n == Byte.TYPE) {
+ return "B";
+ }
+ else if (n == Boolean.TYPE) {
+ return "Z";
+ }
+ else if (n == Short.TYPE) {
+ return "S";
+ }
+ else if (n == Character.TYPE) {
+ return "C";
+ }
+ else if (n == Integer.TYPE) {
+ return "I";
+ }
+ else if (n == Float.TYPE) {
+ return "F";
+ }
+ else if (n == Double.TYPE) {
+ return "D";
+ }
+ else if (n == Long.TYPE) {
+ return "J";
+ }
+ else if (n == Void.TYPE) {
+ return "V";
+ }
+ else {
+ throw new RuntimeException("Unrecognized type in compiler: " + n.getName());
+ }
+ }
+ else {
+ return "L" + getPathName(n) + ";";
+ }
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Scope.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Scope.java
new file mode 100644
index 0000000..46c7060
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Scope.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import com.facebook.presto.bytecode.expression.BytecodeExpression;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.TreeMap;
+import org.objectweb.asm.Type;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.BytecodeUtils.checkState;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.util.Objects.requireNonNull;
+
+public class Scope {
+ private final Map<String, Variable> variables = new TreeMap<>();
+ private final List<Variable> allVariables = new ArrayList<>();
+
+ private final Variable thisVariable;
+
+ private int nextTempVariableId;
+
+ // This can only be constructed by a method definition
+ Scope(Optional<ParameterizedType> thisType, Iterable<Parameter> parameters) {
+ if (thisType.isPresent()) {
+ thisVariable = new Variable("this", thisType.get());
+ variables.put("this", thisVariable);
+ allVariables.add(thisVariable);
+ }
+ else {
+ thisVariable = null;
+ }
+
+ for (Parameter parameter : parameters) {
+ variables.put(parameter.getName(), parameter);
+ allVariables.add(parameter);
+ }
+ }
+
+ public List<Variable> getVariables() {
+ return List.copyOf(allVariables);
+ }
+
+ public Variable createTempVariable(Class<?> type) {
+ // reserve a slot for this variable
+ Variable variable = new Variable("temp_" + nextTempVariableId, type(type));
+ nextTempVariableId += Type.getType(type(type).getType()).getSize();
+
+ allVariables.add(variable);
+
+ return variable;
+ }
+
+ public Variable getThis() {
+ checkState(thisVariable != null, "Static methods do not have a 'this' variable");
+ return thisVariable;
+ }
+
+ public Variable getVariable(String name) {
+ Variable variable = variables.get(name);
+ checkArgument(variable != null, "Variable %s not defined", name);
+ return variable;
+ }
+
+ public Variable declareVariable(Class<?> type, String variableName) {
+ return declareVariable(type(type), variableName);
+ }
+
+ public Variable declareVariable(ParameterizedType type, String variableName) {
+ requireNonNull(type, "type is null");
+ requireNonNull(variableName, "variableName is null");
+ checkArgument(!variables.containsKey(variableName), "There is already a variable named %s", variableName);
+ checkArgument(!"this".equals(variableName), "The 'this' variable can not be declared");
+
+ Variable variable = new Variable(variableName, type);
+
+ variables.put(variableName, variable);
+ allVariables.add(variable);
+
+ return variable;
+ }
+
+ public Variable declareVariable(String variableName, BytecodeBlock block, BytecodeExpression initialValue) {
+ Variable variable = declareVariable(initialValue.getType(), variableName);
+ block.append(variable.set(initialValue));
+ return variable;
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/SmartClassWriter.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/SmartClassWriter.java
new file mode 100644
index 0000000..12eda2f
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/SmartClassWriter.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import org.objectweb.asm.ClassWriter;
+
+import static com.facebook.presto.bytecode.ParameterizedType.typeFromPathName;
+
+public class SmartClassWriter
+ extends ClassWriter
+{
+ private final ClassInfoLoader classInfoLoader;
+
+ public SmartClassWriter(ClassInfoLoader classInfoLoader)
+ {
+ super(ClassWriter.COMPUTE_FRAMES);
+ this.classInfoLoader = classInfoLoader;
+ }
+
+ @Override
+ protected String getCommonSuperClass(String aType, String bType)
+ {
+ ClassInfo aClassInfo = classInfoLoader.loadClassInfo(typeFromPathName(aType));
+ ClassInfo bClassInfo = classInfoLoader.loadClassInfo(typeFromPathName(bType));
+
+ if (aClassInfo.isAssignableFrom(bClassInfo)) {
+ return aType;
+ }
+ if (bClassInfo.isAssignableFrom(aClassInfo)) {
+ return bType;
+ }
+ if (aClassInfo.isInterface() || bClassInfo.isInterface()) {
+ return "java/lang/Object";
+ }
+ else {
+ do {
+ aClassInfo = aClassInfo.getSuperclass();
+ }
+ while (!aClassInfo.isAssignableFrom(bClassInfo));
+ return aClassInfo.getType().getClassName();
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Variable.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Variable.java
new file mode 100644
index 0000000..fb46c2e
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/Variable.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode;
+
+import com.facebook.presto.bytecode.expression.BytecodeExpression;
+import com.facebook.presto.bytecode.instruction.VariableInstruction;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.add;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantInt;
+import static java.util.Objects.requireNonNull;
+
+public class Variable
+ extends BytecodeExpression {
+ private final String name;
+
+ public Variable(String name, ParameterizedType type) {
+ super(type);
+ this.name = requireNonNull(name, "name is null");
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public BytecodeExpression set(BytecodeExpression value) {
+ return new SetVariableBytecodeExpression(this, value);
+ }
+
+ public BytecodeExpression increment() {
+ return new SetVariableBytecodeExpression(this, add(this, constantInt(1)));
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ return VariableInstruction.loadVariable(this);
+ }
+
+ @Override
+ protected String formatOneLine() {
+ return name;
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of();
+ }
+
+ private static final class SetVariableBytecodeExpression
+ extends BytecodeExpression {
+ private final Variable variable;
+ private final BytecodeExpression value;
+
+ SetVariableBytecodeExpression(Variable variable, BytecodeExpression value) {
+ super(type(void.class));
+ this.variable = requireNonNull(variable, "variable is null");
+ this.value = requireNonNull(value, "value is null");
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ return new BytecodeBlock()
+ .append(value)
+ .putVariable(variable);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of(value);
+ }
+
+ @Override
+ protected String formatOneLine() {
+ return variable.getName() + " = " + value;
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/CaseStatement.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/CaseStatement.java
new file mode 100644
index 0000000..dafe6d7
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/CaseStatement.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.control;
+
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+public class CaseStatement
+ implements Comparable<CaseStatement> {
+ private final int key;
+ private final BytecodeNode body;
+ private final LabelNode label;
+
+ CaseStatement(int key, BytecodeNode body, LabelNode label) {
+ this.key = key;
+ this.body = requireNonNull(body, "body is null");
+ this.label = requireNonNull(label, "label is null");
+ }
+
+ public int getKey() {
+ return key;
+ }
+
+ public BytecodeNode getBody() {
+ return body;
+ }
+
+ public LabelNode getLabel() {
+ return label;
+ }
+
+ @Override
+ public int compareTo(CaseStatement o) {
+ return Integer.compare(key, o.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return key;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ CaseStatement other = (CaseStatement)obj;
+ return Objects.equals(this.key, other.key);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{key=" + key + '}';
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/DoWhileLoop.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/DoWhileLoop.java
new file mode 100644
index 0000000..9e4046d
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/DoWhileLoop.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.control;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.BytecodeVisitor;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkState;
+
+public class DoWhileLoop
+ implements FlowControl {
+ private final String comment;
+ private final BytecodeBlock body = new BytecodeBlock();
+ private final BytecodeBlock condition = new BytecodeBlock();
+
+ private final LabelNode beginLabel = new LabelNode("begin");
+ private final LabelNode continueLabel = new LabelNode("continue");
+ private final LabelNode endLabel = new LabelNode("end");
+
+ public DoWhileLoop() {
+ this.comment = null;
+ }
+
+ public DoWhileLoop(String format, Object... args) {
+ this.comment = String.format(format, args);
+ }
+
+ @Override
+ public String getComment() {
+ return comment;
+ }
+
+ public LabelNode getContinueLabel() {
+ return continueLabel;
+ }
+
+ public LabelNode getEndLabel() {
+ return endLabel;
+ }
+
+ public BytecodeBlock body() {
+ return body;
+ }
+
+ public DoWhileLoop body(BytecodeNode node) {
+ checkState(body.isEmpty(), "body already set");
+ body.append(node);
+ return this;
+ }
+
+ public BytecodeBlock condition() {
+ return condition;
+ }
+
+ public DoWhileLoop condition(BytecodeNode node) {
+ checkState(condition.isEmpty(), "condition already set");
+ condition.append(node);
+ return this;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ checkState(!condition.isEmpty(), "DoWhileLoop does not have a condition set");
+
+ BytecodeBlock block = new BytecodeBlock()
+ .visitLabel(beginLabel)
+ .append(new BytecodeBlock()
+ .setDescription("body")
+ .append(body))
+ .visitLabel(continueLabel)
+ .append(new BytecodeBlock()
+ .setDescription("condition")
+ .append(condition))
+ .ifFalseGoto(endLabel)
+ .gotoLabel(beginLabel)
+ .visitLabel(endLabel);
+
+ block.accept(visitor, generationContext);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of(body, condition);
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitDoWhile(parent, this);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/FlowControl.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/FlowControl.java
new file mode 100644
index 0000000..013d997
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/FlowControl.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.control;
+
+import com.facebook.presto.bytecode.BytecodeNode;
+
+public interface FlowControl
+ extends BytecodeNode
+{
+ String getComment();
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/ForLoop.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/ForLoop.java
new file mode 100644
index 0000000..cdbd4c8
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/ForLoop.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.control;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.BytecodeVisitor;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkState;
+
+public class ForLoop
+ implements FlowControl
+{
+ private final String comment;
+ private final BytecodeBlock initialize = new BytecodeBlock();
+ private final BytecodeBlock condition = new BytecodeBlock();
+ private final BytecodeBlock update = new BytecodeBlock();
+ private final BytecodeBlock body = new BytecodeBlock();
+
+ private final LabelNode beginLabel = new LabelNode("beginLabel");
+ private final LabelNode continueLabel = new LabelNode("continue");
+ private final LabelNode endLabel = new LabelNode("end");
+
+ public ForLoop()
+ {
+ this.comment = null;
+ }
+
+ public ForLoop(String format, Object... args)
+ {
+ this.comment = String.format(format, args);
+ }
+
+ @Override
+ public String getComment()
+ {
+ return comment;
+ }
+
+ public LabelNode getContinueLabel()
+ {
+ return continueLabel;
+ }
+
+ public LabelNode getEndLabel()
+ {
+ return endLabel;
+ }
+
+ public BytecodeBlock initialize()
+ {
+ return initialize;
+ }
+
+ public ForLoop initialize(BytecodeNode node)
+ {
+ checkState(initialize.isEmpty(), "initialize already set");
+ initialize.append(node);
+ return this;
+ }
+
+ public BytecodeBlock condition()
+ {
+ return condition;
+ }
+
+ public ForLoop condition(BytecodeNode node)
+ {
+ checkState(condition.isEmpty(), "condition already set");
+ condition.append(node);
+ return this;
+ }
+
+ public BytecodeBlock update()
+ {
+ return update;
+ }
+
+ public ForLoop update(BytecodeNode node)
+ {
+ checkState(update.isEmpty(), "update already set");
+ update.append(node);
+ return this;
+ }
+
+ public BytecodeBlock body()
+ {
+ return body;
+ }
+
+ public ForLoop body(BytecodeNode node)
+ {
+ checkState(body.isEmpty(), "body already set");
+ body.append(node);
+ return this;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext)
+ {
+ checkState(!condition.isEmpty(), "ForLoop does not have a condition set");
+
+ BytecodeBlock block = new BytecodeBlock();
+
+ block.append(new BytecodeBlock()
+ .setDescription("initialize")
+ .append(initialize));
+
+ block.visitLabel(beginLabel)
+ .append(new BytecodeBlock()
+ .setDescription("condition")
+ .append(condition))
+ .ifFalseGoto(endLabel);
+
+ block.append(new BytecodeBlock()
+ .setDescription("body")
+ .append(body));
+
+ block.visitLabel(continueLabel)
+ .append(new BytecodeBlock()
+ .setDescription("update")
+ .append(update))
+ .gotoLabel(beginLabel)
+ .visitLabel(endLabel);
+
+ block.accept(visitor, generationContext);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes()
+ {
+ return List.of(initialize, condition, update, body);
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor)
+ {
+ return visitor.visitFor(parent, this);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/IfStatement.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/IfStatement.java
new file mode 100644
index 0000000..3fbdd4b
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/IfStatement.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.control;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.BytecodeVisitor;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkState;
+
+public class IfStatement
+ implements FlowControl {
+ private final String comment;
+ private final BytecodeBlock condition = new BytecodeBlock();
+ private final BytecodeBlock ifTrue = new BytecodeBlock();
+ private final BytecodeBlock ifFalse = new BytecodeBlock();
+
+ private final LabelNode falseLabel = new LabelNode("false");
+ private final LabelNode outLabel = new LabelNode("out");
+
+ public IfStatement() {
+ this.comment = null;
+ }
+
+ public IfStatement(String format, Object... args) {
+ this.comment = String.format(format, args);
+ }
+
+ @Override
+ public String getComment() {
+ return comment;
+ }
+
+ public BytecodeBlock condition() {
+ return condition;
+ }
+
+ public IfStatement condition(BytecodeNode node) {
+ checkState(condition.isEmpty(), "condition already set");
+ condition.append(node);
+ return this;
+ }
+
+ public BytecodeBlock ifTrue() {
+ return ifTrue;
+ }
+
+ public IfStatement ifTrue(BytecodeNode node) {
+ checkState(ifTrue.isEmpty(), "ifTrue already set");
+ ifTrue.append(node);
+ return this;
+ }
+
+ public BytecodeBlock ifFalse() {
+ return ifFalse;
+ }
+
+ public IfStatement ifFalse(BytecodeNode node) {
+ checkState(ifFalse.isEmpty(), "ifFalse already set");
+ ifFalse.append(node);
+ return this;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ checkState(!condition.isEmpty(), "IfStatement does not have a condition set");
+ checkState(!ifTrue.isEmpty() || !ifFalse.isEmpty(), "IfStatement does not have a true or false block set");
+
+ BytecodeBlock block = new BytecodeBlock();
+
+ // if !condition goto false;
+ block.append(new BytecodeBlock()
+ .setDescription("condition")
+ .append(condition));
+ block.ifFalseGoto(falseLabel);
+
+ if (!ifTrue.isEmpty()) {
+ block.append(new BytecodeBlock()
+ .setDescription("ifTrue")
+ .append(ifTrue));
+ }
+
+ if (!ifFalse.isEmpty()) {
+ // close true case by skipping to end
+ block.gotoLabel(outLabel);
+
+ block.visitLabel(falseLabel);
+ block.append(new BytecodeBlock()
+ .setDescription("ifFalse")
+ .append(ifFalse));
+ block.visitLabel(outLabel);
+ }
+ else {
+ block.visitLabel(falseLabel);
+ }
+
+ block.accept(visitor, generationContext);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of(condition, ifTrue, ifFalse);
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitIf(parent, this);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/SwitchStatement.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/SwitchStatement.java
new file mode 100644
index 0000000..c2f38a9
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/SwitchStatement.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.control;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.BytecodeVisitor;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.expression.BytecodeExpression;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkState;
+import static java.util.Comparator.comparing;
+import static java.util.Objects.requireNonNull;
+
+public class SwitchStatement
+ implements FlowControl {
+ public static SwitchBuilder switchBuilder() {
+ return new SwitchBuilder();
+ }
+
+ private final LabelNode endLabel = new LabelNode("switchEnd");
+ private final LabelNode defaultLabel = new LabelNode("switchDefault");
+ private final String comment;
+ private final BytecodeExpression expression;
+ private final SortedSet<CaseStatement> cases;
+ private final BytecodeNode defaultBody;
+
+ private SwitchStatement(
+ String comment,
+ BytecodeExpression expression,
+ Collection<CaseStatement> cases,
+ BytecodeNode defaultBody) {
+ this.comment = comment;
+ this.expression = requireNonNull(expression, "expression is null");
+
+ final TreeSet<CaseStatement> sorted = new TreeSet<>(comparing(CaseStatement::getKey));
+ sorted.addAll(cases);
+
+ this.cases = Collections.unmodifiableSortedSet(sorted);
+ this.defaultBody = defaultBody;
+ }
+
+ @Override
+ public String getComment() {
+ return comment;
+ }
+
+ public BytecodeExpression expression() {
+ return expression;
+ }
+
+ public SortedSet<CaseStatement> cases() {
+ return cases;
+ }
+
+ public LabelNode getDefaultLabel() {
+ return defaultLabel;
+ }
+
+ public BytecodeNode getDefaultBody() {
+ return defaultBody;
+ }
+
+ public LabelNode getEndLabel() {
+ return endLabel;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ // build switch table
+ int[] keys = new int[cases.size()];
+ Label[] labels = new Label[cases.size()];
+
+ int index = 0;
+ for (CaseStatement caseStatement : cases) {
+ keys[index] = caseStatement.getKey();
+ labels[index] = caseStatement.getLabel().getLabel();
+ index++;
+ }
+
+ // build case blocks
+ BytecodeBlock block = new BytecodeBlock();
+
+ for (CaseStatement caseStatement : cases) {
+ block.visitLabel(caseStatement.getLabel())
+ .append(caseStatement.getBody())
+ .gotoLabel(endLabel);
+ }
+
+ // build default block
+ block.visitLabel(defaultLabel);
+
+ if (defaultBody != null) {
+ block.append(defaultBody);
+ }
+
+ block.visitLabel(endLabel);
+
+ // emit code
+ expression.accept(visitor, generationContext);
+ visitor.visitLookupSwitchInsn(defaultLabel.getLabel(), keys, labels);
+ block.accept(visitor, generationContext);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of();
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitSwitch(parent, this);
+ }
+
+ public static class SwitchBuilder {
+ private final Set<CaseStatement> cases = new HashSet<>();
+ private String comment;
+ private BytecodeExpression expression;
+ private LabelNode defaultLabel;
+ private BytecodeNode defaultBody;
+
+ public SwitchBuilder comment(String format, Object... args) {
+ this.comment = String.format(format, args);
+ return this;
+ }
+
+ public SwitchBuilder expression(BytecodeExpression expression) {
+ this.expression = expression;
+ return this;
+ }
+
+ public SwitchBuilder addCase(int key, BytecodeNode body) {
+ LabelNode label = new LabelNode("switchCase:" + key);
+ CaseStatement statement = new CaseStatement(key, body, label);
+ checkState(cases.add(statement), "case already exists for value [%s]", key);
+ return this;
+ }
+
+ public SwitchBuilder defaultCase(BytecodeNode body) {
+ checkState(defaultBody == null, "default case already set");
+ this.defaultBody = requireNonNull(body, "body is null");
+ return this;
+ }
+
+ public SwitchStatement build() {
+ checkState(expression != null, "expression is not set");
+ return new SwitchStatement(comment, expression, cases, defaultBody);
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/TryCatch.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/TryCatch.java
new file mode 100644
index 0000000..c932494
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/TryCatch.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.control;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.BytecodeVisitor;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.ParameterizedType;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+
+import static java.util.Objects.requireNonNull;
+
+public class TryCatch
+ implements FlowControl {
+ private final String comment;
+ private final BytecodeNode tryNode;
+ private final BytecodeNode catchNode;
+ private final String exceptionName;
+
+ public TryCatch(BytecodeNode tryNode, BytecodeNode catchNode, ParameterizedType exceptionType) {
+ this(null, tryNode, catchNode, exceptionType);
+ }
+
+ public TryCatch(String comment, BytecodeNode tryNode, BytecodeNode catchNode, ParameterizedType exceptionType) {
+ this.comment = comment;
+ this.tryNode = requireNonNull(tryNode, "tryNode is null");
+ this.catchNode = requireNonNull(catchNode, "catchNode is null");
+ this.exceptionName = (exceptionType != null) ? exceptionType.getClassName() : null;
+ }
+
+ @Override
+ public String getComment() {
+ return comment;
+ }
+
+ public BytecodeNode getTryNode() {
+ return tryNode;
+ }
+
+ public BytecodeNode getCatchNode() {
+ return catchNode;
+ }
+
+ public String getExceptionName() {
+ return exceptionName;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ LabelNode tryStart = new LabelNode("tryStart");
+ LabelNode tryEnd = new LabelNode("tryEnd");
+ LabelNode handler = new LabelNode("handler");
+ LabelNode done = new LabelNode("done");
+
+ BytecodeBlock block = new BytecodeBlock();
+
+ // try block
+ block.visitLabel(tryStart)
+ .append(tryNode)
+ .visitLabel(tryEnd)
+ .gotoLabel(done);
+
+ // handler block
+ block.visitLabel(handler)
+ .append(catchNode);
+
+ // all done
+ block.visitLabel(done);
+
+ block.accept(visitor, generationContext);
+ visitor.visitTryCatchBlock(tryStart.getLabel(), tryEnd.getLabel(), handler.getLabel(), exceptionName);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of(tryNode, catchNode);
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitTryCatch(parent, this);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/WhileLoop.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/WhileLoop.java
new file mode 100644
index 0000000..3ad6298
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/control/WhileLoop.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.control;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.BytecodeVisitor;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkState;
+
+public class WhileLoop
+ implements FlowControl {
+ private final String comment;
+ private final BytecodeBlock condition = new BytecodeBlock();
+ private final BytecodeBlock body = new BytecodeBlock();
+
+ private final LabelNode continueLabel = new LabelNode("continue");
+ private final LabelNode endLabel = new LabelNode("end");
+
+ public WhileLoop() {
+ this.comment = null;
+ }
+
+ public WhileLoop(String format, Object... args) {
+ this.comment = String.format(format, args);
+ }
+
+ @Override
+ public String getComment() {
+ return comment;
+ }
+
+ public LabelNode getContinueLabel() {
+ return continueLabel;
+ }
+
+ public LabelNode getEndLabel() {
+ return endLabel;
+ }
+
+ public BytecodeBlock condition() {
+ return condition;
+ }
+
+ public WhileLoop condition(BytecodeNode node) {
+ checkState(condition.isEmpty(), "condition already set");
+ condition.append(node);
+ return this;
+ }
+
+ public BytecodeBlock body() {
+ return body;
+ }
+
+ public WhileLoop body(BytecodeNode node) {
+ checkState(body.isEmpty(), "body already set");
+ body.append(node);
+ return this;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ checkState(!condition.isEmpty(), "WhileLoop does not have a condition set");
+
+ BytecodeBlock block = new BytecodeBlock()
+ .visitLabel(continueLabel)
+ .append(condition)
+ .ifZeroGoto(endLabel)
+ .append(body)
+ .gotoLabel(continueLabel)
+ .visitLabel(endLabel);
+
+ block.accept(visitor, generationContext);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of(condition, body);
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitWhile(parent, this);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/debug/DebugNode.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/debug/DebugNode.java
new file mode 100644
index 0000000..80dffb3
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/debug/DebugNode.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.debug;
+
+import com.facebook.presto.bytecode.BytecodeNode;
+
+public interface DebugNode
+ extends BytecodeNode
+{
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/debug/LineNumberNode.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/debug/LineNumberNode.java
new file mode 100644
index 0000000..f735cdb
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/debug/LineNumberNode.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.debug;
+
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.BytecodeVisitor;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+
+public class LineNumberNode
+ implements DebugNode {
+ private final int lineNumber;
+ private final LabelNode label = new LabelNode();
+
+ public LineNumberNode(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ if (generationContext.updateLineNumber(lineNumber)) {
+ label.accept(visitor, generationContext);
+ visitor.visitLineNumber(lineNumber, label.getLabel());
+ }
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() +
+ "{line=" + lineNumber + '}';
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of();
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitLineNumber(parent, this);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/debug/LocalVariableNode.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/debug/LocalVariableNode.java
new file mode 100644
index 0000000..c27cb73
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/debug/LocalVariableNode.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.debug;
+
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.BytecodeVisitor;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.Variable;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+
+public class LocalVariableNode
+ implements DebugNode {
+ private final Variable variable;
+ private final LabelNode start;
+ private final LabelNode end;
+
+ public LocalVariableNode(Variable variable, LabelNode start, LabelNode end) {
+ this.variable = variable;
+ this.start = start;
+ this.end = end;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ visitor.visitLocalVariable(variable.getName(),
+ variable.getType().getType(),
+ variable.getType().getGenericSignature(),
+ start.getLabel(),
+ end.getLabel(),
+ generationContext.getVariableSlot(variable));
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{variable=" + variable +
+ ", start=" + start +
+ ", end=" + end + '}';
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of();
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitLocalVariable(parent, this);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/AndBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/AndBytecodeExpression.java
new file mode 100644
index 0000000..ee1eaef
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/AndBytecodeExpression.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.util.Objects.requireNonNull;
+
+class AndBytecodeExpression
+ extends BytecodeExpression {
+ private final BytecodeExpression left;
+ private final BytecodeExpression right;
+
+ AndBytecodeExpression(BytecodeExpression left, BytecodeExpression right) {
+ super(type(boolean.class));
+ this.left = requireNonNull(left, "left is null");
+ checkArgument(left.getType().getPrimitiveType() == boolean.class, "Expected left to be type boolean but is %s", left.getType());
+ this.right = requireNonNull(right, "right is null");
+ checkArgument(right.getType().getPrimitiveType() == boolean.class, "Expected right to be type boolean but is %s", right.getType());
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ LabelNode falseLabel = new LabelNode("false");
+ LabelNode endLabel = new LabelNode("end");
+ return new BytecodeBlock()
+ .append(left)
+ .ifFalseGoto(falseLabel)
+ .append(right)
+ .ifFalseGoto(falseLabel)
+ .push(true)
+ .gotoLabel(endLabel)
+ .visitLabel(falseLabel)
+ .push(false)
+ .visitLabel(endLabel);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of(left, right);
+ }
+
+ @Override
+ protected String formatOneLine() {
+ return "(" + left + " && " + right + ")";
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ArithmeticBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ArithmeticBytecodeExpression.java
new file mode 100644
index 0000000..f3c31c5
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ArithmeticBytecodeExpression.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.OpCode;
+import com.facebook.presto.bytecode.ParameterizedType;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class ArithmeticBytecodeExpression
+ extends BytecodeExpression
+{
+ public static BytecodeExpression createArithmeticBytecodeExpression(OpCode baseOpCode, BytecodeExpression left, BytecodeExpression right)
+ {
+ requireNonNull(baseOpCode, "baseOpCode is null");
+ String name = getName(baseOpCode);
+ String infixSymbol = getInfixSymbol(baseOpCode);
+
+ checkArgumentTypes(baseOpCode, name, left, right);
+
+ OpCode opCode = getNumericOpCode(name, baseOpCode, left.getType().getPrimitiveType());
+ return new ArithmeticBytecodeExpression(infixSymbol, left.getType(), opCode, left, right);
+ }
+
+ private static String getName(OpCode baseOpCode)
+ {
+ switch (baseOpCode) {
+ case IAND:
+ return "Bitwise AND";
+ case IOR:
+ return "Bitwise OR";
+ case IXOR:
+ return "Bitwise XOR";
+ case IADD:
+ return "Add";
+ case ISUB:
+ return "Subtract";
+ case IMUL:
+ return "Multiply";
+ case IDIV:
+ return "Divide";
+ case IREM:
+ return "Remainder";
+ case ISHL:
+ return "Shift left";
+ case ISHR:
+ return "Shift right";
+ case IUSHR:
+ return "Shift right unsigned";
+ default:
+ throw new IllegalArgumentException("Unsupported OpCode " + baseOpCode);
+ }
+ }
+
+ private static String getInfixSymbol(OpCode baseOpCode)
+ {
+ switch (baseOpCode) {
+ case IAND:
+ return "&";
+ case IOR:
+ return "|";
+ case IXOR:
+ return "^";
+ case IADD:
+ return "+";
+ case ISUB:
+ return "-";
+ case IMUL:
+ return "*";
+ case IDIV:
+ return "/";
+ case IREM:
+ return "%";
+ case ISHL:
+ return "<<";
+ case ISHR:
+ return ">>";
+ case IUSHR:
+ return ">>>";
+ default:
+ throw new IllegalArgumentException("Unsupported OpCode " + baseOpCode);
+ }
+ }
+
+ private static void checkArgumentTypes(OpCode baseOpCode, String name, BytecodeExpression left, BytecodeExpression right)
+ {
+ Class<?> leftType = getPrimitiveType(left, "left");
+ Class<?> rightType = getPrimitiveType(right, "right");
+ switch (baseOpCode) {
+ case IAND:
+ case IOR:
+ case IXOR:
+ checkArgument(leftType == rightType, "left and right must be the same type");
+ checkArgument(leftType == int.class || leftType == long.class, "%s argument must be int or long, but is %s", name, leftType);
+ return;
+ case IADD:
+ case ISUB:
+ case IMUL:
+ case IDIV:
+ case IREM:
+ checkArgument(leftType == rightType, "left and right must be the same type");
+ checkArgument(leftType == int.class || leftType == long.class || leftType == float.class || leftType == double.class,
+ "%s argument must be int, long, float, or double, but is %s",
+ name,
+ leftType);
+ return;
+ case ISHL:
+ case ISHR:
+ case IUSHR:
+ checkArgument(leftType == int.class || leftType == long.class, "%s left argument be int or long, but is %s", name, leftType);
+ checkArgument(rightType == int.class, "%s right argument be and int, but is %s", name, rightType);
+ return;
+ default:
+ throw new IllegalArgumentException("Unsupported OpCode " + baseOpCode);
+ }
+ }
+
+ static OpCode getNumericOpCode(String name, OpCode baseOpCode, Class<?> type)
+ {
+ // Arithmetic OpCodes are laid out int, long, float and then double
+ if (type == int.class) {
+ return baseOpCode;
+ }
+ else if (type == long.class) {
+ return OpCode.getOpCode(baseOpCode.getOpCode() + 1);
+ }
+ else if (type == float.class) {
+ return OpCode.getOpCode(baseOpCode.getOpCode() + 2);
+ }
+ else if (type == double.class) {
+ return OpCode.getOpCode(baseOpCode.getOpCode() + 3);
+ }
+ else {
+ throw new IllegalArgumentException(name + " does not support " + type);
+ }
+ }
+
+ private static Class<?> getPrimitiveType(BytecodeExpression expression, String name)
+ {
+ requireNonNull(expression, name + " is null");
+ Class<?> leftType = expression.getType().getPrimitiveType();
+ checkArgument(leftType != null, name + " is not a primitive");
+ checkArgument(leftType != void.class, name + " is void");
+ return leftType;
+ }
+
+ private final String infixSymbol;
+ private final OpCode opCode;
+ private final BytecodeExpression left;
+ private final BytecodeExpression right;
+
+ private ArithmeticBytecodeExpression(
+ String infixSymbol,
+ ParameterizedType type,
+ OpCode opCode,
+ BytecodeExpression left,
+ BytecodeExpression right)
+ {
+ super(type);
+ this.infixSymbol = infixSymbol;
+ this.opCode = opCode;
+ this.left = left;
+ this.right = right;
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext)
+ {
+ return new BytecodeBlock()
+ .append(left)
+ .append(right)
+ .append(opCode);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes()
+ {
+ return List.of(left, right);
+ }
+
+ @Override
+ protected String formatOneLine()
+ {
+ return "(" + left + " " + infixSymbol + " " + right + ")";
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ArrayLengthBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ArrayLengthBytecodeExpression.java
new file mode 100644
index 0000000..ba56a83
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ArrayLengthBytecodeExpression.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.OpCode.ARRAYLENGTH;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.util.Objects.requireNonNull;
+
+class ArrayLengthBytecodeExpression
+ extends BytecodeExpression
+{
+ private final BytecodeExpression instance;
+
+ ArrayLengthBytecodeExpression(BytecodeExpression instance)
+ {
+ super(type(int.class));
+ this.instance = requireNonNull(instance, "instance is null");
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext)
+ {
+ return new BytecodeBlock()
+ .append(instance.getBytecode(generationContext))
+ .append(ARRAYLENGTH);
+ }
+
+ @Override
+ protected String formatOneLine()
+ {
+ return instance + ".length";
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes()
+ {
+ return List.of();
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/BytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/BytecodeExpression.java
new file mode 100644
index 0000000..5ce36c1
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/BytecodeExpression.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.BytecodeVisitor;
+import com.facebook.presto.bytecode.FieldDefinition;
+import com.facebook.presto.bytecode.MethodDefinition;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.ParameterizedType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.objectweb.asm.MethodVisitor;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantInt;
+import static java.util.Arrays.stream;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A BytecodeExpression is chain of Java like expressions that results in at most
+ * a single value being pushed on the stack. The chain starts with a constant,
+ * local variable, static field, static method or invoke dynamic followed
+ * by zero or more invocations, field dereferences, array element fetches, or casts.
+ * The expression can optionally be terminated by a set expression, and in this
+ * case no value is pushed on the stack.
+ * <p>
+ * A BytecodeExpression is a BytecodeNode so it works with tools like tree dump.
+ * <p>
+ * This abstraction makes it easy to write generic byte code generators that can
+ * work with data that may come from a parameter, field or the result of a method
+ * invocation.
+ */
+public abstract class BytecodeExpression
+ implements BytecodeNode {
+ private final ParameterizedType type;
+
+ protected BytecodeExpression(ParameterizedType type) {
+ this.type = requireNonNull(type, "type is null");
+ }
+
+ public final ParameterizedType getType() {
+ return type;
+ }
+
+ public abstract BytecodeNode getBytecode(MethodGenerationContext generationContext);
+
+ protected abstract String formatOneLine();
+
+ @Override
+ public final String toString() {
+ return formatOneLine() + (type.getPrimitiveType() == void.class ? ";" : "");
+ }
+
+ public final BytecodeExpression getField(Class<?> declaringClass, String name) {
+ return new GetFieldBytecodeExpression(this, declaringClass, name);
+ }
+
+ public final BytecodeExpression getField(String name, Class<?> type) {
+ return new GetFieldBytecodeExpression(this, this.getType(), name, type(type));
+ }
+
+ public final BytecodeExpression getField(Field field) {
+ return new GetFieldBytecodeExpression(this, field);
+ }
+
+ public final BytecodeExpression getField(FieldDefinition field) {
+ return new GetFieldBytecodeExpression(this, field);
+ }
+
+ public final BytecodeExpression getField(ParameterizedType declaringClass, String name, ParameterizedType type) {
+ return new GetFieldBytecodeExpression(this, declaringClass, name, type);
+ }
+
+ public final BytecodeExpression setField(String name, BytecodeExpression value) {
+ return new SetFieldBytecodeExpression(this, this.getType(), name, value);
+ }
+
+ public final BytecodeExpression setField(Field field, BytecodeExpression value) {
+ return new SetFieldBytecodeExpression(this, field, value);
+ }
+
+ public final BytecodeExpression setField(FieldDefinition field, BytecodeExpression value) {
+ return new SetFieldBytecodeExpression(this, field, value);
+ }
+
+ public final BytecodeExpression cast(Class<?> type) {
+ return new CastBytecodeExpression(this, type(type));
+ }
+
+ public final BytecodeExpression cast(ParameterizedType type) {
+ return new CastBytecodeExpression(this, type);
+ }
+
+ public final BytecodeExpression invoke(Method method, BytecodeExpression... parameters) {
+ return invoke(method, List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public final BytecodeExpression invoke(MethodDefinition method,
+ Collection<? extends BytecodeExpression> parameters) {
+ List<BytecodeExpression> params = new ArrayList<>(parameters);
+
+ checkArgument(method.getParameters().size() == params.size(), "Expected %s params found %s", method.getParameters().size(), params.size());
+ return invoke(method.getName(), method.getReturnType(), method.getParameterTypes(), parameters);
+ }
+
+ public final BytecodeExpression invoke(Method method, Collection<? extends BytecodeExpression> parameters) {
+ return invoke(
+ method.getName(),
+ type(method.getReturnType()),
+ stream(method.getParameterTypes())
+ .map(ParameterizedType::type)
+ .collect(Collectors.toList()),
+ parameters);
+ }
+
+ public final BytecodeExpression invoke(String methodName, Class<?> returnType, BytecodeExpression... parameters) {
+ return invoke(methodName, type(returnType), List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public final BytecodeExpression invoke(String methodName, Class<?> returnType,
+ Collection<? extends BytecodeExpression> parameters) {
+ return invoke(methodName, type(returnType), parameters);
+ }
+
+ public final BytecodeExpression invoke(String methodName, ParameterizedType returnType,
+ Collection<? extends BytecodeExpression> parameters) {
+ requireNonNull(parameters, "parameters is null");
+
+ return invoke(methodName,
+ returnType,
+ parameters.stream().map(BytecodeExpression::getType).collect(Collectors.toList()),
+ parameters);
+ }
+
+ public final BytecodeExpression invoke(String methodName, Class<?> returnType,
+ Collection<? extends Class<?>> parameterTypes, BytecodeExpression... parameters) {
+ return invoke(methodName, type(returnType),
+ parameterTypes.stream().map(ParameterizedType::type).collect(Collectors.toList()),
+ List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public final BytecodeExpression invoke(String methodName, ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes, BytecodeExpression... parameters) {
+ return invoke(methodName, returnType, parameterTypes, List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public final BytecodeExpression invoke(
+ String methodName,
+ ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes,
+ Collection<? extends BytecodeExpression> parameters) {
+ return InvokeBytecodeExpression.createInvoke(
+ this,
+ methodName,
+ returnType,
+ parameterTypes,
+ parameters);
+ }
+
+ public final BytecodeExpression getElement(int index) {
+ return new GetElementBytecodeExpression(this, constantInt(index));
+ }
+
+ public final BytecodeExpression getElement(BytecodeExpression index) {
+ return new GetElementBytecodeExpression(this, index);
+ }
+
+ public final BytecodeExpression setElement(int index, BytecodeExpression value) {
+ return new SetArrayElementBytecodeExpression(this, constantInt(index), value);
+ }
+
+ public final BytecodeExpression setElement(BytecodeExpression index, BytecodeExpression value) {
+ return new SetArrayElementBytecodeExpression(this, index, value);
+ }
+
+ public final BytecodeExpression length() {
+ return new ArrayLengthBytecodeExpression(this);
+ }
+
+ public final BytecodeExpression ret() {
+ return new ReturnBytecodeExpression(this);
+ }
+
+ public final BytecodeExpression pop() {
+ if (this.getType().getPrimitiveType() == void.class) {
+ return this;
+ }
+ return new PopBytecodeExpression(this);
+ }
+
+ @Override
+ public final void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ getBytecode(generationContext).accept(visitor, generationContext);
+ }
+
+ @Override
+ public final <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitBytecodeExpression(parent, this);
+ }
+
+ public BytecodeExpression instanceOf(Class<?> type) {
+ return InstanceOfBytecodeExpression.instanceOf(this, type);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/BytecodeExpressions.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/BytecodeExpressions.java
new file mode 100644
index 0000000..448ee8a
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/BytecodeExpressions.java
@@ -0,0 +1,623 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.FieldDefinition;
+import com.facebook.presto.bytecode.MethodDefinition;
+import com.facebook.presto.bytecode.OpCode;
+import com.facebook.presto.bytecode.ParameterizedType;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static com.facebook.presto.bytecode.expression.ArithmeticBytecodeExpression.createArithmeticBytecodeExpression;
+import static com.facebook.presto.bytecode.instruction.Constant.loadBoolean;
+import static com.facebook.presto.bytecode.instruction.Constant.loadClass;
+import static com.facebook.presto.bytecode.instruction.Constant.loadDouble;
+import static com.facebook.presto.bytecode.instruction.Constant.loadFloat;
+import static com.facebook.presto.bytecode.instruction.Constant.loadInt;
+import static com.facebook.presto.bytecode.instruction.Constant.loadLong;
+import static com.facebook.presto.bytecode.instruction.Constant.loadNull;
+import static com.facebook.presto.bytecode.instruction.Constant.loadString;
+import static java.util.Arrays.stream;
+import static java.util.Objects.requireNonNull;
+
+public final class BytecodeExpressions {
+ private BytecodeExpressions() {
+ }
+
+ //
+ // Constants
+ //
+
+ public static BytecodeExpression constantTrue() {
+ return new ConstantBytecodeExpression(boolean.class, loadBoolean(true));
+ }
+
+ public static BytecodeExpression constantFalse() {
+ return new ConstantBytecodeExpression(boolean.class, loadBoolean(false));
+ }
+
+ public static BytecodeExpression constantBoolean(boolean value) {
+ return new ConstantBytecodeExpression(boolean.class, loadBoolean(value));
+ }
+
+ public static BytecodeExpression constantClass(Class<?> value) {
+ return new ConstantBytecodeExpression(Class.class, loadClass(value));
+ }
+
+ public static BytecodeExpression constantClass(ParameterizedType value) {
+ return new ConstantBytecodeExpression(Class.class, loadClass(value));
+ }
+
+ public static BytecodeExpression constantDouble(double value) {
+ return new ConstantBytecodeExpression(double.class, loadDouble(value));
+ }
+
+ public static BytecodeExpression constantFloat(float value) {
+ return new ConstantBytecodeExpression(float.class, loadFloat(value));
+ }
+
+ public static BytecodeExpression constantInt(int value) {
+ return new ConstantBytecodeExpression(int.class, loadInt(value));
+ }
+
+ public static BytecodeExpression constantLong(long value) {
+ return new ConstantBytecodeExpression(long.class, loadLong(value));
+ }
+
+ public static BytecodeExpression constantNumber(Number value) {
+ if (value instanceof Byte) {
+ return constantInt((value).intValue()).cast(byte.class);
+ }
+ if (value instanceof Short) {
+ return constantInt((value).intValue()).cast(short.class);
+ }
+ if (value instanceof Integer) {
+ return constantInt((Integer)value);
+ }
+ if (value instanceof Long) {
+ return constantLong((Long)value);
+ }
+ if (value instanceof Float) {
+ return constantFloat((Float)value);
+ }
+ if (value instanceof Double) {
+ return constantDouble((Double)value);
+ }
+ throw new IllegalStateException("Unsupported number type " + value.getClass().getSimpleName());
+ }
+
+ public static BytecodeExpression constantNull(Class<?> type) {
+ return new ConstantBytecodeExpression(type, loadNull());
+ }
+
+ public static BytecodeExpression constantNull(ParameterizedType type) {
+ return new ConstantBytecodeExpression(type, loadNull());
+ }
+
+ public static BytecodeExpression constantString(String value) {
+ return new ConstantBytecodeExpression(String.class, loadString(value));
+ }
+
+ public static BytecodeExpression defaultValue(ParameterizedType type) {
+ if (type.isPrimitive()) {
+ return defaultValue(type.getPrimitiveType());
+ }
+ return constantNull(type);
+ }
+
+ public static BytecodeExpression defaultValue(Class<?> type) {
+ requireNonNull(type, "type is null");
+ if (type == boolean.class) {
+ return constantInt(0).cast(boolean.class);
+ }
+ if (type == byte.class) {
+ return constantInt(0).cast(byte.class);
+ }
+ if (type == int.class) {
+ return constantInt(0);
+ }
+ if (type == short.class) {
+ return constantInt(0).cast(short.class);
+ }
+ if (type == long.class) {
+ return constantLong(0L);
+ }
+ if (type == float.class) {
+ return constantFloat(0.0f);
+ }
+ if (type == double.class) {
+ return constantDouble(0.0d);
+ }
+ checkArgument(!type.isPrimitive(), "Unsupported type %s", type);
+ return constantNull(type);
+ }
+
+ //
+ // Get static field
+ //
+
+ public static BytecodeExpression getStatic(Class<?> declaringClass, String name) {
+ return new GetFieldBytecodeExpression(null, declaringClass, name);
+ }
+
+ public static BytecodeExpression getStatic(Field staticField) {
+ return new GetFieldBytecodeExpression(null, staticField);
+ }
+
+ public static BytecodeExpression getStatic(FieldDefinition staticField) {
+ return new GetFieldBytecodeExpression(null, staticField);
+ }
+
+ public static BytecodeExpression getStatic(ParameterizedType declaringClass, String name, ParameterizedType type) {
+ return new GetFieldBytecodeExpression(null, declaringClass, name, type);
+ }
+
+ //
+ // Set static field
+ //
+
+ public static BytecodeExpression setStatic(Class<?> declaringClass, String name, BytecodeExpression value) {
+ return new SetFieldBytecodeExpression(null, declaringClass, name, value);
+ }
+
+ public static BytecodeExpression setStatic(Field staticField, BytecodeExpression value) {
+ return new SetFieldBytecodeExpression(null, staticField, value);
+ }
+
+ public static BytecodeExpression setStatic(FieldDefinition staticField, BytecodeExpression value) {
+ return new SetFieldBytecodeExpression(null, staticField, value);
+ }
+
+ public static BytecodeExpression setStatic(ParameterizedType declaringClass, String name,
+ BytecodeExpression value) {
+ return new SetFieldBytecodeExpression(null, declaringClass, name, value);
+ }
+
+ //
+ // New instance
+ //
+
+ public static BytecodeExpression newInstance(Constructor<?> constructor, BytecodeExpression... parameters) {
+ return newInstance(constructor, List.of(parameters));
+ }
+
+ public static BytecodeExpression newInstance(Constructor<?> constructor,
+ Collection<? extends BytecodeExpression> parameters) {
+ return newInstance(
+ type(constructor.getDeclaringClass()),
+ stream(constructor.getParameterTypes())
+ .map(ParameterizedType::type)
+ .collect(Collectors.toUnmodifiableList()),
+ parameters);
+ }
+
+ public static BytecodeExpression newInstance(Class<?> returnType, BytecodeExpression... parameters) {
+ return newInstance(type(returnType), List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public static BytecodeExpression newInstance(Class<?> returnType,
+ Collection<? extends BytecodeExpression> parameters) {
+ return newInstance(type(returnType), parameters);
+ }
+
+ public static BytecodeExpression newInstance(ParameterizedType returnType, BytecodeExpression... parameters) {
+ requireNonNull(parameters, "parameters is null");
+
+ return newInstance(returnType, List.of(parameters));
+ }
+
+ public static BytecodeExpression newInstance(ParameterizedType returnType,
+ Collection<? extends BytecodeExpression> parameters) {
+ requireNonNull(parameters, "parameters is null");
+
+ return newInstance(
+ returnType,
+ parameters.stream().map(BytecodeExpression::getType).collect(Collectors.toList()),
+ parameters);
+ }
+
+ public static BytecodeExpression newInstance(Class<?> returnType, Collection<? extends Class<?>> parameterTypes,
+ BytecodeExpression... parameters) {
+ return newInstance(type(returnType), parameterTypes.stream().map(ParameterizedType::type).collect(Collectors.toList()),
+ List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public static BytecodeExpression newInstance(ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes, BytecodeExpression... parameters) {
+ return newInstance(returnType, parameterTypes, List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public static BytecodeExpression newInstance(
+ ParameterizedType type,
+ Collection<ParameterizedType> parameterTypes,
+ Collection<? extends BytecodeExpression> parameters) {
+ return new NewInstanceBytecodeExpression(type, parameterTypes, parameters);
+ }
+
+ //
+ // Array
+ //
+ public static BytecodeExpression newArray(ParameterizedType type, int length) {
+ return new NewArrayBytecodeExpression(type, length);
+ }
+
+ public static BytecodeExpression newArray(ParameterizedType type, BytecodeExpression length) {
+ return new NewArrayBytecodeExpression(type, length);
+ }
+
+ public static BytecodeExpression newArray(ParameterizedType type, BytecodeExpression... elements) {
+ return new NewArrayBytecodeExpression(type, List.of(elements));
+ }
+
+ public static BytecodeExpression newArray(ParameterizedType type,
+ Collection<? extends BytecodeExpression> elements) {
+ return new NewArrayBytecodeExpression(type, List.copyOf(elements));
+ }
+
+ public static BytecodeExpression length(BytecodeExpression instance) {
+ return new ArrayLengthBytecodeExpression(instance);
+ }
+
+ public static BytecodeExpression get(BytecodeExpression instance, BytecodeExpression index) {
+ return new GetElementBytecodeExpression(instance, index);
+ }
+
+ public static BytecodeExpression set(BytecodeExpression instance, BytecodeExpression index,
+ BytecodeExpression value) {
+ return new SetArrayElementBytecodeExpression(instance, index, value);
+ }
+
+ //
+ // Invoke static method
+ //
+
+ public static BytecodeExpression invokeStatic(MethodDefinition method, BytecodeExpression... parameters) {
+ return invokeStatic(
+ method.getDeclaringClass().getType(),
+ method.getName(),
+ method.getReturnType(),
+ method.getParameterTypes(),
+ List.of(parameters));
+ }
+
+ public static BytecodeExpression invokeStatic(Method method, BytecodeExpression... parameters) {
+ return invokeStatic(method, List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public static BytecodeExpression invokeStatic(Method method, Collection<? extends BytecodeExpression> parameters) {
+ return invokeStatic(
+ type(method.getDeclaringClass()),
+ method.getName(),
+ type(method.getReturnType()),
+ stream(method.getParameterTypes())
+ .map(ParameterizedType::type)
+ .collect(Collectors.toUnmodifiableList()),
+ parameters);
+ }
+
+ public static BytecodeExpression invokeStatic(Class<?> methodTargetType, String methodName, Class<?> returnType,
+ BytecodeExpression... parameters) {
+ return invokeStatic(methodTargetType, methodName, returnType, List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public static BytecodeExpression invokeStatic(
+ Class<?> methodTargetType,
+ String methodName,
+ Class<?> returnType,
+ Collection<? extends BytecodeExpression> parameters) {
+ return invokeStatic(type(methodTargetType), methodName, type(returnType), parameters);
+ }
+
+ public static BytecodeExpression invokeStatic(
+ ParameterizedType methodTargetType,
+ String methodName,
+ ParameterizedType returnType,
+ Collection<? extends BytecodeExpression> parameters) {
+ requireNonNull(methodTargetType, "methodTargetType is null");
+ requireNonNull(returnType, "returnType is null");
+ requireNonNull(parameters, "parameters is null");
+
+ return invokeStatic(
+ methodTargetType,
+ methodName,
+ returnType,
+ parameters.stream()
+ .map(BytecodeExpression::getType)
+ .collect(Collectors.toUnmodifiableList()),
+ parameters);
+ }
+
+ public static BytecodeExpression invokeStatic(
+ Class<?> methodTargetType,
+ String methodName,
+ Class<?> returnType,
+ Collection<? extends Class<?>> parameterTypes,
+ BytecodeExpression... parameters) {
+ requireNonNull(methodTargetType, "methodTargetType is null");
+ requireNonNull(returnType, "returnType is null");
+ requireNonNull(parameterTypes, "parameterTypes is null");
+ requireNonNull(parameters, "parameters is null");
+
+ return invokeStatic(
+ type(methodTargetType),
+ methodName,
+ type(returnType),
+ parameterTypes.stream()
+ .map(ParameterizedType::type)
+ .collect(Collectors.toUnmodifiableList()),
+ List.of(parameters));
+ }
+
+ public static BytecodeExpression invokeStatic(
+ ParameterizedType methodTargetType,
+ String methodName,
+ ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes,
+ BytecodeExpression... parameters) {
+ return invokeStatic(methodTargetType, methodName, returnType, parameterTypes, List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public static BytecodeExpression invokeStatic(
+ ParameterizedType methodTargetType,
+ String methodName,
+ ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes,
+ Collection<? extends BytecodeExpression> parameters) {
+ return new InvokeBytecodeExpression(
+ null,
+ methodTargetType,
+ methodName,
+ returnType,
+ parameterTypes,
+ parameters);
+ }
+
+ //
+ // Invoke dynamic
+ //
+
+ public static BytecodeExpression invokeDynamic(
+ Method bootstrapMethod,
+ Collection<? extends Object> bootstrapArgs,
+ String methodName,
+ Class<?> returnType,
+ BytecodeExpression... parameters) {
+ return invokeDynamic(bootstrapMethod, bootstrapArgs, methodName, returnType, List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public static BytecodeExpression invokeDynamic(
+ Method bootstrapMethod,
+ Collection<? extends Object> bootstrapArgs,
+ String methodName,
+ Class<?> returnType,
+ Collection<? extends BytecodeExpression> parameters) {
+ requireNonNull(returnType, "returnType is null");
+ requireNonNull(parameters, "parameters is null");
+
+ return invokeDynamic(
+ bootstrapMethod,
+ bootstrapArgs,
+ methodName,
+ type(returnType),
+ parameters.stream()
+ .map(BytecodeExpression::getType)
+ .collect(Collectors.toUnmodifiableList()),
+ parameters);
+ }
+
+ public static BytecodeExpression invokeDynamic(
+ Method bootstrapMethod,
+ Collection<? extends Object> bootstrapArgs,
+ String methodName,
+ ParameterizedType returnType,
+ BytecodeExpression... parameters) {
+ return invokeDynamic(bootstrapMethod, bootstrapArgs, methodName, returnType, List.of(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public static BytecodeExpression invokeDynamic(
+ Method bootstrapMethod,
+ Collection<? extends Object> bootstrapArgs,
+ String methodName,
+ ParameterizedType returnType,
+ Collection<? extends BytecodeExpression> parameters) {
+ requireNonNull(returnType, "returnType is null");
+ requireNonNull(parameters, "parameters is null");
+
+ return invokeDynamic(
+ bootstrapMethod,
+ bootstrapArgs,
+ methodName,
+ returnType,
+ parameters.stream()
+ .map(BytecodeExpression::getType)
+ .collect(Collectors.toUnmodifiableList()),
+ parameters);
+ }
+
+ public static BytecodeExpression invokeDynamic(
+ Method bootstrapMethod,
+ Collection<? extends Object> bootstrapArgs,
+ String methodName,
+ MethodType methodType,
+ BytecodeExpression... parameters) {
+ requireNonNull(methodType, "methodType is null");
+ requireNonNull(parameters, "parameters is null");
+
+ return invokeDynamic(bootstrapMethod, bootstrapArgs, methodName, methodType, List.of(parameters));
+ }
+
+ public static BytecodeExpression invokeDynamic(
+ Method bootstrapMethod,
+ Collection<? extends Object> bootstrapArgs,
+ String methodName,
+ MethodType methodType,
+ Collection<? extends BytecodeExpression> parameters) {
+ return invokeDynamic(
+ bootstrapMethod,
+ bootstrapArgs,
+ methodName,
+ type(methodType.returnType()),
+ methodType.parameterList().stream().map(ParameterizedType::type).collect(Collectors.toList()),
+ List.copyOf(requireNonNull(parameters, "parameters is null")));
+ }
+
+ public static BytecodeExpression invokeDynamic(
+ Method bootstrapMethod,
+ Collection<? extends Object> bootstrapArgs,
+ String methodName,
+ ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes,
+ Collection<? extends BytecodeExpression> parameters) {
+ return new InvokeDynamicBytecodeExpression(
+ bootstrapMethod,
+ bootstrapArgs,
+ methodName,
+ returnType,
+ parameters,
+ parameterTypes);
+ }
+
+ //
+ // Arithmetic operations
+ //
+
+ public static BytecodeExpression add(BytecodeExpression left, BytecodeExpression right) {
+ return createArithmeticBytecodeExpression(OpCode.IADD, left, right);
+ }
+
+ public static BytecodeExpression subtract(BytecodeExpression left, BytecodeExpression right) {
+ return createArithmeticBytecodeExpression(OpCode.ISUB, left, right);
+ }
+
+ public static BytecodeExpression multiply(BytecodeExpression left, BytecodeExpression right) {
+ return createArithmeticBytecodeExpression(OpCode.IMUL, left, right);
+ }
+
+ public static BytecodeExpression divide(BytecodeExpression left, BytecodeExpression right) {
+ return createArithmeticBytecodeExpression(OpCode.IDIV, left, right);
+ }
+
+ public static BytecodeExpression remainder(BytecodeExpression left, BytecodeExpression right) {
+ return createArithmeticBytecodeExpression(OpCode.IREM, left, right);
+ }
+
+ public static BytecodeExpression bitwiseAnd(BytecodeExpression left, BytecodeExpression right) {
+ return createArithmeticBytecodeExpression(OpCode.IAND, left, right);
+ }
+
+ public static BytecodeExpression bitwiseOr(BytecodeExpression left, BytecodeExpression right) {
+ return createArithmeticBytecodeExpression(OpCode.IOR, left, right);
+ }
+
+ public static BytecodeExpression bitwiseXor(BytecodeExpression left, BytecodeExpression right) {
+ return createArithmeticBytecodeExpression(OpCode.IXOR, left, right);
+ }
+
+ public static BytecodeExpression shiftLeft(BytecodeExpression left, BytecodeExpression right) {
+ return createArithmeticBytecodeExpression(OpCode.ISHL, left, right);
+ }
+
+ public static BytecodeExpression shiftRight(BytecodeExpression left, BytecodeExpression right) {
+ return createArithmeticBytecodeExpression(OpCode.ISHR, left, right);
+ }
+
+ public static BytecodeExpression shiftRightUnsigned(BytecodeExpression left, BytecodeExpression right) {
+ return createArithmeticBytecodeExpression(OpCode.IUSHR, left, right);
+ }
+
+ public static BytecodeExpression negate(BytecodeExpression value) {
+ return new NegateBytecodeExpression(value);
+ }
+
+ //
+ // Comparison operations
+ //
+
+ public static BytecodeExpression lessThan(BytecodeExpression left, BytecodeExpression right) {
+ return ComparisonBytecodeExpression.lessThan(left, right);
+ }
+
+ public static BytecodeExpression greaterThan(BytecodeExpression left, BytecodeExpression right) {
+ return ComparisonBytecodeExpression.greaterThan(left, right);
+ }
+
+ public static BytecodeExpression lessThanOrEqual(BytecodeExpression left, BytecodeExpression right) {
+ return ComparisonBytecodeExpression.lessThanOrEqual(left, right);
+ }
+
+ public static BytecodeExpression greaterThanOrEqual(BytecodeExpression left, BytecodeExpression right) {
+ return ComparisonBytecodeExpression.greaterThanOrEqual(left, right);
+ }
+
+ public static BytecodeExpression equal(BytecodeExpression left, BytecodeExpression right) {
+ return ComparisonBytecodeExpression.equal(left, right);
+ }
+
+ public static BytecodeExpression notEqual(BytecodeExpression left, BytecodeExpression right) {
+ return ComparisonBytecodeExpression.notEqual(left, right);
+ }
+
+ //
+ // Null comparison operations
+ //
+
+ public static BytecodeExpression isNull(BytecodeExpression value) {
+ return equal(value, constantNull(value.getType()));
+ }
+
+ public static BytecodeExpression isNotNull(BytecodeExpression value) {
+ return notEqual(value, constantNull(value.getType()));
+ }
+
+ //
+ // Logical binary operations
+ //
+
+ public static BytecodeExpression and(BytecodeExpression left, BytecodeExpression right) {
+ return new AndBytecodeExpression(left, right);
+ }
+
+ public static BytecodeExpression or(BytecodeExpression left, BytecodeExpression right) {
+ return new OrBytecodeExpression(left, right);
+ }
+
+ public static BytecodeExpression not(BytecodeExpression value) {
+ return new NotBytecodeExpression(value);
+ }
+
+ //
+ // Complex expressions
+ //
+
+ public static BytecodeExpression inlineIf(BytecodeExpression condition, BytecodeExpression ifTrue,
+ BytecodeExpression ifFalse) {
+ return new InlineIfBytecodeExpression(condition, ifTrue, ifFalse);
+ }
+
+ //
+ // Print
+ //
+ public static BytecodeExpression print(BytecodeExpression variable) {
+ BytecodeExpression out = getStatic(System.class, "out");
+ return out.invoke("println", void.class, variable);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/CastBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/CastBytecodeExpression.java
new file mode 100644
index 0000000..d08cc83
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/CastBytecodeExpression.java
@@ -0,0 +1,328 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.BytecodeUtils;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.OpCode;
+import com.facebook.presto.bytecode.ParameterizedType;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+class CastBytecodeExpression
+ extends BytecodeExpression
+{
+ private static final ParameterizedType OBJECT_TYPE = type(Object.class);
+
+ private final BytecodeExpression instance;
+
+ CastBytecodeExpression(BytecodeExpression instance, ParameterizedType type)
+ {
+ super(type);
+
+ this.instance = requireNonNull(instance, "instance is null");
+
+ checkArgument(type.getPrimitiveType() != void.class, "Type %s can not be cast to %s", instance.getType(), type);
+
+ // Call generateBytecode to run the validation logic. The result is thrown away.
+ // Duplicating the validation logic here is error-prone and introduces duplicate code.
+ generateBytecode(instance.getType(), getType());
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext)
+ {
+ return new BytecodeBlock()
+ .append(instance.getBytecode(generationContext))
+ .append(generateBytecode(instance.getType(), getType()));
+ }
+
+ private static BytecodeBlock generateBytecode(ParameterizedType sourceType, ParameterizedType targetType)
+ {
+ BytecodeBlock block = new BytecodeBlock();
+
+ switch (getTypeKind(sourceType)) {
+ case PRIMITIVE:
+ switch (getTypeKind(targetType)) {
+ case PRIMITIVE:
+ castPrimitiveToPrimitive(block, sourceType.getPrimitiveType(), targetType.getPrimitiveType());
+ return block;
+ case BOXED_PRIMITVE:
+ checkArgument(sourceType.getPrimitiveType() == unwrapPrimitiveType(targetType), "Type %s can not be cast to %s", sourceType, targetType);
+ return block.invokeStatic(targetType, "valueOf", targetType, sourceType);
+ case OTHER:
+ checkArgument(OBJECT_TYPE.equals(targetType), "Type %s can not be cast to %s", sourceType, targetType);
+ Class<?> sourceClass = sourceType.getPrimitiveType();
+ return block
+ .invokeStatic(BytecodeUtils.wrap(sourceClass), "valueOf", BytecodeUtils.wrap(sourceClass), sourceClass)
+ .checkCast(targetType);
+ }
+ case BOXED_PRIMITVE:
+ switch (getTypeKind(targetType)) {
+ case PRIMITIVE:
+ checkArgument(unwrapPrimitiveType(sourceType) == targetType.getPrimitiveType(), "Type %s can not be cast to %s", sourceType, targetType);
+ return block.invokeVirtual(sourceType, targetType.getPrimitiveType().getSimpleName() + "Value", targetType);
+ case BOXED_PRIMITVE:
+ checkArgument(sourceType.equals(targetType), "Type %s can not be cast to %s", sourceType, targetType);
+ return block;
+ case OTHER:
+ return block.checkCast(targetType);
+ }
+ case OTHER:
+ switch (getTypeKind(targetType)) {
+ case PRIMITIVE:
+ checkArgument(OBJECT_TYPE.equals(sourceType), "Type %s can not be cast to %s", sourceType, targetType);
+ return block
+ .checkCast(BytecodeUtils.wrap(targetType.getPrimitiveType()))
+ .invokeVirtual(BytecodeUtils.wrap(targetType.getPrimitiveType()), targetType.getPrimitiveType().getSimpleName() + "Value", targetType.getPrimitiveType());
+ case BOXED_PRIMITVE:
+ case OTHER:
+ return block.checkCast(targetType);
+ }
+ }
+ throw new UnsupportedOperationException("unexpected enum value");
+ }
+
+ private static BytecodeBlock castPrimitiveToPrimitive(BytecodeBlock block, Class<?> sourceType, Class<?> targetType)
+ {
+ if (sourceType == boolean.class) {
+ if (targetType == boolean.class) {
+ return block;
+ }
+ }
+ if (sourceType == byte.class) {
+ if (targetType == byte.class) {
+ return block;
+ }
+ if (targetType == char.class) {
+ return block;
+ }
+ if (targetType == short.class) {
+ return block;
+ }
+ if (targetType == int.class) {
+ return block;
+ }
+ if (targetType == long.class) {
+ return block.append(OpCode.I2L);
+ }
+ if (targetType == float.class) {
+ return block.append(OpCode.I2F);
+ }
+ if (targetType == double.class) {
+ return block.append(OpCode.I2D);
+ }
+ }
+ if (sourceType == char.class) {
+ if (targetType == byte.class) {
+ return block.append(OpCode.I2B);
+ }
+ if (targetType == char.class) {
+ return block;
+ }
+ if (targetType == short.class) {
+ return block;
+ }
+ if (targetType == int.class) {
+ return block;
+ }
+ if (targetType == long.class) {
+ return block.append(OpCode.I2L);
+ }
+ if (targetType == float.class) {
+ return block.append(OpCode.I2F);
+ }
+ if (targetType == double.class) {
+ return block.append(OpCode.I2D);
+ }
+ }
+ if (sourceType == short.class) {
+ if (targetType == byte.class) {
+ return block.append(OpCode.I2B);
+ }
+ if (targetType == char.class) {
+ return block.append(OpCode.I2C);
+ }
+ if (targetType == short.class) {
+ return block;
+ }
+ if (targetType == int.class) {
+ return block;
+ }
+ if (targetType == long.class) {
+ return block.append(OpCode.I2L);
+ }
+ if (targetType == float.class) {
+ return block.append(OpCode.I2F);
+ }
+ if (targetType == double.class) {
+ return block.append(OpCode.I2D);
+ }
+ }
+ if (sourceType == int.class) {
+ if (targetType == boolean.class) {
+ return block;
+ }
+ if (targetType == byte.class) {
+ return block.append(OpCode.I2B);
+ }
+ if (targetType == char.class) {
+ return block.append(OpCode.I2C);
+ }
+ if (targetType == short.class) {
+ return block.append(OpCode.I2S);
+ }
+ if (targetType == int.class) {
+ return block;
+ }
+ if (targetType == long.class) {
+ return block.append(OpCode.I2L);
+ }
+ if (targetType == float.class) {
+ return block.append(OpCode.I2F);
+ }
+ if (targetType == double.class) {
+ return block.append(OpCode.I2D);
+ }
+ }
+ if (sourceType == long.class) {
+ if (targetType == byte.class) {
+ return block.append(OpCode.L2I).append(OpCode.I2B);
+ }
+ if (targetType == char.class) {
+ return block.append(OpCode.L2I).append(OpCode.I2C);
+ }
+ if (targetType == short.class) {
+ return block.append(OpCode.L2I).append(OpCode.I2S);
+ }
+ if (targetType == int.class) {
+ return block.append(OpCode.L2I);
+ }
+ if (targetType == long.class) {
+ return block;
+ }
+ if (targetType == float.class) {
+ return block.append(OpCode.L2F);
+ }
+ if (targetType == double.class) {
+ return block.append(OpCode.L2D);
+ }
+ }
+ if (sourceType == float.class) {
+ if (targetType == byte.class) {
+ return block.append(OpCode.F2I).append(OpCode.I2B);
+ }
+ if (targetType == char.class) {
+ return block.append(OpCode.F2I).append(OpCode.I2C);
+ }
+ if (targetType == short.class) {
+ return block.append(OpCode.F2I).append(OpCode.I2S);
+ }
+ if (targetType == int.class) {
+ return block.append(OpCode.F2I);
+ }
+ if (targetType == long.class) {
+ return block.append(OpCode.F2L);
+ }
+ if (targetType == float.class) {
+ return block;
+ }
+ if (targetType == double.class) {
+ return block.append(OpCode.F2D);
+ }
+ }
+ if (sourceType == double.class) {
+ if (targetType == byte.class) {
+ return block.append(OpCode.D2I).append(OpCode.I2B);
+ }
+ if (targetType == char.class) {
+ return block.append(OpCode.D2I).append(OpCode.I2C);
+ }
+ if (targetType == short.class) {
+ return block.append(OpCode.D2I).append(OpCode.I2S);
+ }
+ if (targetType == int.class) {
+ return block.append(OpCode.D2I);
+ }
+ if (targetType == long.class) {
+ return block.append(OpCode.D2L);
+ }
+ if (targetType == float.class) {
+ return block.append(OpCode.D2F);
+ }
+ if (targetType == double.class) {
+ return block;
+ }
+ }
+ throw new IllegalArgumentException(format("Type %s can not be cast to %s", sourceType, targetType));
+ }
+
+ private static TypeKind getTypeKind(ParameterizedType type)
+ {
+ if (type.isPrimitive()) {
+ return TypeKind.PRIMITIVE;
+ }
+ if (unwrapPrimitiveType(type) != null) {
+ return TypeKind.BOXED_PRIMITVE;
+ }
+ return TypeKind.OTHER;
+ }
+
+ private static Class<?> unwrapPrimitiveType(ParameterizedType boxedPrimitiveType)
+ {
+ switch (boxedPrimitiveType.getJavaClassName()) {
+ case "java.lang.Boolean":
+ return boolean.class;
+ case "java.lang.Byte":
+ return byte.class;
+ case "java.lang.Character":
+ return char.class;
+ case "java.lang.Short":
+ return short.class;
+ case "java.lang.Integer":
+ return int.class;
+ case "java.lang.Long":
+ return long.class;
+ case "java.lang.Float":
+ return float.class;
+ case "java.lang.Double":
+ return double.class;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ protected String formatOneLine()
+ {
+ return "((" + getType().getSimpleName() + ") " + instance + ")";
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes()
+ {
+ return List.of(instance);
+ }
+
+ private enum TypeKind
+ {
+ PRIMITIVE, BOXED_PRIMITVE, OTHER
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ComparisonBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ComparisonBytecodeExpression.java
new file mode 100644
index 0000000..8267648
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ComparisonBytecodeExpression.java
@@ -0,0 +1,313 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.OpCode;
+import com.facebook.presto.bytecode.instruction.JumpInstruction;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.OpCode.DCMPG;
+import static com.facebook.presto.bytecode.OpCode.DCMPL;
+import static com.facebook.presto.bytecode.OpCode.FCMPG;
+import static com.facebook.presto.bytecode.OpCode.FCMPL;
+import static com.facebook.presto.bytecode.OpCode.IFEQ;
+import static com.facebook.presto.bytecode.OpCode.IFGE;
+import static com.facebook.presto.bytecode.OpCode.IFGT;
+import static com.facebook.presto.bytecode.OpCode.IFLE;
+import static com.facebook.presto.bytecode.OpCode.IFLT;
+import static com.facebook.presto.bytecode.OpCode.IFNE;
+import static com.facebook.presto.bytecode.OpCode.IF_ACMPEQ;
+import static com.facebook.presto.bytecode.OpCode.IF_ACMPNE;
+import static com.facebook.presto.bytecode.OpCode.IF_ICMPEQ;
+import static com.facebook.presto.bytecode.OpCode.IF_ICMPGE;
+import static com.facebook.presto.bytecode.OpCode.IF_ICMPGT;
+import static com.facebook.presto.bytecode.OpCode.IF_ICMPLE;
+import static com.facebook.presto.bytecode.OpCode.IF_ICMPLT;
+import static com.facebook.presto.bytecode.OpCode.IF_ICMPNE;
+import static com.facebook.presto.bytecode.OpCode.LCMP;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.util.Objects.requireNonNull;
+
+class ComparisonBytecodeExpression
+ extends BytecodeExpression
+{
+ static BytecodeExpression lessThan(BytecodeExpression left, BytecodeExpression right)
+ {
+ checkArgumentTypes(left, right);
+
+ OpCode comparisonInstruction;
+ OpCode noMatchJumpInstruction;
+
+ Class<?> type = left.getType().getPrimitiveType();
+ if (type == int.class) {
+ comparisonInstruction = null;
+ noMatchJumpInstruction = IF_ICMPGE;
+ }
+ else if (type == long.class) {
+ comparisonInstruction = LCMP;
+ noMatchJumpInstruction = IFGE;
+ }
+ else if (type == float.class) {
+ comparisonInstruction = FCMPG;
+ noMatchJumpInstruction = IFGE;
+ }
+ else if (type == double.class) {
+ comparisonInstruction = DCMPG;
+ noMatchJumpInstruction = IFGE;
+ }
+ else {
+ throw new IllegalArgumentException("Less than does not support " + type);
+ }
+
+ return new ComparisonBytecodeExpression("<", comparisonInstruction, noMatchJumpInstruction, left, right);
+ }
+
+ static BytecodeExpression greaterThan(BytecodeExpression left, BytecodeExpression right)
+ {
+ checkArgumentTypes(left, right);
+
+ OpCode comparisonInstruction;
+ OpCode noMatchJumpInstruction;
+
+ Class<?> type = left.getType().getPrimitiveType();
+ if (type == int.class) {
+ comparisonInstruction = null;
+ noMatchJumpInstruction = IF_ICMPLE;
+ }
+ else if (type == long.class) {
+ comparisonInstruction = LCMP;
+ noMatchJumpInstruction = IFLE;
+ }
+ else if (type == float.class) {
+ comparisonInstruction = FCMPL;
+ noMatchJumpInstruction = IFLE;
+ }
+ else if (type == double.class) {
+ comparisonInstruction = DCMPL;
+ noMatchJumpInstruction = IFLE;
+ }
+ else {
+ throw new IllegalArgumentException("Greater than does not support " + type);
+ }
+ return new ComparisonBytecodeExpression(">", comparisonInstruction, noMatchJumpInstruction, left, right);
+ }
+
+ static BytecodeExpression lessThanOrEqual(BytecodeExpression left, BytecodeExpression right)
+ {
+ checkArgumentTypes(left, right);
+
+ OpCode comparisonInstruction;
+ OpCode noMatchJumpInstruction;
+
+ Class<?> type = left.getType().getPrimitiveType();
+ if (type == int.class) {
+ comparisonInstruction = null;
+ noMatchJumpInstruction = IF_ICMPGT;
+ }
+ else if (type == long.class) {
+ comparisonInstruction = LCMP;
+ noMatchJumpInstruction = IFGT;
+ }
+ else if (type == float.class) {
+ comparisonInstruction = FCMPG;
+ noMatchJumpInstruction = IFGT;
+ }
+ else if (type == double.class) {
+ comparisonInstruction = DCMPG;
+ noMatchJumpInstruction = IFGT;
+ }
+ else {
+ throw new IllegalArgumentException("Less than or equal does not support " + type);
+ }
+ return new ComparisonBytecodeExpression("<=", comparisonInstruction, noMatchJumpInstruction, left, right);
+ }
+
+ static BytecodeExpression greaterThanOrEqual(BytecodeExpression left, BytecodeExpression right)
+ {
+ checkArgumentTypes(left, right);
+
+ OpCode comparisonInstruction;
+ OpCode noMatchJumpInstruction;
+
+ Class<?> type = left.getType().getPrimitiveType();
+ if (type == int.class) {
+ comparisonInstruction = null;
+ noMatchJumpInstruction = IF_ICMPLT;
+ }
+ else if (type == long.class) {
+ comparisonInstruction = LCMP;
+ noMatchJumpInstruction = IFLT;
+ }
+ else if (type == float.class) {
+ comparisonInstruction = FCMPL;
+ noMatchJumpInstruction = IFLT;
+ }
+ else if (type == double.class) {
+ comparisonInstruction = DCMPL;
+ noMatchJumpInstruction = IFLT;
+ }
+ else {
+ throw new IllegalArgumentException("Greater than or equal does not support " + type);
+ }
+ return new ComparisonBytecodeExpression(">=", comparisonInstruction, noMatchJumpInstruction, left, right);
+ }
+
+ static BytecodeExpression equal(BytecodeExpression left, BytecodeExpression right)
+ {
+ requireNonNull(left, "left is null");
+ requireNonNull(right, "right is null");
+ checkArgument(left.getType().equals(right.getType()), "left and right must be the same type");
+
+ OpCode comparisonInstruction;
+ OpCode noMatchJumpInstruction;
+
+ Class<?> type = left.getType().getPrimitiveType();
+ if (type == int.class) {
+ comparisonInstruction = null;
+ noMatchJumpInstruction = IF_ICMPNE;
+ }
+ else if (type == long.class) {
+ comparisonInstruction = LCMP;
+ noMatchJumpInstruction = IFNE;
+ }
+ else if (type == float.class) {
+ comparisonInstruction = FCMPL;
+ noMatchJumpInstruction = IFNE;
+ }
+ else if (type == double.class) {
+ comparisonInstruction = DCMPL;
+ noMatchJumpInstruction = IFNE;
+ }
+ else if (type == null) {
+ comparisonInstruction = null;
+ noMatchJumpInstruction = IF_ACMPNE;
+ }
+ else {
+ throw new IllegalArgumentException("Equal does not support " + type);
+ }
+ return new ComparisonBytecodeExpression("==", comparisonInstruction, noMatchJumpInstruction, left, right);
+ }
+
+ static BytecodeExpression notEqual(BytecodeExpression left, BytecodeExpression right)
+ {
+ requireNonNull(left, "left is null");
+ requireNonNull(right, "right is null");
+ checkArgument(left.getType().equals(right.getType()), "left and right must be the same type");
+
+ OpCode comparisonInstruction;
+ OpCode noMatchJumpInstruction;
+
+ Class<?> type = left.getType().getPrimitiveType();
+ if (type == int.class) {
+ comparisonInstruction = null;
+ noMatchJumpInstruction = IF_ICMPEQ;
+ }
+ else if (type == long.class) {
+ comparisonInstruction = LCMP;
+ noMatchJumpInstruction = IFEQ;
+ }
+ else if (type == float.class) {
+ comparisonInstruction = FCMPL;
+ noMatchJumpInstruction = IFEQ;
+ }
+ else if (type == double.class) {
+ comparisonInstruction = DCMPL;
+ noMatchJumpInstruction = IFEQ;
+ }
+ else if (type == null) {
+ comparisonInstruction = null;
+ noMatchJumpInstruction = IF_ACMPEQ;
+ }
+ else {
+ throw new IllegalArgumentException("Not equal than does not support " + type);
+ }
+ return new ComparisonBytecodeExpression("!=", comparisonInstruction, noMatchJumpInstruction, left, right);
+ }
+
+ private static void checkArgumentTypes(BytecodeExpression left, BytecodeExpression right)
+ {
+ Class<?> leftType = getPrimitiveType(left, "left");
+ Class<?> rightType = getPrimitiveType(right, "right");
+ checkArgument(leftType == rightType, "left and right must be the same type");
+ }
+
+ private static Class<?> getPrimitiveType(BytecodeExpression expression, String name)
+ {
+ requireNonNull(expression, name + " is null");
+ Class<?> leftType = expression.getType().getPrimitiveType();
+ checkArgument(leftType != null, name + " is not a primitive");
+ checkArgument(leftType != void.class, name + " is void");
+ return leftType;
+ }
+
+ private final String infixSymbol;
+ private final OpCode comparisonInstruction;
+ private final OpCode noMatchJumpInstruction;
+ private final BytecodeExpression left;
+ private final BytecodeExpression right;
+
+ private ComparisonBytecodeExpression(
+ String infixSymbol,
+ OpCode comparisonInstruction,
+ OpCode noMatchJumpInstruction,
+ BytecodeExpression left,
+ BytecodeExpression right)
+ {
+ super(type(boolean.class));
+ this.infixSymbol = infixSymbol;
+ this.comparisonInstruction = comparisonInstruction;
+ this.noMatchJumpInstruction = noMatchJumpInstruction;
+ this.left = left;
+ this.right = right;
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext)
+ {
+ BytecodeBlock block = new BytecodeBlock()
+ .append(left)
+ .append(right);
+
+ if (comparisonInstruction != null) {
+ block.append(comparisonInstruction);
+ }
+
+ LabelNode noMatch = new LabelNode("no_match");
+ LabelNode end = new LabelNode("end");
+ return block
+ .append(new JumpInstruction(noMatchJumpInstruction, noMatch))
+ .push(true)
+ .gotoLabel(end)
+ .append(noMatch)
+ .push(false)
+ .append(end);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes()
+ {
+ return List.of(left, right);
+ }
+
+ @Override
+ protected String formatOneLine()
+ {
+ return "(" + left + " " + infixSymbol + " " + right + ")";
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ConstantBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ConstantBytecodeExpression.java
new file mode 100644
index 0000000..e1fc0f2
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ConstantBytecodeExpression.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.ParameterizedType;
+import com.facebook.presto.bytecode.instruction.Constant;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+
+class ConstantBytecodeExpression
+ extends BytecodeExpression {
+ private final Constant value;
+
+ ConstantBytecodeExpression(Class<?> type, Constant value) {
+ this(type(type), value);
+ }
+
+ ConstantBytecodeExpression(ParameterizedType type, Constant value) {
+ super(type);
+ this.value = value;
+ }
+
+ @Override
+ public Constant getBytecode(MethodGenerationContext generationContext) {
+ return value;
+ }
+
+ @Override
+ protected String formatOneLine() {
+ return renderConstant(value.getValue());
+ }
+
+ public static String renderConstant(Object value) {
+ if (value instanceof Long) {
+ return value + "L";
+ }
+ if (value instanceof Float) {
+ return value + "f";
+ }
+ if (value instanceof ParameterizedType) {
+ return ((ParameterizedType)value).getSimpleName() + ".class";
+ }
+ // todo escape string
+ if (value instanceof String) {
+ return "\"" + value + "\"";
+ }
+ return String.valueOf(value);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of();
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/GetElementBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/GetElementBytecodeExpression.java
new file mode 100644
index 0000000..c0b11bd
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/GetElementBytecodeExpression.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.instruction.InstructionNode;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.ArrayOpCode.getArrayOpCode;
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+class GetElementBytecodeExpression
+ extends BytecodeExpression
+{
+ private final BytecodeExpression instance;
+ private final BytecodeExpression index;
+ private final InstructionNode arrayLoadInstruction;
+
+ GetElementBytecodeExpression(BytecodeExpression instance, BytecodeExpression index)
+ {
+ super(instance.getType().getArrayComponentType());
+ this.instance = requireNonNull(instance, "instance is null");
+ this.index = requireNonNull(index, "index is null");
+
+ checkArgument(index.getType().getPrimitiveType() == int.class, "index must be int type, but is " + index.getType());
+ this.arrayLoadInstruction = getArrayOpCode(instance.getType().getArrayComponentType()).getLoad();
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext)
+ {
+ return new BytecodeBlock()
+ .append(instance.getBytecode(generationContext))
+ .append(index)
+ .append(arrayLoadInstruction);
+ }
+
+ @Override
+ protected String formatOneLine()
+ {
+ return instance + "[" + index + "]";
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes()
+ {
+ return List.of(index);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/GetFieldBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/GetFieldBytecodeExpression.java
new file mode 100644
index 0000000..44fb202
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/GetFieldBytecodeExpression.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.FieldDefinition;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.ParameterizedType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.List;
+import org.jetbrains.annotations.Nullable;
+
+import static com.facebook.presto.bytecode.Access.STATIC;
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static com.facebook.presto.bytecode.instruction.FieldInstruction.getStaticInstruction;
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+class GetFieldBytecodeExpression
+ extends BytecodeExpression {
+ private final BytecodeExpression instance;
+ private final ParameterizedType declaringClass;
+ private final String name;
+
+ GetFieldBytecodeExpression(@Nullable BytecodeExpression instance, Class<?> declaringClass, String name) {
+ this(instance, getDeclaredField(declaringClass, name));
+ }
+
+ GetFieldBytecodeExpression(@Nullable BytecodeExpression instance, Field field) {
+ this(instance, type(requireNonNull(field, "field is null").getDeclaringClass()), field.getName(), type(field.getType()));
+
+ boolean isStatic = Modifier.isStatic(field.getModifiers());
+ if (instance == null) {
+ checkArgument(isStatic, "Field is not static: %s", field);
+ }
+ else {
+ checkArgument(!isStatic, "Field is static: %s", field);
+ }
+ }
+
+ GetFieldBytecodeExpression(@Nullable BytecodeExpression instance, FieldDefinition field) {
+ this(instance, requireNonNull(field, "field is null").getDeclaringClass().getType(), field.getName(), field.getType());
+ if (instance == null) {
+ checkArgument(field.getAccess().contains(STATIC), "Field is not static: %s", field);
+ }
+ else {
+ checkArgument(!field.getAccess().contains(STATIC), "Field is static: %s", field);
+ }
+ }
+
+ GetFieldBytecodeExpression(@Nullable BytecodeExpression instance, ParameterizedType declaringClass,
+ String name, ParameterizedType type) {
+ super(type);
+ checkArgument(instance == null || !instance.getType().isPrimitive(), "Type %s does not have fields", getType());
+ this.instance = instance;
+ this.declaringClass = requireNonNull(declaringClass, "declaringClass is null");
+ this.name = requireNonNull(name, "name is null");
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ if (instance == null) {
+ return getStaticInstruction(declaringClass, name, getType());
+ }
+
+ return new BytecodeBlock()
+ .append(instance.getBytecode(generationContext))
+ .getField(declaringClass, name, getType());
+ }
+
+ @Override
+ protected String formatOneLine() {
+ if (instance == null) {
+ return declaringClass.getSimpleName() + "." + name;
+ }
+ return instance + "." + name;
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return (instance == null) ? List.of() : List.of(instance);
+ }
+
+ private static Field getDeclaredField(Class<?> declaringClass, String name) {
+ requireNonNull(declaringClass, "declaringClass is null");
+ requireNonNull(name, "name is null");
+
+ try {
+ return declaringClass.getField(name);
+ }
+ catch (NoSuchFieldException e) {
+ throw new IllegalArgumentException(format("Class %s does not have a '%s' field", declaringClass.getName(), name));
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InlineIfBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InlineIfBytecodeExpression.java
new file mode 100644
index 0000000..1ab706e
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InlineIfBytecodeExpression.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+class InlineIfBytecodeExpression
+ extends BytecodeExpression
+{
+ private final BytecodeExpression condition;
+ private final BytecodeExpression ifTrue;
+ private final BytecodeExpression ifFalse;
+
+ InlineIfBytecodeExpression(BytecodeExpression condition, BytecodeExpression ifTrue, BytecodeExpression ifFalse)
+ {
+ super(ifTrue.getType());
+ this.condition = condition;
+ this.ifTrue = requireNonNull(ifTrue, "ifTrue is null");
+ this.ifFalse = requireNonNull(ifFalse, "ifFalse is null");
+
+ checkArgument(condition.getType().getPrimitiveType() == boolean.class, "Expected condition to be type boolean but is %s", condition.getType());
+ checkArgument(ifTrue.getType().equals(ifFalse.getType()), "Expected ifTrue and ifFalse to be the same type");
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext)
+ {
+ LabelNode falseLabel = new LabelNode("false");
+ LabelNode endLabel = new LabelNode("end");
+ return new BytecodeBlock()
+ .append(condition)
+ .ifFalseGoto(falseLabel)
+ .append(ifTrue)
+ .gotoLabel(endLabel)
+ .visitLabel(falseLabel)
+ .append(ifFalse)
+ .visitLabel(endLabel);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes()
+ {
+ return List.of(condition, ifTrue, ifFalse);
+ }
+
+ @Override
+ protected String formatOneLine()
+ {
+ return "(" + condition + " ? " + ifTrue + " : " + ifFalse + ")";
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InstanceOfBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InstanceOfBytecodeExpression.java
new file mode 100644
index 0000000..39ed17c
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InstanceOfBytecodeExpression.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.util.Objects.requireNonNull;
+
+class InstanceOfBytecodeExpression
+ extends BytecodeExpression
+{
+ private final BytecodeExpression instance;
+ private final Class<?> type;
+
+ InstanceOfBytecodeExpression(BytecodeExpression instance, Class<?> type)
+ {
+ super(type(boolean.class));
+
+ this.instance = requireNonNull(instance, "instance is null");
+ this.type = requireNonNull(type, "type is null");
+ }
+
+ public static BytecodeExpression instanceOf(BytecodeExpression instance, Class<?> type)
+ {
+ return new InstanceOfBytecodeExpression(instance, type);
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext)
+ {
+ return new BytecodeBlock()
+ .append(instance)
+ .isInstanceOf(type);
+ }
+
+ @Override
+ protected String formatOneLine()
+ {
+ return instance + " instanceof " + type;
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes()
+ {
+ return List.of();
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InvokeBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InvokeBytecodeExpression.java
new file mode 100644
index 0000000..f80ed6b
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InvokeBytecodeExpression.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.ParameterizedType;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.jetbrains.annotations.Nullable;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+class InvokeBytecodeExpression
+ extends BytecodeExpression {
+ public static InvokeBytecodeExpression createInvoke(
+ BytecodeExpression instance,
+ String methodName,
+ ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes,
+ Collection<? extends BytecodeExpression> parameters) {
+ return new InvokeBytecodeExpression(
+ requireNonNull(instance, "instance is null"),
+ instance.getType(),
+ requireNonNull(methodName, "methodName is null"),
+ requireNonNull(returnType, "returnType is null"),
+ requireNonNull(parameterTypes, "parameterTypes is null"),
+ requireNonNull(parameters, "parameters is null"));
+ }
+
+ @Nullable
+ private final BytecodeExpression instance;
+ private final ParameterizedType methodTargetType;
+ private final String methodName;
+ private final ParameterizedType returnType;
+ private final List<BytecodeExpression> parameters;
+ private final List<ParameterizedType> parameterTypes;
+
+ InvokeBytecodeExpression(
+ @Nullable BytecodeExpression instance,
+ ParameterizedType methodTargetType,
+ String methodName,
+ ParameterizedType returnType,
+ Collection<ParameterizedType> parameterTypes,
+ Collection<? extends BytecodeExpression> parameters) {
+ super(requireNonNull(returnType, "returnType is null"));
+ checkArgument(instance == null || !instance.getType().isPrimitive(), "Type %s does not have methods", getType());
+ this.instance = instance;
+ this.methodTargetType = requireNonNull(methodTargetType, "methodTargetType is null");
+ this.methodName = requireNonNull(methodName, "methodName is null");
+ this.returnType = returnType;
+ this.parameterTypes = List.copyOf(requireNonNull(parameterTypes, "parameterTypes is null"));
+ this.parameters = List.copyOf(requireNonNull(parameters, "parameters is null"));
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ BytecodeBlock block = new BytecodeBlock();
+ if (instance != null) {
+ block.append(instance);
+ }
+
+ for (BytecodeExpression parameter : parameters) {
+ block.append(parameter);
+ }
+
+ if (instance == null) {
+ return block.invokeStatic(methodTargetType, methodName, returnType, parameterTypes);
+ }
+ else if (instance.getType().isInterface()) {
+ return block.invokeInterface(methodTargetType, methodName, returnType, parameterTypes);
+ }
+ else {
+ return block.invokeVirtual(methodTargetType, methodName, returnType, parameterTypes);
+ }
+ }
+
+ @Override
+ protected String formatOneLine() {
+ if (instance == null) {
+ return methodTargetType.getSimpleName() + "." + methodName + "(" +
+ parameters.stream().map(BytecodeExpression::toString).collect(Collectors.joining(", ")) + ")";
+ }
+
+ return instance + "." + methodName + "(" +
+ parameters.stream().map(BytecodeExpression::toString).collect(Collectors.joining(", ")) + ")";
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ if (instance == null)
+ return List.copyOf(parameters);
+
+ final ArrayList<BytecodeNode> children = new ArrayList<>(parameters.size() + 1);
+ children.add(instance);
+ children.addAll(parameters);
+ return Collections.unmodifiableList(children);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InvokeDynamicBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InvokeDynamicBytecodeExpression.java
new file mode 100644
index 0000000..d8ff52c
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/InvokeDynamicBytecodeExpression.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.ParameterizedType;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.requireNonNull;
+
+class InvokeDynamicBytecodeExpression
+ extends BytecodeExpression {
+ private final Method bootstrapMethod;
+ private final List<Object> bootstrapArgs;
+ private final String methodName;
+ private final ParameterizedType returnType;
+ private final List<BytecodeExpression> parameters;
+ private final List<ParameterizedType> parameterTypes;
+
+ InvokeDynamicBytecodeExpression(
+ Method bootstrapMethod,
+ Collection<?> bootstrapArgs,
+ String methodName,
+ ParameterizedType returnType,
+ Collection<? extends BytecodeExpression> parameters,
+ Collection<ParameterizedType> parameterTypes
+ ) {
+ super(returnType);
+ this.bootstrapMethod = requireNonNull(bootstrapMethod, "bootstrapMethod is null");
+ this.bootstrapArgs = List.copyOf(requireNonNull(bootstrapArgs, "bootstrapArgs is null"));
+ this.methodName = requireNonNull(methodName, "methodName is null");
+ this.returnType = requireNonNull(returnType, "returnType is null");
+ this.parameters = List.copyOf(requireNonNull(parameters, "parameters is null"));
+ this.parameterTypes = List.copyOf(requireNonNull(parameterTypes, "parameterTypes is null"));
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ BytecodeBlock block = new BytecodeBlock();
+ for (BytecodeExpression parameter : parameters) {
+ block.append(parameter);
+ }
+ return block.invokeDynamic(methodName, returnType, parameterTypes, bootstrapMethod, bootstrapArgs);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of();
+ }
+
+ @Override
+ protected String formatOneLine() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[").append(bootstrapMethod.getName());
+ if (!bootstrapArgs.isEmpty()) {
+ builder.append("(").append(bootstrapArgs.stream().map(ConstantBytecodeExpression::renderConstant)
+ .collect(Collectors.joining(", "))).append(")");
+ }
+ builder.append("]=>");
+
+ builder.append(methodName)
+ .append("(")
+ .append(parameters.stream().map(BytecodeExpression::toString).collect(Collectors.joining(", ")))
+ .append(")");
+
+ return builder.toString();
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NegateBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NegateBytecodeExpression.java
new file mode 100644
index 0000000..f1873c5
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NegateBytecodeExpression.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.OpCode;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.expression.ArithmeticBytecodeExpression.getNumericOpCode;
+import static java.util.Objects.requireNonNull;
+
+class NegateBytecodeExpression
+ extends BytecodeExpression
+{
+ private final BytecodeExpression value;
+ private final OpCode negateOpCode;
+
+ NegateBytecodeExpression(BytecodeExpression value)
+ {
+ super(requireNonNull(value, "value is null").getType());
+ this.value = value;
+
+ Class<?> type = value.getType().getPrimitiveType();
+ checkArgument(type != null, "value is not a primitive");
+ checkArgument(type != void.class, "value is void");
+ checkArgument(type == int.class || type == long.class || type == float.class || type == double.class,
+ "value argument must be int, long, float, or double, but is %s",
+ type);
+
+ negateOpCode = getNumericOpCode("Negate", OpCode.INEG, type);
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext)
+ {
+ return new BytecodeBlock()
+ .append(value)
+ .append(negateOpCode);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes()
+ {
+ return List.of(value);
+ }
+
+ @Override
+ protected String formatOneLine()
+ {
+ return "-(" + value + ")";
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NewArrayBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NewArrayBytecodeExpression.java
new file mode 100644
index 0000000..7e2c25a
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NewArrayBytecodeExpression.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.ParameterizedType;
+import com.facebook.presto.bytecode.instruction.TypeInstruction;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.jetbrains.annotations.Nullable;
+
+import static com.facebook.presto.bytecode.ArrayOpCode.getArrayOpCode;
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantInt;
+import static java.util.Objects.requireNonNull;
+
+class NewArrayBytecodeExpression
+ extends BytecodeExpression {
+ private final BytecodeExpression length;
+ private final ParameterizedType elementType;
+
+ @Nullable
+ private final List<BytecodeExpression> elements;
+
+ NewArrayBytecodeExpression(ParameterizedType type, int length) {
+ this(type, constantInt(length));
+ }
+
+ NewArrayBytecodeExpression(ParameterizedType type, BytecodeExpression length) {
+ this(type, length, null);
+ }
+
+ NewArrayBytecodeExpression(ParameterizedType type, Collection<BytecodeExpression> elements) {
+ this(type, constantInt(elements.size()), elements);
+ }
+
+ private NewArrayBytecodeExpression(ParameterizedType type, BytecodeExpression length,
+ Collection<BytecodeExpression> elements) {
+ super(type);
+ requireNonNull(type, "type is null");
+ checkArgument(type.getArrayComponentType() != null, "type %s must be array type", type);
+ this.elementType = type.getArrayComponentType();
+ this.length = requireNonNull(length, "length is null");
+ this.elements = (elements == null) ? null : List.copyOf(elements);
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ BytecodeBlock bytecodeBlock;
+ if (elementType.isPrimitive()) {
+ bytecodeBlock = new BytecodeBlock()
+ .append(length)
+ .append(TypeInstruction.newPrimitiveArray(elementType));
+ }
+ else {
+ bytecodeBlock = new BytecodeBlock()
+ .append(length)
+ .append(TypeInstruction.newObjectArray(elementType));
+ }
+ if (elements != null) {
+ for (int i = 0; i < elements.size(); i++) {
+ BytecodeExpression element = elements.get(i);
+ bytecodeBlock
+ .dup()
+ .append(constantInt(i))
+ .append(element)
+ .append(getArrayOpCode(elementType).getStore());
+ }
+ }
+ return bytecodeBlock;
+ }
+
+ @Override
+ protected String formatOneLine() {
+ if (elements == null) {
+ return "new " + elementType.getSimpleName() + "[" + length + "]";
+ }
+ return "new " + elementType.getSimpleName() + "[] {" + elements.stream().map(BytecodeExpression::toString).collect(Collectors.joining(", ")) + "}";
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of();
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NewInstanceBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NewInstanceBytecodeExpression.java
new file mode 100644
index 0000000..53c09b7
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NewInstanceBytecodeExpression.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.ParameterizedType;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.requireNonNull;
+
+class NewInstanceBytecodeExpression
+ extends BytecodeExpression
+{
+ private final List<BytecodeExpression> parameters;
+ private final List<ParameterizedType> parameterTypes;
+
+ NewInstanceBytecodeExpression(
+ ParameterizedType type,
+ Collection<ParameterizedType> parameterTypes,
+ Collection<? extends BytecodeExpression> parameters)
+ {
+ super(type);
+ this.parameterTypes = List.copyOf(requireNonNull(parameterTypes, "parameterTypes is null"));
+ this.parameters = List.copyOf(requireNonNull(parameters, "parameters is null"));
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext)
+ {
+ BytecodeBlock block = new BytecodeBlock()
+ .newObject(getType())
+ .dup();
+
+ for (BytecodeExpression parameter : parameters) {
+ block.append(parameter);
+ }
+ return block.invokeConstructor(getType(), parameterTypes);
+ }
+
+ @Override
+ protected String formatOneLine()
+ {
+ return "new " + getType().getSimpleName() + "(" +
+ parameters.stream().map(BytecodeExpression::toString).collect(Collectors.joining(", ")) + ")";
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes()
+ {
+ return List.copyOf(parameters);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NotBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NotBytecodeExpression.java
new file mode 100644
index 0000000..ed8d7ae
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/NotBytecodeExpression.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+
+class NotBytecodeExpression
+ extends BytecodeExpression
+{
+ private final BytecodeExpression value;
+
+ NotBytecodeExpression(BytecodeExpression value)
+ {
+ super(type(boolean.class));
+ this.value = value;
+ checkArgument(value.getType().getPrimitiveType() == boolean.class, "Expected value to be type boolean but is %s", value.getType());
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext)
+ {
+ LabelNode trueLabel = new LabelNode("true");
+ LabelNode endLabel = new LabelNode("end");
+ return new BytecodeBlock()
+ .append(value)
+ .ifTrueGoto(trueLabel)
+ .push(true)
+ .gotoLabel(endLabel)
+ .visitLabel(trueLabel)
+ .push(false)
+ .visitLabel(endLabel);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes()
+ {
+ return List.of(value);
+ }
+
+ @Override
+ protected String formatOneLine()
+ {
+ return "(!" + value + ")";
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/OrBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/OrBytecodeExpression.java
new file mode 100644
index 0000000..25dfa79
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/OrBytecodeExpression.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.instruction.LabelNode;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.util.Objects.requireNonNull;
+
+class OrBytecodeExpression
+ extends BytecodeExpression {
+ private final BytecodeExpression left;
+ private final BytecodeExpression right;
+
+ OrBytecodeExpression(BytecodeExpression left, BytecodeExpression right) {
+ super(type(boolean.class));
+ this.left = requireNonNull(left, "left is null");
+ checkArgument(left.getType().getPrimitiveType() == boolean.class, "Expected left to be type boolean but is %s", left.getType());
+ this.right = requireNonNull(right, "right is null");
+ checkArgument(right.getType().getPrimitiveType() == boolean.class, "Expected right to be type boolean but is %s", right.getType());
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ LabelNode trueLabel = new LabelNode("true");
+ LabelNode endLabel = new LabelNode("end");
+ return new BytecodeBlock()
+ .append(left)
+ .ifTrueGoto(trueLabel)
+ .append(right)
+ .ifTrueGoto(trueLabel)
+ .push(false)
+ .gotoLabel(endLabel)
+ .visitLabel(trueLabel)
+ .push(true)
+ .visitLabel(endLabel);
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of(left, right);
+ }
+
+ @Override
+ protected String formatOneLine() {
+ return "(" + left + " || " + right + ")";
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/PopBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/PopBytecodeExpression.java
new file mode 100644
index 0000000..c00cb32
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/PopBytecodeExpression.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.util.Objects.requireNonNull;
+
+class PopBytecodeExpression
+ extends BytecodeExpression {
+ private final BytecodeExpression instance;
+
+ PopBytecodeExpression(BytecodeExpression instance) {
+ super(type(void.class));
+ this.instance = requireNonNull(instance, "instance is null");
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ return new BytecodeBlock()
+ .append(instance.getBytecode(generationContext))
+ .pop(instance.getType());
+ }
+
+ @Override
+ protected String formatOneLine() {
+ return instance.toString();
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of(instance);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ReturnBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ReturnBytecodeExpression.java
new file mode 100644
index 0000000..8066509
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/ReturnBytecodeExpression.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.OpCode;
+import com.facebook.presto.bytecode.ParameterizedType;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.util.Objects.requireNonNull;
+
+class ReturnBytecodeExpression
+ extends BytecodeExpression {
+ private final BytecodeExpression instance;
+ private final OpCode returnOpCode;
+
+ ReturnBytecodeExpression(BytecodeExpression instance) {
+ super(type(void.class));
+ this.instance = requireNonNull(instance, "instance is null");
+ this.returnOpCode = returnOpCode(instance.getType());
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ return new BytecodeBlock()
+ .append(instance.getBytecode(generationContext))
+ .append(returnOpCode);
+ }
+
+ @Override
+ protected String formatOneLine() {
+ return "return " + instance;
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of(instance);
+ }
+
+ private static OpCode returnOpCode(ParameterizedType componentType) {
+ Class<?> primitiveType = componentType.getPrimitiveType();
+ if (primitiveType != null) {
+ if (primitiveType == byte.class ||
+ primitiveType == boolean.class ||
+ primitiveType == char.class ||
+ primitiveType == short.class ||
+ primitiveType == int.class) {
+ return OpCode.IRETURN;
+ }
+ if (primitiveType == long.class) {
+ return OpCode.LRETURN;
+ }
+ if (primitiveType == float.class) {
+ return OpCode.FRETURN;
+ }
+ if (primitiveType == double.class) {
+ return OpCode.DRETURN;
+ }
+ if (primitiveType == void.class) {
+ return OpCode.RETURN;
+ }
+ throw new IllegalArgumentException("Unsupported array type: " + primitiveType);
+ }
+ else {
+ return OpCode.ARETURN;
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/SetArrayElementBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/SetArrayElementBytecodeExpression.java
new file mode 100644
index 0000000..06406f9
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/SetArrayElementBytecodeExpression.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.ParameterizedType;
+import com.facebook.presto.bytecode.instruction.InstructionNode;
+import java.util.List;
+
+import static com.facebook.presto.bytecode.ArrayOpCode.getArrayOpCode;
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.util.Objects.requireNonNull;
+
+class SetArrayElementBytecodeExpression
+ extends BytecodeExpression {
+ private final BytecodeExpression instance;
+ private final BytecodeExpression index;
+ private final BytecodeExpression value;
+ private final InstructionNode arrayStoreInstruction;
+
+ SetArrayElementBytecodeExpression(BytecodeExpression instance, BytecodeExpression index,
+ BytecodeExpression value) {
+ super(type(void.class));
+
+ this.instance = requireNonNull(instance, "instance is null");
+ this.index = requireNonNull(index, "index is null");
+ this.value = requireNonNull(value, "value is null");
+
+ ParameterizedType componentType = instance.getType().getArrayComponentType();
+ checkArgument(index.getType().getPrimitiveType() == int.class, "index must be int type, but is " + index.getType());
+ checkArgument(componentType.equals(value.getType()), "value must be %s type, but is %s", componentType, value.getType());
+
+ this.arrayStoreInstruction = getArrayOpCode(componentType).getStore();
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ return new BytecodeBlock()
+ .append(instance.getBytecode(generationContext))
+ .append(index)
+ .append(value)
+ .append(arrayStoreInstruction);
+ }
+
+ @Override
+ protected String formatOneLine() {
+ return instance + "[" + index + "] = " + value;
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of(index, value);
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/SetFieldBytecodeExpression.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/SetFieldBytecodeExpression.java
new file mode 100644
index 0000000..f1dc2c4
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/expression/SetFieldBytecodeExpression.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.expression;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.FieldDefinition;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.ParameterizedType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.List;
+import org.jetbrains.annotations.Nullable;
+
+import static com.facebook.presto.bytecode.Access.STATIC;
+import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+class SetFieldBytecodeExpression
+ extends BytecodeExpression {
+ private final BytecodeExpression instance;
+ private final ParameterizedType declaringClass;
+ private final String name;
+ private final BytecodeExpression value;
+ private final ParameterizedType fieldType;
+
+ SetFieldBytecodeExpression(@Nullable BytecodeExpression instance, Class<?> declaringClass, String name,
+ BytecodeExpression value) {
+ this(instance, getDeclaredField(declaringClass, name), value);
+ }
+
+ SetFieldBytecodeExpression(@Nullable BytecodeExpression instance, Field field, BytecodeExpression value) {
+ this(instance, type(requireNonNull(field, "field is null").getDeclaringClass()), field.getName(), value, type(field.getType()));
+
+ boolean isStatic = Modifier.isStatic(field.getModifiers());
+ if (instance == null) {
+ checkArgument(isStatic, "Field is not static: %s", field);
+ }
+ else {
+ checkArgument(!isStatic, "Field is static: %s", field);
+ }
+ }
+
+ SetFieldBytecodeExpression(@Nullable BytecodeExpression instance, FieldDefinition field,
+ BytecodeExpression value) {
+ this(instance, requireNonNull(field, "field is null").getDeclaringClass().getType(), field.getName(), value, field.getType());
+ if (instance == null) {
+ checkArgument(field.getAccess().contains(STATIC), "Field is not static: %s", field);
+ }
+ else {
+ checkArgument(!field.getAccess().contains(STATIC), "Field is static: %s", field);
+ }
+ }
+
+ SetFieldBytecodeExpression(@Nullable BytecodeExpression instance, ParameterizedType declaringClass,
+ String name, BytecodeExpression value) {
+ this(instance, declaringClass, name, value, value.getType());
+ }
+
+ SetFieldBytecodeExpression(@Nullable BytecodeExpression instance,
+ ParameterizedType declaringClass,
+ String name,
+ BytecodeExpression value,
+ ParameterizedType fieldType) {
+ super(type(void.class));
+ if (instance != null) {
+ checkArgument(!instance.getType().isPrimitive(), "Type %s does not have fields", instance.getType());
+ }
+ this.instance = instance;
+ this.declaringClass = requireNonNull(declaringClass, "declaringClass is null");
+ this.name = requireNonNull(name, "name is null");
+ this.fieldType = requireNonNull(fieldType, "fieldType is null");
+ this.value = requireNonNull(value, "value is null");
+ }
+
+ @Override
+ public BytecodeNode getBytecode(MethodGenerationContext generationContext) {
+ if (instance == null) {
+ return new BytecodeBlock()
+ .append(value.getBytecode(generationContext))
+ .putStaticField(declaringClass, name, fieldType);
+ }
+
+ return new BytecodeBlock()
+ .append(instance.getBytecode(generationContext))
+ .append(value.getBytecode(generationContext))
+ .putField(declaringClass, name, fieldType);
+ }
+
+ @Override
+ protected String formatOneLine() {
+ if (instance == null) {
+ return declaringClass.getSimpleName() + "." + name + " = " + value;
+ }
+ else {
+ return instance + "." + name + " = " + value;
+ }
+ }
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return instance != null ? List.of(instance, value) : List.of(value);
+ }
+
+ private static Field getDeclaredField(Class<?> declaringClass, String name) {
+ requireNonNull(declaringClass, "declaringClass is null");
+ requireNonNull(name, "name is null");
+
+ try {
+ return declaringClass.getField(name);
+ }
+ catch (NoSuchFieldException e) {
+ throw new IllegalArgumentException(format("Class %s does not have a '%s' field", declaringClass.getName(), name));
+ }
+ }
+}
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/instruction/Constant.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/instruction/Constant.java
new file mode 100644
index 0000000..abecbc8
--- /dev/null
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/instruction/Constant.java
@@ -0,0 +1,542 @@
+/*
+ * Licensed 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 com.facebook.presto.bytecode.instruction;
+
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.BytecodeUtils;
+import com.facebook.presto.bytecode.BytecodeVisitor;
+import com.facebook.presto.bytecode.MethodGenerationContext;
+import com.facebook.presto.bytecode.ParameterizedType;
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+
+import static com.facebook.presto.bytecode.OpCode.ACONST_NULL;
+import static com.facebook.presto.bytecode.OpCode.BIPUSH;
+import static com.facebook.presto.bytecode.OpCode.DCONST_0;
+import static com.facebook.presto.bytecode.OpCode.DCONST_1;
+import static com.facebook.presto.bytecode.OpCode.FCONST_0;
+import static com.facebook.presto.bytecode.OpCode.FCONST_1;
+import static com.facebook.presto.bytecode.OpCode.FCONST_2;
+import static com.facebook.presto.bytecode.OpCode.ICONST_0;
+import static com.facebook.presto.bytecode.OpCode.ICONST_1;
+import static com.facebook.presto.bytecode.OpCode.ICONST_2;
+import static com.facebook.presto.bytecode.OpCode.ICONST_3;
+import static com.facebook.presto.bytecode.OpCode.ICONST_4;
+import static com.facebook.presto.bytecode.OpCode.ICONST_5;
+import static com.facebook.presto.bytecode.OpCode.ICONST_M1;
+import static com.facebook.presto.bytecode.OpCode.LCONST_0;
+import static com.facebook.presto.bytecode.OpCode.LCONST_1;
+import static com.facebook.presto.bytecode.OpCode.SIPUSH;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static com.facebook.presto.bytecode.instruction.FieldInstruction.getStaticInstruction;
+import static com.facebook.presto.bytecode.instruction.InvokeInstruction.invokeStatic;
+import static java.util.Objects.requireNonNull;
+
+@SuppressWarnings("UnusedDeclaration")
+public abstract class Constant
+ implements InstructionNode {
+ public static Constant loadNull() {
+ return new NullConstant();
+ }
+
+ public static Constant loadBoolean(boolean value) {
+ return new BooleanConstant(value);
+ }
+
+ public static Constant loadBoxedBoolean(boolean value) {
+ return new BoxedBooleanConstant(value);
+ }
+
+ public static Constant loadInt(int value) {
+ return new IntConstant(value);
+ }
+
+ public static Constant loadBoxedInt(int value) {
+ return new BoxedIntegerConstant(value);
+ }
+
+ public static Constant loadFloat(float value) {
+ return new FloatConstant(value);
+ }
+
+ public static Constant loadBoxedFloat(float value) {
+ return new BoxedFloatConstant(value);
+ }
+
+ public static Constant loadLong(long value) {
+ return new LongConstant(value);
+ }
+
+ public static Constant loadBoxedLong(long value) {
+ return new BoxedLongConstant(value);
+ }
+
+ public static Constant loadDouble(double value) {
+ return new DoubleConstant(value);
+ }
+
+ public static Constant loadBoxedDouble(double value) {
+ return new BoxedDoubleConstant(value);
+ }
+
+ public static Constant loadNumber(Number value) {
+ requireNonNull(value, "value is null");
+ if (value instanceof Byte) {
+ return loadInt((value).intValue());
+ }
+ if (value instanceof Short) {
+ return loadInt((value).intValue());
+ }
+ if (value instanceof Integer) {
+ return loadInt((Integer)value);
+ }
+ if (value instanceof Long) {
+ return loadLong((Long)value);
+ }
+ if (value instanceof Float) {
+ return loadFloat((Float)value);
+ }
+ if (value instanceof Double) {
+ return loadDouble((Double)value);
+ }
+ throw new IllegalStateException("Unsupported number type " + value.getClass().getSimpleName());
+ }
+
+ public static Constant loadString(String value) {
+ requireNonNull(value, "value is null");
+ return new StringConstant(value);
+ }
+
+ public static Constant loadClass(Class<?> value) {
+ requireNonNull(value, "value is null");
+ return new ClassConstant(type(value));
+ }
+
+ public static Constant loadClass(ParameterizedType value) {
+ requireNonNull(value, "value is null");
+ return new ClassConstant(value);
+ }
+
+ public abstract Object getValue();
+
+ @Override
+ public List<BytecodeNode> getChildNodes() {
+ return List.of();
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitConstant(parent, this);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{value=" + getValue() + '}';
+ }
+
+ public static class NullConstant
+ extends Constant {
+ @Override
+ public Object getValue() {
+ return null;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ visitor.visitInsn(ACONST_NULL.getOpCode());
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitConstant(parent, this);
+ }
+ }
+
+ public static class BooleanConstant
+ extends Constant {
+ private final boolean value;
+
+ private BooleanConstant(boolean value) {
+ this.value = value;
+ }
+
+ @Override
+ public Boolean getValue() {
+ return value;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ if (value) {
+ visitor.visitInsn(ICONST_1.getOpCode());
+ }
+ else {
+ visitor.visitInsn(ICONST_0.getOpCode());
+ }
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitBooleanConstant(parent, this);
+ }
+ }
+
+ public static class BoxedBooleanConstant
+ extends Constant {
+ private final boolean value;
+
+ private BoxedBooleanConstant(boolean value) {
+ this.value = value;
+ }
+
+ @Override
+ public Boolean getValue() {
+ return value;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ if (value) {
+ getStaticInstruction(Boolean.class, "TRUE", Boolean.class).accept(visitor, generationContext);
+ }
+ else {
+ getStaticInstruction(Boolean.class, "FALSE", Boolean.class).accept(visitor, generationContext);
+ }
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitBoxedBooleanConstant(parent, this);
+ }
+ }
+
+ public static class IntConstant
+ extends Constant {
+ private final int value;
+
+ private IntConstant(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public Integer getValue() {
+ return value;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) {
+ switch (value) {
+ case -1:
+ visitor.visitInsn(ICONST_M1.getOpCode());
+ break;
+ case 0:
+ visitor.visitInsn(ICONST_0.getOpCode());
+ break;
+ case 1:
+ visitor.visitInsn(ICONST_1.getOpCode());
+ break;
+ case 2:
+ visitor.visitInsn(ICONST_2.getOpCode());
+ break;
+ case 3:
+ visitor.visitInsn(ICONST_3.getOpCode());
+ break;
+ case 4:
+ visitor.visitInsn(ICONST_4.getOpCode());
+ break;
+ case 5:
+ visitor.visitInsn(ICONST_5.getOpCode());
+ break;
+ default:
+ visitor.visitIntInsn(BIPUSH.getOpCode(), value);
+ break;
+ }
+ }
+ else if (value <= Short.MAX_VALUE && value >= Short.MIN_VALUE) {
+ visitor.visitIntInsn(SIPUSH.getOpCode(), value);
+ }
+ else {
+ visitor.visitLdcInsn(value);
+ }
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitIntConstant(parent, this);
+ }
+ }
+
+ public static class BoxedIntegerConstant
+ extends Constant {
+ private final int value;
+
+ private BoxedIntegerConstant(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public Integer getValue() {
+ return value;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ loadInt(value).accept(visitor, generationContext);
+ invokeStatic(Integer.class, "valueOf", Integer.class, int.class).accept(visitor, generationContext);
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitBoxedIntegerConstant(parent, this);
+ }
+ }
+
+ public static class FloatConstant
+ extends Constant {
+ private final float value;
+
+ private FloatConstant(float value) {
+ this.value = value;
+ }
+
+ @Override
+ public Float getValue() {
+ return value;
+ }
+
+ @Override
+ @SuppressWarnings("FloatingPointEquality")
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ // We can not use "value == 0.0" because when value is "-0.0" the expression
+ // will evaluate to true and we would convert "-0.0" to "0.0" which is
+ // not the same value
+ if (Float.floatToIntBits(value) == Float.floatToIntBits(0.0f)) {
+ visitor.visitInsn(FCONST_0.getOpCode());
+ }
+ else if (value == 1.0f) {
+ visitor.visitInsn(FCONST_1.getOpCode());
+ }
+ else if (value == 2.0f) {
+ visitor.visitInsn(FCONST_2.getOpCode());
+ }
+ else {
+ visitor.visitLdcInsn(value);
+ }
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitFloatConstant(parent, this);
+ }
+ }
+
+ public static class BoxedFloatConstant
+ extends Constant {
+ private final float value;
+
+ private BoxedFloatConstant(float value) {
+ this.value = value;
+ }
+
+ @Override
+ public Float getValue() {
+ return value;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ loadFloat(value).accept(visitor, generationContext);
+ invokeStatic(Float.class, "valueOf", Float.class, float.class).accept(visitor, generationContext);
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitBoxedFloatConstant(parent, this);
+ }
+ }
+
+ public static class LongConstant
+ extends Constant {
+ private final long value;
+
+ private LongConstant(long value) {
+ this.value = value;
+ }
+
+ @Override
+ public Long getValue() {
+ return value;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ if (value == 0) {
+ visitor.visitInsn(LCONST_0.getOpCode());
+ }
+ else if (value == 1) {
+ visitor.visitInsn(LCONST_1.getOpCode());
+ }
+ else {
+ visitor.visitLdcInsn(value);
+ }
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
+ return visitor.visitLongConstant(parent, this);
+ }
+ }
+
+ public static class BoxedLongConstant
+ extends Constant {
+ private final long value;
+
+ private BoxedLongConstant(long value) {
+ this.value = value;
+ }
+
+ @Override
+ public Long getValue() {
+ return value;
+ }
+
+ @Override
+ public void accept(MethodVisitor visitor, MethodGenerationContext generationContext) {
+ loadLong(value).accept(visitor, generationContext);
+ invokeStatic(Long.class, "valueOf", Long.class, long.class).accept(visitor, generationContext);
+ }
+
+ @Override
+ public <T> T accept(BytecodeNode parent, BytecodeVisitor<T> visitor) {
... 10568 lines suppressed ...