OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 part of dart._runtime; |
5 library dart._utils; | |
6 | |
7 import 'dart:_foreign_helper' show JS, JSExportName; | |
8 | 5 |
9 /// This library defines a set of general javascript utilities for us | 6 /// This library defines a set of general javascript utilities for us |
10 /// by the Dart runtime. | 7 /// by the Dart runtime. |
11 // TODO(ochafik): Rewrite some of these in Dart when possible. | 8 // TODO(ochafik): Rewrite some of these in Dart when possible. |
12 | 9 |
13 final defineProperty = JS('', 'Object.defineProperty'); | 10 final defineProperty = JS('', 'Object.defineProperty'); |
14 final getOwnPropertyDescriptor = JS('', 'Object.getOwnPropertyDescriptor'); | 11 final getOwnPropertyDescriptor = JS('', 'Object.getOwnPropertyDescriptor'); |
15 final getOwnPropertyNames = JS('', 'Object.getOwnPropertyNames'); | 12 final getOwnPropertyNames = JS('', 'Object.getOwnPropertyNames'); |
16 final getOwnPropertySymbols = JS('', 'Object.getOwnPropertySymbols'); | 13 final getOwnPropertySymbols = JS('', 'Object.getOwnPropertySymbols'); |
17 | 14 |
18 final hasOwnProperty = JS('', 'Object.prototype.hasOwnProperty'); | 15 final hasOwnProperty = JS('', 'Object.prototype.hasOwnProperty'); |
19 | 16 |
20 // TODO(ochafik): Add ES6 class syntax support to JS intrinsics to avoid this. | 17 // TODO(ochafik): Add ES6 class syntax support to JS intrinsics to avoid this. |
21 final StrongModeError = JS('', '''(function() { | 18 final StrongModeError = JS('', '''(function() { |
22 function StrongModeError(message) { | 19 function StrongModeError(message) { |
23 Error.call(this); | 20 Error.call(this); |
24 this.message = message; | 21 this.message = message; |
25 }; | 22 }; |
26 Object.setPrototypeOf(StrongModeError.prototype, Error.prototype); | 23 Object.setPrototypeOf(StrongModeError.prototype, Error.prototype); |
27 return StrongModeError; | 24 return StrongModeError; |
28 })()'''); | 25 })()'''); |
29 | 26 |
30 /// This error indicates a strong mode specific failure. | 27 /// This error indicates a strong mode specific failure. |
31 void throwStrongModeError(String message) => JS('', '''(() => { | 28 void throwStrongModeError(String message) => JS('', '''(() => { |
32 throw new StrongModeError($message); | 29 throw new $StrongModeError($message); |
33 })()'''); | 30 })()'''); |
34 | 31 |
35 /// This error indicates a bug in the runtime or the compiler. | 32 /// This error indicates a bug in the runtime or the compiler. |
36 void throwInternalError(String message) => JS('', '''(() => { | 33 void throwInternalError(String message) => JS('', '''(() => { |
37 throw Error($message); | 34 throw Error($message); |
38 })()'''); | 35 })()'''); |
39 | 36 |
40 // TODO(ochafik): Re-introduce a @JS annotation in the SDK (same as package:js) | |
41 // so that this is named 'assert' in JavaScript. | |
42 @JSExportName('assert') | |
43 void assert_(bool condition) => JS('', '''(() => { | |
44 if (!condition) throwInternalError("The compiler is broken: failed assert"); | |
45 })()'''); | |
46 | |
47 getOwnNamesAndSymbols(obj) => JS('', '''(() => { | 37 getOwnNamesAndSymbols(obj) => JS('', '''(() => { |
48 return getOwnPropertyNames(obj).concat(getOwnPropertySymbols(obj)); | 38 return $getOwnPropertyNames($obj).concat($getOwnPropertySymbols($obj)); |
49 })()'''); | 39 })()'''); |
50 | 40 |
51 safeGetOwnProperty(obj, String name) => JS('', '''(() => { | 41 safeGetOwnProperty(obj, String name) => JS('', '''(() => { |
52 let desc = getOwnPropertyDescriptor($obj, $name); | 42 let desc = $getOwnPropertyDescriptor($obj, $name); |
53 if (desc) return desc.value; | 43 if (desc) return desc.value; |
54 })()'''); | 44 })()'''); |
55 | 45 |
56 /// Defines a lazy property. | 46 /// Defines a lazy property. |
57 /// After initial get or set, it will replace itself with a value property. | 47 /// After initial get or set, it will replace itself with a value property. |
58 // TODO(jmesserly): reusing descriptor objects has been shown to improve | 48 // TODO(jmesserly): reusing descriptor objects has been shown to improve |
59 // performance in other projects (e.g. webcomponents.js ShadowDOM polyfill). | 49 // performance in other projects (e.g. webcomponents.js ShadowDOM polyfill). |
60 defineLazyProperty(to, name, desc) => JS('', '''(() => { | 50 defineLazyProperty(to, name, desc) => JS('', '''(() => { |
61 let init = $desc.get; | 51 let init = $desc.get; |
62 let value = null; | 52 let value = null; |
63 | 53 |
64 function lazySetter(x) { | 54 function lazySetter(x) { |
65 init = null; | 55 init = null; |
66 value = x; | 56 value = x; |
67 } | 57 } |
68 function circularInitError() { | 58 function circularInitError() { |
69 throwInternalError('circular initialization for field ' + $name); | 59 $throwInternalError('circular initialization for field ' + $name); |
70 } | 60 } |
71 function lazyGetter() { | 61 function lazyGetter() { |
72 if (init == null) return value; | 62 if (init == null) return value; |
73 | 63 |
74 // Compute and store the value, guarding against reentry. | 64 // Compute and store the value, guarding against reentry. |
75 let f = init; | 65 let f = init; |
76 init = circularInitError; | 66 init = circularInitError; |
77 lazySetter(f()); | 67 lazySetter(f()); |
78 return value; | 68 return value; |
79 } | 69 } |
80 $desc.get = lazyGetter; | 70 $desc.get = lazyGetter; |
81 $desc.configurable = true; | 71 $desc.configurable = true; |
82 if ($desc.set) $desc.set = lazySetter; | 72 if ($desc.set) $desc.set = lazySetter; |
83 return defineProperty($to, $name, $desc); | 73 return $defineProperty($to, $name, $desc); |
84 })()'''); | 74 })()'''); |
85 | 75 |
86 void defineLazy(to, from) => JS('', '''(() => { | 76 void defineLazy(to, from) => JS('', '''(() => { |
87 for (let name of getOwnNamesAndSymbols($from)) { | 77 for (let name of $getOwnNamesAndSymbols($from)) { |
88 defineLazyProperty($to, name, getOwnPropertyDescriptor($from, name)); | 78 $defineLazyProperty($to, name, $getOwnPropertyDescriptor($from, name)); |
89 } | 79 } |
90 })()'''); | 80 })()'''); |
91 | 81 |
92 defineMemoizedGetter(obj, String name, getter) => JS('', '''(() => { | 82 defineMemoizedGetter(obj, String name, getter) => JS('', '''(() => { |
93 return defineLazyProperty($obj, $name, {get: $getter}); | 83 return $defineLazyProperty($obj, $name, {get: $getter}); |
94 })()'''); | 84 })()'''); |
95 | 85 |
96 copyTheseProperties(to, from, names) => JS('', '''(() => { | 86 copyTheseProperties(to, from, names) => JS('', '''(() => { |
97 for (let name of $names) { | 87 for (let name of $names) { |
98 var desc = $getOwnPropertyDescriptor($from, name); | 88 var desc = $getOwnPropertyDescriptor($from, name); |
99 if (desc != void 0) { | 89 if (desc != void 0) { |
100 defineProperty($to, name, desc); | 90 $defineProperty($to, name, desc); |
101 } else { | 91 } else { |
102 defineLazyProperty($to, name, () => $from[name]); | 92 $defineLazyProperty($to, name, () => $from[name]); |
103 } | 93 } |
104 } | 94 } |
105 return $to; | 95 return $to; |
106 })()'''); | 96 })()'''); |
107 | 97 |
108 /// Copy properties from source to destination object. | 98 /// Copy properties from source to destination object. |
109 /// This operation is commonly called `mixin` in JS. | 99 /// This operation is commonly called `mixin` in JS. |
110 copyProperties(to, from) => JS('', '''(() => { | 100 copyProperties(to, from) => JS('', '''(() => { |
111 return $copyTheseProperties($to, $from, $getOwnNamesAndSymbols(from)); | 101 return $copyTheseProperties($to, $from, $getOwnNamesAndSymbols($from)); |
112 })()'''); | 102 })()'''); |
113 | 103 |
114 /// Exports from one Dart module to another. | 104 /// Exports from one Dart module to another. |
115 // TODO(ochafik): Re-introduce a @JS annotation in the SDK (same as package:js) | 105 @JSExportName('export') |
116 // so that this is named 'export' in JavaScript. | |
117 export_(to, from, show, hide) => JS('', '''(() => { | 106 export_(to, from, show, hide) => JS('', '''(() => { |
118 if ($show == void 0 || $show.length == 0) { | 107 if ($show == void 0 || $show.length == 0) { |
119 $show = $getOwnNamesAndSymbols($from); | 108 $show = $getOwnNamesAndSymbols($from); |
120 } | 109 } |
121 if ($hide != void 0) { | 110 if ($hide != void 0) { |
122 var hideMap = new Set($hide); | 111 var hideMap = new Set($hide); |
123 $show = $show.filter((k) => !hideMap.has(k)); | 112 $show = $show.filter((k) => !hideMap.has(k)); |
124 } | 113 } |
125 return $copyTheseProperties($to, $from, $show); | 114 return $copyTheseProperties($to, $from, $show); |
126 })()'''); | 115 })()'''); |
OLD | NEW |