Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1878)

Unified Diff: frog/value.dart

Issue 9270048: Lots of frog cleanup (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« frog/gen.dart ('K') | « frog/utils.dart ('k') | frog/var_member.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: frog/value.dart
diff --git a/frog/value.dart b/frog/value.dart
index a1c7499c7cfb75b8e4bc86138662e5c8aa8e7236..b13ea6fa35da5112158c5cb7b6ada53445b41940 100644
--- a/frog/value.dart
+++ b/frog/value.dart
@@ -3,6 +3,138 @@
// BSD-style license that can be found in the LICENSE file.
+interface CallingContext {
+ MemberSet findMembers(String name);
+ CounterLog get counters();
+ Library get library();
+ bool get isStatic();
+ MethodMember get method();
+
+ bool get needsCode();
+ bool get showWarnings();
+
+ // Hopefully remove the 5 members below that are only used for code gen.
+ String _makeThisCode();
+
+ Value getTemp(Value value);
+ VariableValue forceTemp(Value value);
+ Value assignTemp(Value tmp, Value v);
+ void freeTemp(VariableValue value);
+}
+
+// TODO(jimhug): Value needs better separation into three parts:
+// 1. Static analysis
+// 2. Type inferred abstract interpretation analysis
+// 3. Actual code generation
+/**
+ * This subtype of value is the one and only version used for static
+ * analysis. It has no code and its type is always the static type.
+ */
+class PureStaticValue extends Value {
+ bool isConst;
+ bool isType;
+
+ // TODO(jimhug): Can we remove span?
+ PureStaticValue(Type type, SourceSpan span,
+ [this.isConst = false, this.isType = false]):
+ super(type, null, span);
+
+ Member getMem(CallingContext context, String name, Node node) {
+ var member = type.getMember(name);
+
+ if (member == null) {
+ world.warning('can not find "$name" on "${type.name}"', node.span);
+ }
+
+ if (isType && !member.isStatic) {
+ world.error('can not refer to instance member as static', node.span);
+ }
+
+ return member;
+ }
+
+ Value get_(CallingContext context, String name, Node node) {
+ if (type.isVar) return new PureStaticValue(world.varType, node.span);
+ var member = getMem(context, name, node);
+ if (member == null) return new PureStaticValue(world.varType, node.span);
+
+ return member._get(context, node, this);
+ }
+
+ Value set_(CallingContext context, String name, Node node, Value value,
+ [int kind=0, int returnKind=ReturnKind.IGNORE]) {
+ if (type.isVar) return new PureStaticValue(world.varType, node.span);
+
+ var member = getMem(context, name, node);
+ if (member != null) {
+ member._set(context, node, this, value);
+ }
+ return new PureStaticValue(value.type, node.span);
+ }
+
+ Value setIndex(CallingContext context, Value index, Node node, Value value,
+ [int kind=0, int returnKind=ReturnKind.IGNORE]) {
+ return invoke(context, ':setindex', node,
+ new Arguments(null, [index, value]));
+ }
+
+ Value unop(int kind, CallingContext context, var node) {
+ switch (kind) {
+ case TokenKind.NOT:
+ // TODO(jimhug): Issue #359 seeks to clarify this behavior.
+ // ?var newVal = convertTo(context, world.nonNullBool);
+ return new PureStaticValue(world.boolType, node.span);
+ case TokenKind.ADD:
+ if (!isConst && !type.isNum) {
+ world.error('no unary add operator in dart', node.span);
+ }
+ return new PureStaticValue(world.numType, node.span);
+ case TokenKind.SUB:
+ return invoke(context, ':negate', node, Arguments.EMPTY);
+ case TokenKind.BIT_NOT:
+ return invoke(context, ':bit_not', node, Arguments.EMPTY);
+ }
+ world.internalError('unimplemented: ${node.op}', node.span);
+ }
+
+ Value binop(int kind, Value other, CallingContext context, var node) {
+ var isConst = isConst && other.isConst;
+
+
+ switch (kind) {
+ case TokenKind.AND:
+ case TokenKind.OR:
+ return new PureStaticValue(world.boolType, node.span, isConst);
+ case TokenKind.EQ_STRICT:
+ return new PureStaticValue(world.boolType, node.span, isConst);
+ case TokenKind.NE_STRICT:
+ return new PureStaticValue(world.boolType, node.span, isConst);
+ }
+
+ var name = kind == TokenKind.NE ? ':ne': TokenKind.binaryMethodName(kind);
+ var ret = invoke(context, name, node, new Arguments(null, [other]));
+ if (isConst) {
+ ret = new PureStaticValue(ret.type, node.span, isConst);
+ }
+ return ret;
+ }
+
+
+ Value invoke(CallingContext context, String name, Node node,
+ Arguments args) {
+ if (type.isVar) return new PureStaticValue(world.varType, node.span);
+ if (type.isFunction && name == ':call') {
+ return new PureStaticValue(world.varType, node.span);
+ }
+
+ var member = getMem(context, name, node);
+ if (member == null) return new PureStaticValue(world.varType, node.span);
+
+ return member.invoke(context, node, this, args);
+ }
+}
+
+
/**
* Represents a meta-value for code generation.
*/
@@ -83,7 +215,7 @@ class Value {
// TODO(jimhug): Fix these names once get/set are truly pseudo-keywords.
// See issue #379.
- Value get_(MethodGenerator context, String name, Node node) {
+ Value get_(CallingContext context, String name, Node node) {
final member = _resolveMember(context, name, node);
if (member != null) {
return member._get(context, node, this);
@@ -92,9 +224,9 @@ class Value {
}
}
- Value set_(MethodGenerator context, String name, Node node, Value value,
- [bool isDynamic=false, int kind=0, int returnKind=ReturnKind.IGNORE]) {
- final member = _resolveMember(context, name, node, isDynamic);
+ Value set_(CallingContext context, String name, Node node, Value value,
+ [int kind=0, int returnKind=ReturnKind.IGNORE]) {
+ final member = _resolveMember(context, name, node);
if (member != null) {
var thisValue = this;
var thisTmp = null;
@@ -117,7 +249,7 @@ class Value {
value = context.assignTemp(retTmp, value);
}
- var ret = member._set(context, node, thisValue, value, isDynamic);
+ var ret = member._set(context, node, thisValue, value);
if (thisTmp != null && thisTmp != this) context.freeTemp(thisTmp);
if (retTmp != null) {
context.freeTemp(retTmp);
@@ -133,9 +265,9 @@ class Value {
}
// TODO(jimhug): This method body has too much in common with set_ above.
- Value setIndex(MethodGenerator context, Value index, Node node, Value value,
- [bool isDynamic=false, int kind=0, int returnKind=ReturnKind.IGNORE]) {
- final member = _resolveMember(context, ':setindex', node, isDynamic);
+ Value setIndex(CallingContext context, Value index, Node node, Value value,
+ [int kind=0, int returnKind=ReturnKind.IGNORE]) {
+ final member = _resolveMember(context, ':setindex', node);
if (member != null) {
var thisValue = this;
var indexValue = index;
@@ -169,7 +301,7 @@ class Value {
}
var ret = member.invoke(context, node, thisValue,
- new Arguments(null, [indexValue, value]), isDynamic);
+ new Arguments(null, [indexValue, value]));
if (thisTmp != null && thisTmp != this) context.freeTemp(thisTmp);
if (indexTmp != null && indexTmp != index) context.freeTemp(indexTmp);
if (retTmp != null) {
@@ -185,10 +317,10 @@ class Value {
}
}
- //Value getIndex(MethodGenerator context, Value index, var node) {
+ //Value getIndex(CallingContext context, Value index, var node) {
//}
- Value unop(int kind, MethodGenerator context, var node) {
+ Value unop(int kind, CallingContext context, var node) {
switch (kind) {
case TokenKind.NOT:
// TODO(jimhug): Issue #359 seeks to clarify this behavior.
@@ -205,19 +337,49 @@ class Value {
world.internalError('unimplemented: ${node.op}', node.span);
}
- Value binop(int kind, Value other, MethodGenerator context, var node) {
+ bool _mayOverrideEqual() {
+ // TODO(jimhug): Need to check subtypes as well
+ return type.isVar || type.isObject ||
+ !type.getMember(':eq').declaringType.isObject;
+ }
+
+ Value binop(int kind, Value other, CallingContext context, var node) {
switch (kind) {
case TokenKind.AND:
case TokenKind.OR:
final code = '${code} ${node.op} ${other.code}';
return new Value(world.nonNullBool, code, node.span);
- // TODO(jimhug): Lot's to resolve here.
+ // TODO(jimhug): Wrong on primitives, need new fix for null == undefined
case TokenKind.EQ_STRICT:
return new Value(world.nonNullBool, '${code} == ${other.code}',
node.span);
case TokenKind.NE_STRICT:
return new Value(world.nonNullBool, '${code} != ${other.code}',
node.span);
+
+ case TokenKind.EQ:
+ if (other.code == 'null') {
+ if (!_mayOverrideEqual()) {
+ return new Value(world.nonNullBool, '${code} == ${other.code}',
+ node.span);
+ }
+ } else if (code == 'null') {
+ return new Value(world.nonNullBool, '${code} == ${other.code}',
+ node.span);
+ }
+ break;
+ case TokenKind.NE:
+ if (other.code == 'null') {
+ if (!_mayOverrideEqual()) {
+ return new Value(world.nonNullBool, '${code} != ${other.code}',
+ node.span);
+ }
+ } else if (code == 'null') {
+ return new Value(world.nonNullBool, '${code} != ${other.code}',
+ node.span);
+ }
+ break;
+
}
var name = kind == TokenKind.NE ? ':ne': TokenKind.binaryMethodName(kind);
@@ -225,9 +387,8 @@ class Value {
}
- Value invoke(MethodGenerator context, String name, Node node, Arguments args,
- [bool isDynamic=false]) {
-
+ Value invoke(CallingContext context, String name, Node node,
+ Arguments args) {
// TODO(jmesserly): it'd be nice to remove these special cases
// We could create a :call in world members, and have that handle the
// canInvoke/Invoke logic.
@@ -246,21 +407,12 @@ class Value {
}
}
- var member = _resolveMember(context, name, node, isDynamic);
+ var member = _resolveMember(context, name, node);
if (member == null) {
return invokeNoSuchMethod(context, name, node, args);
} else {
- return member.invoke(context, node, this, args, isDynamic);
- }
- }
-
- bool canInvoke(MethodGenerator context, String name, Arguments args) {
- if (type.isVarOrFunction && name == ':call') {
- return true;
+ return member.invoke(context, node, this, args);
}
-
- var member = _resolveMember(context, name, null, isDynamic:true);
- return member != null && member.canInvoke(context, args);
}
/**
@@ -276,13 +428,13 @@ class Value {
// TODO(jimhug): Handle more precise types here, i.e. consts or closed...
bool get isPreciseType() => isSuper || isType;
- void _missingMemberError(MethodGenerator context, String name, bool isDynamic, Node node) {
+ void _missingMemberError(CallingContext context, String name, Node node) {
bool onStaticType = false;
if (type != staticType) {
onStaticType = staticType.getMember(name) !== null;
}
- if (!onStaticType && !isDynamic &&
+ if (!onStaticType && context.showWarnings &&
!_isVarOrParameterType(staticType) && !_hasOverriddenNoSuchMethod()) {
// warn if the member was not found, or error if it is a static lookup.
var typeName = staticType.name;
@@ -298,16 +450,14 @@ class Value {
- MemberSet _tryResolveMember(MethodGenerator context, String name, bool isDynamic, Node node) {
+ MemberSet _tryResolveMember(CallingContext context, String name, Node node) {
var member = type.getMember(name);
if (member == null) {
- _missingMemberError(context, name, isDynamic, node);
+ _missingMemberError(context, name, node);
return null;
} else {
- if (isType && !member.isStatic) {
- if (!isDynamic) {
- world.error('can not refer to instance member as static', node.span);
- }
+ if (isType && !member.isStatic && context.showWarnings) {
+ world.error('can not refer to instance member as static', node.span);
return null;
}
}
@@ -328,17 +478,16 @@ class Value {
}
// TODO(jimhug): Better type here - currently is union(Member, MemberSet)
- MemberSet _resolveMember(MethodGenerator context, String name, Node node,
- [bool isDynamic=false]) {
+ MemberSet _resolveMember(CallingContext context, String name, Node node) {
var member = null;
if (!_shouldBindDynamically()) {
- member = _tryResolveMember(context, name, isDynamic, node);
+ member = _tryResolveMember(context, name, node);
}
// Fall back to a dynamic operation for instance members
if (member == null && !isSuper && !isType) {
member = context.findMembers(name);
- if (member == null && !isDynamic) {
+ if (member == null && context.showWarnings) {
var where = 'the world';
if (name.startsWith('_')) {
where = 'library "${context.library.name}"';
@@ -358,7 +507,7 @@ class Value {
}
/** Generate a call to an unknown function type. */
- Value _varCall(MethodGenerator context, Node node, Arguments args) {
+ Value _varCall(CallingContext context, Node node, Arguments args) {
// TODO(jmesserly): calls to unknown functions will bypass type checks,
// which normally happen on the caller side, or in the generated stub for
// dynamic method calls. What should we do?
@@ -368,7 +517,7 @@ class Value {
/** True if convertTo would generate a conversion. */
bool needsConversion(Type toType) {
- var c = convertTo(null, toType, isDynamic:true);
+ var c = convertTo(null, toType);
return c == null || code != c.code;
}
@@ -378,11 +527,10 @@ class Value {
* checks when --enable_type_checks is enabled, and wrapping callback
* functions passed to the dom so we can restore their isolate context.
*/
- Value convertTo(MethodGenerator context, Type toType,
- [bool isDynamic=false]) {
+ Value convertTo(CallingContext context, Type toType) {
// Issue type warnings unless we are processing a dynamic operation.
- bool checked = !isDynamic;
+ bool checked = context != null && context.showWarnings;
var callMethod = toType.getCallMethod();
if (callMethod != null) {
@@ -420,19 +568,17 @@ class Value {
// Generate a runtime checks if they're turned on, otherwise skip it.
if (options.enableTypeChecks) {
- if (context == null && isDynamic) {
+ if (context == null) {
// If we're called from needsConversion, we don't need a context.
// Just return null so it knows a conversion is required.
return null;
}
- return _typeAssert(context, toType, isDynamic);
+ return _typeAssert(context, toType);
} else {
return changeStaticType(toType);
}
}
- // TODO(jmesserly): I think we can replace our usage of the "isDynamic" flag
- // by changing the static type of the target to "Dynamic" instead.
changeStaticType(Type toType) {
// Ensure that we return something with the right type for inference
// purposes.
@@ -484,7 +630,7 @@ class Value {
* [instanceOf], but it allows null since Dart types are nullable.
* Also it will throw a TypeError if it gets the wrong type.
*/
- Value _typeAssert(MethodGenerator context, Type toType, bool isDynamic) {
+ Value _typeAssert(CallingContext context, Type toType) {
if (toType is ParameterType) {
ParameterType p = toType;
toType = p.extendsType;
@@ -506,8 +652,7 @@ class Value {
new TypeValue(world.typeErrorType, null),
new Arguments(null, [
new Value(world.objectType, paramName, null),
- new Value(world.stringType, '"${toType.name}"', null)]),
- isDynamic);
+ new Value(world.stringType, '"${toType.name}"', null)]));
world.gen.corejs.useThrow = true;
return '\$throw(${result.code})';
});
@@ -575,7 +720,7 @@ function \$assert_${toType.name}(x) {
* - Otherwise add a fake member to test for. This value is generated
* as a function so that it can be called for a runtime failure.
*/
- Value instanceOf(MethodGenerator context, Type toType, SourceSpan span,
+ Value instanceOf(CallingContext context, Type toType, SourceSpan span,
[bool isTrue=true, bool forceCheck=false]) {
// TODO(jimhug): Optimize away tests that will always pass unless
// forceCheck is true.
@@ -601,8 +746,9 @@ function \$assert_${toType.name}(x) {
testCode = "(typeof($code) ${isTrue ? '==' : '!='} '$typeofName')";
}
}
- if (toType.isClass && toType is !ConcreteType
- && !toType.isHiddenNativeType) {
+
+ if (toType.isClass
+ && !toType.isHiddenNativeType && !toType.isConcreteGeneric) {
toType.markUsed();
testCode = '($code instanceof ${toType.jsname})';
if (!isTrue) {
@@ -644,7 +790,7 @@ function \$assert_${toType.name}(x) {
span);
}
- Value invokeNoSuchMethod(MethodGenerator context, String name, Node node,
+ Value invokeNoSuchMethod(CallingContext context, String name, Node node,
[Arguments args]) {
if (isType) {
world.error('member lookup failed for "$name"', node.span);
@@ -735,7 +881,7 @@ class NullValue extends EvaluatedValue {
String get code() => 'null';
- Value binop(int kind, var other, MethodGenerator context, var node) {
+ Value binop(int kind, var other, CallingContext context, var node) {
if (other is! NullValue) return super.binop(kind, other, context, node);
final c = isConst && other.isConst;
@@ -761,7 +907,7 @@ class BoolValue extends EvaluatedValue {
String get code() => actualValue ? 'true' : 'false';
- Value unop(int kind, MethodGenerator context, var node) {
+ Value unop(int kind, CallingContext context, var node) {
switch (kind) {
case TokenKind.NOT:
return new BoolValue(!actualValue, isConst, node.span);
@@ -769,7 +915,7 @@ class BoolValue extends EvaluatedValue {
return super.unop(kind, context, node);
}
- Value binop(int kind, var other, MethodGenerator context, var node) {
+ Value binop(int kind, var other, CallingContext context, var node) {
if (other is! BoolValue) return super.binop(kind, other, context, node);
final c = isConst && other.isConst;
@@ -801,7 +947,7 @@ class IntValue extends EvaluatedValue {
// TODO(jimhug): Only add parens when needed.
String get code() => '(${actualValue})';
- Value unop(int kind, MethodGenerator context, var node) {
+ Value unop(int kind, CallingContext context, var node) {
switch (kind) {
case TokenKind.ADD:
// This is allowed on numeric constants only
@@ -815,7 +961,7 @@ class IntValue extends EvaluatedValue {
}
- Value binop(int kind, var other, MethodGenerator context, var node) {
+ Value binop(int kind, var other, CallingContext context, var node) {
final c = isConst && other.isConst;
final s = node.span;
if (other is IntValue) {
@@ -907,7 +1053,7 @@ class DoubleValue extends EvaluatedValue {
String get code() => '(${actualValue})';
- Value unop(int kind, MethodGenerator context, var node) {
+ Value unop(int kind, CallingContext context, var node) {
switch (kind) {
case TokenKind.ADD:
// This is allowed on numeric constants only
@@ -918,7 +1064,7 @@ class DoubleValue extends EvaluatedValue {
return super.unop(kind, context, node);
}
- Value binop(int kind, var other, MethodGenerator context, var node) {
+ Value binop(int kind, var other, CallingContext context, var node) {
final c = isConst && other.isConst;
final s = node.span;
if (other is DoubleValue) {
@@ -999,7 +1145,7 @@ class StringValue extends EvaluatedValue {
StringValue(this.actualValue, bool isConst, SourceSpan span):
super(isConst, world.stringType, span);
- Value binop(int kind, var other, MethodGenerator context, var node) {
+ Value binop(int kind, var other, CallingContext context, var node) {
if (other is! StringValue) return super.binop(kind, other, context, node);
final c = isConst && other.isConst;
@@ -1078,12 +1224,12 @@ class ListValue extends EvaluatedValue {
var v = new Value(world.listType, listCode, span);
final immutableListCtor = world.immutableListType.getConstructor('from');
- final result = immutableListCtor.invoke(null, null,
+ final result = immutableListCtor.invoke(world.gen.mainContext, null,
new TypeValue(v.type, span), new Arguments(null, [v]));
return result.code;
}
- Value binop(int kind, var other, MethodGenerator context, var node) {
+ Value binop(int kind, var other, CallingContext context, var node) {
// TODO(jimhug): Support int/double better
if (other is! ListValue) return super.binop(kind, other, context, node);
@@ -1119,7 +1265,7 @@ class MapValue extends EvaluatedValue {
var tp = world.coreimpl.topType;
Member f = isConst ? tp.getMember('_constMap') : tp.getMember('_map');
// TODO(jimhug): Clean up invoke signature
- var value = f.invoke(null, null, new TypeValue(tp, null),
+ var value = f.invoke(world.gen.mainContext, null, new TypeValue(tp, null),
new Arguments(null, [items]));
return value.code;
}
@@ -1130,7 +1276,7 @@ class MapValue extends EvaluatedValue {
return world.gen.globalForConst(this, values);
}
- Value binop(int kind, var other, MethodGenerator context, var node) {
+ Value binop(int kind, var other, CallingContext context, var node) {
if (other is! MapValue) return super.binop(kind, other, context, node);
switch (kind) {
@@ -1162,7 +1308,7 @@ class ObjectValue extends EvaluatedValue {
}
initFields() {
- var allMembers = world.gen._orderValues(type.getAllMembers());
+ var allMembers = world.gen._orderValues(type.genericType.getAllMembers());
for (var f in allMembers) {
if (f.isField && !f.isStatic && f.declaringType.isClass) {
fields[f] = f.computeValue();
@@ -1220,7 +1366,7 @@ class ObjectValue extends EvaluatedValue {
_code = buf.toString();
}
- Value binop(int kind, var other, MethodGenerator context, var node) {
+ Value binop(int kind, var other, CallingContext context, var node) {
if (other is! ObjectValue) return super.binop(kind, other, context, node);
switch (kind) {
@@ -1318,11 +1464,11 @@ class GlobalValue extends Value implements Comparable {
*/
class BareValue extends Value {
final bool isType;
- final MethodGenerator home;
+ final CallingContext home;
String _code;
- BareValue(this.home, MethodGenerator outermost, SourceSpan span):
+ BareValue(this.home, CallingContext outermost, SourceSpan span):
isType = outermost.isStatic,
super(outermost.method.declaringType, null, span);
@@ -1336,7 +1482,7 @@ class BareValue extends Value {
if (_code === null) _code = isType ? type.jsname : home._makeThisCode();
}
- MemberSet _tryResolveMember(MethodGenerator context, String name, bool isDynamic, Node node) {
+ MemberSet _tryResolveMember(CallingContext context, String name, Node node) {
assert(context == home);
// TODO(jimhug): Confirm this matches final resolution of issue 641.
@@ -1349,7 +1495,7 @@ class BareValue extends Value {
}
_ensureCode();
- return super._tryResolveMember(context, name, isDynamic, node);
+ return super._tryResolveMember(context, name, node);
}
}
@@ -1411,8 +1557,6 @@ class ConvertedValue extends Value {
Value _tryUnion(Value right) => Value.union(value, right);
// TODO(jmessery): override get/set/invoke/unop/binop?
- // (The tricky part is we want them to use our staticType for warning
- // purposes. It's like the whole ConcreteType/ConcreteMember issues...)
}
/**
@@ -1461,13 +1605,13 @@ class VariableValue extends Value {
new VariableValue(staticType, code, span, isFinal, v);
// TODO(jmesserly): anything else to override?
- Value unop(int kind, MethodGenerator context, var node) {
+ Value unop(int kind, CallingContext context, var node) {
if (value != null) {
return replaceValue(value.unop(kind, context, node));
}
return super.unop(kind, context, node);
}
- Value binop(int kind, var other, MethodGenerator context, var node) {
+ Value binop(int kind, var other, CallingContext context, var node) {
if (value != null) {
return replaceValue(value.binop(kind, _unwrap(other), context, node));
}
« frog/gen.dart ('K') | « frog/utils.dart ('k') | frog/var_member.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698