org.codehaus.groovy.transform.stc.SignatureCodecVersion1 Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2003-2012 the original author or authors.
*
* 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 org.codehaus.groovy.transform.stc;
import groovy.lang.GroovyRuntimeException;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.GenericsType;
import org.codehaus.groovy.ast.tools.WideningCategories;
import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.runtime.EncodingGroovyMethods;
import java.io.*;
import static org.codehaus.groovy.ast.ClassHelper.*;
/**
* First implementation of an inferred type signature codec.
*
* @author Cedric Champeau
*/
public class SignatureCodecVersion1 implements SignatureCodec {
private void doEncode(final ClassNode node, DataOutputStream dos) throws IOException {
dos.writeUTF(node.getClass().getSimpleName());
if (node instanceof UnionTypeClassNode) {
UnionTypeClassNode union = (UnionTypeClassNode) node;
ClassNode[] delegates = union.getDelegates();
dos.writeInt(delegates.length);
for (ClassNode delegate : delegates) {
doEncode(delegate, dos);
}
return;
} else if (node instanceof WideningCategories.LowestUpperBoundClassNode) {
WideningCategories.LowestUpperBoundClassNode lub = (WideningCategories.LowestUpperBoundClassNode) node;
dos.writeUTF(lub.getLubName());
doEncode(lub.getUnresolvedSuperClass(), dos);
ClassNode[] interfaces = lub.getInterfaces();
if (interfaces == null) {
dos.writeInt(-1);
} else {
dos.writeInt(interfaces.length);
for (ClassNode anInterface : interfaces) {
doEncode(anInterface, dos);
}
}
return;
}
if (node.isArray()) {
dos.writeBoolean(true);
doEncode(node.getComponentType(), dos);
} else {
dos.writeBoolean(false);
dos.writeUTF(BytecodeHelper.getTypeDescription(node));
dos.writeBoolean(node.isUsingGenerics());
GenericsType[] genericsTypes = node.getGenericsTypes();
if (genericsTypes == null) {
dos.writeInt(-1);
} else {
dos.writeInt(genericsTypes.length);
for (GenericsType type : genericsTypes) {
dos.writeBoolean(type.isPlaceholder());
dos.writeBoolean(type.isWildcard());
doEncode(type.getType(), dos);
ClassNode lb = type.getLowerBound();
if (lb == null) {
dos.writeBoolean(false);
} else {
dos.writeBoolean(true);
doEncode(lb, dos);
}
ClassNode[] upperBounds = type.getUpperBounds();
if (upperBounds == null) {
dos.writeInt(-1);
} else {
dos.writeInt(upperBounds.length);
for (ClassNode bound : upperBounds) {
doEncode(bound, dos);
}
}
}
}
}
}
public String encode(final ClassNode node) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
DataOutputStream dos = new DataOutputStream(baos);
StringWriter wrt = new StringWriter();
String encoded = null;
try {
doEncode(node, dos);
EncodingGroovyMethods.encodeBase64(baos.toByteArray()).writeTo(wrt);
encoded = wrt.toString();
} catch (IOException e) {
throw new GroovyRuntimeException("Unable to serialize type information", e);
}
return encoded;
}
private ClassNode doDecode(final DataInputStream dis) throws IOException {
String classNodeType = dis.readUTF();
if (UnionTypeClassNode.class.getSimpleName().equals(classNodeType)) {
int len = dis.readInt();
ClassNode[] delegates = new ClassNode[len];
for (int i = 0; i < len; i++) {
delegates[i] = doDecode(dis);
}
return new UnionTypeClassNode(delegates);
} else if (WideningCategories.LowestUpperBoundClassNode.class.getSimpleName().equals(classNodeType)) {
String name = dis.readUTF();
ClassNode upper = doDecode(dis);
int len = dis.readInt();
ClassNode[] interfaces = null;
if (len >= 0) {
interfaces = new ClassNode[len];
for (int i = 0; i < len; i++) {
interfaces[i] = doDecode(dis);
}
}
return new WideningCategories.LowestUpperBoundClassNode(name, upper, interfaces);
}
boolean makeArray = dis.readBoolean();
if (makeArray) {
return doDecode(dis).makeArray();
}
String typedesc = dis.readUTF();
char typeCode = typedesc.charAt(0);
ClassNode result = OBJECT_TYPE;
if (typeCode == 'L') {
// object type
String className = typedesc.replace('/', '.').substring(1, typedesc.length() - 1);
try {
result = ClassHelper.make(Class.forName(className)).getPlainNodeReference();
} catch (ClassNotFoundException e) {
result = ClassHelper.make(className);
}
result.setUsingGenerics(dis.readBoolean());
int len = dis.readInt();
if (len >= 0) {
GenericsType[] gts = new GenericsType[len];
for (int i = 0; i < len; i++) {
boolean placeholder = dis.readBoolean();
boolean wildcard = dis.readBoolean();
ClassNode type = doDecode(dis);
boolean low = dis.readBoolean();
ClassNode lb = null;
if (low) {
lb = doDecode(dis);
}
int upc = dis.readInt();
ClassNode[] ups = null;
if (upc >= 0) {
ups = new ClassNode[upc];
for (int j = 0; j < upc; j++) {
ups[j] = doDecode(dis);
}
}
GenericsType gt = new GenericsType(
type, ups, lb
);
gt.setPlaceholder(placeholder);
gt.setWildcard(wildcard);
gts[i] = gt;
}
result.setGenericsTypes(gts);
}
} else {
// primitive type
switch (typeCode) {
case 'I': result = int_TYPE; break;
case 'Z': result = boolean_TYPE; break;
case 'B': result = byte_TYPE; break;
case 'C': result = char_TYPE; break;
case 'S': result = short_TYPE; break;
case 'D': result = double_TYPE; break;
case 'F': result = float_TYPE; break;
case 'J': result = long_TYPE; break;
case 'V': result = VOID_TYPE; break;
}
}
return result;
}
public ClassNode decode(final String signature) {
DataInputStream dis = new DataInputStream(
new ByteArrayInputStream(EncodingGroovyMethods.decodeBase64(signature)));
try {
return doDecode(dis);
} catch (IOException e) {
throw new GroovyRuntimeException("Unable to read type information", e);
}
}
}