/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.visit;
import java.io.IOException;
import java.util.*;
import polyglot.ast.*;
import polyglot.main.Reporter;
import polyglot.main.Version;
import polyglot.types.*;
import polyglot.util.*;
import x10.ast.X10ClassDecl;
import x10.types.X10ClassDef;
import x10.types.X10ParsedClassType_c;
import x10.types.constants.ConstantValue;
/**
* Visitor which serializes class objects and adds a field to the class
* containing the serialization.
*/
public class ClassSerializer extends NodeVisitor
{
/**
* The maximum number of characters that will be assigned to an encoded type string field.
* More characters than this will be broken up over several fields.
*/
private static final int MAX_ENCODED_TYPE_INFO_STRING_LENGTH = 8192;
protected TypeEncoder te;
protected ErrorQueue eq;
protected Date date;
protected TypeSystem ts;
protected Reporter reporter;
protected NodeFactory nf;
protected Version ver;
public ClassSerializer(TypeSystem ts, NodeFactory nf, Date date, ErrorQueue eq, Version ver) {
this.ts = ts;
this.reporter = ts.extensionInfo().getOptions().reporter;
this.nf = nf;
this.te = new TypeEncoder(ts);
this.eq = eq;
this.date = date;
this.ver = ver;
}
public Node override(Node n) {
// Stop at class members. We only want to encode top-level classes.
if (n instanceof ClassMember && ! (n instanceof ClassDecl)) {
return n;
}
return null;
}
public Node leave(Node old, Node n, NodeVisitor v) {
if (! (n instanceof ClassDecl)) {
return n;
}
X10ClassDecl cd = (X10ClassDecl) n;
ClassBody body = cd.body();
List<ClassMember> l = createSerializationMembers(cd);
for (ClassMember m : l) {
body = body.addMember(m);
}
return cd.body(body);
}
public List<ClassMember> createSerializationMembers(X10ClassDecl cd) {
return createSerializationMembers(cd.classDef());
}
public List<ClassMember> createSerializationMembers(X10ClassDef cd) {
try {
byte[] b;
List<ClassMember> newMembers = new ArrayList<ClassMember>(3);
ClassType ct = cd.asType();
// HACK: force class members to get created from lazy class
// initializer.
ct.memberClasses();
ct.constructors();
ct.methods();
ct.fields();
ct.interfaces();
ct.superClass();
// Only serialize top-level and member classes.
if (! ct.isTopLevel() && ! ct.isMember()) {
return Collections.<ClassMember>emptyList();
}
/* Add the compiler version number. */
String suffix = ver.name();
// Check if we've already serialized.
if (ct.fieldNamed(Name.make("jlc$CompilerVersion$" + suffix)) != null ||
ct.fieldNamed(Name.make("jlc$SourceLastModified$" + suffix)) != null ||
ct.fieldNamed(Name.make("jlc$ClassType$" + suffix)) != null) {
eq.enqueue(ErrorInfo.SEMANTIC_ERROR,
"Cannot serialize class information " +
"more than once.");
return Collections.<ClassMember>emptyList();
}
Flags flags = Flags.PUBLIC.set(Flags.STATIC).set(Flags.FINAL);
FieldDecl f;
FieldDef fi;
InitializerDef ii;
/* Add the compiler version number. */
String version = ver.major() + "." +
ver.minor() + "." +
ver.patch_level();
Position pos = Position.COMPILER_GENERATED;
fi = ts.fieldDef(pos, Types.ref(new X10ParsedClassType_c(cd)),
flags, Types.ref(ts.String()),
Name.make("jlc$CompilerVersion$" + suffix));
fi.setConstantValue(ConstantValue.makeString(version));
ii = ts.initializerDef(pos, Types.ref(new X10ParsedClassType_c(cd)), Flags.STATIC);
f = nf.FieldDecl(fi.position(), nf.FlagsNode(fi.position(), fi.flags()),
nf.CanonicalTypeNode(fi.position(), fi.type()),
nf.Id(fi.position(), fi.name()),
nf.StringLit(pos, version).type(ts.String()));
f = f.fieldDef(fi);
f = f.initializerDef(ii);
newMembers.add(f);
/* Add the date of the last source file modification. */
long time = date.getTime();
fi = ts.fieldDef(pos, Types.ref(new X10ParsedClassType_c(cd)),
flags, Types.ref(ts.Long()),
Name.make("jlc$SourceLastModified$" + suffix));
fi.setConstantValue(ConstantValue.makeLong(time));
ii = ts.initializerDef(pos, Types.ref(new X10ParsedClassType_c(cd)), Flags.STATIC);
f = nf.FieldDecl(fi.position(), nf.FlagsNode(fi.position(), fi.flags()),
nf.CanonicalTypeNode(fi.position(), fi.type()),
nf.Id(fi.position(), fi.name()),
nf.IntLit(pos, IntLit.LONG, time).type(ts.Long()));
f = f.fieldDef(fi);
f = f.initializerDef(ii);
newMembers.add(f);
// output the encoded type info, over several fields if needed.
String encodedTypeInfo = te.encode(ct);
int etiStart = 0;
int etiEnd = 0;
int numberETIFields = 0;
do {
etiEnd = encodedTypeInfo.length();
if (etiEnd - etiStart > MAX_ENCODED_TYPE_INFO_STRING_LENGTH) {
etiEnd = etiStart + MAX_ENCODED_TYPE_INFO_STRING_LENGTH;
}
// add an additional suffix to distinguish fields.
String additionalFieldSuffix = numberETIFields==0?"":("$" + numberETIFields);
String encoded = encodedTypeInfo.substring(etiStart, etiEnd);
fi = ts.fieldDef(pos, Types.ref(new X10ParsedClassType_c(cd)),
flags, Types.ref(ts.String()),
Name.make("jlc$ClassType$" + suffix + additionalFieldSuffix));
fi.setConstantValue(ConstantValue.makeString(encoded));
ii = ts.initializerDef(pos, Types.ref(new X10ParsedClassType_c(cd)), Flags.STATIC);
f = nf.FieldDecl(fi.position(), nf.FlagsNode(fi.position(), fi.flags()),
nf.CanonicalTypeNode(fi.position(), fi.type()),
nf.Id(fi.position(), fi.name()),
nf.StringLit(pos, encoded).type(ts.String()));
f = f.fieldDef(fi);
f = f.initializerDef(ii);
newMembers.add(f);
numberETIFields++;
etiStart = etiEnd;
}
while (etiEnd != encodedTypeInfo.length());
return newMembers;
}
catch (IOException e) {
if (reporter.should_report(Reporter.serialize, 1))
e.printStackTrace();
eq.enqueue(ErrorInfo.IO_ERROR,
"Unable to serialize class information.");
return Collections.<ClassMember>emptyList();
}
}
}