com.redhat.ceylon.compiler.js.BmeGenerator Maven / Gradle / Ivy
package com.redhat.ceylon.compiler.js;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.redhat.ceylon.common.Backend;
import com.redhat.ceylon.compiler.js.GenerateJsVisitor.GenerateCallback;
import com.redhat.ceylon.compiler.js.util.JsWriter;
import com.redhat.ceylon.compiler.js.util.TypeUtils;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.TypeArguments;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.model.typechecker.model.Constructor;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Functional;
import com.redhat.ceylon.model.typechecker.model.Generic;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.Package;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypeParameter;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.Value;
public class BmeGenerator {
static void generateBme(final Tree.BaseMemberExpression bme, final GenerateJsVisitor gen) {
final boolean forInvoke = bme.getDirectlyInvoked();
Declaration decl = bme.getDeclaration();
if (decl != null) {
String name = decl.getName();
Package pkg = decl.getUnit().getPackage();
// map Ceylon true/false/null directly to JS true/false/null
if (pkg.isLanguagePackage()) {
if ("true".equals(name) || "false".equals(name) || "null".equals(name)) {
gen.out(name);
return;
}
}
if (TypeUtils.isConstructor(decl)) {
Constructor cd = TypeUtils.getConstructor(decl);
Declaration cdc = (Declaration)cd.getContainer();
if (!gen.qualify(bme, cd)) {
gen.out(gen.getNames().name(cdc), gen.getNames().constructorSeparator(decl));
}
if (decl instanceof Value) {
gen.out(gen.getNames().name(decl));
} else {
gen.out(gen.getNames().name(cd));
}
if (!forInvoke && decl instanceof Value) {
gen.out("()");
}
return;
}
}
String exp = gen.memberAccess(bme, null);
if (decl == null && gen.isInDynamicBlock()) {
if ("undefined".equals(exp)) {
gen.out(exp);
} else {
gen.out("(typeof ", exp, "==='undefined'||", exp, "===null?");
gen.generateThrow(null, "Undefined or null reference: " + exp, bme);
gen.out(":", exp, ")");
}
} else {
final boolean isCallable = !forInvoke && (decl instanceof Functional
|| bme.getUnit().getCallableDeclaration().equals(bme.getTypeModel().getDeclaration()));
final boolean hasTparms = hasTypeParameters(bme);
if (isCallable && (decl.isParameter() || (decl.isToplevel() && !hasTparms))) {
//Callables passed as arguments are already wrapped in JsCallable
gen.out(exp);
return;
}
String who = isCallable && decl.isMember() ? gen.getMember(bme, null) : null;
if (who == null || who.isEmpty()) {
//We may not need to wrap this in certain cases
ClassOrInterface cont = ModelUtil.getContainingClassOrInterface(bme.getScope());
who = cont == null ? "0" : gen.getNames().self(cont);
}
if (isCallable && (who != null || hasTparms)) {
if (hasTparms) {
//Function refs with type arguments must be passed as a special function
printGenericMethodReference(gen, bme, who, exp);
} else {
//Member methods must be passed as JsCallables
gen.out(gen.getClAlias(), "jsc$3(", who, ",", exp, ")");
}
} else {
gen.out(exp);
}
}
}
static boolean hasTypeParameters(final Tree.StaticMemberOrTypeExpression expr) {
return expr.getTypeArguments() != null && expr.getTypeArguments().getTypeModels() != null
&& !expr.getTypeArguments().getTypeModels().isEmpty();
}
/** Create a map with type arguments from the type parameter list in the expression's declaration and the
* type argument list in the expression itself. */
static Map createTypeArguments(final Tree.StaticMemberOrTypeExpression expr) {
List tparams = null;
Declaration declaration = expr.getDeclaration();
if (declaration instanceof Generic) {
tparams = ((Generic)declaration).getTypeParameters();
}
else if (declaration instanceof TypedDeclaration &&
((TypedDeclaration)declaration).getType()!=null &&
((TypedDeclaration)declaration).getType().isTypeConstructor()) {
tparams = ((TypedDeclaration)declaration).getType().getDeclaration().getTypeParameters();
}
else {
expr.addUnexpectedError("Getting type parameters from unidentified declaration type "
+ declaration, Backend.JavaScript);
return null;
}
final HashMap targs = new HashMap<>();
TypeArguments typeArguments = expr.getTypeArguments();
if (typeArguments!=null) {
List typeModels = typeArguments.getTypeModels();
if (typeModels!=null) {
final Iterator iter = typeModels.iterator();
for (TypeParameter tp : tparams) {
Type pt = iter.hasNext() ? iter.next() : tp.getDefaultTypeArgument();
targs.put(tp, pt);
}
}
}
return targs;
}
static void printGenericMethodReference(final GenerateJsVisitor gen,
final Tree.StaticMemberOrTypeExpression expr, final String who, final String member) {
//Function refs with type arguments must be passed as a special function
gen.out(gen.getClAlias(), "jsc$3(", who, ",", member, ",");
TypeUtils.printTypeArguments(expr, createTypeArguments(expr), gen, false,
expr.getTypeModel().getVarianceOverrides());
gen.out(")");
}
/**
* Generates a write access to a member, as represented by the given expression.
* The given callback is responsible for generating the assigned value.
* If lhs==null and the expression is a BaseMemberExpression
* then the qualified path is prepended.
*/
static void generateMemberAccess(Tree.StaticMemberOrTypeExpression expr,
GenerateCallback callback, String lhs, final GenerateJsVisitor gen) {
Declaration decl = expr.getDeclaration();
boolean paren = false;
String plainName = null;
if (decl == null && gen.isInDynamicBlock()) {
plainName = expr.getIdentifier().getText();
} else if (TypeUtils.isNativeJs(decl)) {
// direct access to a native element
plainName = decl.getName();
}
if (plainName != null) {
if ((lhs != null) && (lhs.length() > 0)) {
gen.out(lhs, ".");
}
gen.out(plainName, "=");
}
else {
boolean protoCall = gen.opts.isOptimize() && (gen.getSuperMemberScope(expr) != null);
if (gen.accessDirectly(decl) && !(protoCall && AttributeGenerator.defineAsProperty(decl))) {
// direct access, without setter
gen.out(gen.memberAccessBase(expr, decl, true, lhs), "=");
}
else {
// access through setter
gen.out(gen.memberAccessBase(expr, decl, true, lhs),
protoCall ? ".call(this," : "(");
paren = true;
}
}
callback.generateValue();
if (paren) { gen.out(")"); }
}
static void generateMemberAccess(final Tree.StaticMemberOrTypeExpression expr,
final String strValue, final String lhs, final GenerateJsVisitor gen) {
generateMemberAccess(expr, new GenerateCallback() {
@Override public void generateValue() { gen.out(strValue); }
}, lhs, gen);
}
static void generateQte(final Tree.QualifiedTypeExpression that, final GenerateJsVisitor gen) {
Tree.Primary prim = that.getPrimary();
final Declaration d = that.getDeclaration();
final boolean dyncall = gen.isInDynamicBlock() && d == null;
if (that.getMemberOperator() instanceof Tree.SpreadOp) {
SequenceGenerator.generateSpread(that, gen);
} else if ((that.getDirectlyInvoked() && that.getMemberOperator() instanceof Tree.SafeMemberOp==false
&& prim instanceof Tree.BaseTypeExpression == false) || dyncall) {
final boolean isQte = prim instanceof Tree.QualifiedTypeExpression;
if (dyncall && that.getDirectlyInvoked() && !isQte) {
gen.out("new ");
}
if (prim instanceof Tree.BaseMemberExpression) {
generateBme((Tree.BaseMemberExpression)prim, gen);
} else if (isQte) {
generateQte((Tree.QualifiedTypeExpression)prim, gen);
} else {
prim.visit(gen);
}
if (dyncall) {
gen.out(".", that.getIdentifier().getText());
} else if (TypeUtils.isConstructor(d)) {
gen.out(gen.getNames().constructorSeparator(d),
gen.getNames().name(d));
} else {
gen.out(".", gen.getNames().name(d));
}
} else {
final boolean parens = that.getDirectlyInvoked() &&
prim instanceof Tree.BaseTypeExpression;
if (parens)gen.out("(");
FunctionHelper.generateCallable(that, gen.getNames().name(d), gen);
if (parens)gen.out(")");
}
}
static void generateBte(final Tree.BaseTypeExpression that, final GenerateJsVisitor gen,
final boolean forceReference) {
Declaration d = that.getDeclaration();
if (d == null && gen.isInDynamicBlock()) {
//It's a native js type but will be wrapped in dyntype() call
String id = that.getIdentifier().getText();
gen.out("(typeof ", id, "==='undefined'?");
gen.generateThrow(null, "Undefined type " + id, that);
gen.out(":", id, ")");
} else {
boolean wrap = false;
String pname = null;
List params = null;
TypeDeclaration td = null;
if ((forceReference || !that.getDirectlyInvoked()) && d instanceof TypeDeclaration) {
td = (TypeDeclaration)d;
if (td.getTypeParameters() != null && td.getTypeParameters().size() > 0) {
wrap = true;
pname = gen.getNames().createTempVariable();
gen.out("function(");
if (td instanceof Class) {
params = ((Class)td).getParameterList().getParameters();
} else if (td instanceof Constructor) {
params = ((Constructor)td).getFirstParameterList().getParameters();
}
for (int i=0;i0)gen.out(",");
gen.out(pname, "$", Integer.toString(i));
}
gen.out("){return ");
}
}
if (d instanceof Constructor) {
//This is an ugly-ass hack for when the typechecker incorrectly reports
//the declaration as the constructor instead of the class;
//this happens with classes that have a default constructor with the same name as the type
if (gen.getNames().name(d).equals(gen.getNames().name((TypeDeclaration)d.getContainer()))) {
gen.qualify(that, (TypeDeclaration)d.getContainer());
} else {
gen.qualify(that, d);
}
} else {
if (d instanceof Class && d.isDynamic()) {
gen.out("new ");
}
gen.qualify(that, d);
}
gen.out(gen.getNames().name(d));
if (wrap) {
gen.out("(");
for (int i=0;i targs = that.getTypeArguments() == null ? null :
that.getTypeArguments().getTypeModels();
TypeUtils.printTypeArguments(that, TypeUtils.matchTypeParametersWithArguments(
td.getTypeParameters(), targs), gen, false, null);
gen.out(");}");
}
}
}
static void generateStaticReference(Node n, Declaration d, GenerateJsVisitor gen) {
Declaration orig = d instanceof TypedDeclaration ? ((TypedDeclaration)d).getOriginalDeclaration() : d;
ClassOrInterface coi = (ClassOrInterface)(orig == null ? d : orig).getContainer();
gen.qualify(n, coi);
gen.out(gen.getNames().name(coi), ".$st$.", gen.getNames().name(d));
if (d instanceof Value && ((Value)d).getType().getDeclaration().isAnonymous()) {
gen.out("()");
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy