OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library _js_helper; | 5 library _js_helper; |
6 | 6 |
7 import 'dart:collection'; | 7 import 'dart:collection'; |
8 import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS, | 8 |
9 JS, | 9 import 'dart:_foreign_helper' show |
10 JS_CALL_IN_ISOLATE, | 10 DART_CLOSURE_TO_JS, |
11 JS_CONST, | 11 JS, |
12 JS_CURRENT_ISOLATE, | 12 JS_CALL_IN_ISOLATE, |
13 JS_CURRENT_ISOLATE_CONTEXT, | 13 JS_CONST, |
14 JS_DART_OBJECT_CONSTRUCTOR, | 14 JS_CURRENT_ISOLATE, |
15 JS_FUNCTION_CLASS_NAME, | 15 JS_CURRENT_ISOLATE_CONTEXT, |
16 JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG, | 16 JS_DART_OBJECT_CONSTRUCTOR, |
17 JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG, | 17 JS_EFFECT, |
18 JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG, | 18 JS_FUNCTION_CLASS_NAME, |
19 JS_FUNCTION_TYPE_RETURN_TYPE_TAG, | 19 JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG, |
20 JS_FUNCTION_TYPE_TAG, | 20 JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG, |
21 JS_FUNCTION_TYPE_VOID_RETURN_TAG, | 21 JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG, |
22 JS_GET_NAME, | 22 JS_FUNCTION_TYPE_RETURN_TYPE_TAG, |
23 JS_HAS_EQUALS, | 23 JS_FUNCTION_TYPE_TAG, |
24 JS_IS_INDEXABLE_FIELD_NAME, | 24 JS_FUNCTION_TYPE_VOID_RETURN_TAG, |
25 JS_OBJECT_CLASS_NAME, | 25 JS_GET_NAME, |
26 JS_NULL_CLASS_NAME, | 26 JS_HAS_EQUALS, |
27 JS_OPERATOR_AS_PREFIX, | 27 JS_IS_INDEXABLE_FIELD_NAME, |
28 JS_OPERATOR_IS_PREFIX, | 28 JS_NULL_CLASS_NAME, |
29 JS_SIGNATURE_NAME, | 29 JS_OBJECT_CLASS_NAME, |
30 RAW_DART_FUNCTION_REF; | 30 JS_OPERATOR_AS_PREFIX, |
| 31 JS_OPERATOR_IS_PREFIX, |
| 32 JS_SIGNATURE_NAME, |
| 33 RAW_DART_FUNCTION_REF; |
| 34 |
31 import 'dart:_interceptors'; | 35 import 'dart:_interceptors'; |
32 import 'dart:_collection-dev' as _symbol_dev; | 36 import 'dart:_collection-dev' as _symbol_dev; |
33 import 'dart:_collection-dev' show MappedIterable; | 37 import 'dart:_collection-dev' show MappedIterable; |
34 | 38 |
35 import 'dart:_js_names' show | 39 import 'dart:_js_names' show |
36 extractKeys, | 40 extractKeys, |
37 mangledNames, | 41 mangledNames, |
38 unmangleGlobalNameIfPreservedAnyways; | 42 unmangleGlobalNameIfPreservedAnyways; |
39 | 43 |
40 part 'annotations.dart'; | 44 part 'annotations.dart'; |
(...skipping 1693 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1734 abstract class Closure implements Function { | 1738 abstract class Closure implements Function { |
1735 // TODO(ahe): These constants must be in sync with | 1739 // TODO(ahe): These constants must be in sync with |
1736 // reflection_data_parser.dart. | 1740 // reflection_data_parser.dart. |
1737 static const FUNCTION_INDEX = 0; | 1741 static const FUNCTION_INDEX = 0; |
1738 static const NAME_INDEX = 1; | 1742 static const NAME_INDEX = 1; |
1739 static const CALL_NAME_INDEX = 2; | 1743 static const CALL_NAME_INDEX = 2; |
1740 static const REQUIRED_PARAMETER_INDEX = 3; | 1744 static const REQUIRED_PARAMETER_INDEX = 3; |
1741 static const OPTIONAL_PARAMETER_INDEX = 4; | 1745 static const OPTIONAL_PARAMETER_INDEX = 4; |
1742 static const DEFAULT_ARGUMENTS_INDEX = 5; | 1746 static const DEFAULT_ARGUMENTS_INDEX = 5; |
1743 | 1747 |
| 1748 /** |
| 1749 * Global counter to prevent reusing function code objects. |
| 1750 * |
| 1751 * V8 will share the underlying function code objects when the same string is |
| 1752 * passed to "new Function". Shared function code objects can lead to |
| 1753 * sub-optimal performance due to polymorhism, and can be prevented by |
| 1754 * ensuring the strings are different. |
| 1755 */ |
| 1756 static int functionCounter = 0; |
| 1757 |
1744 Closure(); | 1758 Closure(); |
1745 | 1759 |
1746 /** | 1760 /** |
1747 * Creates a closure for use by implicit getters associated with a method. | 1761 * Creates a new closure class for use by implicit getters associated with a |
| 1762 * method. |
1748 * | 1763 * |
1749 * In other words, creates a tear-off closure. | 1764 * In other words, creates a tear-off closure. |
1750 * | 1765 * |
1751 * Called from [closureFromTearOff] as well as from reflection when tearing | 1766 * Called from [closureFromTearOff] as well as from reflection when tearing |
1752 * of a method via [:getField:]. | 1767 * of a method via [:getField:]. |
1753 * | 1768 * |
1754 * This method assumes that [functions] was created by the JavaScript function | 1769 * This method assumes that [functions] was created by the JavaScript function |
1755 * `addStubs` in `reflection_data_parser.dart`. That is, a list of JavaScript | 1770 * `addStubs` in `reflection_data_parser.dart`. That is, a list of JavaScript |
1756 * function objects with properties `$stubName` and `$callName`. | 1771 * function objects with properties `$stubName` and `$callName`. |
1757 * | 1772 * |
1758 * Further assumes that [reflectionInfo] is the end of the array created by | 1773 * Further assumes that [reflectionInfo] is the end of the array created by |
1759 * [dart2js.js_emitter.ContainerBuilder.addMemberMethod] starting with | 1774 * [dart2js.js_emitter.ContainerBuilder.addMemberMethod] starting with |
1760 * required parameter count. | 1775 * required parameter count. |
1761 * | 1776 * |
1762 * Caution: this function may be called when building constants. | 1777 * Caution: this function may be called when building constants. |
1763 * TODO(ahe): Don't call this function when building constants. | 1778 * TODO(ahe): Don't call this function when building constants. |
1764 */ | 1779 */ |
1765 factory Closure.fromTearOff(receiver, | 1780 static fromTearOff(receiver, |
1766 List functions, | 1781 List functions, |
1767 List reflectionInfo, | 1782 List reflectionInfo, |
1768 bool isStatic, | 1783 bool isStatic, |
1769 jsArguments, | 1784 jsArguments, |
1770 String propertyName) { | 1785 String propertyName) { |
| 1786 JS_EFFECT(() { |
| 1787 BoundClosure.receiverOf(JS('BoundClosure', 'void 0')); |
| 1788 BoundClosure.selfOf(JS('BoundClosure', 'void 0')); |
| 1789 }); |
1771 // TODO(ahe): All the place below using \$ should be rewritten to go | 1790 // TODO(ahe): All the place below using \$ should be rewritten to go |
1772 // through the namer. | 1791 // through the namer. |
1773 var function = JS('', '#[#]', functions, 0); | 1792 var function = JS('', '#[#]', functions, 0); |
1774 // TODO(ahe): Try fetching the property directly instead of using "in". | |
1775 if (isStatic && JS('bool', '"\$tearOff" in #', function)) { | |
1776 // The implicit closure of a static function is always the same. | |
1777 return JS('Closure', '#.\$tearOff', function); | |
1778 } | |
1779 | |
1780 String name = JS('String|Null', '#.\$stubName', function); | 1793 String name = JS('String|Null', '#.\$stubName', function); |
1781 String callName = JS('String|Null', '#.\$callName', function); | 1794 String callName = JS('String|Null', '#.\$callName', function); |
1782 | 1795 |
1783 JS('', r'#.$reflectionInfo = #', function, reflectionInfo); | 1796 JS('', '#.\$reflectionInfo = #', function, reflectionInfo); |
1784 ReflectionInfo info = new ReflectionInfo(function); | 1797 ReflectionInfo info = new ReflectionInfo(function); |
1785 | 1798 |
1786 var functionType = info.functionType; | 1799 var functionType = info.functionType; |
1787 | 1800 |
| 1801 // function tmp() {}; |
| 1802 // tmp.prototype = BC.prototype; |
| 1803 // var proto = new tmp; |
| 1804 // for each computed prototype property: |
| 1805 // proto[property] = ...; |
| 1806 // proto._init = BC; |
| 1807 // var dynClosureConstructor = |
| 1808 // new Function('self', 'target', 'receiver', 'name', |
| 1809 // 'this._init(self, target, receiver, name)'); |
| 1810 // proto.constructor = dynClosureConstructor; // Necessary? |
| 1811 // dynClosureConstructor.prototype = proto; |
| 1812 // return dynClosureConstructor; |
| 1813 |
| 1814 // We need to create a new subclass of either TearOffClosure or |
| 1815 // BoundClosure. For this, we need to create an object whose prototype is |
| 1816 // the prototype is either TearOffClosure.prototype or |
| 1817 // BoundClosure.prototype, respectively in pseudo JavaScript code. The |
| 1818 // simplest way to access the JavaScript construction function of a Dart |
| 1819 // class is to create an instance and access its constructor property. The |
| 1820 // newly created instance could in theory be used directly as the |
| 1821 // prototype, but it might include additional fields that we don't need. |
| 1822 // So we only use the new instance to access the constructor property and |
| 1823 // use Object.create to create the desired prototype. |
| 1824 var prototype = isStatic |
| 1825 // TODO(ahe): Safe to use Object.create? |
| 1826 ? JS('TearOffClosure', 'Object.create(#.constructor.prototype)', |
| 1827 new TearOffClosure()) |
| 1828 : JS('BoundClosure', 'Object.create(#.constructor.prototype)', |
| 1829 new BoundClosure(null, null, null, null)); |
| 1830 |
| 1831 JS('', '#.\$initialize = #', prototype, JS('', '#.constructor', prototype)); |
| 1832 var constructor = isStatic |
| 1833 ? JS('', 'function(){this.\$initialize()}') |
| 1834 : isCsp |
| 1835 ? JS('', 'function(a,b,c,d) {this.\$initialize(a,b,c,d)}') |
| 1836 : JS('', |
| 1837 'new Function("a","b","c","d",' |
| 1838 '"this.\$initialize(a,b,c,d);"+#)', |
| 1839 functionCounter++); |
| 1840 |
| 1841 // TODO(ahe): Is it necessary to set the constructor property? |
| 1842 JS('', '#.constructor = #', prototype, constructor); |
| 1843 |
| 1844 JS('', '#.prototype = #', constructor, prototype); |
| 1845 |
1788 // Create a closure and "monkey" patch it with call stubs. | 1846 // Create a closure and "monkey" patch it with call stubs. |
1789 Closure closure; | |
1790 var trampoline = function; | 1847 var trampoline = function; |
| 1848 var isIntercepted = false; |
1791 if (!isStatic) { | 1849 if (!isStatic) { |
1792 if (JS('bool', '#.length == 1', jsArguments)) { | 1850 if (JS('bool', '#.length == 1', jsArguments)) { |
1793 // Intercepted call. | 1851 // Intercepted call. |
1794 var argument = JS('', '#[0]', jsArguments); | 1852 isIntercepted = true; |
1795 trampoline = forwardInterceptedCallTo(argument, receiver, function); | |
1796 closure = new BoundClosure(receiver, function, argument, name); | |
1797 } else { | |
1798 trampoline = forwardTo(receiver, function); | |
1799 closure = new BoundClosure(receiver, function, null, name); | |
1800 } | 1853 } |
| 1854 trampoline = forwardCallTo(function, isIntercepted); |
1801 } else { | 1855 } else { |
1802 closure = new TearOffClosure(); | 1856 JS('', '#.\$name = #', prototype, propertyName); |
1803 JS('', '#.\$tearOff = #', function, closure); | |
1804 JS('', r'#.$name = #', closure, propertyName); | |
1805 } | 1857 } |
1806 | 1858 |
1807 var signatureFunction; | 1859 var signatureFunction; |
1808 if (JS('bool', 'typeof # == "number"', functionType)) { | 1860 if (JS('bool', 'typeof # == "number"', functionType)) { |
1809 signatureFunction = | 1861 signatureFunction = |
1810 JS('', '(function(s){return function(){return init.metadata[s]}})(#)', | 1862 JS('', '(function(s){return function(){return init.metadata[s]}})(#)', |
1811 functionType); | 1863 functionType); |
1812 } else if (!isStatic | 1864 } else if (!isStatic |
1813 && JS('bool', 'typeof # == "function"', functionType)) { | 1865 && JS('bool', 'typeof # == "function"', functionType)) { |
1814 signatureFunction = functionType; | 1866 var getReceiver = isIntercepted |
1815 JS('', r'#.$receiver = #', closure, receiver); | 1867 ? RAW_DART_FUNCTION_REF(BoundClosure.receiverOf) |
| 1868 : RAW_DART_FUNCTION_REF(BoundClosure.selfOf); |
| 1869 signatureFunction = JS( |
| 1870 '', |
| 1871 'function(f,r){' |
| 1872 'return function(){' |
| 1873 'return f.apply({\$receiver:r(this)},arguments)' |
| 1874 '}' |
| 1875 '}(#,#)', functionType, getReceiver); |
1816 } else { | 1876 } else { |
1817 throw 'Error in reflectionInfo.'; | 1877 throw 'Error in reflectionInfo.'; |
1818 } | 1878 } |
1819 | 1879 |
1820 JS('', '#.\$signature = #', closure, signatureFunction); | 1880 JS('', '#.\$signature = #', prototype, signatureFunction); |
1821 | 1881 |
1822 JS('', '#[#] = #', closure, callName, trampoline); | 1882 JS('', '#[#] = #', prototype, callName, trampoline); |
1823 for (int i = 1; i < functions.length; i++) { | 1883 for (int i = 1; i < functions.length; i++) { |
1824 var stub = functions[i]; | 1884 var stub = functions[i]; |
1825 var stubCallName = JS('String|Null', '#.\$callName', stub); | 1885 var stubCallName = JS('String|Null', '#.\$callName', stub); |
1826 // TODO(ahe): Support interceptors here. | 1886 if (stubCallName != null) { |
1827 JS('', '#[#] = #', closure, stubCallName, | 1887 JS('', '#[#] = #', prototype, stubCallName, |
1828 isStatic ? stub : forwardTo(receiver, stub)); | 1888 isStatic ? stub : forwardCallTo(stub, isIntercepted)); |
1829 } | 1889 } |
1830 | 1890 } |
1831 JS('', '#["call*"] = #', closure, function); | 1891 |
1832 | 1892 JS('', '#["call*"] = #', prototype, function); |
1833 return closure; | 1893 |
1834 } | 1894 return constructor; |
1835 | 1895 } |
1836 static forwardTo(receiver, function) { | 1896 |
1837 return JS( | 1897 static cspForwardCall(int arity, function) { |
1838 '', | 1898 var getSelf = RAW_DART_FUNCTION_REF(BoundClosure.selfOf); |
1839 'function(r,f){return function(){return f.apply(r,arguments)}}(#,#)', | 1899 switch (arity) { |
1840 receiver, function); | 1900 case 0: |
1841 } | 1901 return JS( |
1842 | 1902 '', |
1843 static forwardInterceptedCallTo(self, interceptor, function) { | 1903 'function(F,S){' |
1844 return JS( | 1904 'return function(){' |
1845 '', | 1905 'return F.call(S(this))' |
1846 'function(i,s,f){return function(){' | 1906 '}' |
1847 'return f.call.bind(f,i,s).apply(i,arguments)}}(#,#,#)', | 1907 '}(#,#)', function, getSelf); |
1848 interceptor, self, function); | 1908 case 1: |
| 1909 return JS( |
| 1910 '', |
| 1911 'function(F,S){' |
| 1912 'return function(a){' |
| 1913 'return F.call(S(this),a)' |
| 1914 '}' |
| 1915 '}(#,#)', function, getSelf); |
| 1916 case 2: |
| 1917 return JS( |
| 1918 '', |
| 1919 'function(F,S){' |
| 1920 'return function(a,b){' |
| 1921 'return F.call(S(this),a,b)' |
| 1922 '}' |
| 1923 '}(#,#)', function, getSelf); |
| 1924 case 3: |
| 1925 return JS( |
| 1926 '', |
| 1927 'function(F,S){' |
| 1928 'return function(a,b,c){' |
| 1929 'return F.call(S(this),a,b,c)' |
| 1930 '}' |
| 1931 '}(#,#)', function, getSelf); |
| 1932 case 4: |
| 1933 return JS( |
| 1934 '', |
| 1935 'function(F,S){' |
| 1936 'return function(a,b,c,d){' |
| 1937 'return F.call(S(this),a,b,c,d)' |
| 1938 '}' |
| 1939 '}(#,#)', function, getSelf); |
| 1940 case 5: |
| 1941 return JS( |
| 1942 '', |
| 1943 'function(F,S){' |
| 1944 'return function(a,b,c,d,e){' |
| 1945 'return F.call(S(this),a,b,c,d,e)' |
| 1946 '}' |
| 1947 '}(#,#)', function, getSelf); |
| 1948 default: |
| 1949 return JS( |
| 1950 '', |
| 1951 'function(f,s){' |
| 1952 'return function(){' |
| 1953 'return f.apply(s(this),arguments)' |
| 1954 '}' |
| 1955 '}(#,#)', function, getSelf); |
| 1956 } |
| 1957 } |
| 1958 |
| 1959 static bool get isCsp => JS('bool', 'typeof dart_precompiled == "function"'); |
| 1960 |
| 1961 static forwardCallTo(function, bool isIntercepted) { |
| 1962 if (isIntercepted) return forwardInterceptedCallTo(function); |
| 1963 int arity = JS('int', '#.length', function); |
| 1964 if (isCsp) { |
| 1965 return cspForwardCall(arity, function); |
| 1966 } else if (arity == 0) { |
| 1967 return JS( |
| 1968 '', |
| 1969 '(new Function("F",#))(#)', |
| 1970 'return function(){' |
| 1971 'return F.call(this.${BoundClosure.selfFieldName()});${functionCount
er++}' |
| 1972 '}', |
| 1973 function); |
| 1974 } else if (1 <= arity && arity < 27) { |
| 1975 String arguments = JS( |
| 1976 'String', |
| 1977 '"abcdefghijklmnopqrstuvwxyz".split("").splice(0,#).join(",")', |
| 1978 arity); |
| 1979 return JS( |
| 1980 '', |
| 1981 '(new Function("F",#))(#)', |
| 1982 'return function($arguments){' |
| 1983 'return F.call(this.${BoundClosure.selfFieldName()},$arguments);' |
| 1984 '${functionCounter++}' |
| 1985 '}', |
| 1986 function); |
| 1987 } else { |
| 1988 return cspForwardCall(arity, function); |
| 1989 } |
| 1990 } |
| 1991 |
| 1992 static cspForwardInterceptedCall(int arity, String name, function) { |
| 1993 var getSelf = RAW_DART_FUNCTION_REF(BoundClosure.selfOf); |
| 1994 var getReceiver = RAW_DART_FUNCTION_REF(BoundClosure.receiverOf); |
| 1995 switch (arity) { |
| 1996 case 0: |
| 1997 // Intercepted functions always takes at least one argument (the |
| 1998 // receiver). |
| 1999 throw new RuntimeError('Intercepted function with no arguments.'); |
| 2000 case 1: |
| 2001 return JS( |
| 2002 '', |
| 2003 'function(n,s,r){' |
| 2004 'return function(){' |
| 2005 'return s(this)[n](r(this))' |
| 2006 '}' |
| 2007 '}(#,#,#)', name, getSelf, getReceiver); |
| 2008 case 2: |
| 2009 return JS( |
| 2010 '', |
| 2011 'function(n,s,r){' |
| 2012 'return function(a){' |
| 2013 'return s(this)[n](r(this),a)' |
| 2014 '}' |
| 2015 '}(#,#,#)', name, getSelf, getReceiver); |
| 2016 case 3: |
| 2017 return JS( |
| 2018 '', |
| 2019 'function(n,s,r){' |
| 2020 'return function(a,b){' |
| 2021 'return s(this)[n](r(this),a,b)' |
| 2022 '}' |
| 2023 '}(#,#,#)', name, getSelf, getReceiver); |
| 2024 case 4: |
| 2025 return JS( |
| 2026 '', |
| 2027 'function(n,s,r){' |
| 2028 'return function(a,b,c){' |
| 2029 'return s(this)[n](r(this),a,b,c)' |
| 2030 '}' |
| 2031 '}(#,#,#)', name, getSelf, getReceiver); |
| 2032 case 5: |
| 2033 return JS( |
| 2034 '', |
| 2035 'function(n,s,r){' |
| 2036 'return function(a,b,c,d){' |
| 2037 'return s(this)[n](r(this),a,b,c,d)' |
| 2038 '}' |
| 2039 '}(#,#,#)', name, getSelf, getReceiver); |
| 2040 case 6: |
| 2041 return JS( |
| 2042 '', |
| 2043 'function(n,s,r){' |
| 2044 'return function(a,b,c,d,e){' |
| 2045 'return s(this)[n](r(this),a,b,c,d,e)' |
| 2046 '}' |
| 2047 '}(#,#,#)', name, getSelf, getReceiver); |
| 2048 default: |
| 2049 return JS( |
| 2050 '', |
| 2051 'function(f,s,r,a){' |
| 2052 'return function(){' |
| 2053 'a=[r(this)];' |
| 2054 'Array.prototype.push.apply(a,arguments);' |
| 2055 'return f.apply(s(this),a)' |
| 2056 '}' |
| 2057 '}(#,#,#)', function, getSelf, getReceiver); |
| 2058 } |
| 2059 } |
| 2060 |
| 2061 static forwardInterceptedCallTo(function) { |
| 2062 String stubName = JS('String|Null', '#.\$stubName', function); |
| 2063 int arity = JS('int', '#.length', function); |
| 2064 bool isCsp = JS('bool', 'typeof dart_precompiled == "function"'); |
| 2065 if (isCsp) { |
| 2066 return cspForwardInterceptedCall(arity, stubName, function); |
| 2067 } else if (arity == 1) { |
| 2068 return JS('', 'new Function(#)', |
| 2069 'return this.${BoundClosure.selfFieldName()}.$stubName(' |
| 2070 'this.${BoundClosure.receiverFieldName()});' |
| 2071 '${functionCounter++}'); |
| 2072 } else if (1 < arity && arity < 28) { |
| 2073 String arguments = JS( |
| 2074 'String', |
| 2075 '"abcdefghijklmnopqrstuvwxyz".split("").splice(0,#).join(",")', |
| 2076 arity - 1); |
| 2077 return JS( |
| 2078 '', |
| 2079 '(new Function(#))()', |
| 2080 'return function($arguments){' |
| 2081 'return this.${BoundClosure.selfFieldName()}.$stubName(' |
| 2082 'this.${BoundClosure.receiverFieldName()},$arguments);' |
| 2083 '${functionCounter++}' |
| 2084 '}'); |
| 2085 } else { |
| 2086 return cspForwardInterceptedCall(arity, stubName, function); |
| 2087 } |
1849 } | 2088 } |
1850 | 2089 |
1851 String toString() => "Closure"; | 2090 String toString() => "Closure"; |
1852 } | 2091 } |
1853 | 2092 |
1854 /// Called from implicit method getter (aka tear-off). | 2093 /// Called from implicit method getter (aka tear-off). |
1855 Closure closureFromTearOff(receiver, | 2094 closureFromTearOff(receiver, |
1856 functions, | 2095 functions, |
1857 reflectionInfo, | 2096 reflectionInfo, |
1858 isStatic, | 2097 isStatic, |
1859 jsArguments, | 2098 jsArguments, |
1860 name) { | 2099 name) { |
1861 return new Closure.fromTearOff( | 2100 return Closure.fromTearOff( |
1862 receiver, | 2101 receiver, |
1863 JSArray.markFixedList(functions), | 2102 JSArray.markFixedList(functions), |
1864 JSArray.markFixedList(reflectionInfo), | 2103 JSArray.markFixedList(reflectionInfo), |
1865 JS('bool', '!!#', isStatic), | 2104 JS('bool', '!!#', isStatic), |
1866 jsArguments, | 2105 jsArguments, |
1867 JS('String', '#', name)); | 2106 JS('String', '#', name)); |
1868 } | 2107 } |
1869 | 2108 |
1870 /// Represents an implicit closure of a function. | 2109 /// Represents an implicit closure of a function. |
1871 class TearOffClosure extends Closure { | 2110 class TearOffClosure extends Closure { |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1910 // use the hashCode method we define for those primitive types. | 2149 // use the hashCode method we define for those primitive types. |
1911 receiverHashCode = _receiver.hashCode; | 2150 receiverHashCode = _receiver.hashCode; |
1912 } else { | 2151 } else { |
1913 // A bound closure on an intercepted native class, just use the | 2152 // A bound closure on an intercepted native class, just use the |
1914 // identity hash code. | 2153 // identity hash code. |
1915 receiverHashCode = Primitives.objectHashCode(_receiver); | 2154 receiverHashCode = Primitives.objectHashCode(_receiver); |
1916 } | 2155 } |
1917 return receiverHashCode ^ Primitives.objectHashCode(_target); | 2156 return receiverHashCode ^ Primitives.objectHashCode(_target); |
1918 } | 2157 } |
1919 | 2158 |
| 2159 @NoInline |
1920 static selfOf(BoundClosure closure) => closure._self; | 2160 static selfOf(BoundClosure closure) => closure._self; |
1921 | 2161 |
1922 static targetOf(BoundClosure closure) => closure._target; | 2162 static targetOf(BoundClosure closure) => closure._target; |
1923 | 2163 |
| 2164 @NoInline |
1924 static receiverOf(BoundClosure closure) => closure._receiver; | 2165 static receiverOf(BoundClosure closure) => closure._receiver; |
1925 | 2166 |
1926 static nameOf(BoundClosure closure) => closure._name; | 2167 static nameOf(BoundClosure closure) => closure._name; |
| 2168 |
| 2169 static String selfFieldNameCache; |
| 2170 |
| 2171 static String selfFieldName() { |
| 2172 if (selfFieldNameCache == null) { |
| 2173 selfFieldNameCache = computeFieldNamed('self'); |
| 2174 } |
| 2175 return selfFieldNameCache; |
| 2176 } |
| 2177 |
| 2178 static String receiverFieldNameCache; |
| 2179 |
| 2180 static String receiverFieldName() { |
| 2181 if (receiverFieldNameCache == null) { |
| 2182 receiverFieldNameCache = computeFieldNamed('receiver'); |
| 2183 } |
| 2184 return receiverFieldNameCache; |
| 2185 } |
| 2186 |
| 2187 @NoInline() @NoSideEffects() |
| 2188 static String computeFieldNamed(String fieldName) { |
| 2189 var template = new BoundClosure('self', 'target', 'receiver', 'name'); |
| 2190 var names = JSArray.markFixedList( |
| 2191 JS('', 'Object.getOwnPropertyNames(#)', template)); |
| 2192 for (int i = 0; i < names.length; i++) { |
| 2193 var name = names[i]; |
| 2194 if (JS('bool', '#[#] === #', template, name, fieldName)) { |
| 2195 return JS('String', '#', name); |
| 2196 } |
| 2197 } |
| 2198 } |
1927 } | 2199 } |
1928 | 2200 |
1929 bool jsHasOwnProperty(var jsObject, String property) { | 2201 bool jsHasOwnProperty(var jsObject, String property) { |
1930 return JS('bool', r'#.hasOwnProperty(#)', jsObject, property); | 2202 return JS('bool', r'#.hasOwnProperty(#)', jsObject, property); |
1931 } | 2203 } |
1932 | 2204 |
1933 jsPropertyAccess(var jsObject, String property) { | 2205 jsPropertyAccess(var jsObject, String property) { |
1934 return JS('var', r'#[#]', jsObject, property); | 2206 return JS('var', r'#[#]', jsObject, property); |
1935 } | 2207 } |
1936 | 2208 |
(...skipping 805 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2742 | 3014 |
2743 // TODO(ahe): Remove this class and call noSuchMethod instead. | 3015 // TODO(ahe): Remove this class and call noSuchMethod instead. |
2744 class UnimplementedNoSuchMethodError extends Error | 3016 class UnimplementedNoSuchMethodError extends Error |
2745 implements NoSuchMethodError { | 3017 implements NoSuchMethodError { |
2746 final String _message; | 3018 final String _message; |
2747 | 3019 |
2748 UnimplementedNoSuchMethodError(this._message); | 3020 UnimplementedNoSuchMethodError(this._message); |
2749 | 3021 |
2750 String toString() => "Unsupported operation: $_message"; | 3022 String toString() => "Unsupported operation: $_message"; |
2751 } | 3023 } |
OLD | NEW |