use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.
the class Singletons method defineObject.
/**
* Generate an object definition, that is, define an anonymous class and then a function
* to return a single instance of it.
* @param that The node with the definition (can be ObjectDefinition, ObjectExpression, ObjectArgument)
* @param d The Value declaration for the object
* @param sats The list of satisfied types of the anonymous class
* @param superType The supertype of the anonymous class
* @param superCall The invocation of the supertype (object bla extends Foo(x))
* @param body The object definition's body
* @param annots The annotations (in case of ObjectDefinition)
* @param gen The main visitor/generator.
*/
static void defineObject(final Node that, final Value d, final List<Type> sats, final Tree.SimpleType superType, final Tree.InvocationExpression superCall, final Tree.Body body, final Tree.AnnotationList annots, final GenerateJsVisitor gen, InitDeferrer initDeferrer) {
final boolean addToPrototype = gen.opts.isOptimize() && d != null && d.isClassOrInterfaceMember();
final boolean isObjExpr = that instanceof Tree.ObjectExpression;
final TypeDeclaration _td = isObjExpr ? ((Tree.ObjectExpression) that).getAnonymousClass() : d.getTypeDeclaration();
final Class c = (Class) (_td instanceof Constructor ? _td.getContainer() : _td);
final String className = gen.getNames().name(c);
final String objectName = gen.getNames().name(d);
final String selfName = gen.getNames().self(c);
final Value natd = d == null ? null : (Value) ModelUtil.getNativeDeclaration(d, Backend.JavaScript);
if (that instanceof Tree.Declaration) {
if (NativeUtil.isNativeHeader((Tree.Declaration) that) && natd != null) {
// It's a native header, remember it for later when we deal with its implementation
gen.saveNativeHeader((Tree.Declaration) that);
return;
}
if (!(NativeUtil.isForBackend((Tree.Declaration) that, Backend.JavaScript) || NativeUtil.isHeaderWithoutBackend((Tree.Declaration) that, Backend.JavaScript))) {
return;
}
}
final List<Tree.Statement> stmts;
if (d != null && NativeUtil.isForBackend(d, Backend.JavaScript)) {
Tree.Declaration nh = gen.getNativeHeader(d);
if (nh == null && NativeUtil.hasNativeMembers(c) && that instanceof Tree.Declaration) {
nh = (Tree.Declaration) that;
}
stmts = NativeUtil.mergeStatements(body, nh, Backend.JavaScript);
} else {
stmts = body.getStatements();
}
Map<TypeParameter, Type> targs = new HashMap<>();
if (sats != null) {
for (Type st : sats) {
Map<TypeParameter, Type> stargs = st.getTypeArguments();
if (stargs != null && !stargs.isEmpty()) {
targs.putAll(stargs);
}
}
}
gen.out(GenerateJsVisitor.function, className, targs.isEmpty() ? "()" : "($$targs$$)");
gen.beginBlock();
if (isObjExpr) {
gen.out("var ", selfName, "=new ", className, ".$$;");
final ClassOrInterface coi = ModelUtil.getContainingClassOrInterface(c.getContainer());
if (coi != null) {
gen.out(selfName, ".outer$=", gen.getNames().self(coi));
gen.endLine(true);
}
} else {
if (c.isMember() && !d.isStatic()) {
gen.initSelf(that);
}
gen.instantiateSelf(c);
gen.referenceOuter(c);
}
// TODO should we generate all this code for native headers?
// Really we should merge the body of the header with that of the impl
// It's the only way to make this shit work in lexical scope mode
final List<Declaration> superDecs = new ArrayList<>();
if (!gen.opts.isOptimize()) {
final SuperVisitor superv = new SuperVisitor(superDecs);
for (Tree.Statement st : stmts) {
st.visit(superv);
}
}
if (!targs.isEmpty()) {
gen.out(selfName, ".$$targs$$=$$targs$$");
gen.endLine(true);
}
TypeGenerator.callSupertypes(sats, superType, c, that, superDecs, superCall, superType == null ? null : ((Class) c.getExtendedType().getDeclaration()).getParameterList(), gen);
gen.visitStatements(stmts);
gen.out("return ", selfName, ";");
gen.endBlock();
gen.out(";", className, ".$crtmm$=");
TypeUtils.encodeForRuntime(that, c, gen);
gen.endLine(true);
TypeGenerator.initializeType(that, gen, initDeferrer);
final String objvar = (addToPrototype ? "this." : "") + gen.getNames().createTempVariable();
if (d != null && !addToPrototype) {
gen.out("var ", objvar);
// If it's a property, create the object here
if (AttributeGenerator.defineAsProperty(d)) {
gen.out("=", className, "(");
if (!targs.isEmpty()) {
TypeUtils.printTypeArguments(that, targs, gen, false, null);
}
gen.out(")");
}
gen.endLine(true);
}
if (d != null && AttributeGenerator.defineAsProperty(d)) {
gen.out(gen.getClAlias(), "atr$(");
gen.outerSelf(d);
gen.out(",'", objectName, "',function(){return ");
if (addToPrototype) {
gen.out("this.", gen.getNames().privateName(d));
} else {
gen.out(objvar);
}
gen.out(";},undefined,");
TypeUtils.encodeForRuntime(that, d, annots, gen);
gen.out(")");
gen.endLine(true);
} else if (d != null) {
final String objectGetterName = gen.getNames().getter(d, false);
gen.out(GenerateJsVisitor.function, objectGetterName, "()");
gen.beginBlock();
// Create the object lazily
final String oname = gen.getNames().objectName(c);
gen.out("if(", objvar, "===", gen.getClAlias(), "INIT$)");
gen.generateThrow(gen.getClAlias() + "InitializationError", "Cyclic initialization trying to read the value of '" + d.getName() + "' before it was set", that);
gen.endLine(true);
gen.out("if(", objvar, "===undefined){", objvar, "=", gen.getClAlias(), "INIT$;", objvar, "=$init$", oname);
if (!oname.endsWith("()")) {
gen.out("()");
}
gen.out("(");
if (!targs.isEmpty()) {
TypeUtils.printTypeArguments(that, targs, gen, false, null);
}
gen.out(");", objvar, ".$crtmm$=", objectGetterName, ".$crtmm$;}");
gen.endLine();
gen.out("return ", objvar, ";");
gen.endBlockNewLine();
if (addToPrototype || d.isShared()) {
gen.outerSelf(d);
gen.out(".", objectGetterName, "=", objectGetterName);
gen.endLine(true);
}
if (!d.isToplevel()) {
if (gen.outerSelf(d))
gen.out(".");
}
gen.out(objectGetterName, ".$crtmm$=");
TypeUtils.encodeForRuntime(that, d, annots, gen);
gen.endLine(true);
gen.out(gen.getNames().getter(c, true), "=", objectGetterName);
gen.endLine(true);
if (d.isToplevel()) {
final String objectGetterNameMM = gen.getNames().getter(d, true);
gen.out("ex$.", objectGetterNameMM, "=", objectGetterNameMM);
gen.endLine(true);
}
} else if (isObjExpr) {
gen.out("return ", className, "();");
}
}
use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.
the class TypeGenerator method initializeType.
/**
* Generates a function to initialize the specified type.
* @param initDeferrer
*/
static void initializeType(final Node type, final GenerateJsVisitor gen, InitDeferrer initDeferrer) {
Tree.ExtendedType extendedType = null;
Tree.SatisfiedTypes satisfiedTypes = null;
final ClassOrInterface decl;
final List<Tree.Statement> stmts;
Value objDecl = null;
if (type instanceof Tree.ClassDefinition) {
Tree.ClassDefinition classDef = (Tree.ClassDefinition) type;
extendedType = classDef.getExtendedType();
satisfiedTypes = classDef.getSatisfiedTypes();
decl = classDef.getDeclarationModel();
Tree.Declaration nh = gen.getNativeHeader(decl);
if (nh == null && NativeUtil.hasNativeMembers(decl)) {
nh = classDef;
}
stmts = NativeUtil.mergeStatements(classDef.getClassBody(), nh, Backend.JavaScript);
} else if (type instanceof Tree.InterfaceDefinition) {
satisfiedTypes = ((Tree.InterfaceDefinition) type).getSatisfiedTypes();
decl = ((Tree.InterfaceDefinition) type).getDeclarationModel();
final Tree.InterfaceDefinition idef = (Tree.InterfaceDefinition) type;
Tree.Declaration nh = gen.getNativeHeader(decl);
if (nh == null && NativeUtil.hasNativeMembers(decl)) {
nh = idef;
}
stmts = NativeUtil.mergeStatements(idef.getInterfaceBody(), nh, Backend.JavaScript);
} else if (type instanceof Tree.ObjectDefinition) {
Tree.ObjectDefinition objectDef = (Tree.ObjectDefinition) type;
extendedType = objectDef.getExtendedType();
satisfiedTypes = objectDef.getSatisfiedTypes();
decl = (ClassOrInterface) objectDef.getDeclarationModel().getTypeDeclaration();
objDecl = objectDef.getDeclarationModel();
Tree.Declaration nh = gen.getNativeHeader(decl);
if (nh == null && NativeUtil.hasNativeMembers(decl)) {
nh = objectDef;
}
stmts = NativeUtil.mergeStatements(objectDef.getClassBody(), nh, Backend.JavaScript);
} else if (type instanceof Tree.ObjectExpression) {
Tree.ObjectExpression objectDef = (Tree.ObjectExpression) type;
extendedType = objectDef.getExtendedType();
satisfiedTypes = objectDef.getSatisfiedTypes();
decl = (ClassOrInterface) objectDef.getAnonymousClass();
stmts = objectDef.getClassBody().getStatements();
} else if (type instanceof Tree.Enumerated) {
Tree.Enumerated vc = (Tree.Enumerated) type;
stmts = vc.getBlock().getStatements();
decl = (ClassOrInterface) vc.getDeclarationModel().getTypeDeclaration().getContainer();
} else {
stmts = null;
decl = null;
}
final PrototypeInitCallback callback = new PrototypeInitCallback() {
@Override
public void addToPrototypeCallback() {
if (decl != null) {
gen.addToPrototype(type, decl, stmts);
}
}
};
typeInitialization(extendedType, satisfiedTypes, decl, callback, gen, objDecl, initDeferrer);
}
use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.
the class TypeGenerator method typeInitialization.
/**
* This is now the main method to generate the type initialization code.
* @param extendedType The type that is being extended.
* @param satisfiedTypes The types satisfied by the type being initialized.
* @param d The declaration for the type being initialized
* @param callback A callback to add something more to the type initializer in prototype style.
* @param initDeferrer something which lets us put statements at the end of the container initialiser, if it's not null (it's null for toplevels)
*/
static void typeInitialization(final Tree.ExtendedType extendedType, final Tree.SatisfiedTypes satisfiedTypes, final ClassOrInterface d, PrototypeInitCallback callback, final GenerateJsVisitor gen, final Value objectDeclaration, InitDeferrer initDeferrer) {
final boolean isInterface = d instanceof org.eclipse.ceylon.model.typechecker.model.Interface;
String initFuncName = isInterface ? "initTypeProtoI" : "initTypeProto";
final String typename = gen.getNames().name(d);
final String initname;
if (d.isAnonymous()) {
String _initname = gen.getNames().objectName(d);
if (d.isToplevel()) {
initname = "$init$" + _initname.substring(0, _initname.length() - 2);
} else {
initname = "$init$" + _initname;
}
} else {
initname = "$init$" + typename;
}
gen.out("function ", initname, "()");
gen.beginBlock();
gen.out("if(", typename, ".$$===undefined)");
gen.beginBlock();
boolean genIniter = true;
if (TypeUtils.isNativeExternal(d)) {
// Allow native types to have their own initialization code
genIniter = !gen.stitchInitializer(d);
}
if (genIniter) {
gen.out(gen.getClAlias(), initFuncName, "(", typename, ",'", d.getQualifiedNameString(), "'");
final List<Tree.StaticType> supers = satisfiedTypes == null ? Collections.<Tree.StaticType>emptyList() : new ArrayList<Tree.StaticType>(satisfiedTypes.getTypes().size() + 1);
if (extendedType != null) {
if (satisfiedTypes == null) {
String fname = typeFunctionName(extendedType.getType(), d, gen);
gen.out(",", fname);
} else {
supers.add(extendedType.getType());
}
} else if (!isInterface) {
gen.out(",", gen.getClAlias(), "Basic");
}
if (satisfiedTypes != null) {
supers.addAll(satisfiedTypes.getTypes());
Collections.sort(supers, new StaticTypeComparator());
for (Tree.StaticType satType : supers) {
String fname = typeFunctionName(satType, d, gen);
gen.out(",", fname);
}
}
gen.out(");");
}
// Add ref to outer type
if (d.isMember()) {
StringBuilder containers = new StringBuilder();
Scope _d2 = d;
while (_d2 instanceof ClassOrInterface) {
if (containers.length() > 0) {
containers.insert(0, '.');
}
containers.insert(0, gen.getNames().name((Declaration) _d2));
_d2 = _d2.getContainer();
}
gen.endLine();
gen.out(containers.toString(), "=", typename, ";");
}
// The class definition needs to be inside the init function if we want forwards decls to work in prototype style
if (gen.opts.isOptimize()) {
gen.endLine();
callback.addToPrototypeCallback();
}
gen.endBlockNewLine();
gen.out("return ", typename, ";");
gen.endBlockNewLine();
// If it's nested, share the init function
if (d.isStatic()) {
gen.out(gen.getNames().name(ModelUtil.getContainingClassOrInterface(d.getContainer())), ".$st$.", initname, "=", initname, ";");
} else if (gen.outerSelf(d)) {
gen.out(".", initname, "=", initname, ";");
}
if (initDeferrer != null) {
initDeferrer.deferred.add(initname + "();");
} else {
gen.out(initname, "();");
}
}
use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.
the class TypeGenerator method typeFunctionName.
/**
* Returns the name of the type or its $init$ function if it's local.
*/
static String typeFunctionName(final Tree.StaticType type, final ClassOrInterface coi, final GenerateJsVisitor gen) {
TypeDeclaration d = type.getTypeModel().getDeclaration();
final boolean removeAlias = d == null || !d.isClassOrInterfaceMember() || d instanceof Interface;
if ((removeAlias && d.isAlias()) || d instanceof Constructor) {
Type extendedType = d.getExtendedType();
d = extendedType == null ? null : extendedType.getDeclaration();
}
Declaration cont = ModelUtil.getContainingDeclaration(d);
final boolean inProto = gen.opts.isOptimize() && cont instanceof TypeDeclaration;
final boolean imported = gen.isImported(type.getUnit().getPackage(), d);
String dname = gen.getNames().name(d);
if (d.isAlias()) {
TypeDeclaration d2 = d;
while (d2.isAlias()) {
d2 = d2.getExtendedType().getDeclaration();
}
dname = gen.getNames().name(d2);
}
final String initName = "$init$" + dname + "()";
if (!imported && !d.isClassOrInterfaceMember()) {
return initName;
}
if (inProto && coi.isMember() && !d.isAlias() && (coi.getContainer() == cont || ModelUtil.contains(d, coi))) {
// use its $init$ function
return initName;
}
String tfn;
// #628 If coi is anonymous and inside cont, qualify the path from cont instead
if (coi != null && coi.isAnonymous() && cont instanceof Scope && ModelUtil.contains((Scope) cont, coi)) {
tfn = gen.qualifiedPath(type, cont, inProto);
} else if (inProto && d.isClassOrInterfaceMember()) {
return pathToType(type, d, gen);
} else {
tfn = gen.qualifiedPath(type, d, inProto);
}
tfn = gen.memberAccessBase(type, d, false, tfn);
if (removeAlias && !imported) {
int idx = tfn.lastIndexOf('.');
if (idx > 0) {
tfn = tfn.substring(0, idx + 1) + initName;
} else {
tfn = initName;
}
}
return tfn;
}
use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.
the class JsonPackage method getTypeFromJson.
/**
* Looks up a type from model data, creating it if necessary. The returned type will have its
* type parameters substituted if needed.
*/
private Type getTypeFromJson(Map<String, Object> m, Declaration container, List<TypeParameter> typeParams) {
TypeDeclaration td = null;
if (m.get(KEY_METATYPE) instanceof TypeDeclaration) {
td = (TypeDeclaration) m.get(KEY_METATYPE);
if (td instanceof ClassOrInterface && td.getUnit().getPackage() instanceof JsonPackage) {
((JsonPackage) td.getUnit().getPackage()).load(td.getName(), typeParams);
}
}
final String tname = (String) m.get(KEY_NAME);
if ("$U".equals(tname)) {
m.put(KEY_METATYPE, unknown);
return unknown.getType();
}
if (td == null && m.containsKey("comp")) {
@SuppressWarnings("unchecked") final List<Map<String, Object>> tmaps = (List<Map<String, Object>>) m.get(KEY_TYPES);
final ArrayList<Type> types = new ArrayList<>(tmaps.size());
if ("u".equals(m.get("comp"))) {
UnionType ut = new UnionType(u2);
for (Map<String, Object> tmap : tmaps) {
types.add(getTypeFromJson(tmap, container, typeParams));
}
ut.setCaseTypes(types);
td = ut;
} else if ("i".equals(m.get("comp"))) {
IntersectionType it = new IntersectionType(u2);
for (Map<String, Object> tmap : tmaps) {
types.add(getTypeFromJson(tmap, container, typeParams));
}
it.setSatisfiedTypes(types);
td = it;
} else {
throw new IllegalArgumentException("Invalid composite type '" + m.get("comp") + "'");
}
} else if (td == null) {
final String pname = (String) m.get(KEY_PACKAGE);
if (pname == null) {
// It's a ref to a type parameter
final List<TypeParameter> containerTypeParameters;
if (container instanceof Constructor) {
containerTypeParameters = ((Generic) container.getContainer()).getTypeParameters();
} else if (container instanceof Generic) {
containerTypeParameters = container.getTypeParameters();
} else {
containerTypeParameters = null;
}
if (containerTypeParameters != null) {
for (TypeParameter typeParam : containerTypeParameters) {
if (typeParam.getName().equals(tname)) {
td = typeParam;
}
}
}
if (td == null && typeParams != null) {
for (TypeParameter typeParam : typeParams) {
if (typeParam.getName().equals(tname)) {
td = typeParam;
}
}
}
} else {
String mname = (String) m.get(KEY_MODULE);
if ("$".equals(mname)) {
mname = LANGUAGE_MODULE_NAME;
}
org.eclipse.ceylon.model.typechecker.model.Package rp;
if ("$".equals(pname) || LANGUAGE_MODULE_NAME.equals(pname)) {
// Language module package
rp = isLanguagePackage() ? this : getModule().getLanguageModule().getDirectPackage(LANGUAGE_MODULE_NAME);
} else if (mname == null) {
// local type
if (".".equals(pname)) {
rp = this;
if (container instanceof TypeDeclaration && tname.equals(container.getName())) {
td = (TypeDeclaration) container;
}
} else {
rp = getModule().getDirectPackage(pname);
}
} else {
rp = getModule().getPackage(pname);
}
if (rp == null) {
throw new CompilerErrorException("Package not found: " + pname);
}
if (rp != this && rp instanceof JsonPackage && !((JsonPackage) rp).loaded) {
((JsonPackage) rp).loadIfNecessary();
}
final boolean nested = tname.indexOf('.') > 0;
final String level1 = nested ? tname.substring(0, tname.indexOf('.')) : tname;
if (rp != null && !nested) {
Declaration d = rp.getDirectMember(tname, null, false);
if (d instanceof TypeDeclaration) {
td = (TypeDeclaration) d;
if (td.isTuple()) {
if (m.containsKey(KEY_TYPES)) {
@SuppressWarnings("unchecked") List<Map<String, Object>> elemaps = (List<Map<String, Object>>) m.get(KEY_TYPES);
ArrayList<Type> elems = new ArrayList<>(elemaps.size());
for (Map<String, Object> elem : elemaps) {
elems.add(getTypeFromJson(elem, container, typeParams));
}
Type tail = elems.get(elems.size() - 1);
if ((tail.isSequence() || tail.isSequential()) && !tail.isTuple() && !tail.isEmpty()) {
elems.remove(elems.size() - 1);
} else {
tail = null;
}
return u2.getTupleType(elems, tail, -1);
} else if (m.containsKey("count")) {
@SuppressWarnings("unchecked") Map<String, Object> elem = (Map<String, Object>) m.get(KEY_TYPE);
Type[] elems = new Type[(int) m.remove("count")];
Arrays.fill(elems, getTypeFromJson(elem, container, typeParams));
return u2.getTupleType(Arrays.asList(elems), null, -1);
}
}
} else if (d instanceof FunctionOrValue) {
td = ((FunctionOrValue) d).getTypeDeclaration();
}
}
if (td == null && rp instanceof JsonPackage) {
if (nested) {
td = ((JsonPackage) rp).loadNestedType(tname, typeParams);
} else {
td = (TypeDeclaration) ((JsonPackage) rp).load(tname, typeParams);
}
}
// Then look in the top-level declarations
if (nested && td == null) {
for (Declaration d : rp.getMembers()) {
if (d instanceof TypeDeclaration && level1.equals(d.getName())) {
td = (TypeDeclaration) d;
}
}
final String[] path = tname.split("\\.");
for (int i = 1; i < path.length; i++) {
td = (TypeDeclaration) td.getDirectMember(path[i], null, false);
}
}
}
}
// From 1.2.3 we stored type arguments in maps
final Type newType = loadTypeArguments(m, td, container, typeParams);
if (newType != null) {
return newType;
}
// This is the old pre 1.2.3 stuff
@SuppressWarnings("unchecked") final List<Map<String, Object>> modelParms = (List<Map<String, Object>>) m.get(KEY_TYPE_PARAMS);
if (td != null && modelParms != null) {
// Substitute type parameters
final HashMap<TypeParameter, Type> concretes = new HashMap<>();
HashMap<TypeParameter, SiteVariance> variances = null;
if (td.getTypeParameters().size() < modelParms.size()) {
if (td.getUnit().getPackage() == this) {
parseTypeParameters(modelParms, td, null);
}
}
final Iterator<TypeParameter> viter = td.getTypeParameters().iterator();
for (Map<String, Object> ptparm : modelParms) {
TypeParameter _cparm = viter.next();
if (ptparm.containsKey(KEY_PACKAGE) || ptparm.containsKey(KEY_TYPES)) {
// Substitute for proper type
final Type _pt = getTypeFromJson(ptparm, container, typeParams);
concretes.put(_cparm, _pt);
} else if (ptparm.containsKey(KEY_NAME) && typeParams != null) {
// Look for type parameter with same name
for (TypeParameter typeParam : typeParams) {
if (typeParam.getName().equals(ptparm.get(KEY_NAME))) {
concretes.put(_cparm, typeParam.getType());
}
}
}
Integer usv = (Integer) ptparm.get(KEY_US_VARIANCE);
if (usv != null) {
if (variances == null) {
variances = new HashMap<>();
}
variances.put(_cparm, SiteVariance.values()[usv]);
}
}
if (!concretes.isEmpty()) {
return td.getType().substitute(concretes, variances);
}
}
if (td == null) {
try {
throw new IllegalArgumentException(String.format("Couldn't find type %s::%s for %s in %s<%s> (FROM pkg %s)", m.get(KEY_PACKAGE), m.get(KEY_NAME), m.get(KEY_MODULE), m, typeParams, getNameAsString()));
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
}
return td.getType();
}
Aggregations