Index: frog/gen.dart |
diff --git a/frog/gen.dart b/frog/gen.dart |
index 029c1a600d342812e4c0c82147c8fda705bb46e3..85d2311bd9c0dfcef34b7c694c21ba5332c0c6a1 100644 |
--- a/frog/gen.dart |
+++ b/frog/gen.dart |
@@ -19,6 +19,8 @@ class WorldGenerator { |
CodeWriter writer; |
CodeWriter _mixins; |
+ CallingContext mainContext; |
+ |
/** |
* Whether the app has any static fields used. Note this could still be true |
* and [globals] be empty if no static field has a default initialization. |
@@ -35,10 +37,31 @@ class WorldGenerator { |
WorldGenerator(this.main, this.writer) |
: globals = {}, corejs = new CoreJs(); |
+ analyze() { |
+ // Walk all code and find all NewExpressions - to determine possible types |
+ int nlibs=0, ntypes=0, nmems=0, nnews=0; |
+ //Set<Type> newedTypes = new Set<Type>(); |
+ for (var lib in world.libraries.getValues()) { |
+ nlibs += 1; |
+ for (var type in lib.types.getValues()) { |
+ ntypes += 1; |
+ var allMembers = []; |
+ allMembers.addAll(type.constructors.getValues()); |
+ allMembers.addAll(type.members.getValues()); |
+ type.factories.forEach((f) => allMembers.add(f)); |
+ for (var m in allMembers) { |
+ if (m.isAbstract || !m.isMethod) continue; |
+ |
+ m.methodData.analyze(); |
+ } |
+ } |
+ } |
+ } |
+ |
run() { |
- var metaGen = new MethodGenerator(main, null); |
+ mainContext = new MethodGenerator(main, null); |
var mainTarget = new TypeValue(main.declaringType, main.span); |
- var mainCall = main.invoke(metaGen, null, mainTarget, Arguments.EMPTY); |
+ var mainCall = main.invoke(mainContext, null, mainTarget, Arguments.EMPTY); |
main.declaringType.markUsed(); |
if (options.compileAll) { |
@@ -64,8 +87,8 @@ class WorldGenerator { |
MethodMember isolateMain = |
world.coreimpl.lookup('startRootIsolate', main.span); |
var isolateMainTarget = new TypeValue(world.coreimpl.topType, main.span); |
- mainCall = isolateMain.invoke(metaGen, null, isolateMainTarget, |
- new Arguments(null, [main._get(metaGen, main.definition, null)])); |
+ mainCall = isolateMain.invoke(mainContext, null, isolateMainTarget, |
+ new Arguments(null, [main._get(mainContext, main.definition, null)])); |
} |
writeTypes(world.coreimpl); |
@@ -208,10 +231,11 @@ class WorldGenerator { |
for (var type in orderedTypes) { |
if (type.isUsed && type.isClass) { |
writeType(type); |
- |
- if (type.isGeneric) { |
+ // TODO(jimhug): Performance is terrible if we use current |
+ // reified generics approach for reified generic Arrays. |
+ if (type.isGeneric && type !== world.listFactoryType) { |
for (var ct in _orderValues(type._concreteTypes)) { |
- writeType(ct); |
+ if (ct.isUsed) writeType(ct); |
} |
} |
} else if (type.isFunction && type.varStubs.length > 0) { |
@@ -226,10 +250,8 @@ class WorldGenerator { |
} |
} |
- genMethod(Member meth, [MethodGenerator enclosingMethod=null]) { |
- if (!meth.isGenerated && !meth.isAbstract && meth.definition != null) { |
- new MethodGenerator(meth, enclosingMethod).run(); |
- } |
+ genMethod(MethodMember meth) { |
+ meth.methodData.run(meth); |
} |
String _prototypeOf(Type type, String name) { |
@@ -313,17 +335,9 @@ class WorldGenerator { |
writeType(type.parent); |
} |
- // TODO(jimhug): Workaround for problems with reified generic Array. |
- if (type.name != null && type is ConcreteType && |
- type.library == world.coreimpl && |
- type.name.startsWith('ListFactory')) { |
- writer.writeln('${type.jsname} = ${type.genericType.jsname};'); |
- return; |
- } |
- |
var typeName = type.jsname != null ? type.jsname : 'top level'; |
writer.comment('// ********** Code for ${typeName} **************'); |
- if (type.isNative && !type.isTop) { |
+ if (type.isNative && !type.isTop && !type.isConcreteGeneric) { |
var nativeName = type.definition.nativeType.name; |
if (nativeName == '') { |
writer.writeln('function ${type.jsname}() {}'); |
@@ -339,10 +353,17 @@ class WorldGenerator { |
} |
} |
+ // TODO(jimhug): This comment below seems out-of-order now? |
// We need the $inherits function to be declared before factory constructors |
// so that inheritance ($inherits) will work correctly in IE. |
if (!type.isTop) { |
- if (type is ConcreteType) { |
+ if (type.genericType !== type) { |
+ corejs.ensureInheritsHelper(); |
+ writer.writeln('\$inherits(${type.jsname}, ${type.genericType.jsname});'); |
+ } |
+ |
+ // TODO(jimhug): Do we still need this code below? |
+ /*if (type is ConcreteType) { |
ConcreteType c = type; |
corejs.ensureInheritsHelper(); |
writer.writeln('\$inherits(${c.jsname}, ${c.genericType.jsname});'); |
@@ -356,7 +377,8 @@ class WorldGenerator { |
_ensureInheritMembersHelper(); |
_mixins.writeln('\$inheritsMembers(${c.jsname}, ${p.jsname});'); |
} |
- } else if (!type.isNative) { |
+ } else*/ |
+ else if (!type.isNative) { |
if (type.parent != null && !type.parent.isObject) { |
corejs.ensureInheritsHelper(); |
writer.writeln('\$inherits(${type.jsname}, ${type.parent.jsname});'); |
@@ -367,31 +389,26 @@ class WorldGenerator { |
if (type.isTop) { |
// no preludes for top type |
} else if (type.constructors.length == 0) { |
- if (!type.isNative) { |
+ if (!type.isNative || type.isConcreteGeneric) { |
// TODO(jimhug): More guards to guarantee staticness |
writer.writeln('function ${type.jsname}() {}'); |
} |
} else { |
- Member standardConstructor = type.constructors['']; |
- if (standardConstructor == null || |
- standardConstructor.generator == null) { |
- if (!type.isNative) { |
- writer.writeln('function ${type.jsname}() {}'); |
+ bool wroteStandard = false; |
+ for (var c in type.constructors.getValues()) { |
+ if (c.methodData.writeDefinition(c, writer)) { |
+ if (c.isConstructor && c.constructorName == '') wroteStandard = true; |
} |
- } else { |
- standardConstructor.generator.writeDefinition(writer, null); |
} |
- for (var c in type.constructors.getValues()) { |
- if (c.generator != null && c != standardConstructor) { |
- c.generator.writeDefinition(writer, null); |
- } |
+ if (!wroteStandard && (!type.isNative || type.genericType !== type)) { |
+ writer.writeln('function ${type.jsname}() {}'); |
} |
} |
// Concrete types (like List<String>) will have this already defined on |
// their prototype from the generic type (like List) |
- if (type is! ConcreteType) { |
+ if (!type.isConcreteGeneric) { |
_maybeIsTest(type, type); |
} |
if (type.genericType._concreteTypes != null) { |
@@ -459,16 +476,7 @@ class WorldGenerator { |
* This predicate determines when we need to define lib_Float32Array. |
*/ |
_typeNeedsHolderForStaticMethods(Type type) { |
- for (var member in type.members.getValues()) { |
- if (member.isMethod) { |
- if (member.isConstructor || member.isStatic) { |
- if (member.isGenerated) { |
- return true; |
- } |
- } |
- } |
- } |
- return false; |
+ return type.isUsed; |
sra1
2012/01/23 20:40:30
This new code causes 500 useless dummy declaration
|
} |
/** |
@@ -520,7 +528,8 @@ function $inheritsMembers(child, parent) { |
} |
// generate code for instance fields |
- if (field._providePropertySyntax) { |
+ if (field._providePropertySyntax && |
+ !field.declaringType.isConcreteGeneric) { |
_writePrototypePatch(field.declaringType, 'get\$${field.jsname}', |
'function() { return this.${field.jsname}; }', writer); |
if (!field.isFinal) { |
@@ -539,7 +548,7 @@ function $inheritsMembers(child, parent) { |
if (property.setter != null) _writeMethod(property.setter); |
// TODO(jmesserly): make sure we don't do this on hidden native types! |
- if (property._provideFieldSyntax) { |
+ if (property.needsFieldSyntax) { |
writer.enterBlock('Object.defineProperty(' + |
'${property.declaringType.jsname}.prototype, "${property.jsname}", {'); |
if (property.getter != null) { |
@@ -557,11 +566,10 @@ function $inheritsMembers(child, parent) { |
} |
} |
- _writeMethod(Member m) { |
- if (m.generator != null) { |
- m.generator.writeDefinition(writer, null); |
- } else if (m is MethodMember && m.isNative |
- && m._providePropertySyntax && !m._provideFieldSyntax) { |
+ _writeMethod(MethodMember m) { |
+ m.methodData.writeDefinition(m, writer); |
+ |
+ if (m.isNative && m._providePropertySyntax) { |
MethodGenerator._maybeGenerateBoundGetter(m, writer); |
} |
} |
@@ -747,7 +755,7 @@ function $inheritsMembers(child, parent) { |
/** |
* A naive code generator for Dart. |
*/ |
-class MethodGenerator implements TreeVisitor { |
+class MethodGenerator implements TreeVisitor, CallingContext { |
Member method; |
CodeWriter writer; |
BlockScope _scope; |
@@ -791,6 +799,9 @@ class MethodGenerator implements TreeVisitor { |
return library._findMembers(name); |
} |
+ bool get needsCode() => true; |
+ bool get showWarnings() => false; |
+ |
bool get isClosure() => (enclosingMethod != null); |
bool get isStatic() => method.isStatic; |
@@ -834,12 +845,6 @@ class MethodGenerator implements TreeVisitor { |
} |
run() { |
- if (method.isGenerated) return; |
- |
- // This avoids any attempts to infer across recursion. |
- method.isGenerated = true; |
- method.generator = this; |
- |
// Create most generic possible call for this method. |
var thisObject; |
if (method.isConstructor) { |
@@ -855,17 +860,6 @@ class MethodGenerator implements TreeVisitor { |
var args = new Arguments(null, values); |
evalBody(thisObject, args); |
- |
- if (method.definition.nativeBody != null) { |
- // Throw away the code--it was just used for tree shaking purposes. |
- writer = new CodeWriter(); |
- if (method.definition.nativeBody == '') { |
- method.generator = null; |
- } else { |
- _paramCode = map(method.parameters, (p) => p.name); |
- writer.write(method.definition.nativeBody); |
- } |
- } |
} |
@@ -956,11 +950,6 @@ class MethodGenerator implements TreeVisitor { |
// TODO(jimhug): Bind not available in older Safari, need fallback? |
defWriter.writeln('return this.${m.jsname}.bind(this);'); |
defWriter.exitBlock(suffix); |
- |
- if (m._provideFieldSyntax) { |
- world.internalError('bound "${m.name}" accessed with field syntax', |
- m.definition.span); |
- } |
} |
} |
@@ -975,7 +964,7 @@ class MethodGenerator implements TreeVisitor { |
if (meth._provideOptionalParamInfo) { |
var optNames = []; |
var optValues = []; |
- meth.genParameterValues(); |
+ meth.genParameterValues(this); |
for (var param in meth.parameters) { |
if (param.isOptional) { |
optNames.add(param.name); |
@@ -1033,7 +1022,7 @@ class MethodGenerator implements TreeVisitor { |
currentArg = args.getValue(p.name); |
if (currentArg === null) { |
// Ensure default value for param has been generated |
- p.genValue(method, method.generator); |
+ p.genValue(method, this); |
currentArg = p.value; |
} |
} |
@@ -1052,7 +1041,7 @@ class MethodGenerator implements TreeVisitor { |
} |
var initializerCall = null; |
- final declaredInitializers = method.definition.initializers; |
+ final declaredInitializers = method.definition.dynamic.initializers; |
if (declaredInitializers != null) { |
for (var init in declaredInitializers) { |
if (init is CallExpression) { |
@@ -1120,7 +1109,7 @@ class MethodGenerator implements TreeVisitor { |
} |
} |
- var body = method.definition.body; |
+ var body = method.definition.dynamic.body; |
if (body === null) { |
// TODO(jimhug): Move check into resolve on method. |
@@ -1316,6 +1305,7 @@ class MethodGenerator implements TreeVisitor { |
var meth = new MethodMember(name, method.declaringType, func); |
meth.isLambda = true; |
meth.enclosingElement = method; |
+ meth._methodData = new MethodData(meth, this); |
meth.resolve(); |
return meth; |
} |
@@ -1376,7 +1366,7 @@ class MethodGenerator implements TreeVisitor { |
isFinal = true; |
} |
writer.write('var '); |
- var type = method.resolveType(node.type, false); |
+ var type = method.resolveType(node.type, false, true); |
for (int i=0; i < node.names.length; i++) { |
if (i > 0) { |
writer.write(', '); |
@@ -1413,8 +1403,7 @@ class MethodGenerator implements TreeVisitor { |
var funcValue = _scope.create(meth.name, meth.functionType, |
method.definition.span, isFinal:true); |
- world.gen.genMethod(meth, this); |
- meth.generator.writeDefinition(writer, null); |
+ meth.methodData.createFunction(writer); |
return false; |
} |
@@ -1574,13 +1563,15 @@ class MethodGenerator implements TreeVisitor { |
bool _isFinal(typeRef) { |
if (typeRef is GenericTypeReference) { |
typeRef = typeRef.baseType; |
+ } else if (typeRef is SimpleTypeReference) { |
+ return false; |
} |
return typeRef != null && typeRef.isFinal; |
} |
bool visitForInStatement(ForInStatement node) { |
// TODO(jimhug): visitValue and other cleanups here. |
- var itemType = method.resolveType(node.item.type, false); |
+ var itemType = method.resolveType(node.item.type, false, true); |
var list = node.list.visit(this); |
_visitLoop(node, () { |
_visitForInBody(node, itemType, list); |
@@ -1599,8 +1590,8 @@ class MethodGenerator implements TreeVisitor { |
list = listVar; |
} |
- // Special path for list for readability and perf optimization. |
- if (list.type.isList) { |
+ // Special path for concrete Arrays for readability and perf optimization. |
+ if (list.type.genericType == world.listFactoryType) { |
var tmpi = _scope.create('\$i', world.numType, null); |
var listLength = list.get_(this, 'length', node.list); |
writer.enterBlock('for (var ${tmpi.code} = 0;' + |
@@ -1645,7 +1636,7 @@ class MethodGenerator implements TreeVisitor { |
// multiple catch, such as no extra temp or if-else-if chain. |
var catch_ = node.catches[0]; |
_pushBlock(catch_); |
- var exType = method.resolveType(catch_.exception.type, false); |
+ var exType = method.resolveType(catch_.exception.type, false, true); |
var ex = _scope.declare(catch_.exception); |
_scope.rethrow = ex.code; |
writer.nextBlock('} catch (${ex.code}) {'); |
@@ -1682,7 +1673,7 @@ class MethodGenerator implements TreeVisitor { |
var catch_ = node.catches[i]; |
_pushBlock(catch_); |
- var tmpType = method.resolveType(catch_.exception.type, false); |
+ var tmpType = method.resolveType(catch_.exception.type, false, true); |
var tmp = _scope.declare(catch_.exception); |
if (!tmpType.isVarOrObject) { |
var test = ex.instanceOf(this, tmpType, catch_.exception.span, |
@@ -1887,21 +1878,7 @@ class MethodGenerator implements TreeVisitor { |
var name = (node.func.name != null) ? node.func.name.name : ''; |
MethodMember meth = _makeLambdaMethod(name, node.func); |
- final lambdaGen = new MethodGenerator(meth, this); |
- if (name != '') { |
- // Note: we don't want to put this in our enclosing scope because the |
- // name shouldn't be visible except inside the lambda. We also don't want |
- // to put the name directly in the lambda's scope because parameters are |
- // allowed to shadow it. So we create an extra scope for it to go into. |
- lambdaGen._scope.create(name, meth.functionType, meth.definition.span, |
- isFinal:true); |
- lambdaGen._pushBlock(node); |
- } |
- lambdaGen.run(); |
- |
- var w = new CodeWriter(); |
- meth.generator.writeDefinition(w, node); |
- return new Value(meth.functionType, w.text, node.span); |
+ return meth.methodData.createLambda(node, this); |
} |
visitCallExpression(CallExpression node) { |
@@ -2067,13 +2044,12 @@ class MethodGenerator implements TreeVisitor { |
switch (node.op.kind) { |
case TokenKind.INCR: |
case TokenKind.DECR: |
- // TODO(jimhug): Requires non-null num to be correct. |
- if (value.type.isNum) { |
+ // TODO(jimhug): Hackish optimization not always correct |
+ if (value.type.isNum && !value.isFinal && node.self is VarExpression) { |
return new Value(value.type, '${node.op}${value.code}', node.span); |
} else { |
// ++x becomes x += 1 |
// --x becomes x -= 1 |
- // TODO(jimhug): Confirm that --x becomes x -= 1 as it is in VM. |
var kind = (TokenKind.INCR == node.op.kind ? |
TokenKind.ADD : TokenKind.SUB); |
// TODO(jimhug): Shouldn't need a full-expression here. |
@@ -2100,9 +2076,10 @@ class MethodGenerator implements TreeVisitor { |
} |
visitPostfixExpression(PostfixExpression node, [bool isVoid = false]) { |
+ // TODO(jimhug): Hackish optimization here to revisit in many ways... |
var value = visitValue(node.body); |
- // TODO(jimhug): Move and validate this code with nullable ints. |
- if (value.type.isNum && !value.isFinal) { |
+ if (value.type.isNum && !value.isFinal && node.body is VarExpression) { |
+ // Would like to also do on "pure" fields - check to see if possible... |
return new Value(value.type, '${value.code}${node.op}', node.span); |
} |
@@ -2129,7 +2106,7 @@ class MethodGenerator implements TreeVisitor { |
// Named constructors and library prefixes, oh my! |
// At last, we can collapse the ambiguous wave function... |
- if (constructorName == '' && typeRef is !GenericTypeReference && |
+ if (constructorName == '' && typeRef is NameTypeReference && |
typeRef.names != null) { |
// Pull off the last name from the type, guess it's the constructor name. |
@@ -2141,7 +2118,7 @@ class MethodGenerator implements TreeVisitor { |
typeRef.isFinal, typeRef.name, names, typeRef.span); |
} |
- var type = method.resolveType(typeRef, true); |
+ var type = method.resolveType(typeRef, true, true); |
if (type.isTop) { |
type = type.library.findTypeByName(constructorName); |
constructorName = ''; |
@@ -2186,7 +2163,7 @@ class MethodGenerator implements TreeVisitor { |
var listType = world.listType; |
var type = world.varType; |
if (node.itemType != null) { |
- type = method.resolveType(node.itemType, true); |
+ type = method.resolveType(node.itemType, true, !node.isConst); |
if (node.isConst && (type is ParameterType || type.hasTypeParams)) { |
world.error('type parameter cannot be used in const list literals'); |
} |
@@ -2220,7 +2197,7 @@ class MethodGenerator implements TreeVisitor { |
var mapType = world.mapType; // TODO(jimhug): immutable type? |
if (node.valueType !== null) { |
if (node.keyType !== null) { |
- keyType = method.resolveType(node.keyType, true); |
+ keyType = method.resolveType(node.keyType, true, !node.isConst); |
// TODO(jimhug): Would be nice to allow arbitrary keys here (this is |
// currently not allowed by the spec). |
if (!keyType.isString) { |
@@ -2233,7 +2210,7 @@ class MethodGenerator implements TreeVisitor { |
} |
} |
- valueType = method.resolveType(node.valueType, true); |
+ valueType = method.resolveType(node.valueType, true, !node.isConst); |
if (node.isConst && |
(valueType is ParameterType || valueType.hasTypeParams)) { |
world.error('type parameter cannot be used in const map literals'); |
@@ -2274,7 +2251,11 @@ class MethodGenerator implements TreeVisitor { |
visitIsExpression(IsExpression node) { |
var value = visitValue(node.x); |
- var type = method.resolveType(node.type, false); |
+ var type = method.resolveType(node.type, true, true); |
+ if (type.isVar) { |
+ return Value.comma(value, Value.fromBool(true, node.span)); |
+ } |
+ |
return value.instanceOf(this, type, node.span, node.isTrue); |
} |
@@ -2472,6 +2453,18 @@ class Arguments { |
} |
return new Arguments(nodes, result); |
} |
+ |
+ bool matches(Arguments other) { |
+ if (length != other.length) return false; |
+ if (bareCount != other.bareCount) return false; |
+ |
+ for (int i = 0; i < bareCount; i++) { |
+ if (values[i].type != other.values[i].type) return false; |
+ } |
+ // TODO(jimhug): Needs to check that named args also match! |
+ return true; |
+ } |
+ |
} |
class ReturnKind { |