| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Top level generator object for writing code and keeping track of | 6 * Top level generator object for writing code and keeping track of |
| 7 * dependencies. | 7 * dependencies. |
| 8 * | 8 * |
| 9 * Should have two compilation models, but only one implemented so far. | 9 * Should have two compilation models, but only one implemented so far. |
| 10 * | 10 * |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 55 || world.coreimpl.types['ReceivePortImpl'].isUsed) { | 55 || world.coreimpl.types['ReceivePortImpl'].isUsed) { |
| 56 | 56 |
| 57 // Generate callbacks from JS to isolate code if needed | 57 // Generate callbacks from JS to isolate code if needed |
| 58 if (corejs.useWrap0 || corejs.useWrap1) { | 58 if (corejs.useWrap0 || corejs.useWrap1) { |
| 59 genMethod(world.coreimpl.types['IsolateContext'].getMember('eval')); | 59 genMethod(world.coreimpl.types['IsolateContext'].getMember('eval')); |
| 60 genMethod(world.coreimpl.types['EventLoop'].getMember('run')); | 60 genMethod(world.coreimpl.types['EventLoop'].getMember('run')); |
| 61 } | 61 } |
| 62 | 62 |
| 63 corejs.useIsolates = true; | 63 corejs.useIsolates = true; |
| 64 MethodMember isolateMain = | 64 MethodMember isolateMain = |
| 65 world.coreimpl.lookup('startRootIsolate', main.span); | 65 world.coreimpl.topType.resolveMember('startRootIsolate').members[0]; |
| 66 var isolateMainTarget = new TypeValue(world.coreimpl.topType, main.span); | 66 var isolateMainTarget = new TypeValue(world.coreimpl.topType, main.span); |
| 67 mainCall = isolateMain.invoke(metaGen, null, isolateMainTarget, | 67 mainCall = isolateMain.invoke(metaGen, null, isolateMainTarget, |
| 68 new Arguments(null, [main._get(metaGen, main.definition, null)])); | 68 new Arguments(null, [main._get(metaGen, main.definition, null)])); |
| 69 } | 69 } |
| 70 | 70 |
| 71 writeTypes(world.coreimpl); | 71 writeTypes(world.coreimpl); |
| 72 writeTypes(world.corelib); | 72 writeTypes(world.corelib); |
| 73 | 73 |
| 74 // Write the main library. This will cause all libraries to be written in | 74 // Write the main library. This will cause all libraries to be written in |
| 75 // the topographic sort order. | 75 // the topographic sort order. |
| (...skipping 739 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 815 return v; | 815 return v; |
| 816 } else { | 816 } else { |
| 817 // TODO(jmesserly): we should mark this returned value with the temp | 817 // TODO(jmesserly): we should mark this returned value with the temp |
| 818 // somehow, so getTemp will reuse it instead of allocating a new one. | 818 // somehow, so getTemp will reuse it instead of allocating a new one. |
| 819 // (we could do this now if we had a "TempValue" or something like that) | 819 // (we could do this now if we had a "TempValue" or something like that) |
| 820 return new Value(v.type, '(${tmp.code} = ${v.code})', v.span); | 820 return new Value(v.type, '(${tmp.code} = ${v.code})', v.span); |
| 821 } | 821 } |
| 822 } | 822 } |
| 823 | 823 |
| 824 void freeTemp(VariableValue value) { | 824 void freeTemp(VariableValue value) { |
| 825 // TODO(jimhug): Need to do this right - for now we can just skip freeing. | |
| 826 /* | |
| 827 if (_usedTemps.remove(value.code)) { | 825 if (_usedTemps.remove(value.code)) { |
| 828 _freeTemps.add(value.code); | 826 _freeTemps.add(value.code); |
| 829 } else { | 827 } else { |
| 830 world.internalError( | 828 world.internalError( |
| 831 'tried to free unused value or non-temp "${value.code}"'); | 829 'tried to free unused value or non-temp "${value.code}"'); |
| 832 } | 830 } |
| 833 */ | |
| 834 } | 831 } |
| 835 | 832 |
| 833 /* |
| 834 run1(Value thisValue, Arguments args) { |
| 835 // Use some sort of key to do a lookup |
| 836 |
| 837 |
| 838 }*/ |
| 839 |
| 836 run() { | 840 run() { |
| 837 if (method.isGenerated) return; | 841 if (method.isGenerated) return; |
| 838 | 842 |
| 839 // This avoids any attempts to infer across recursion. | 843 // This avoids any attempts to infer across recursion. |
| 840 method.isGenerated = true; | 844 method.isGenerated = true; |
| 841 method.generator = this; | 845 method.generator = this; |
| 842 | 846 |
| 843 // Create most generic possible call for this method. | 847 // Create most generic possible call for this method. |
| 844 var thisObject; | 848 var thisObject; |
| 845 if (method.isConstructor) { | 849 if (method.isConstructor) { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 912 } else { | 916 } else { |
| 913 suffix = world.gen._writePrototypePatch(method.declaringType, | 917 suffix = world.gen._writePrototypePatch(method.declaringType, |
| 914 method.jsname, 'function$_params {', defWriter, false); | 918 method.jsname, 'function$_params {', defWriter, false); |
| 915 } | 919 } |
| 916 | 920 |
| 917 if (needsThis) { | 921 if (needsThis) { |
| 918 defWriter.writeln('var \$this = this; // closure support'); | 922 defWriter.writeln('var \$this = this; // closure support'); |
| 919 } | 923 } |
| 920 | 924 |
| 921 if (_usedTemps.length > 0 || _freeTemps.length > 0) { | 925 if (_usedTemps.length > 0 || _freeTemps.length > 0) { |
| 922 //TODO(jimhug): assert(_usedTemps.length == 0); // all temps should be fre
ed. | 926 assert(_usedTemps.length == 0); // all temps should be freed. |
| 923 _freeTemps.addAll(_usedTemps); | 927 _freeTemps.addAll(_usedTemps); |
| 924 _freeTemps.sort((x, y) => x.compareTo(y)); | 928 _freeTemps.sort((x, y) => x.compareTo(y)); |
| 925 defWriter.writeln('var ${Strings.join(_freeTemps, ", ")};'); | 929 defWriter.writeln('var ${Strings.join(_freeTemps, ", ")};'); |
| 926 } | 930 } |
| 927 | 931 |
| 928 // TODO(jimhug): Lots of string translation here - perf bottleneck? | 932 // TODO(jimhug): Lots of string translation here - perf bottleneck? |
| 929 defWriter.writeln(writer.text); | 933 defWriter.writeln(writer.text); |
| 930 | 934 |
| 931 if (names != null) { | 935 if (names != null) { |
| 932 // TODO(jmesserly): bind isn't implemented in older Safari. | 936 // TODO(jmesserly): bind isn't implemented in older Safari. |
| (...skipping 1022 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1955 return x.binop(kind, y, this, node); | 1959 return x.binop(kind, y, this, node); |
| 1956 } | 1960 } |
| 1957 | 1961 |
| 1958 final assignKind = TokenKind.kindFromAssign(node.op.kind); | 1962 final assignKind = TokenKind.kindFromAssign(node.op.kind); |
| 1959 if (assignKind == -1) { | 1963 if (assignKind == -1) { |
| 1960 final x = visitValue(node.x); | 1964 final x = visitValue(node.x); |
| 1961 final y = visitValue(node.y); | 1965 final y = visitValue(node.y); |
| 1962 return x.binop(kind, y, this, node); | 1966 return x.binop(kind, y, this, node); |
| 1963 } else if ((assignKind != 0) && _expressionNeedsParens(node.y)) { | 1967 } else if ((assignKind != 0) && _expressionNeedsParens(node.y)) { |
| 1964 return _visitAssign(assignKind, node.x, | 1968 return _visitAssign(assignKind, node.x, |
| 1965 new ParenExpression(node.y, node.y.span), node, | 1969 new ParenExpression(node.y, node.y.span), node, null, isVoid); |
| 1966 isVoid ? ReturnKind.IGNORE : ReturnKind.POST); | |
| 1967 } else { | 1970 } else { |
| 1968 return _visitAssign(assignKind, node.x, node.y, node, | 1971 return _visitAssign(assignKind, node.x, node.y, node, null, isVoid); |
| 1969 isVoid ? ReturnKind.IGNORE : ReturnKind.POST); | |
| 1970 } | 1972 } |
| 1971 } | 1973 } |
| 1972 | 1974 |
| 1973 /** | 1975 /** |
| 1974 * Visits an assignment expression. | 1976 * Visits an assignment expression. |
| 1977 * Note: captureOriginal can optionally capture the original value of the |
| 1978 * left side. This is used by postfix expressions to ensure they return the |
| 1979 * original value, before it has been modified. |
| 1975 */ | 1980 */ |
| 1976 _visitAssign(int kind, Expression xn, Expression yn, Node position, | 1981 _visitAssign(int kind, Expression xn, Expression yn, Node position, |
| 1977 int returnKind) { | 1982 Value captureOriginal(Value right), [bool isVoid = false]) { |
| 1983 |
| 1984 if (captureOriginal == null) { |
| 1985 captureOriginal = (x) => x; |
| 1986 } |
| 1987 |
| 1978 // TODO(jimhug): The usual battle with making assign impl not look ugly. | 1988 // TODO(jimhug): The usual battle with making assign impl not look ugly. |
| 1979 if (xn is VarExpression) { | 1989 if (xn is VarExpression) { |
| 1980 return _visitVarAssign(kind, xn, yn, position, returnKind); | 1990 return _visitVarAssign(kind, xn, yn, position, captureOriginal); |
| 1981 } else if (xn is IndexExpression) { | 1991 } else if (xn is IndexExpression) { |
| 1982 return _visitIndexAssign(kind, xn, yn, position, returnKind); | 1992 return _visitIndexAssign(kind, xn, yn, position, captureOriginal, isVoid); |
| 1983 } else if (xn is DotExpression) { | 1993 } else if (xn is DotExpression) { |
| 1984 return _visitDotAssign(kind, xn, yn, position, returnKind); | 1994 return _visitDotAssign(kind, xn, yn, position, captureOriginal); |
| 1985 } else { | 1995 } else { |
| 1986 world.error('illegal lhs', xn.span); | 1996 world.error('illegal lhs', xn.span); |
| 1987 } | 1997 } |
| 1988 } | 1998 } |
| 1989 | 1999 |
| 1990 // TODO(jmesserly): it'd be nice if we didn't have to deal directly with | 2000 // TODO(jmesserly): it'd be nice if we didn't have to deal directly with |
| 1991 // MemberSets here and in visitVarExpression. | 2001 // MemberSets here and in visitVarExpression. |
| 1992 _visitVarAssign(int kind, VarExpression xn, Expression yn, Node position, | 2002 _visitVarAssign(int kind, VarExpression xn, Expression yn, Node position, |
| 1993 int returnKind) { | 2003 Value captureOriginal(Value right)) { |
| 1994 final name = xn.name.name; | 2004 final name = xn.name.name; |
| 1995 | 2005 |
| 1996 // First check in block scopes. | 2006 // First check in block scopes. |
| 1997 var x = _scope.lookup(name); | 2007 var x = _scope.lookup(name); |
| 1998 var y = visitValue(yn); | 2008 var y = visitValue(yn); |
| 1999 | 2009 |
| 2000 if (x != null) { | 2010 if (x != null) { |
| 2001 y = y.convertTo(this, x.staticType); | 2011 y = y.convertTo(this, x.staticType); |
| 2002 // Update the inferred value | 2012 // Update the inferred value |
| 2003 // Note: for now we aren't very flow sensitive, so this is a "union" | 2013 // Note: for now we aren't very flow sensitive, so this is a "union" |
| 2004 // rather than simply setting it to "y" | 2014 // rather than simply setting it to "y" |
| 2005 _scope.inferAssign(name, Value.union(x, y)); | 2015 _scope.inferAssign(name, Value.union(x, y)); |
| 2006 | 2016 } else { |
| 2007 // TODO(jimhug): This is "legacy" and should be cleaned ASAP | 2017 // TODO(jmesserly): this needs serious cleanup... |
| 2008 if (x.isFinal) { | 2018 // Look for a setter in the class |
| 2009 world.error('final variable "${x.code}" is not assignable', | 2019 var members = method.declaringType.resolveMember(name); |
| 2010 position.span); | 2020 x = _makeThisOrType(position.span); |
| 2021 if (members != null) { |
| 2022 if (options.forceDynamic && !members.isStatic) { |
| 2023 members = findMembers(xn.name.name); |
| 2024 } |
| 2025 if (kind == 0) { |
| 2026 return x.set_(this, name, position, y); |
| 2027 } else if (!members.treatAsField || members.containsMethods) { |
| 2028 var right = x.get_(this, name, position); |
| 2029 right = captureOriginal(right); |
| 2030 y = right.invoke(this, TokenKind.binaryMethodName(kind), |
| 2031 position, new Arguments(null, [y])); |
| 2032 return x.set_(this, name, position, y); |
| 2033 } else { |
| 2034 x = x.get_(this, name, position); |
| 2035 } |
| 2036 } else { |
| 2037 // Look for a top-level setter |
| 2038 final member = library.lookup(name, xn.name.span); |
| 2039 if (member == null) { |
| 2040 world.warning('can not resolve ${name}', xn.span); |
| 2041 return _makeMissingValue(name); |
| 2042 } |
| 2043 members = new MemberSet(member); |
| 2044 // If we can't treat it as a field, generate a setter call. |
| 2045 // Also make sure we dont't try to set a method. |
| 2046 if (!members.treatAsField || members.containsMethods) { |
| 2047 if (kind != 0) { |
| 2048 var right = members._get(this, position, x); |
| 2049 right = captureOriginal(right); |
| 2050 y = right.invoke(this, TokenKind.binaryMethodName(kind), |
| 2051 position, new Arguments(null, [y])); |
| 2052 } |
| 2053 return members._set(this, position, x, y); |
| 2054 } else { |
| 2055 x = members._get(this, position, x); |
| 2056 } |
| 2011 } | 2057 } |
| 2012 | 2058 |
| 2013 // Handle different ReturnKind values here... | 2059 // Otherwise treat it as a field. |
| 2014 if (kind == 0) { | 2060 // This makes for nicer code in the $op= case |
| 2015 return new Value(y.type, '${x.code} = ${y.code}', position.span); | 2061 y = y.convertTo(this, x.staticType); |
| 2016 } else if (x.type.isNum && y.type.isNum && (kind != TokenKind.TRUNCDIV)) { | 2062 } |
| 2017 // Process everything but ~/ , which has no equivalent JS operator | 2063 |
| 2018 // Very localized optimization for numbers! | 2064 if (x.isFinal) { |
| 2019 if (returnKind == ReturnKind.PRE) { | 2065 world.error('final variable "${x.code}" is not assignable', |
| 2020 world.internalError('should not be here', position.span); | 2066 position.span); |
| 2021 } | 2067 } |
| 2022 final op = TokenKind.kindToString(kind); | 2068 |
| 2023 return new Value(y.type, '${x.code} $op= ${y.code}', position.span); | 2069 if (kind == 0) { |
| 2024 } else { | 2070 x = captureOriginal(x); |
| 2025 var right = x; | 2071 return new Value(y.type, '${x.code} = ${y.code}', position.span); |
| 2026 y = right.binop(kind, y, this, position); | 2072 } else if (x.type.isNum && y.type.isNum && (kind != TokenKind.TRUNCDIV)) { |
| 2027 if (returnKind == ReturnKind.PRE) { | 2073 // Process everything but ~/ , which has no equivalent JS operator |
| 2028 var tmp = forceTemp(x); | 2074 x = captureOriginal(x); |
| 2029 var ret = new Value(x.type, | 2075 // Very localized optimization for numbers! |
| 2030 '(${tmp.code} = ${x.code}, ${x.code} = ${y.code}, ${tmp.code})', | 2076 final op = TokenKind.kindToString(kind); |
| 2031 position.span); | 2077 return new Value(y.type, '${x.code} $op= ${y.code}', position.span); |
| 2032 freeTemp(tmp); | |
| 2033 return ret; | |
| 2034 } else { | |
| 2035 return new Value(x.type, '${x.code} = ${y.code}', position.span); | |
| 2036 } | |
| 2037 } | |
| 2038 } else { | 2078 } else { |
| 2039 x = _makeThisOrType(position.span); | 2079 var right = x; |
| 2040 return x.set_(this, name, position, y, kind: kind, | 2080 right = captureOriginal(right); |
| 2041 returnKind: returnKind); | 2081 y = right.invoke(this, TokenKind.binaryMethodName(kind), |
| 2082 position, new Arguments(null, [y])); |
| 2083 return new Value(y.type, '${x.code} = ${y.code}', position.span); |
| 2042 } | 2084 } |
| 2043 } | 2085 } |
| 2044 | 2086 |
| 2045 _visitIndexAssign(int kind, IndexExpression xn, Expression yn, | 2087 _visitIndexAssign(int kind, IndexExpression xn, Expression yn, Node position, |
| 2046 Node position, int returnKind) { | 2088 Value captureOriginal(Value right), [bool isVoid = false]) { |
| 2047 var target = visitValue(xn.target); | 2089 var target = visitValue(xn.target); |
| 2090 |
| 2091 // Note: need to get temps eagerly so they don't get reused |
| 2092 var tmptarget = target; |
| 2093 if (kind != 0) tmptarget = getTemp(target); |
| 2094 |
| 2048 var index = visitValue(xn.index); | 2095 var index = visitValue(xn.index); |
| 2096 var tmpindex = index; |
| 2097 if (kind != 0) tmpindex = getTemp(index); |
| 2098 |
| 2049 var y = visitValue(yn); | 2099 var y = visitValue(yn); |
| 2100 if (kind != 0) { |
| 2101 index = assignTemp(tmpindex, index); |
| 2102 var right = tmptarget.invoke(this, ':index', |
| 2103 position, new Arguments(null, [tmpindex])); |
| 2104 right = captureOriginal(right); |
| 2105 y = right.invoke(this, TokenKind.binaryMethodName(kind), |
| 2106 position, new Arguments(null, [y])); |
| 2107 } |
| 2050 | 2108 |
| 2051 return target.setIndex(this, index, position, y, kind: kind, | 2109 var tmpy = null; |
| 2052 returnKind: returnKind); | 2110 // If the assignment is an expression statement (x[i] = y;) it is translated |
| 2111 // as (x.$setindex(i, y)), otherwise as (x.$setindex(i, t = y), t). |
| 2112 if (!isVoid) { |
| 2113 tmpy = getTemp(y); |
| 2114 y = assignTemp(tmpy, y); |
| 2115 } |
| 2116 var ret = assignTemp(tmptarget, target).invoke(this, ':setindex', |
| 2117 position, new Arguments(null, [index, y])); |
| 2118 if (tmpy != null) { |
| 2119 ret = new Value(ret.type, '(${ret.code}, ${tmpy.code})', ret.span); |
| 2120 if (tmpy != y) freeTemp(tmpy); |
| 2121 } |
| 2122 if (tmptarget != target) freeTemp(tmptarget); |
| 2123 if (tmpindex != index) freeTemp(tmpindex); |
| 2124 return ret; |
| 2053 } | 2125 } |
| 2054 | 2126 |
| 2055 _visitDotAssign(int kind, DotExpression xn, Expression yn, Node position, | 2127 _visitDotAssign(int kind, DotExpression xn, Expression yn, Node position, |
| 2056 int returnKind) { | 2128 Value captureOriginal(Value right)) { |
| 2057 // This is not visitValue because types members are assignable. | 2129 |
| 2130 // This is not visitValue because types are assignable through . |
| 2058 var target = xn.self.visit(this); | 2131 var target = xn.self.visit(this); |
| 2132 |
| 2059 var y = visitValue(yn); | 2133 var y = visitValue(yn); |
| 2060 | 2134 var tmptarget = target; |
| 2061 return target.set_(this, xn.name.name, xn.name, y, kind: kind, | 2135 if (kind != 0) { |
| 2062 returnKind: returnKind); | 2136 tmptarget = getTemp(target); |
| 2137 var right = tmptarget.get_(this, xn.name.name, xn.name); |
| 2138 right = captureOriginal(right); |
| 2139 y = right.invoke(this, TokenKind.binaryMethodName(kind), |
| 2140 position, new Arguments(null, [y])); |
| 2141 } |
| 2142 var ret = assignTemp(tmptarget, target).set_( |
| 2143 this, xn.name.name, xn.name, y); |
| 2144 if (tmptarget != target) freeTemp(tmptarget); |
| 2145 return ret; |
| 2063 } | 2146 } |
| 2064 | 2147 |
| 2065 visitUnaryExpression(UnaryExpression node) { | 2148 visitUnaryExpression(UnaryExpression node) { |
| 2066 var value = visitValue(node.self); | 2149 var value = visitValue(node.self); |
| 2067 switch (node.op.kind) { | 2150 switch (node.op.kind) { |
| 2068 case TokenKind.INCR: | 2151 case TokenKind.INCR: |
| 2069 case TokenKind.DECR: | 2152 case TokenKind.DECR: |
| 2070 // TODO(jimhug): Requires non-null num to be correct. | 2153 // TODO(jimhug): Requires non-null num to be correct. |
| 2071 if (value.type.isNum) { | 2154 if (value.type.isNum) { |
| 2072 return new Value(value.type, '${node.op}${value.code}', node.span); | 2155 return new Value(value.type, '${node.op}${value.code}', node.span); |
| 2073 } else { | 2156 } else { |
| 2074 // ++x becomes x += 1 | 2157 // ++x becomes x += 1 |
| 2075 // --x becomes x -= 1 | 2158 // --x becomes x -= 1 |
| 2076 // TODO(jimhug): Confirm that --x becomes x -= 1 as it is in VM. | 2159 // TODO(jimhug): Confirm that --x becomes x -= 1 as it is in VM. |
| 2077 var kind = (TokenKind.INCR == node.op.kind ? | 2160 var kind = (TokenKind.INCR == node.op.kind ? |
| 2078 TokenKind.ADD : TokenKind.SUB); | 2161 TokenKind.ADD : TokenKind.SUB); |
| 2079 // TODO(jimhug): Shouldn't need a full-expression here. | 2162 // TODO(jimhug): Shouldn't need a full-expression here. |
| 2080 var operand = new LiteralExpression(Value.fromInt(1, node.span), | 2163 var operand = new LiteralExpression(Value.fromInt(1, node.span), |
| 2081 node.span); | 2164 node.span); |
| 2082 | 2165 |
| 2083 var assignValue = _visitAssign(kind, node.self, operand, node, | 2166 var assignValue = _visitAssign(kind, node.self, operand, node, null); |
| 2084 ReturnKind.POST); | |
| 2085 return new Value(assignValue.type, '(${assignValue.code})', | 2167 return new Value(assignValue.type, '(${assignValue.code})', |
| 2086 node.span); | 2168 node.span); |
| 2087 } | 2169 } |
| 2088 } | 2170 } |
| 2089 return value.unop(node.op.kind, this, node); | 2171 return value.unop(node.op.kind, this, node); |
| 2090 } | 2172 } |
| 2091 | 2173 |
| 2092 visitDeclaredIdentifier(DeclaredIdentifier node) { | 2174 visitDeclaredIdentifier(DeclaredIdentifier node) { |
| 2093 world.error('Expected expression', node.span); | 2175 world.error('Expected expression', node.span); |
| 2094 } | 2176 } |
| 2095 | 2177 |
| 2096 visitAwaitExpression(AwaitExpression node) { | 2178 visitAwaitExpression(AwaitExpression node) { |
| 2097 world.internalError( | 2179 world.internalError( |
| 2098 'Await expressions should have been eliminated before code generation', | 2180 'Await expressions should have been eliminated before code generation', |
| 2099 node.span); | 2181 node.span); |
| 2100 } | 2182 } |
| 2101 | 2183 |
| 2102 visitPostfixExpression(PostfixExpression node, [bool isVoid = false]) { | 2184 visitPostfixExpression(PostfixExpression node, [bool isVoid = false]) { |
| 2103 var value = visitValue(node.body); | 2185 var value = visitValue(node.body); |
| 2104 // TODO(jimhug): Move and validate this code with nullable ints. | |
| 2105 if (value.type.isNum && !value.isFinal) { | 2186 if (value.type.isNum && !value.isFinal) { |
| 2106 return new Value(value.type, '${value.code}${node.op}', node.span); | 2187 return new Value(value.type, '${value.code}${node.op}', node.span); |
| 2107 } | 2188 } |
| 2108 | 2189 |
| 2109 // x++ is equivalent to (t = x, x = t + 1, t), where we capture all temps | 2190 // x++ is equivalent to (t = x, x = t + 1, t), where we capture all temps |
| 2110 // needed to evaluate x so we're not evaluating multiple times. Likewise, | 2191 // needed to evaluate x so we're not evaluating multiple times. Likewise, |
| 2111 // x-- is equivalent to (t = x, x = t - 1, t). | 2192 // x-- is equivalent to (t = x, x = t - 1, t). |
| 2112 var kind = (TokenKind.INCR == node.op.kind) ? | 2193 var kind = (TokenKind.INCR == node.op.kind) ? TokenKind.ADD : TokenKind.SUB; |
| 2113 TokenKind.ADD : TokenKind.SUB; | |
| 2114 // TODO(jimhug): Shouldn't need a full-expression here. | 2194 // TODO(jimhug): Shouldn't need a full-expression here. |
| 2115 var operand = new LiteralExpression(Value.fromInt(1, node.span), | 2195 var operand = new LiteralExpression(Value.fromInt(1, node.span), |
| 2116 node.span); | 2196 node.span); |
| 2117 var ret = _visitAssign(kind, node.body, operand, node, | 2197 |
| 2118 isVoid ? ReturnKind.IGNORE : ReturnKind.PRE); | 2198 // Use _visitAssign to do most of the work, but save the right side in a |
| 2199 // temporary variable if needed. |
| 2200 // TODO(jmesserly): I don't like passing function args like this, but the |
| 2201 // alternative is duplicating most of the _visitAssign logic. Needs cleanup. |
| 2202 var tmpleft = null, left = null; |
| 2203 var ret = _visitAssign(kind, node.body, operand, node, (l) { |
| 2204 if (isVoid) { |
| 2205 // No need for a temp if we're throwing away the result. |
| 2206 return l; |
| 2207 } else { |
| 2208 // We always need a temp to capture the old value |
| 2209 left = l; |
| 2210 tmpleft = forceTemp(l); |
| 2211 return assignTemp(tmpleft, left); |
| 2212 } |
| 2213 }); |
| 2214 |
| 2215 if (tmpleft != null) { |
| 2216 ret = new Value(ret.type, "(${ret.code}, ${tmpleft.code})", node.span); |
| 2217 } |
| 2218 if (tmpleft != left) { |
| 2219 freeTemp(tmpleft); |
| 2220 } |
| 2119 return ret; | 2221 return ret; |
| 2120 } | 2222 } |
| 2121 | 2223 |
| 2122 visitNewExpression(NewExpression node) { | 2224 visitNewExpression(NewExpression node) { |
| 2123 var typeRef = node.type; | 2225 var typeRef = node.type; |
| 2124 | 2226 |
| 2125 var constructorName = ''; | 2227 var constructorName = ''; |
| 2126 if (node.name != null) { | 2228 if (node.name != null) { |
| 2127 constructorName = node.name.name; | 2229 constructorName = node.name.name; |
| 2128 } | 2230 } |
| (...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2466 result.add(new VariableValue(world.varType, '\$$i', null)); | 2568 result.add(new VariableValue(world.varType, '\$$i', null)); |
| 2467 } | 2569 } |
| 2468 for (int i = bareCount; i < length; i++) { | 2570 for (int i = bareCount; i < length; i++) { |
| 2469 var name = getName(i); | 2571 var name = getName(i); |
| 2470 if (name == null) name = '\$$i'; | 2572 if (name == null) name = '\$$i'; |
| 2471 result.add(new VariableValue(world.varType, name, null)); | 2573 result.add(new VariableValue(world.varType, name, null)); |
| 2472 } | 2574 } |
| 2473 return new Arguments(nodes, result); | 2575 return new Arguments(nodes, result); |
| 2474 } | 2576 } |
| 2475 } | 2577 } |
| 2476 | |
| 2477 class ReturnKind { | |
| 2478 static final int IGNORE = 1; | |
| 2479 static final int POST = 2; | |
| 2480 static final int PRE = 3; | |
| 2481 } | |
| OLD | NEW |