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

Unified Diff: lib/dartdoc/frog/value.dart

Issue 10696191: Frog removed from dartdoc. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 5 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
Index: lib/dartdoc/frog/value.dart
diff --git a/lib/dartdoc/frog/value.dart b/lib/dartdoc/frog/value.dart
deleted file mode 100644
index 13e9c0547efa42ea399710700fcbfcb78aea8501..0000000000000000000000000000000000000000
--- a/lib/dartdoc/frog/value.dart
+++ /dev/null
@@ -1,1665 +0,0 @@
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// 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('cannot find "$name" on "${type.name}"', node.span);
- return null;
- }
-
- if (isType && !member.isStatic) {
- world.error('cannot 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]) {
- var tmp = invoke(context, ':setindex', node,
- new Arguments(null, [index, value]));
- return new PureStaticValue(value.type, node.span);
- }
-
- 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);
- }
-
- Value invokeNoSuchMethod(CallingContext context, String name, Node node,
- [Arguments args]) {
- if (isType) {
- world.error('member lookup failed for "$name"', node.span);
- }
-
- var member = getMem(context, 'noSuchMethod', node);
- if (member == null) return new PureStaticValue(world.varType, node.span);
-
- final noSuchArgs = new Arguments(null, [
- new PureStaticValue(world.stringType, node.span),
- new PureStaticValue(world.listType, node.span)]);
-
- return member.invoke(context, node, this, noSuchArgs);
- }
-
- // These are implementation details of convertTo. (Eventually we might find it
- // easier to just implement convertTo itself).
-
- Value _typeAssert(CallingContext context, Type toType) {
- return _changeStaticType(toType);
- }
-
- Value _changeStaticType(Type toType) {
- if (toType === type) return this;
- return new PureStaticValue(toType, span, isConst, isType);
- }
-}
-
-
-/**
- * Represents a meta-value for code generation.
- */
-class Value {
- /** The inferred (i.e. most precise) [Type] of the [Value]. */
- final Type type;
-
- /** The javascript code to generate this value. */
- final String code;
-
- /** The source location that created this value for error messages. */
- final SourceSpan span;
-
- Value(this.type, this.code, this.span) {
- if (type == null) world.internalError('type passed as null', span);
- }
-
-
- /** Is this a pretend first-class type? */
- bool get isType() => false;
-
- /** Is this a reference to super? */
- bool get isSuper() => false;
-
- /** Is this value a constant expression? */
- bool get isConst() => false;
-
- /** Is this a final variable? */
- bool get isFinal() => false;
-
- /** If we reference this value multiple times, do we need a temp? */
- bool get needsTemp() => true;
-
- /**
- * The statically declared [Type] of the [Value]. This type determines which
- * kind of static type warnings are issued. It's also the type that is used
- * for generating type assertions (i.e. given `Foo x; ...; x = expr;`,
- * expr will be checked against "Foo" regardless of the inferred type of `x`).
- */
- Type get staticType() => type;
-
- /** If [isConst], the [EvaluatedValue] that defines this value. */
- EvaluatedValue get constValue() => null;
-
- static Value comma(Value x, Value y) {
- return new Value(y.type, '(${x.code}, ${y.code})', null);
- }
-
- // TODO(jmesserly): more work is needed to make unifying all kinds of Values
- // work properly.
- static Value union(Value x, Value y) {
- if (y === null || x == y) return x;
- if (x === null) return y;
-
- var ret = x._tryUnion(y);
- if (ret != null) return ret;
-
- // TODO(jmesserly): might want to call a _tryUnionReversed here.
- ret = y._tryUnion(x);
- if (ret != null) return ret;
-
- // TODO(jmesserly): should use something like UnionValue and track the
- // precise set of types. For now we find the Type.union.
-
- // TODO(jmesserly): What to do about code? Right now, we're intentionally
- // throwing it away because they aren't used in the current flow-insensitive
- // inference.
- return new Value(Type.union(x.type, y.type), null, null);
- }
-
- Value _tryUnion(Value right) => null;
-
- // TODO(jimhug): remove once type system works better.
- setField(Member field, Value value, [bool duringInit = false]) { }
-
- // Nothing to do in general?
- validateInitialized(SourceSpan span) { }
-
- // TODO(jimhug): Fix these names once get/set are truly pseudo-keywords.
- // See issue #379.
- Value get_(CallingContext context, String name, Node node) {
- final member = _resolveMember(context, name, node);
- if (member != null) {
- return member._get(context, node, this);
- } else {
- return invokeNoSuchMethod(context, 'get:$name', node);
- }
- }
-
- 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;
- var retTmp = null;
- if (kind != 0) {
- // TODO(jimhug): Very special number optimizations will go here...
- thisTmp = context.getTemp(thisValue);
- thisValue = context.assignTemp(thisTmp, thisValue);
- var lhs = member._get(context, node, thisTmp);
- if (returnKind == ReturnKind.PRE) {
- retTmp = context.forceTemp(lhs);
- lhs = context.assignTemp(retTmp, lhs);
- }
- value = lhs.binop(kind, value, context, node);
- }
-
- if (returnKind == ReturnKind.POST) {
- // TODO(jimhug): Optimize this away when native JS is detected.
- retTmp = context.forceTemp(value);
- value = context.assignTemp(retTmp, value);
- }
-
- var ret = member._set(context, node, thisValue, value);
- if (thisTmp != null && thisTmp != this) context.freeTemp(thisTmp);
- if (retTmp != null) {
- context.freeTemp(retTmp);
- return Value.comma(ret, retTmp);
- } else {
- return ret;
- }
- } else {
- // TODO(jimhug): Need to support += and noSuchMethod better.
- return invokeNoSuchMethod(context, 'set:$name', node,
- new Arguments(null, [value]));
- }
- }
-
- // TODO(jimhug): This method body has too much in common with set_ above.
- 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;
- var thisTmp = null;
- var indexTmp = null;
- var retTmp = null;
- if (returnKind == ReturnKind.POST) {
- // TODO(jimhug): Optimize this away when native JS works.
- retTmp = context.forceTemp(value);
- }
- if (kind != 0) {
- // TODO(jimhug): Very special number optimizations will go here...
- thisTmp = context.getTemp(this);
- indexTmp = context.getTemp(index);
- thisValue = context.assignTemp(thisTmp, thisValue);
- indexValue = context.assignTemp(indexTmp, indexValue);
-
- if (returnKind == ReturnKind.PRE) {
- retTmp = context.forceTemp(value);
- }
-
- var lhs = thisTmp.invoke(context, ':index', node,
- new Arguments(null, [indexTmp]));
- if (returnKind == ReturnKind.PRE) {
- lhs = context.assignTemp(retTmp, lhs);
- }
- value = lhs.binop(kind, value, context, node);
- }
- if (returnKind == ReturnKind.POST) {
- value = context.assignTemp(retTmp, value);
- }
-
- var ret = member.invoke(context, node, thisValue,
- new Arguments(null, [indexValue, value]));
- if (thisTmp != null && thisTmp != this) context.freeTemp(thisTmp);
- if (indexTmp != null && indexTmp != index) context.freeTemp(indexTmp);
- if (retTmp != null) {
- context.freeTemp(retTmp);
- return Value.comma(ret, retTmp);
- } else {
- return ret;
- }
- } else {
- // TODO(jimhug): Need to support += and noSuchMethod better.
- return invokeNoSuchMethod(context, ':index', node,
- new Arguments(null, [index, value]));
- }
- }
-
- //Value getIndex(CallingContext context, Value index, var node) {
- //}
-
- 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 Value(newVal.type, '!${newVal.code}', node.span);
- case TokenKind.ADD:
- world.error('no unary add operator in dart', node.span);
- break;
- 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);
- }
-
- 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);
-
- case TokenKind.EQ_STRICT:
- case TokenKind.NE_STRICT:
- var op = kind == TokenKind.EQ_STRICT ? '==' : '!=';
- if (code == 'null') {
- return new Value(world.nonNullBool,
- 'null ${op} ${other.code}', node.span);
- } else if (other.code == 'null') {
- return new Value(world.nonNullBool,
- 'null ${op} ${code}', node.span);
- } else {
- // TODO(jimhug): Add check to see if we can just use op on this type
- // TODO(jimhug): Optimize case of other.needsTemp == false.
- var ret;
- var check;
- if (needsTemp) {
- var tmp = context.forceTemp(this);
- ret = tmp.code;
- check = '(${ret} = ${code}) == null';
- } else {
- ret = code;
- check = 'null == ${code}';
- }
- return new Value(world.nonNullBool,
- '(${check} ? null ${op} (${other.code}) : ${ret} ${op}= ${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);
- return invoke(context, name, node, new Arguments(null, [other]));
- }
-
-
- 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.
-
- // Note: this check is a little different than the one in canInvoke, because
- // sometimes we need to call dynamically even if we found the :call method
- // statically.
-
- if (name == ':call') {
- if (isType) {
- world.error('must use "new" or "const" to construct a new instance',
- node.span);
- }
- if (type.needsVarCall(args)) {
- return _varCall(context, node, args);
- }
- }
-
- var member = _resolveMember(context, name, node);
- if (member == null) {
- return invokeNoSuchMethod(context, name, node, args);
- } else {
- return member.invoke(context, node, this, args);
- }
- }
-
- /**
- * True if this class (or some related class that is not Object) overrides
- * noSuchMethod. If it does we suppress warnings about unknown members.
- */
- // TODO(jmesserly): should we be doing this?
- bool _hasOverriddenNoSuchMethod() {
- var m = type.getMember('noSuchMethod');
- return m != null && !m.declaringType.isObject;
- }
-
- // TODO(jimhug): Handle more precise types here, i.e. consts or closed...
- bool get isPreciseType() => isSuper || isType;
-
- void _missingMemberError(CallingContext context, String name, Node node) {
- bool onStaticType = false;
- if (type != staticType) {
- onStaticType = staticType.getMember(name) !== null;
- }
-
- 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;
- if (typeName == null) typeName = staticType.library.name;
- var message = 'cannot resolve "$name" on "${typeName}"';
- if (isType) {
- world.error(message, node.span);
- } else {
- world.warning(message, node.span);
- }
- }
- }
-
-
-
- MemberSet _tryResolveMember(CallingContext context, String name, Node node) {
- var member = type.getMember(name);
- if (member == null) {
- _missingMemberError(context, name, node);
- return null;
- } else {
- if (isType && !member.isStatic && context.showWarnings) {
- world.error('cannot refer to instance member as static', node.span);
- return null;
- }
- }
-
- if (isPreciseType || member.isStatic) {
- return member.preciseMemberSet;
- } else {
- return member.potentialMemberSet;
- }
- }
-
- // TODO(jmesserly): until reified generics are fixed, treat ParameterType as
- // "var".
- bool _isVarOrParameterType(Type t) => t.isVar || t is ParameterType;
-
- bool _shouldBindDynamically() {
- return _isVarOrParameterType(type) || options.forceDynamic && !isConst;
- }
-
- // TODO(jimhug): Better type here - currently is union(Member, MemberSet)
- MemberSet _resolveMember(CallingContext context, String name, Node node) {
- var member = null;
- if (!_shouldBindDynamically()) {
- 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 && context.showWarnings) {
- var where = 'the world';
- if (name.startsWith('_')) {
- where = 'library "${context.library.name}"';
- }
- world.warning('$name is not defined anywhere in $where.',
- node.span);
- }
- }
-
- return member;
- }
-
- checkFirstClass(SourceSpan span) {
- if (isType) {
- world.error('Types are not first class', span);
- }
- }
-
- /** Generate a call to an unknown function type. */
- 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?
- var stub = world.functionType.getCallStub(args);
- return stub.invoke(context, node, this, args);
- }
-
- /** True if convertTo would generate a conversion. */
- bool needsConversion(Type toType) {
- var c = convertTo(null, toType);
- return c == null || code != c.code;
- }
-
- /**
- * Assign or convert this value to another type.
- * This is used for converting between function types, inserting type
- * checks when --enable_type_checks is enabled, and wrapping callback
- * functions passed to the dom so we can restore their isolate context.
- */
- Value convertTo(CallingContext context, Type toType) {
-
- // Issue type warnings unless we are processing a dynamic operation.
- bool checked = context != null && context.showWarnings;
-
- var callMethod = toType.getCallMethod();
- if (callMethod != null) {
- if (checked && !toType.isAssignable(type)) {
- convertWarning(toType);
- }
-
- return _maybeWrapFunction(toType, callMethod);
- }
-
- // If we're assigning from a var, pretend it's Object for the purpose of
- // runtime checks.
-
- // TODO(jmesserly): I'm a little bothered by the fact that we can't call
- // isSubtypeOf directly. If we tracked null literals as the bottom type,
- // and then only allowed Dynamic to be bottom for generic type args, I think
- // we'd get the right behavior from isSubtypeOf.
- Type fromType = type;
- if (type.isVar && (code != 'null' || !toType.isNullable)) {
- fromType = world.objectType;
- }
-
- // TODO(jmesserly): remove the special case for "num" when our num handling
- // is better.
- bool bothNum = type.isNum && toType.isNum;
- if (!fromType.isSubtypeOf(toType) && !bothNum) {
- // If it is a narrowing conversion, we'll need a check in checked mode.
-
- if (checked && !toType.isSubtypeOf(type)) {
- // According to the static types, this conversion can't work.
- convertWarning(toType);
- }
-
- if (options.enableTypeChecks) {
- 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);
- }
- }
-
- return _changeStaticType(toType);
- }
-
- // Nothing to do in general.
- Value _changeStaticType(Type toType) => this;
-
- /**
- * Wraps a function with a conversion, so it can be called directly from
- * Dart or JS code with the proper arity. We avoid the wrapping if the target
- * function has the same arity.
- *
- * Also wraps a callback attached to the dom (e.g. event listeners,
- * setTimeout) so we can restore it's isolate context information. This is
- * needed so that callbacks are executed within the context of the isolate
- * that created them in the first place.
- */
- Value _maybeWrapFunction(Type toType, MethodMember callMethod) {
- int arity = callMethod.parameters.length;
- var myCall = type.getCallMethod();
-
- Value result = this;
- if (myCall == null || myCall.parameters.length != arity) {
- final stub = world.functionType.getCallStub(new Arguments.bare(arity));
- result = new Value(toType, 'to\$${stub.name}($code)', span);
- }
-
- // TODO(jmesserly): handle when type or toType are type parameters.
- if (toType.library.isDomOrHtml && !type.library.isDomOrHtml) {
- // TODO(jmesserly): either remove this or make it a more first class
- // feature of our native interop. We shouldn't be checking for the DOM
- // library--any host environment (like node.js) might need this feature
- // for isolates too. But we don't want to wrap every function we send to
- // native code--many callbacks like List.filter are perfectly safe.
- if (arity == 0) {
- world.gen.corejs.useWrap0 = true;
- } else if (arity == 1) {
- world.gen.corejs.useWrap1 = true;
- } else if (arity == 2) {
- world.gen.corejs.useWrap2 = true;
- }
-
- result = new Value(toType, '\$wrap_call\$$arity(${result.code})', span);
- }
-
- return result._changeStaticType(toType);
- }
-
- /**
- * Generates a run time type assertion for the given value. This works like
- * [instanceOf], but it allows null since Dart types are nullable.
- * Also it will throw a TypeError if it gets the wrong type.
- */
- Value _typeAssert(CallingContext context, Type toType) {
- if (toType is ParameterType) {
- ParameterType p = toType;
- toType = p.extendsType;
- }
-
- if (toType.isObject || toType.isVar) {
- world.internalError(
- 'We thought ${type.name} is not a subtype of ${toType.name}?');
- }
-
- // Prevent a stack overflow when forceDynamic and type checks are both
- // enabled. forceDynamic would cause the TypeError constructor to type check
- // its arguments, which in turn invokes the TypeError constructor, ad
- // infinitum.
- String throwTypeError(String paramName) => world.withoutForceDynamic(() {
- final typeErrorCtor = world.typeErrorType.getConstructor('_internal');
- world.gen.corejs.ensureTypeNameOf();
- final result = typeErrorCtor.invoke(context, null,
- new TypeValue(world.typeErrorType, null),
- new Arguments(null, [
- new Value(world.objectType, paramName, null),
- new Value(world.stringType, '"${toType.name}"', null)]));
- world.gen.corejs.useThrow = true;
- return '\$throw(${result.code})';
- });
-
- // TODO(jmesserly): better assert for integers?
- if (toType.isNum) toType = world.numType;
-
- // Generate a check like these:
- // obj && obj.is$TypeName()
- // $assert_int(obj)
- //
- // We rely on the fact that calling an undefined method produces a JS
- // TypeError. Alternatively we could define fallbacks on Object that throw.
- String check;
- if (toType.isVoid) {
- check = '\$assert_void($code)';
- if (toType.typeCheckCode == null) {
- toType.typeCheckCode = '''
-function \$assert_void(x) {
- if (x == null) return null;
- ${throwTypeError("x")}
-}''';
- }
- } else if (toType == world.nonNullBool) {
- // This could be made less of a special case
- world.gen.corejs.useNotNullBool = true;
- check = '\$notnull_bool($code)';
-
- } else if (toType.library.isCore && toType.typeofName != null) {
- check = '\$assert_${toType.name}($code)';
-
- if (toType.typeCheckCode == null) {
- toType.typeCheckCode = '''
-function \$assert_${toType.name}(x) {
- if (x == null || typeof(x) == "${toType.typeofName}") return x;
- ${throwTypeError("x")}
-}''';
- }
- } else {
- toType.isChecked = true;
-
- String checkName = 'assert\$${toType.jsname}';
-
- // If we track nullability, we could simplify this check.
- var temp = context.getTemp(this);
- check = '(${context.assignTemp(temp, this).code} == null ? null :';
- check += ' ${temp.code}.$checkName())';
- if (this != temp) context.freeTemp(temp);
-
- // Generate the fallback on Object (that throws a TypeError)
- world.objectType.varStubs.putIfAbsent(checkName,
- () => new VarMethodStub(checkName, null, Arguments.EMPTY,
- throwTypeError('this')));
- }
-
- context.counters.typeAsserts++;
- return new Value(toType, check, span);
- }
-
- /**
- * Test to see if value is an instance of this type.
- *
- * - If a primitive type, then uses the JavaScript typeof.
- * - If it's a non-generic class, use instanceof.
- * - 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(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.
-
- if (toType.isVar) {
- world.error('cannot resolve type', span);
- }
-
- String testCode = null;
- if (toType.isVar || toType.isObject || toType is ParameterType) {
- // Note: everything is an Object, including null.
- if (needsTemp) {
- return new Value(world.nonNullBool, '($code, true)', span);
- } else {
- // TODO(jimhug): Mark non-const?
- return Value.fromBool(true, span);
- }
- }
-
- if (toType.library.isCore) {
- var typeofName = toType.typeofName;
- if (typeofName != null) {
- testCode = "(typeof($code) ${isTrue ? '==' : '!='} '$typeofName')";
- }
- }
-
- if (toType.isClass
- && !toType.isHiddenNativeType && !toType.isConcreteGeneric) {
- toType.markUsed();
- testCode = '($code instanceof ${toType.jsname})';
- if (!isTrue) {
- testCode = '!${testCode}';
- }
- }
- if (testCode == null) {
- toType.isTested = true;
-
- // If we track nullability, we could simplify this check.
- var temp = context.getTemp(this);
-
- String checkName = 'is\$${toType.jsname}';
- testCode = '(${context.assignTemp(temp, this).code} &&'
- ' ${temp.code}.$checkName())';
- if (isTrue) {
- // Add !! to convert to boolean.
- // TODO(jimhug): only do this if needed
- testCode = '!!${testCode}';
- } else {
- // The single ! here nicely converts undefined to false and function
- // to true.
- testCode = '!${testCode}';
- }
- if (this != temp) context.freeTemp(temp);
-
- // Generate the fallback on Object (that returns false)
- if (!world.objectType.varStubs.containsKey(checkName)) {
- world.objectType.varStubs[checkName] =
- new VarMethodStub(checkName, null, Arguments.EMPTY, 'return false');
- }
- }
- return new Value(world.nonNullBool, testCode, span);
- }
-
- void convertWarning(Type toType) {
- // TODO(jmesserly): better error messages for type conversion failures
- world.warning(
- 'type "${type.fullname}" is not assignable to "${toType.fullname}"',
- span);
- }
-
- Value invokeNoSuchMethod(CallingContext context, String name, Node node,
- [Arguments args]) {
- if (isType) {
- world.error('member lookup failed for "$name"', node.span);
- }
-
- var pos = '';
- if (args != null) {
- var argsCode = [];
- for (int i = 0; i < args.length; i++) {
- argsCode.add(args.values[i].code);
- }
- pos = Strings.join(argsCode, ", "); // don't remove trailing nulls
- }
- final noSuchArgs = [
- new Value(world.stringType, '"$name"', node.span),
- new Value(world.listType, '[$pos]', node.span)];
-
- // TODO(jmesserly): should be passing names but that breaks tests. Oh well.
- /*if (args != null && args.hasNames) {
- var names = [];
- for (int i = args.bareCount; i < args.length; i++) {
- names.add('"${args.getName(i)}", ${args.values[i].code}');
- }
- noSuchArgs.add(new Value(world.gen.useMapFactory(),
- '\$map(${Strings.join(names, ", ")})'));
- }*/
-
- // Finally, invoke noSuchMethod
- return _resolveMember(context, 'noSuchMethod', node).invoke(
- context, node, this, new Arguments(null, noSuchArgs));
- }
-
-
- static Value fromBool(bool value, SourceSpan span) {
- return new BoolValue(value, true, span);
- }
-
- static Value fromInt(int value, SourceSpan span) {
- return new IntValue(value, true, span);
- }
-
- static Value fromDouble(double value, SourceSpan span) {
- return new DoubleValue(value, true, span);
- }
-
- static Value fromString(String value, SourceSpan span) {
- return new StringValue(value, true, span);
- }
-
- static Value fromNull(SourceSpan span) {
- return new NullValue(true, span);
- }
-}
-
-
-// TODO(jimhug): rename to PrimitiveValue and refactor further
-class EvaluatedValue extends Value implements Hashable {
- /** Is this value treated as const by dart language? */
- final bool isConst;
-
- EvaluatedValue(this.isConst, Type type, SourceSpan span):
- super(type, '@@@', span);
-
- String get code() {
- world.internalError('Should not be getting code from raw EvaluatedValue',
- span);
- }
-
- get actualValue() {
- world.internalError('Should not be getting actual value '
- 'from raw EvaluatedValue', span);
- }
-
- bool get needsTemp() => false;
-
- EvaluatedValue get constValue() => this;
-
- // TODO(jimhug): Using computed code here without caching is major fear.
- int hashCode() => code.hashCode();
-
- bool operator ==(var other) {
- return other is EvaluatedValue && other.type == this.type &&
- other.code == this.code;
- }
-}
-
-
-class NullValue extends EvaluatedValue {
- NullValue(bool isConst, SourceSpan span):
- super(isConst, world.varType, span);
-
- get actualValue() => null;
-
- String get code() => 'null';
-
- 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;
- final s = node.span;
- switch (kind) {
- case TokenKind.EQ_STRICT:
- case TokenKind.EQ:
- return new BoolValue(true, c, s);
- case TokenKind.NE_STRICT:
- case TokenKind.NE:
- return new BoolValue(false, c, s);
- }
-
- return super.binop(kind, other, context, node);
- }
-}
-
-class BoolValue extends EvaluatedValue {
- final bool actualValue;
-
- BoolValue(this.actualValue, bool isConst, SourceSpan span):
- super(isConst, world.nonNullBool, span);
-
- String get code() => actualValue ? 'true' : 'false';
-
- Value unop(int kind, CallingContext context, var node) {
- switch (kind) {
- case TokenKind.NOT:
- return new BoolValue(!actualValue, isConst, node.span);
- }
- return super.unop(kind, context, 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;
- final s = node.span;
- bool x = actualValue, y = other.actualValue;
- switch (kind) {
- case TokenKind.EQ_STRICT:
- case TokenKind.EQ:
- return new BoolValue(x == y, c, s);
- case TokenKind.NE_STRICT:
- case TokenKind.NE:
- return new BoolValue(x != y, c, s);
- case TokenKind.AND:
- return new BoolValue(x && y, c, s);
- case TokenKind.OR:
- return new BoolValue(x || y, c, s);
- }
-
- return super.binop(kind, other, context, node);
- }
-}
-
-class IntValue extends EvaluatedValue {
- final int actualValue;
-
- IntValue(this.actualValue, bool isConst, SourceSpan span):
- super(isConst, world.intType, span);
-
- // TODO(jimhug): Only add parens when needed.
- String get code() => '(${actualValue})';
-
- Value unop(int kind, CallingContext context, var node) {
- switch (kind) {
- case TokenKind.ADD:
- // This is allowed on numeric constants only
- return new IntValue(actualValue, isConst, span);
- case TokenKind.SUB:
- return new IntValue(-actualValue, isConst, span);
- case TokenKind.BIT_NOT:
- return new IntValue(~actualValue, isConst, span);
- }
- return super.unop(kind, context, node);
- }
-
-
- Value binop(int kind, var other, CallingContext context, var node) {
- final c = isConst && other.isConst;
- final s = node.span;
- if (other is IntValue) {
- int x = actualValue;
- int y = other.actualValue;
- switch (kind) {
- case TokenKind.EQ_STRICT:
- case TokenKind.EQ:
- return new BoolValue(x == y, c, s);
- case TokenKind.NE_STRICT:
- case TokenKind.NE:
- return new BoolValue(x != y, c, s);
-
- case TokenKind.BIT_OR:
- return new IntValue(x | y, c, s);
- case TokenKind.BIT_XOR:
- return new IntValue(x ^ y, c, s);
- case TokenKind.BIT_AND:
- return new IntValue(x & y, c, s);
- case TokenKind.SHL:
- return new IntValue(x << y, c, s);
- case TokenKind.SAR:
- return new IntValue(x >> y, c, s);
- case TokenKind.ADD:
- return new IntValue(x + y, c, s);
- case TokenKind.SUB:
- return new IntValue(x - y, c, s);
- case TokenKind.MUL:
- return new IntValue(x * y, c, s);
- case TokenKind.DIV:
- return new DoubleValue(x / y, c, s);
- case TokenKind.TRUNCDIV:
- return new IntValue(x ~/ y, c, s);
- case TokenKind.MOD:
- return new IntValue(x % y, c, s);
- case TokenKind.LT:
- return new BoolValue(x < y, c, s);
- case TokenKind.GT:
- return new BoolValue(x > y, c, s);
- case TokenKind.LTE:
- return new BoolValue(x <= y, c, s);
- case TokenKind.GTE:
- return new BoolValue(x >= y, c, s);
- }
- } else if (other is DoubleValue) {
- int x = actualValue;
- double y = other.actualValue;
- switch (kind) {
- case TokenKind.EQ_STRICT:
- case TokenKind.EQ:
- return new BoolValue(x == y, c, s);
- case TokenKind.NE_STRICT:
- case TokenKind.NE:
- return new BoolValue(x != y, c, s);
-
- case TokenKind.ADD:
- return new DoubleValue(x + y, c, s);
- case TokenKind.SUB:
- return new DoubleValue(x - y, c, s);
- case TokenKind.MUL:
- return new DoubleValue(x * y, c, s);
- case TokenKind.DIV:
- return new DoubleValue(x / y, c, s);
- case TokenKind.TRUNCDIV:
- // TODO(jimhug): I expected int, but corelib says double here...
- return new DoubleValue(x ~/ y, c, s);
- case TokenKind.MOD:
- return new DoubleValue(x % y, c, s);
- case TokenKind.LT:
- return new BoolValue(x < y, c, s);
- case TokenKind.GT:
- return new BoolValue(x > y, c, s);
- case TokenKind.LTE:
- return new BoolValue(x <= y, c, s);
- case TokenKind.GTE:
- return new BoolValue(x >= y, c, s);
- }
- }
-
- return super.binop(kind, other, context, node);
- }
-}
-
-class DoubleValue extends EvaluatedValue {
- final double actualValue;
-
- DoubleValue(this.actualValue, bool isConst, SourceSpan span):
- super(isConst, world.doubleType, span);
-
- String get code() => '(${actualValue})';
-
- Value unop(int kind, CallingContext context, var node) {
- switch (kind) {
- case TokenKind.ADD:
- // This is allowed on numeric constants only
- return new DoubleValue(actualValue, isConst, span);
- case TokenKind.SUB:
- return new DoubleValue(-actualValue, isConst, span);
- }
- return super.unop(kind, context, node);
- }
-
- Value binop(int kind, var other, CallingContext context, var node) {
- final c = isConst && other.isConst;
- final s = node.span;
- if (other is DoubleValue) {
- double x = actualValue;
- double y = other.actualValue;
- switch (kind) {
- case TokenKind.EQ_STRICT:
- case TokenKind.EQ:
- return new BoolValue(x == y, c, s);
- case TokenKind.NE_STRICT:
- case TokenKind.NE:
- return new BoolValue(x != y, c, s);
-
- case TokenKind.ADD:
- return new DoubleValue(x + y, c, s);
- case TokenKind.SUB:
- return new DoubleValue(x - y, c, s);
- case TokenKind.MUL:
- return new DoubleValue(x * y, c, s);
- case TokenKind.DIV:
- return new DoubleValue(x / y, c, s);
- case TokenKind.TRUNCDIV:
- // TODO(jimhug): I expected int, but corelib says double here...
- return new DoubleValue(x ~/ y, c, s);
- case TokenKind.MOD:
- return new DoubleValue(x % y, c, s);
- case TokenKind.LT:
- return new BoolValue(x < y, c, s);
- case TokenKind.GT:
- return new BoolValue(x > y, c, s);
- case TokenKind.LTE:
- return new BoolValue(x <= y, c, s);
- case TokenKind.GTE:
- return new BoolValue(x >= y, c, s);
- }
- } else if (other is IntValue) {
- double x = actualValue;
- int y = other.actualValue;
- switch (kind) {
- case TokenKind.EQ_STRICT:
- case TokenKind.EQ:
- return new BoolValue(x == y, c, s);
- case TokenKind.NE_STRICT:
- case TokenKind.NE:
- return new BoolValue(x != y, c, s);
-
- case TokenKind.ADD:
- return new DoubleValue(x + y, c, s);
- case TokenKind.SUB:
- return new DoubleValue(x - y, c, s);
- case TokenKind.MUL:
- return new DoubleValue(x * y, c, s);
- case TokenKind.DIV:
- return new DoubleValue(x / y, c, s);
- case TokenKind.TRUNCDIV:
- // TODO(jimhug): I expected int, but corelib says double here...
- return new DoubleValue(x ~/ y, c, s);
- case TokenKind.MOD:
- return new DoubleValue(x % y, c, s);
- case TokenKind.LT:
- return new BoolValue(x < y, c, s);
- case TokenKind.GT:
- return new BoolValue(x > y, c, s);
- case TokenKind.LTE:
- return new BoolValue(x <= y, c, s);
- case TokenKind.GTE:
- return new BoolValue(x >= y, c, s);
- }
- }
-
- return super.binop(kind, other, context, node);
- }
-}
-
-class StringValue extends EvaluatedValue {
- final String actualValue;
-
- StringValue(this.actualValue, bool isConst, SourceSpan span):
- super(isConst, world.stringType, span);
-
- 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;
- final s = node.span;
- String x = actualValue, y = other.actualValue;
- switch (kind) {
- case TokenKind.EQ_STRICT:
- case TokenKind.EQ:
- return new BoolValue(x == y, c, s);
- case TokenKind.NE_STRICT:
- case TokenKind.NE:
- return new BoolValue(x != y, c, s);
- case TokenKind.ADD:
- return new StringValue(x + y, c, s);
- }
-
- return super.binop(kind, other, context, node);
- }
-
-
- // This is expensive and we may want to cache its value if called often
- String get code() {
- // TODO(jimhug): This could be much more efficient
- StringBuffer buf = new StringBuffer();
- buf.add('"');
- for (int i=0; i < actualValue.length; i++) {
- var ch = actualValue.charCodeAt(i);
- switch (ch) {
- case 9/*'\t'*/: buf.add(@'\t'); break;
- case 10/*'\n'*/: buf.add(@'\n'); break;
- case 13/*'\r'*/: buf.add(@'\r'); break;
- case 34/*"*/: buf.add(@'\"'); break;
- case 92/*\*/: buf.add(@'\\'); break;
- default:
- if (ch >= 32 && ch <= 126) {
- buf.add(actualValue[i]);
- } else {
- final hex = ch.toRadixString(16);
- switch (hex.length) {
- case 1: buf.add(@'\x0'); buf.add(hex); break;
- case 2: buf.add(@'\x'); buf.add(hex); break;
- case 3: buf.add(@'\u0'); buf.add(hex); break;
- case 4: buf.add(@'\u'); buf.add(hex); break;
- default:
- world.internalError(
- 'unicode values greater than 2 bytes not implemented');
- break;
- }
- }
- break;
- }
- }
- buf.add('"');
- return buf.toString();
- }
-}
-
-class ListValue extends EvaluatedValue {
- final List<Value> values;
-
- ListValue(this.values, bool isConst, Type type, SourceSpan span):
- super(isConst, type, span);
-
- String get code() {
- final buf = new StringBuffer();
- buf.add('[');
- for (var i = 0; i < values.length; i++) {
- if (i > 0) buf.add(', ');
- buf.add(values[i].code);
- }
- buf.add(']');
- var listCode = buf.toString();
-
- if (!isConst) return listCode;
-
- var v = new Value(world.listType, listCode, span);
- final immutableListCtor = world.immutableListType.getConstructor('from');
- 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, CallingContext context, var node) {
- // TODO(jimhug): Support int/double better
- if (other is! ListValue) return super.binop(kind, other, context, node);
-
- switch (kind) {
- case TokenKind.EQ_STRICT:
- return new BoolValue(type == other.type && code == other.code,
- isConst && other.isConst, node.span);
- case TokenKind.NE_STRICT:
- return new BoolValue(type != other.type || code != other.code,
- isConst && other.isConst, node.span);
- }
-
- return super.binop(kind, other, context, node);
- }
-
- GlobalValue getGlobalValue() {
- assert(isConst);
-
- return world.gen.globalForConst(this, values);
- }
-}
-
-
-class MapValue extends EvaluatedValue {
- final List<Value> values;
-
- MapValue(this.values, bool isConst, Type type, SourceSpan span):
- super(isConst, type, span);
-
- String get code() {
- // Cache?
- var items = new ListValue(values, false, world.listType, span);
- var tp = world.coreimpl.topType;
- Member f = isConst ? tp.getMember('_constMap') : tp.getMember('_map');
- // TODO(jimhug): Clean up invoke signature
- var value = f.invoke(world.gen.mainContext, null, new TypeValue(tp, null),
- new Arguments(null, [items]));
- return value.code;
- }
-
- GlobalValue getGlobalValue() {
- assert(isConst);
-
- return world.gen.globalForConst(this, values);
- }
-
- Value binop(int kind, var other, CallingContext context, var node) {
- if (other is! MapValue) return super.binop(kind, other, context, node);
-
- switch (kind) {
- case TokenKind.EQ_STRICT:
- return new BoolValue(type == other.type && code == other.code,
- isConst && other.isConst, node.span);
- case TokenKind.NE_STRICT:
- return new BoolValue(type != other.type || code != other.code,
- isConst && other.isConst, node.span);
- }
-
- return super.binop(kind, other, context, node);
- }
-}
-
-
-class ObjectValue extends EvaluatedValue {
- final Map<FieldMember, Value> fields;
- final List<FieldMember> fieldsInInitOrder;
- bool seenNativeInitializer = false;
-
- String _code;
-
- ObjectValue(bool isConst, Type type, SourceSpan span)
- : fields = new Map<FieldMember, Value>(),
- fieldsInInitOrder = <FieldMember>[],
- super(isConst, type, span);
-
- String get code() {
- if (_code === null) validateInitialized(null);
- return _code;
- }
-
- initFields() {
- var allMembers = world.gen._orderValues(type.genericType.getAllMembers());
- for (var f in allMembers) {
- if (f.isField && !f.isStatic && f.declaringType.isClass) {
- _replaceField(f, f.computeValue(), true);
- }
- }
- }
-
- setField(Member field, Value value, [bool duringInit = false]) {
- // Unpack constant values
- if (value.isConst && value is VariableValue) {
- value = value.dynamic.value;
- }
- var currentValue = fields[field];
- if (isConst && !value.isConst) {
- world.error('used of non-const value in const intializer', value.span);
- }
-
- if (currentValue === null) {
- _replaceField(field, value, duringInit);
- if (field.isFinal && !duringInit) {
- world.error('cannot initialize final fields outside of initializer',
- value.span);
- }
- } else {
- // TODO(jimhug): Clarify spec on reinitializing fields with defaults.
- if (field.isFinal && field.computeValue() === null) {
- world.error('reassignment of field not allowed', value.span,
- field.span);
- } else {
- _replaceField(field, value, duringInit);
- }
- }
- }
-
- _replaceField(Member field, Value value, bool duringInit) {
- if (duringInit) {
- for (int i = 0; i < fieldsInInitOrder.length; i++) {
- if (fieldsInInitOrder[i] == field) {
- fieldsInInitOrder[i] = null;
- break;
- }
- }
- // TODO(sra): What if the overridden value contains an effect?
- fieldsInInitOrder.add(field);
- }
- fields[field] = value; //currentValue.union(value);
- }
-
- validateInitialized(SourceSpan span) {
- var buf = new StringBuffer();
- buf.add('Object.create(');
- buf.add('${type.jsname}.prototype, ');
-
- buf.add('{');
- bool addComma = false;
- for (var field in fields.getKeys()) {
- if (addComma) buf.add(', ');
- buf.add(field.jsname);
- buf.add(': ');
- buf.add('{"value": ');
- if (fields[field] === null) {
- world.error("Required field '${field.name}' was not initialized",
- span, field.span);
- buf.add('null');
- } else {
- buf.add(fields[field].code);
- }
- buf.add(', writeable: false}');
- addComma = true;
- }
- buf.add('})');
- _code = buf.toString();
- }
-
- Value binop(int kind, var other, CallingContext context, var node) {
- if (other is! ObjectValue) return super.binop(kind, other, context, node);
-
- switch (kind) {
- case TokenKind.EQ_STRICT:
- case TokenKind.EQ:
- return new BoolValue(type == other.type && code == other.code,
- isConst && other.isConst, node.span);
- case TokenKind.NE_STRICT:
- case TokenKind.NE:
- return new BoolValue(type != other.type || code != other.code,
- isConst && other.isConst, node.span);
- }
-
- return super.binop(kind, other, context, node);
- }
-
-}
-
-
-/**
- * A global value in the generated code, which corresponds to either a static
- * field or a memoized const expressions.
- */
-class GlobalValue extends Value implements Comparable {
- /** Static field definition (null for constant exp). */
- final FieldMember field;
-
- /**
- * When [this] represents a constant expression, the global variable name
- * generated for it.
- */
- final String name;
-
- /** The value of the field or constant expression to declare. */
- final Value exp;
-
- /** True for either cont expressions or a final static field. */
- final bool isConst;
-
- /** The actual constant value, when [isConst] is true. */
- get actualValue() => exp.dynamic.actualValue;
-
- /** If [isConst], the [EvaluatedValue] that defines this value. */
- EvaluatedValue get constValue() => isConst ? exp.constValue : null;
-
- /** Other globals that should be defined before this global. */
- final List<GlobalValue> dependencies;
-
- GlobalValue(Type type, String code, bool isConst,
- this.field, this.name, this.exp,
- SourceSpan span, List<Value> deps):
- isConst = isConst,
- dependencies = <GlobalValue>[],
- super(type, code, span) {
-
- // store transitive-dependencies so sorting algorithm works correctly.
- for (var dep in deps) {
- if (dep is GlobalValue) {
- dependencies.add(dep);
- dependencies.addAll(dep.dependencies);
- }
- }
- }
-
- bool get needsTemp() => !isConst;
-
- int compareTo(GlobalValue other) {
- // order by dependencies, o.w. by name
- if (other == this) {
- return 0;
- } else if (dependencies.indexOf(other) >= 0) {
- return 1;
- } else if (other.dependencies.indexOf(this) >= 0) {
- return -1;
- } else if (dependencies.length > other.dependencies.length) {
- return 1;
- } else if (dependencies.length < other.dependencies.length) {
- return -1;
- } else if (name == null && other.name != null) {
- return 1;
- } else if (name != null && other.name == null) {
- return -1;
- } else if (name != null) {
- return name.compareTo(other.name);
- } else {
- return field.name.compareTo(other.field.name);
- }
- }
-}
-
-/**
- * Represents the hidden or implicit value in a bare reference like 'a'.
- * This could be this, the current type, or the current library for purposes
- * of resolving members.
- */
-class BareValue extends Value {
- final bool isType;
- final CallingContext home;
-
- String _code;
-
- BareValue(this.home, CallingContext outermost, SourceSpan span):
- isType = outermost.isStatic,
- super(outermost.method.declaringType, null, span);
-
- bool get needsTemp() => false;
- bool _shouldBindDynamically() => false;
-
- String get code() => _code;
-
- // TODO(jimhug): Lazy initialization here is weird!
- void _ensureCode() {
- if (_code === null) _code = isType ? type.jsname : home._makeThisCode();
- }
-
- MemberSet _tryResolveMember(CallingContext context, String name, Node node) {
- assert(context == home);
-
- // TODO(jimhug): Confirm this matches final resolution of issue 641.
- var member = type.getMember(name);
- if (member == null || member.declaringType != type) {
- var libMember = home.library.lookup(name, span);
- if (libMember !== null) {
- return libMember.preciseMemberSet;
- }
- }
-
- _ensureCode();
- return super._tryResolveMember(context, name, node);
- }
-}
-
-/** A reference to 'super'. */
-// TODO(jmesserly): override resolveMember to clean up the one on Value
-class SuperValue extends Value {
- SuperValue(Type parentType, SourceSpan span):
- super(parentType, 'this', span);
-
- bool get needsTemp() => false;
- bool get isSuper() => true;
- bool _shouldBindDynamically() => false;
-
- Value _tryUnion(Value right) => right is SuperValue ? this : null;
-}
-
-/** A reference to 'this'. */
-class ThisValue extends Value {
- ThisValue(Type type, String code, SourceSpan span):
- super(type, code, span);
-
- bool get needsTemp() => false;
- bool _shouldBindDynamically() => false;
-
- Value _tryUnion(Value right) => right is ThisValue ? this : null;
-}
-
-/** A pretend first-class type. */
-class TypeValue extends Value {
- TypeValue(Type type, SourceSpan span):
- super(type, null, span);
-
- bool get needsTemp() => false;
- bool get isType() => true;
- bool _shouldBindDynamically() => false;
-
- Value _tryUnion(Value right) => right is TypeValue ? this : null;
-}
-
-
-/**
- * A value that represents a variable or parameter. The [assigned] value can be
- * mutated when the variable is assigned to a new Value.
- */
-class VariableValue extends Value {
- final bool isFinal;
- final Value value;
-
- VariableValue(Type staticType, String code, SourceSpan span,
- [this.isFinal=false, Value value]):
- value = _unwrap(value),
- super(staticType, code, span) {
-
- // these are not really first class
- assert(value === null || !value.isType && !value.isSuper);
-
- // TODO(jmesserly): should we do convertTo here, so the check doesn't get
- // missed? There are some cases where this assert doesn't hold.
- // assert(value === null || value.staticType == staticType);
- }
-
- static Value _unwrap(Value v) {
- if (v === null) return null;
- if (v is VariableValue) {
- v = v.dynamic.value;
- }
- return v;
- }
-
- Value _tryUnion(Value right) => Value.union(value, right);
-
- bool get needsTemp() => false;
- Type get type() => value !== null ? value.type : staticType;
- Type get staticType() => super.type;
- bool get isConst() => value !== null ? value.isConst : false;
-
- // TODO(jmesserly): we could use this for checking uninitialized values
- bool get isInitialized() => value != null;
-
- VariableValue replaceValue(Value v) =>
- new VariableValue(staticType, code, span, isFinal, v);
-
- // TODO(jmesserly): anything else to override?
- 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, CallingContext context, var node) {
- if (value != null) {
- return replaceValue(value.binop(kind, _unwrap(other), context, node));
- }
- return super.binop(kind, other, context, node);
- }
-}

Powered by Google App Engine
This is Rietveld 408576698