| 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.topType.resolveMember('startRootIsolate').members[0]; | 65 world.coreimpl.lookup('startRootIsolate', main.span); |
| 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 /* |
| 825 if (_usedTemps.remove(value.code)) { | 827 if (_usedTemps.remove(value.code)) { |
| 826 _freeTemps.add(value.code); | 828 _freeTemps.add(value.code); |
| 827 } else { | 829 } else { |
| 828 world.internalError( | 830 world.internalError( |
| 829 'tried to free unused value or non-temp "${value.code}"'); | 831 'tried to free unused value or non-temp "${value.code}"'); |
| 830 } | 832 } |
| 833 */ |
| 831 } | 834 } |
| 832 | 835 |
| 833 /* | |
| 834 run1(Value thisValue, Arguments args) { | |
| 835 // Use some sort of key to do a lookup | |
| 836 | |
| 837 | |
| 838 }*/ | |
| 839 | |
| 840 run() { | 836 run() { |
| 841 if (method.isGenerated) return; | 837 if (method.isGenerated) return; |
| 842 | 838 |
| 843 // This avoids any attempts to infer across recursion. | 839 // This avoids any attempts to infer across recursion. |
| 844 method.isGenerated = true; | 840 method.isGenerated = true; |
| 845 method.generator = this; | 841 method.generator = this; |
| 846 | 842 |
| 847 // Create most generic possible call for this method. | 843 // Create most generic possible call for this method. |
| 848 var thisObject; | 844 var thisObject; |
| 849 if (method.isConstructor) { | 845 if (method.isConstructor) { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 916 } else { | 912 } else { |
| 917 suffix = world.gen._writePrototypePatch(method.declaringType, | 913 suffix = world.gen._writePrototypePatch(method.declaringType, |
| 918 method.jsname, 'function$_params {', defWriter, false); | 914 method.jsname, 'function$_params {', defWriter, false); |
| 919 } | 915 } |
| 920 | 916 |
| 921 if (needsThis) { | 917 if (needsThis) { |
| 922 defWriter.writeln('var \$this = this; // closure support'); | 918 defWriter.writeln('var \$this = this; // closure support'); |
| 923 } | 919 } |
| 924 | 920 |
| 925 if (_usedTemps.length > 0 || _freeTemps.length > 0) { | 921 if (_usedTemps.length > 0 || _freeTemps.length > 0) { |
| 926 assert(_usedTemps.length == 0); // all temps should be freed. | 922 //TODO(jimhug): assert(_usedTemps.length == 0); // all temps should be fre
ed. |
| 927 _freeTemps.addAll(_usedTemps); | 923 _freeTemps.addAll(_usedTemps); |
| 928 _freeTemps.sort((x, y) => x.compareTo(y)); | 924 _freeTemps.sort((x, y) => x.compareTo(y)); |
| 929 defWriter.writeln('var ${Strings.join(_freeTemps, ", ")};'); | 925 defWriter.writeln('var ${Strings.join(_freeTemps, ", ")};'); |
| 930 } | 926 } |
| 931 | 927 |
| 932 // TODO(jimhug): Lots of string translation here - perf bottleneck? | 928 // TODO(jimhug): Lots of string translation here - perf bottleneck? |
| 933 defWriter.writeln(writer.text); | 929 defWriter.writeln(writer.text); |
| 934 | 930 |
| 935 if (names != null) { | 931 if (names != null) { |
| 936 // TODO(jmesserly): bind isn't implemented in older Safari. | 932 // TODO(jmesserly): bind isn't implemented in older Safari. |
| (...skipping 1022 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1959 return x.binop(kind, y, this, node); | 1955 return x.binop(kind, y, this, node); |
| 1960 } | 1956 } |
| 1961 | 1957 |
| 1962 final assignKind = TokenKind.kindFromAssign(node.op.kind); | 1958 final assignKind = TokenKind.kindFromAssign(node.op.kind); |
| 1963 if (assignKind == -1) { | 1959 if (assignKind == -1) { |
| 1964 final x = visitValue(node.x); | 1960 final x = visitValue(node.x); |
| 1965 final y = visitValue(node.y); | 1961 final y = visitValue(node.y); |
| 1966 return x.binop(kind, y, this, node); | 1962 return x.binop(kind, y, this, node); |
| 1967 } else if ((assignKind != 0) && _expressionNeedsParens(node.y)) { | 1963 } else if ((assignKind != 0) && _expressionNeedsParens(node.y)) { |
| 1968 return _visitAssign(assignKind, node.x, | 1964 return _visitAssign(assignKind, node.x, |
| 1969 new ParenExpression(node.y, node.y.span), node, null, isVoid); | 1965 new ParenExpression(node.y, node.y.span), node, |
| 1966 isVoid ? ReturnKind.IGNORE : ReturnKind.POST); |
| 1970 } else { | 1967 } else { |
| 1971 return _visitAssign(assignKind, node.x, node.y, node, null, isVoid); | 1968 return _visitAssign(assignKind, node.x, node.y, node, |
| 1969 isVoid ? ReturnKind.IGNORE : ReturnKind.POST); |
| 1972 } | 1970 } |
| 1973 } | 1971 } |
| 1974 | 1972 |
| 1975 /** | 1973 /** |
| 1976 * Visits an assignment expression. | 1974 * 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. | |
| 1980 */ | 1975 */ |
| 1981 _visitAssign(int kind, Expression xn, Expression yn, Node position, | 1976 _visitAssign(int kind, Expression xn, Expression yn, Node position, |
| 1982 Value captureOriginal(Value right), [bool isVoid = false]) { | 1977 int returnKind) { |
| 1983 | |
| 1984 if (captureOriginal == null) { | |
| 1985 captureOriginal = (x) => x; | |
| 1986 } | |
| 1987 | |
| 1988 // TODO(jimhug): The usual battle with making assign impl not look ugly. | 1978 // TODO(jimhug): The usual battle with making assign impl not look ugly. |
| 1989 if (xn is VarExpression) { | 1979 if (xn is VarExpression) { |
| 1990 return _visitVarAssign(kind, xn, yn, position, captureOriginal); | 1980 return _visitVarAssign(kind, xn, yn, position, returnKind); |
| 1991 } else if (xn is IndexExpression) { | 1981 } else if (xn is IndexExpression) { |
| 1992 return _visitIndexAssign(kind, xn, yn, position, captureOriginal, isVoid); | 1982 return _visitIndexAssign(kind, xn, yn, position, returnKind); |
| 1993 } else if (xn is DotExpression) { | 1983 } else if (xn is DotExpression) { |
| 1994 return _visitDotAssign(kind, xn, yn, position, captureOriginal); | 1984 return _visitDotAssign(kind, xn, yn, position, returnKind); |
| 1995 } else { | 1985 } else { |
| 1996 world.error('illegal lhs', xn.span); | 1986 world.error('illegal lhs', xn.span); |
| 1997 } | 1987 } |
| 1998 } | 1988 } |
| 1999 | 1989 |
| 2000 // TODO(jmesserly): it'd be nice if we didn't have to deal directly with | 1990 // TODO(jmesserly): it'd be nice if we didn't have to deal directly with |
| 2001 // MemberSets here and in visitVarExpression. | 1991 // MemberSets here and in visitVarExpression. |
| 2002 _visitVarAssign(int kind, VarExpression xn, Expression yn, Node position, | 1992 _visitVarAssign(int kind, VarExpression xn, Expression yn, Node position, |
| 2003 Value captureOriginal(Value right)) { | 1993 int returnKind) { |
| 2004 final name = xn.name.name; | 1994 final name = xn.name.name; |
| 2005 | 1995 |
| 2006 // First check in block scopes. | 1996 // First check in block scopes. |
| 2007 var x = _scope.lookup(name); | 1997 var x = _scope.lookup(name); |
| 2008 var y = visitValue(yn); | 1998 var y = visitValue(yn); |
| 2009 | 1999 |
| 2010 if (x != null) { | 2000 if (x != null) { |
| 2011 y = y.convertTo(this, x.staticType); | 2001 y = y.convertTo(this, x.staticType); |
| 2012 // Update the inferred value | 2002 // Update the inferred value |
| 2013 // Note: for now we aren't very flow sensitive, so this is a "union" | 2003 // Note: for now we aren't very flow sensitive, so this is a "union" |
| 2014 // rather than simply setting it to "y" | 2004 // rather than simply setting it to "y" |
| 2015 _scope.inferAssign(name, Value.union(x, y)); | 2005 _scope.inferAssign(name, Value.union(x, y)); |
| 2016 } else { | 2006 |
| 2017 // TODO(jmesserly): this needs serious cleanup... | 2007 // TODO(jimhug): This is "legacy" and should be cleaned ASAP |
| 2018 // Look for a setter in the class | 2008 if (x.isFinal) { |
| 2019 var members = method.declaringType.resolveMember(name); | 2009 world.error('final variable "${x.code}" is not assignable', |
| 2020 x = _makeThisOrType(position.span); | 2010 position.span); |
| 2021 if (members != null) { | 2011 } |
| 2022 if (options.forceDynamic && !members.isStatic) { | 2012 |
| 2023 members = findMembers(xn.name.name); | 2013 // Handle different ReturnKind values here... |
| 2014 if (kind == 0) { |
| 2015 return new Value(y.type, '${x.code} = ${y.code}', position.span); |
| 2016 } else if (x.type.isNum && y.type.isNum && (kind != TokenKind.TRUNCDIV)) { |
| 2017 // Process everything but ~/ , which has no equivalent JS operator |
| 2018 // Very localized optimization for numbers! |
| 2019 if (returnKind == ReturnKind.PRE) { |
| 2020 world.internalError('should not be here', position.span); |
| 2024 } | 2021 } |
| 2025 if (kind == 0) { | 2022 final op = TokenKind.kindToString(kind); |
| 2026 return x.set_(this, name, position, y); | 2023 return new Value(y.type, '${x.code} $op= ${y.code}', position.span); |
| 2027 } else if (!members.treatAsField || members.containsMethods) { | 2024 } else { |
| 2028 var right = x.get_(this, name, position); | 2025 var right = x; |
| 2029 right = captureOriginal(right); | 2026 y = right.binop(kind, y, this, position); |
| 2030 y = right.invoke(this, TokenKind.binaryMethodName(kind), | 2027 if (returnKind == ReturnKind.PRE) { |
| 2031 position, new Arguments(null, [y])); | 2028 var tmp = forceTemp(x); |
| 2032 return x.set_(this, name, position, y); | 2029 var ret = new Value(x.type, |
| 2030 '(${tmp.code} = ${x.code}, ${x.code} = ${y.code}, ${tmp.code})', |
| 2031 position.span); |
| 2032 freeTemp(tmp); |
| 2033 return ret; |
| 2033 } else { | 2034 } else { |
| 2034 x = x.get_(this, name, position); | 2035 return new Value(x.type, '${x.code} = ${y.code}', position.span); |
| 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 } | 2036 } |
| 2057 } | 2037 } |
| 2058 | |
| 2059 // Otherwise treat it as a field. | |
| 2060 // This makes for nicer code in the $op= case | |
| 2061 y = y.convertTo(this, x.staticType); | |
| 2062 } | |
| 2063 | |
| 2064 if (x.isFinal) { | |
| 2065 world.error('final variable "${x.code}" is not assignable', | |
| 2066 position.span); | |
| 2067 } | |
| 2068 | |
| 2069 if (kind == 0) { | |
| 2070 x = captureOriginal(x); | |
| 2071 return new Value(y.type, '${x.code} = ${y.code}', position.span); | |
| 2072 } else if (x.type.isNum && y.type.isNum && (kind != TokenKind.TRUNCDIV)) { | |
| 2073 // Process everything but ~/ , which has no equivalent JS operator | |
| 2074 x = captureOriginal(x); | |
| 2075 // Very localized optimization for numbers! | |
| 2076 final op = TokenKind.kindToString(kind); | |
| 2077 return new Value(y.type, '${x.code} $op= ${y.code}', position.span); | |
| 2078 } else { | 2038 } else { |
| 2079 var right = x; | 2039 x = _makeThisOrType(position.span); |
| 2080 right = captureOriginal(right); | 2040 return x.set_(this, name, position, y, kind: kind, |
| 2081 y = right.invoke(this, TokenKind.binaryMethodName(kind), | 2041 returnKind: returnKind); |
| 2082 position, new Arguments(null, [y])); | |
| 2083 return new Value(y.type, '${x.code} = ${y.code}', position.span); | |
| 2084 } | 2042 } |
| 2085 } | 2043 } |
| 2086 | 2044 |
| 2087 _visitIndexAssign(int kind, IndexExpression xn, Expression yn, Node position, | 2045 _visitIndexAssign(int kind, IndexExpression xn, Expression yn, |
| 2088 Value captureOriginal(Value right), [bool isVoid = false]) { | 2046 Node position, int returnKind) { |
| 2089 var target = visitValue(xn.target); | 2047 var target = visitValue(xn.target); |
| 2048 var index = visitValue(xn.index); |
| 2049 var y = visitValue(yn); |
| 2090 | 2050 |
| 2091 // Note: need to get temps eagerly so they don't get reused | 2051 return target.setIndex(this, index, position, y, kind: kind, |
| 2092 var tmptarget = target; | 2052 returnKind: returnKind); |
| 2093 if (kind != 0) tmptarget = getTemp(target); | |
| 2094 | |
| 2095 var index = visitValue(xn.index); | |
| 2096 var tmpindex = index; | |
| 2097 if (kind != 0) tmpindex = getTemp(index); | |
| 2098 | |
| 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 } | |
| 2108 | |
| 2109 var tmpy = null; | |
| 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; | |
| 2125 } | 2053 } |
| 2126 | 2054 |
| 2127 _visitDotAssign(int kind, DotExpression xn, Expression yn, Node position, | 2055 _visitDotAssign(int kind, DotExpression xn, Expression yn, Node position, |
| 2128 Value captureOriginal(Value right)) { | 2056 int returnKind) { |
| 2057 // This is not visitValue because types members are assignable. |
| 2058 var target = xn.self.visit(this); |
| 2059 var y = visitValue(yn); |
| 2129 | 2060 |
| 2130 // This is not visitValue because types are assignable through . | 2061 return target.set_(this, xn.name.name, xn.name, y, kind: kind, |
| 2131 var target = xn.self.visit(this); | 2062 returnKind: returnKind); |
| 2132 | |
| 2133 var y = visitValue(yn); | |
| 2134 var tmptarget = target; | |
| 2135 if (kind != 0) { | |
| 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; | |
| 2146 } | 2063 } |
| 2147 | 2064 |
| 2148 visitUnaryExpression(UnaryExpression node) { | 2065 visitUnaryExpression(UnaryExpression node) { |
| 2149 var value = visitValue(node.self); | 2066 var value = visitValue(node.self); |
| 2150 switch (node.op.kind) { | 2067 switch (node.op.kind) { |
| 2151 case TokenKind.INCR: | 2068 case TokenKind.INCR: |
| 2152 case TokenKind.DECR: | 2069 case TokenKind.DECR: |
| 2153 // TODO(jimhug): Requires non-null num to be correct. | 2070 // TODO(jimhug): Requires non-null num to be correct. |
| 2154 if (value.type.isNum) { | 2071 if (value.type.isNum) { |
| 2155 return new Value(value.type, '${node.op}${value.code}', node.span); | 2072 return new Value(value.type, '${node.op}${value.code}', node.span); |
| 2156 } else { | 2073 } else { |
| 2157 // ++x becomes x += 1 | 2074 // ++x becomes x += 1 |
| 2158 // --x becomes x -= 1 | 2075 // --x becomes x -= 1 |
| 2159 // TODO(jimhug): Confirm that --x becomes x -= 1 as it is in VM. | 2076 // TODO(jimhug): Confirm that --x becomes x -= 1 as it is in VM. |
| 2160 var kind = (TokenKind.INCR == node.op.kind ? | 2077 var kind = (TokenKind.INCR == node.op.kind ? |
| 2161 TokenKind.ADD : TokenKind.SUB); | 2078 TokenKind.ADD : TokenKind.SUB); |
| 2162 // TODO(jimhug): Shouldn't need a full-expression here. | 2079 // TODO(jimhug): Shouldn't need a full-expression here. |
| 2163 var operand = new LiteralExpression(Value.fromInt(1, node.span), | 2080 var operand = new LiteralExpression(Value.fromInt(1, node.span), |
| 2164 node.span); | 2081 node.span); |
| 2165 | 2082 |
| 2166 var assignValue = _visitAssign(kind, node.self, operand, node, null); | 2083 var assignValue = _visitAssign(kind, node.self, operand, node, |
| 2084 ReturnKind.POST); |
| 2167 return new Value(assignValue.type, '(${assignValue.code})', | 2085 return new Value(assignValue.type, '(${assignValue.code})', |
| 2168 node.span); | 2086 node.span); |
| 2169 } | 2087 } |
| 2170 } | 2088 } |
| 2171 return value.unop(node.op.kind, this, node); | 2089 return value.unop(node.op.kind, this, node); |
| 2172 } | 2090 } |
| 2173 | 2091 |
| 2174 visitDeclaredIdentifier(DeclaredIdentifier node) { | 2092 visitDeclaredIdentifier(DeclaredIdentifier node) { |
| 2175 world.error('Expected expression', node.span); | 2093 world.error('Expected expression', node.span); |
| 2176 } | 2094 } |
| 2177 | 2095 |
| 2178 visitAwaitExpression(AwaitExpression node) { | 2096 visitAwaitExpression(AwaitExpression node) { |
| 2179 world.internalError( | 2097 world.internalError( |
| 2180 'Await expressions should have been eliminated before code generation', | 2098 'Await expressions should have been eliminated before code generation', |
| 2181 node.span); | 2099 node.span); |
| 2182 } | 2100 } |
| 2183 | 2101 |
| 2184 visitPostfixExpression(PostfixExpression node, [bool isVoid = false]) { | 2102 visitPostfixExpression(PostfixExpression node, [bool isVoid = false]) { |
| 2185 var value = visitValue(node.body); | 2103 var value = visitValue(node.body); |
| 2104 // TODO(jimhug): Move and validate this code with nullable ints. |
| 2186 if (value.type.isNum && !value.isFinal) { | 2105 if (value.type.isNum && !value.isFinal) { |
| 2187 return new Value(value.type, '${value.code}${node.op}', node.span); | 2106 return new Value(value.type, '${value.code}${node.op}', node.span); |
| 2188 } | 2107 } |
| 2189 | 2108 |
| 2190 // x++ is equivalent to (t = x, x = t + 1, t), where we capture all temps | 2109 // x++ is equivalent to (t = x, x = t + 1, t), where we capture all temps |
| 2191 // needed to evaluate x so we're not evaluating multiple times. Likewise, | 2110 // needed to evaluate x so we're not evaluating multiple times. Likewise, |
| 2192 // x-- is equivalent to (t = x, x = t - 1, t). | 2111 // x-- is equivalent to (t = x, x = t - 1, t). |
| 2193 var kind = (TokenKind.INCR == node.op.kind) ? TokenKind.ADD : TokenKind.SUB; | 2112 var kind = (TokenKind.INCR == node.op.kind) ? |
| 2113 TokenKind.ADD : TokenKind.SUB; |
| 2194 // TODO(jimhug): Shouldn't need a full-expression here. | 2114 // TODO(jimhug): Shouldn't need a full-expression here. |
| 2195 var operand = new LiteralExpression(Value.fromInt(1, node.span), | 2115 var operand = new LiteralExpression(Value.fromInt(1, node.span), |
| 2196 node.span); | 2116 node.span); |
| 2197 | 2117 var ret = _visitAssign(kind, node.body, operand, node, |
| 2198 // Use _visitAssign to do most of the work, but save the right side in a | 2118 isVoid ? ReturnKind.IGNORE : ReturnKind.PRE); |
| 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 } | |
| 2221 return ret; | 2119 return ret; |
| 2222 } | 2120 } |
| 2223 | 2121 |
| 2224 visitNewExpression(NewExpression node) { | 2122 visitNewExpression(NewExpression node) { |
| 2225 var typeRef = node.type; | 2123 var typeRef = node.type; |
| 2226 | 2124 |
| 2227 var constructorName = ''; | 2125 var constructorName = ''; |
| 2228 if (node.name != null) { | 2126 if (node.name != null) { |
| 2229 constructorName = node.name.name; | 2127 constructorName = node.name.name; |
| 2230 } | 2128 } |
| (...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2568 result.add(new VariableValue(world.varType, '\$$i', null)); | 2466 result.add(new VariableValue(world.varType, '\$$i', null)); |
| 2569 } | 2467 } |
| 2570 for (int i = bareCount; i < length; i++) { | 2468 for (int i = bareCount; i < length; i++) { |
| 2571 var name = getName(i); | 2469 var name = getName(i); |
| 2572 if (name == null) name = '\$$i'; | 2470 if (name == null) name = '\$$i'; |
| 2573 result.add(new VariableValue(world.varType, name, null)); | 2471 result.add(new VariableValue(world.varType, name, null)); |
| 2574 } | 2472 } |
| 2575 return new Arguments(nodes, result); | 2473 return new Arguments(nodes, result); |
| 2576 } | 2474 } |
| 2577 } | 2475 } |
| 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 |