Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 part of native; | 5 part of native; |
| 6 | 6 |
| 7 /// This class is a temporary work-around until we get a more powerful DartType. | 7 /// This class is a temporary work-around until we get a more powerful DartType. |
| 8 class SpecialType { | 8 class SpecialType { |
| 9 final String name; | 9 final String name; |
| 10 const SpecialType._(this.name); | 10 const SpecialType._(this.name); |
| 11 | 11 |
| 12 /// The type Object, but no subtypes: | 12 /// The type Object, but no subtypes: |
| 13 static const JsObject = const SpecialType._('=Object'); | 13 static const JsObject = const SpecialType._('=Object'); |
| 14 | 14 |
| 15 int get hashCode => name.hashCode; | 15 int get hashCode => name.hashCode; |
| 16 } | 16 } |
| 17 | 17 |
| 18 /// Description of the exception behaviour of native code. | |
| 19 /// | |
| 20 /// TODO(sra): Replace with something that better supports specialization on | |
| 21 /// first argument properties. | |
| 22 class NativeThrowBehavior { | |
| 23 static const NativeThrowBehavior NEVER = const NativeThrowBehavior._(0); | |
| 24 static const NativeThrowBehavior MAY_THROW_ONLY_ON_FIRST_ARGUMENT_ACCESS = | |
| 25 const NativeThrowBehavior._(1); | |
| 26 static const NativeThrowBehavior MAY = const NativeThrowBehavior._(2); | |
| 27 static const NativeThrowBehavior MUST = const NativeThrowBehavior._(3); | |
| 28 | |
| 29 final int _bits; | |
| 30 const NativeThrowBehavior._(this._bits); | |
| 31 | |
| 32 String toString() { | |
| 33 if (this == NEVER) return 'never'; | |
| 34 if (this == MAY) return 'may'; | |
| 35 if (this == MAY_THROW_ONLY_ON_FIRST_ARGUMENT_ACCESS) return 'null(1)'; | |
| 36 if (this == MUST) return 'must'; | |
| 37 return 'NativeThrowBehavior($_bits)'; | |
| 38 } | |
| 39 } | |
| 40 | |
| 18 /** | 41 /** |
| 19 * A summary of the behavior of a native element. | 42 * A summary of the behavior of a native element. |
| 20 * | 43 * |
| 21 * Native code can return values of one type and cause native subtypes of | 44 * Native code can return values of one type and cause native subtypes of |
| 22 * another type to be instantiated. By default, we compute both from the | 45 * another type to be instantiated. By default, we compute both from the |
| 23 * declared type. | 46 * declared type. |
| 24 * | 47 * |
| 25 * A field might yield any native type that 'is' the field type. | 48 * A field might yield any native type that 'is' the field type. |
| 26 * | 49 * |
| 27 * A method might create and return instances of native subclasses of its | 50 * A method might create and return instances of native subclasses of its |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 52 | 75 |
| 53 /// [DartType]s or [SpecialType]s instantiated by the native element. | 76 /// [DartType]s or [SpecialType]s instantiated by the native element. |
| 54 final List typesInstantiated = []; | 77 final List typesInstantiated = []; |
| 55 | 78 |
| 56 // If this behavior is for a JS expression, [codeTemplate] contains the | 79 // If this behavior is for a JS expression, [codeTemplate] contains the |
| 57 // parsed tree. | 80 // parsed tree. |
| 58 js.Template codeTemplate; | 81 js.Template codeTemplate; |
| 59 | 82 |
| 60 final SideEffects sideEffects = new SideEffects.empty(); | 83 final SideEffects sideEffects = new SideEffects.empty(); |
| 61 | 84 |
| 85 NativeThrowBehavior throwBehavior = NativeThrowBehavior.MAY; | |
| 86 | |
| 87 bool isAllocation = false; | |
| 88 bool useGvn = false; | |
| 89 | |
| 62 String toString() { | 90 String toString() { |
| 63 return 'NativeBehavior(returns: ${typesReturned}, ' | 91 return 'NativeBehavior(' |
| 92 'returns: ${typesReturned}, ' | |
| 64 'creates: ${typesInstantiated}, ' | 93 'creates: ${typesInstantiated}, ' |
| 65 'sideEffects: ${sideEffects})'; | 94 'sideEffects: ${sideEffects}, ' |
| 95 'throws: ${throwBehavior}' | |
| 96 '${isAllocation ? ", isAllocation" : ""}' | |
| 97 '${useGvn ? ", useGvn" : ""}' | |
| 98 ')'; | |
| 66 } | 99 } |
| 67 | 100 |
| 68 | 101 |
| 69 /// Processes the type specification string of a call to JS and stores the | 102 /// Processes the type specification string of a call to JS and stores the |
| 70 /// result in the [typesReturned] and [typesInstantiated]. It furthermore | 103 /// result in the [typesReturned] and [typesInstantiated]. It furthermore |
| 71 /// computes the side effects, and, if given, invokes [setSideEffects] with | 104 /// computes the side effects, and, if given, invokes [setSideEffects] with |
| 72 /// the computed effects. If no side effects are encoded in the [specString] | 105 /// the computed effects. If no side effects are encoded in the [specString] |
| 73 /// the [setSideEffects] method is not invoked. | 106 /// the [setSideEffects] method is not invoked. |
| 74 /// | 107 /// |
| 75 /// Two forms of the string is supported: | 108 /// Two forms of the string is supported: |
| 109 /// | |
| 76 /// 1) A single type string of the form 'void', '', 'var' or 'T1|...|Tn' | 110 /// 1) A single type string of the form 'void', '', 'var' or 'T1|...|Tn' |
| 77 /// which defines the types returned and for the later form also created by | 111 /// which defines the types returned and for the later form also created by |
| 78 /// the call to JS. | 112 /// the call to JS. |
| 79 /// 2) A sequence of the form | |
| 80 /// '<type-tag>:<type-string>;<effect-tag>:<effect-string>' | |
| 81 /// where <type-tag> is either 'returns' or 'creates' and where | |
| 82 /// <type-string> is a type string like in 1). The type string marked by | |
| 83 /// 'returns' defines the types returned and 'creates' defines the types | |
| 84 /// created by the call to JS. | |
| 85 /// | 113 /// |
| 86 /// The <effect-tag> is either 'effects' or 'depends' and | 114 /// 2) A sequence of <tag>:<value> pairs of the following kinds |
| 87 /// <effect-string> is either 'all', 'none' or a comma-separated list of | 115 /// |
| 88 /// 'no-index', 'no-instance', 'no-static'. | 116 /// <type-tag>:<type-string> |
| 117 /// <effect-tag>:<effect-string> | |
| 118 /// throws:<throws-string> | |
| 119 /// gvn:<gvn-string> | |
| 120 /// new:<new-string> | |
| 121 /// | |
| 122 /// A <type-tag> is either 'returns' or 'creates' and <type-string> is a | |
| 123 /// type string like in 1). The type string marked by 'returns' defines the | |
| 124 /// types returned and 'creates' defines the types created by the call to | |
| 125 /// JS. | |
| 126 /// | |
| 127 /// An <effect-tag> is either 'effects' or 'depends' and <effect-string> is | |
| 128 /// either 'all', 'none' or a comma-separated list of 'no-index', | |
| 129 /// 'no-instance', 'no-static'. | |
| 89 /// | 130 /// |
| 90 /// The flag 'all' indicates that the call affects/depends on every | 131 /// The flag 'all' indicates that the call affects/depends on every |
| 91 /// side-effect. The flag 'none' indicates that the call does not affect | 132 /// side-effect. The flag 'none' indicates that the call does not affect |
| 92 /// (resp. depends on) anything. | 133 /// (resp. depends on) anything. |
| 93 /// | 134 /// |
| 94 /// 'no-index' indicates that the call does *not* do any array index-store | 135 /// 'no-index' indicates that the call does *not* do any array index-store |
| 95 /// (for 'effects'), or depends on any value in an array (for 'depends'). | 136 /// (for 'effects'), or depends on any value in an array (for 'depends'). |
| 96 /// The flag 'no-instance' indicates that the call does not modify (resp. | 137 /// The flag 'no-instance' indicates that the call does not modify (resp. |
| 97 /// depends on) any instance variable. Similarly static variables are | 138 /// depends on) any instance variable. Similarly static variables are |
| 98 /// indicated with 'no-static'. The flags 'effects' and 'depends' must be | 139 /// indicated with 'no-static'. The flags 'effects' and 'depends' must be |
| 99 /// used in unison (either both are present or none is). | 140 /// used in unison (either both are present or none is). |
| 100 /// | 141 /// |
| 142 /// The <throws-string> values are 'never', 'may', 'must', and 'null(1)'. | |
| 143 /// The default if unspecified is 'may'. 'null(1)' means that the template | |
| 144 /// expression throws if and only if the first template parameter is `null` | |
| 145 /// or `undefined`. | |
| 146 /// TODO(sra): Can we simplify to must/may/never and add null(1) by | |
| 147 /// inspection as an orthogonal attribute? | |
| 148 /// | |
| 149 /// <gvn-string> values are 'true' and 'false'. The default if unspecified | |
| 150 /// is 'false'. | |
| 151 /// | |
| 152 /// <new-string> values are 'true' and 'false'. The default if unspecified | |
| 153 /// is 'false'. A 'true' value means that each evaluation returns a fresh | |
| 154 /// (new) object that cannot be unaliased with existing objects. | |
|
herhut
2015/04/08 07:43:33
Did you mean aliased here?
| |
| 155 /// | |
| 101 /// Each tag kind (including the 'type-tag's) can only occur once in the | 156 /// Each tag kind (including the 'type-tag's) can only occur once in the |
| 102 /// sequence. | 157 /// sequence. |
| 103 /// | 158 /// |
| 104 /// [specString] is the specification string, [resolveType] resolves named | 159 /// [specString] is the specification string, [resolveType] resolves named |
| 105 /// types into type values, [typesReturned] and [typesInstantiated] collects | 160 /// types into type values, [typesReturned] and [typesInstantiated] collects |
| 106 /// the types defined by the specification string, and [objectType] and | 161 /// the types defined by the specification string, and [objectType] and |
| 107 /// [nullType] define the types for `Object` and `Null`, respectively. The | 162 /// [nullType] define the types for `Object` and `Null`, respectively. The |
| 108 /// latter is used for the type strings of the form '' and 'var'. | 163 /// latter is used for the type strings of the form '' and 'var'. |
| 109 // TODO(johnniwinther): Use ';' as a separator instead of a terminator. | 164 /// [validTags] can be used to restrict which tags are accepted. |
| 110 static void processSpecString( | 165 static void processSpecString( |
| 111 DiagnosticListener listener, | 166 DiagnosticListener listener, |
| 112 Spannable spannable, | 167 Spannable spannable, |
| 113 String specString, | 168 String specString, |
| 114 {void setSideEffects(SideEffects newEffects), | 169 {Iterable<String> validTags, |
| 170 void setSideEffects(SideEffects newEffects), | |
| 171 void setThrows(NativeThrowBehavior throwKind), | |
| 172 void setIsAllocation(bool isAllocation), | |
| 173 void setUseGvn(bool useGvn), | |
| 115 dynamic resolveType(String typeString), | 174 dynamic resolveType(String typeString), |
| 116 List typesReturned, List typesInstantiated, | 175 List typesReturned, |
| 176 List typesInstantiated, | |
| 117 objectType, nullType}) { | 177 objectType, nullType}) { |
| 118 | 178 |
| 179 const List<String> knownTags = const [ | |
| 180 'creates', 'returns', 'depends', 'effects', | |
| 181 'throws', 'gvn', 'new']; | |
| 182 | |
| 119 /// Resolve a type string of one of the three forms: | 183 /// Resolve a type string of one of the three forms: |
| 120 /// * 'void' - in which case [onVoid] is called, | 184 /// * 'void' - in which case [onVoid] is called, |
| 121 /// * '' or 'var' - in which case [onVar] is called, | 185 /// * '' or 'var' - in which case [onVar] is called, |
| 122 /// * 'T1|...|Tn' - in which case [onType] is called for each Ti. | 186 /// * 'T1|...|Tn' - in which case [onType] is called for each resolved Ti. |
| 123 void resolveTypesString(String typesString, | 187 void resolveTypesString(String typesString, |
| 124 {onVoid(), onVar(), onType(type)}) { | 188 {onVoid(), onVar(), onType(type)}) { |
| 125 // Various things that are not in fact types. | 189 // Various things that are not in fact types. |
| 126 if (typesString == 'void') { | 190 if (typesString == 'void') { |
| 127 if (onVoid != null) { | 191 if (onVoid != null) { |
| 128 onVoid(); | 192 onVoid(); |
| 129 } | 193 } |
| 130 return; | 194 return; |
| 131 } | 195 } |
| 132 if (typesString == '' || typesString == 'var') { | 196 if (typesString == '' || typesString == 'var') { |
| 133 if (onVar != null) { | 197 if (onVar != null) { |
| 134 onVar(); | 198 onVar(); |
| 135 } | 199 } |
| 136 return; | 200 return; |
| 137 } | 201 } |
| 138 for (final typeString in typesString.split('|')) { | 202 for (final typeString in typesString.split('|')) { |
| 139 onType(resolveType(typeString)); | 203 onType(resolveType(typeString.trim())); |
| 140 } | 204 } |
| 141 } | 205 } |
| 142 | 206 |
| 143 | 207 if (!specString.contains(';') && !specString.contains(':')) { |
| 144 if (specString.contains(':')) { | 208 // Form (1), types or pseudo-types like 'void' and 'var'. |
| 145 /// Find and remove a substring of the form 'tag:<string>;' from | 209 resolveTypesString(specString.trim(), onVar: () { |
| 146 /// [specString]. | |
| 147 String getTagString(String tag) { | |
| 148 String marker = '$tag:'; | |
| 149 int startPos = specString.indexOf(marker); | |
| 150 if (startPos == -1) return null; | |
| 151 int endPos = specString.indexOf(';', startPos); | |
| 152 if (endPos == -1) return null; | |
| 153 String typeString = | |
| 154 specString.substring(startPos + marker.length, endPos); | |
| 155 specString = '${specString.substring(0, startPos)}' | |
| 156 '${specString.substring(endPos + 1)}'.trim(); | |
| 157 return typeString; | |
| 158 } | |
| 159 | |
| 160 String returns = getTagString('returns'); | |
| 161 if (returns != null) { | |
| 162 resolveTypesString(returns, onVar: () { | |
| 163 typesReturned.add(objectType); | |
| 164 typesReturned.add(nullType); | |
| 165 }, onType: (type) { | |
| 166 typesReturned.add(type); | |
| 167 }); | |
| 168 } | |
| 169 | |
| 170 String creates = getTagString('creates'); | |
| 171 if (creates != null) { | |
| 172 resolveTypesString(creates, onVoid: () { | |
| 173 listener.internalError(spannable, | |
| 174 "Invalid type string 'creates:$creates'"); | |
| 175 }, onVar: () { | |
| 176 listener.internalError(spannable, | |
| 177 "Invalid type string 'creates:$creates'"); | |
| 178 }, onType: (type) { | |
| 179 typesInstantiated.add(type); | |
| 180 }); | |
| 181 } | |
| 182 | |
| 183 String effects = getTagString('effects'); | |
| 184 String depends = getTagString('depends'); | |
| 185 if (effects != null && depends == null || | |
| 186 effects == null && depends != null) { | |
| 187 listener.internalError(spannable, | |
| 188 "Invalid JS spec string. " | |
| 189 "'effects' and 'depends' must occur together."); | |
| 190 } | |
| 191 | |
| 192 if (effects != null) { | |
| 193 SideEffects sideEffects = new SideEffects(); | |
| 194 if (effects == "none") { | |
| 195 sideEffects.clearAllSideEffects(); | |
| 196 } else if (effects == "all") { | |
| 197 // Don't do anything. | |
| 198 } else { | |
| 199 List<String> splitEffects = effects.split(","); | |
| 200 if (splitEffects.isEmpty) { | |
| 201 listener.internalError(spannable, "Missing side-effect flag."); | |
| 202 } | |
| 203 for (String effect in splitEffects) { | |
| 204 switch (effect) { | |
| 205 case "no-index": | |
| 206 sideEffects.clearChangesIndex(); | |
| 207 break; | |
| 208 case "no-instance": | |
| 209 sideEffects.clearChangesInstanceProperty(); | |
| 210 break; | |
| 211 case "no-static": | |
| 212 sideEffects.clearChangesStaticProperty(); | |
| 213 break; | |
| 214 default: | |
| 215 listener.internalError(spannable, | |
| 216 "Unrecognized side-effect flag: $effect."); | |
| 217 } | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 if (depends == "none") { | |
| 222 sideEffects.clearAllDependencies(); | |
| 223 } else if (depends == "all") { | |
| 224 // Don't do anything. | |
| 225 } else { | |
| 226 List<String> splitDependencies = depends.split(","); | |
| 227 if (splitDependencies.isEmpty) { | |
| 228 listener.internalError(spannable, | |
| 229 "Missing side-effect dependency flag."); | |
| 230 } | |
| 231 for (String dependency in splitDependencies) { | |
| 232 switch (dependency) { | |
| 233 case "no-index": | |
| 234 sideEffects.clearDependsOnIndexStore(); | |
| 235 break; | |
| 236 case "no-instance": | |
| 237 sideEffects.clearDependsOnInstancePropertyStore(); | |
| 238 break; | |
| 239 case "no-static": | |
| 240 sideEffects.clearDependsOnStaticPropertyStore(); | |
| 241 break; | |
| 242 default: | |
| 243 listener.internalError(spannable, | |
| 244 "Unrecognized side-effect flag: $dependency."); | |
| 245 } | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 setSideEffects(sideEffects); | |
| 250 } | |
| 251 | |
| 252 if (!specString.isEmpty) { | |
| 253 listener.internalError(spannable, "Invalid JS spec string."); | |
| 254 } | |
| 255 } else { | |
| 256 resolveTypesString(specString, onVar: () { | |
| 257 typesReturned.add(objectType); | 210 typesReturned.add(objectType); |
| 258 typesReturned.add(nullType); | 211 typesReturned.add(nullType); |
| 259 }, onType: (type) { | 212 }, onType: (type) { |
| 260 typesInstantiated.add(type); | 213 typesInstantiated.add(type); |
| 261 typesReturned.add(type); | 214 typesReturned.add(type); |
| 262 }); | 215 }); |
| 216 return; | |
| 263 } | 217 } |
| 218 | |
| 219 List<String> specs = specString.split(';') | |
| 220 .map((s) => s.trim()) | |
| 221 .toList(); | |
| 222 if (specs.last == "") specs.removeLast(); // Allow separator to terminate. | |
| 223 | |
| 224 assert(validTags == null || validTags.toSet().removeAll(validTags).isEmpty); | |
| 225 if (validTags == null) validTags = knownTags; | |
| 226 | |
| 227 Map<String, String> values = <String, String>{}; | |
| 228 | |
| 229 for (String spec in specs) { | |
| 230 List<String> tagAndValue = spec.split(':'); | |
| 231 if (tagAndValue.length != 2) { | |
| 232 listener.internalError(spannable, | |
| 233 "Invalid <tag>:<value> pair '$spec'."); | |
| 234 } | |
| 235 String tag = tagAndValue[0].trim(); | |
| 236 String value = tagAndValue[1].trim(); | |
| 237 | |
| 238 if (validTags.contains(tag)) { | |
| 239 if (values[tag] == null) { | |
| 240 values[tag] = value; | |
| 241 } else { | |
| 242 listener.internalError(spannable, "Duplicate tag '$tag'."); | |
| 243 } | |
| 244 } else { | |
| 245 if (knownTags.contains(tag)) { | |
| 246 listener.internalError(spannable, "Tag '$tag' is not valid here."); | |
| 247 } else { | |
| 248 listener.internalError(spannable, "Unknown tag '$tag'."); | |
| 249 } | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 // Enum-like tags are looked up in a map. True signature is: | |
| 254 // | |
| 255 // T tagValueLookup<T>(String tag, Map<String, T> map); | |
| 256 // | |
| 257 dynamic tagValueLookup(String tag, Map<String, dynamic> map) { | |
| 258 String tagString = values[tag]; | |
| 259 if (tagString == null) return null; | |
| 260 var value = map[tagString]; | |
| 261 if (value == null) { | |
| 262 listener.internalError(spannable, | |
| 263 "Unknown '$tag' specification: '$tagString'"); | |
| 264 } | |
| 265 return value; | |
| 266 } | |
| 267 | |
| 268 String returns = values['returns']; | |
| 269 if (returns != null) { | |
| 270 resolveTypesString(returns, onVar: () { | |
| 271 typesReturned.add(objectType); | |
| 272 typesReturned.add(nullType); | |
| 273 }, onType: (type) { | |
| 274 typesReturned.add(type); | |
| 275 }); | |
| 276 } | |
| 277 | |
| 278 String creates = values['creates']; | |
| 279 if (creates != null) { | |
| 280 resolveTypesString(creates, onVoid: () { | |
| 281 listener.internalError(spannable, | |
| 282 "Invalid type string 'creates:$creates'"); | |
| 283 }, onVar: () { | |
| 284 listener.internalError(spannable, | |
| 285 "Invalid type string 'creates:$creates'"); | |
| 286 }, onType: (type) { | |
| 287 typesInstantiated.add(type); | |
| 288 }); | |
| 289 } | |
| 290 | |
| 291 const throwsOption = const <String, NativeThrowBehavior>{ | |
| 292 'never': NativeThrowBehavior.NEVER, | |
| 293 'null(1)': NativeThrowBehavior.MAY_THROW_ONLY_ON_FIRST_ARGUMENT_ACCESS, | |
| 294 'may': NativeThrowBehavior.MAY, | |
| 295 'must': NativeThrowBehavior.MUST }; | |
| 296 | |
| 297 const boolOptions = const<String, bool>{'true': true, 'false': false}; | |
| 298 | |
| 299 SideEffects sideEffects = processEffects(listener, spannable, | |
| 300 values['effects'], values['depends']); | |
| 301 NativeThrowBehavior throwsKind = tagValueLookup('throws', throwsOption); | |
| 302 bool isAllocation = tagValueLookup('new', boolOptions); | |
| 303 bool useGvn = tagValueLookup('gvn', boolOptions); | |
| 304 | |
| 305 if (isAllocation == true && useGvn == true) { | |
| 306 listener.internalError(spannable, "'new' and 'gvn' are incompatible"); | |
| 307 } | |
| 308 | |
| 309 if (sideEffects != null) setSideEffects(sideEffects); | |
| 310 if (throwsKind != null) setThrows(throwsKind); | |
| 311 if (isAllocation != null) setIsAllocation(isAllocation); | |
| 312 if (useGvn != null) setUseGvn(useGvn); | |
| 313 } | |
| 314 | |
| 315 static SideEffects processEffects( | |
| 316 DiagnosticListener listener, | |
| 317 Spannable spannable, | |
| 318 String effects, | |
| 319 String depends) { | |
| 320 | |
| 321 if (effects == null && depends == null) return null; | |
| 322 | |
| 323 if (effects == null || depends == null) { | |
| 324 listener.internalError(spannable, | |
| 325 "Invalid JS spec string. " | |
| 326 "'effects' and 'depends' must occur together."); | |
| 327 return null; | |
| 328 } | |
| 329 | |
| 330 SideEffects sideEffects = new SideEffects(); | |
| 331 if (effects == "none") { | |
| 332 sideEffects.clearAllSideEffects(); | |
| 333 } else if (effects == "all") { | |
| 334 // Don't do anything. | |
| 335 } else { | |
| 336 List<String> splitEffects = effects.split(","); | |
| 337 if (splitEffects.isEmpty) { | |
| 338 listener.internalError(spannable, "Missing side-effect flag."); | |
| 339 } | |
| 340 for (String effect in splitEffects) { | |
| 341 switch (effect) { | |
| 342 case "no-index": | |
| 343 sideEffects.clearChangesIndex(); | |
| 344 break; | |
| 345 case "no-instance": | |
| 346 sideEffects.clearChangesInstanceProperty(); | |
| 347 break; | |
| 348 case "no-static": | |
| 349 sideEffects.clearChangesStaticProperty(); | |
| 350 break; | |
| 351 default: | |
| 352 listener.internalError(spannable, | |
| 353 "Unrecognized side-effect flag: '$effect'."); | |
| 354 } | |
| 355 } | |
| 356 } | |
| 357 | |
| 358 if (depends == "none") { | |
| 359 sideEffects.clearAllDependencies(); | |
| 360 } else if (depends == "all") { | |
| 361 // Don't do anything. | |
| 362 } else { | |
| 363 List<String> splitDependencies = depends.split(","); | |
| 364 if (splitDependencies.isEmpty) { | |
| 365 listener.internalError(spannable, | |
| 366 "Missing side-effect dependency flag."); | |
| 367 } | |
| 368 for (String dependency in splitDependencies) { | |
| 369 switch (dependency) { | |
| 370 case "no-index": | |
| 371 sideEffects.clearDependsOnIndexStore(); | |
| 372 break; | |
| 373 case "no-instance": | |
| 374 sideEffects.clearDependsOnInstancePropertyStore(); | |
| 375 break; | |
| 376 case "no-static": | |
| 377 sideEffects.clearDependsOnStaticPropertyStore(); | |
| 378 break; | |
| 379 default: | |
| 380 listener.internalError(spannable, | |
| 381 "Unrecognized side-effect flag: '$dependency'."); | |
| 382 } | |
| 383 } | |
| 384 } | |
| 385 | |
| 386 return sideEffects; | |
| 264 } | 387 } |
| 265 | 388 |
| 266 static NativeBehavior ofJsCall(Send jsCall, Compiler compiler, resolver) { | 389 static NativeBehavior ofJsCall(Send jsCall, Compiler compiler, resolver) { |
| 267 // The first argument of a JS-call is a string encoding various attributes | 390 // The first argument of a JS-call is a string encoding various attributes |
| 268 // of the code. | 391 // of the code. |
| 269 // | 392 // |
| 270 // 'Type1|Type2'. A union type. | 393 // 'Type1|Type2'. A union type. |
| 271 // '=Object'. A JavaScript Object, no subtype. | 394 // '=Object'. A JavaScript Object, no subtype. |
| 272 | 395 |
| 273 var argNodes = jsCall.arguments; | 396 var argNodes = jsCall.arguments; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 301 jsCall); | 424 jsCall); |
| 302 } | 425 } |
| 303 | 426 |
| 304 bool sideEffectsAreEncodedInSpecString = false; | 427 bool sideEffectsAreEncodedInSpecString = false; |
| 305 | 428 |
| 306 void setSideEffects(SideEffects newEffects) { | 429 void setSideEffects(SideEffects newEffects) { |
| 307 sideEffectsAreEncodedInSpecString = true; | 430 sideEffectsAreEncodedInSpecString = true; |
| 308 behavior.sideEffects.setTo(newEffects); | 431 behavior.sideEffects.setTo(newEffects); |
| 309 } | 432 } |
| 310 | 433 |
| 434 bool throwBehaviorFromSpecString = false; | |
| 435 void setThrows(NativeThrowBehavior throwBehavior) { | |
| 436 throwBehaviorFromSpecString = true; | |
| 437 behavior.throwBehavior = throwBehavior; | |
| 438 } | |
| 439 | |
| 440 void setIsAllocation(bool isAllocation) { | |
| 441 behavior.isAllocation = isAllocation; | |
| 442 } | |
| 443 | |
| 444 void setUseGvn(bool useGvn) { | |
| 445 behavior.useGvn = useGvn; | |
| 446 } | |
| 447 | |
| 311 processSpecString(compiler, jsCall, | 448 processSpecString(compiler, jsCall, |
| 312 specString, | 449 specString, |
| 313 setSideEffects: setSideEffects, | 450 setSideEffects: setSideEffects, |
| 314 resolveType: resolveType, | 451 setThrows: setThrows, |
| 315 typesReturned: behavior.typesReturned, | 452 setIsAllocation: setIsAllocation, |
| 316 typesInstantiated: behavior.typesInstantiated, | 453 setUseGvn: setUseGvn, |
| 317 objectType: compiler.objectClass.computeType(compiler), | 454 resolveType: resolveType, |
| 318 nullType: compiler.nullClass.computeType(compiler)); | 455 typesReturned: behavior.typesReturned, |
| 456 typesInstantiated: behavior.typesInstantiated, | |
| 457 objectType: compiler.objectClass.computeType(compiler), | |
| 458 nullType: compiler.nullClass.computeType(compiler)); | |
| 319 | 459 |
| 320 if (!sideEffectsAreEncodedInSpecString) { | 460 if (!sideEffectsAreEncodedInSpecString) { |
| 321 new SideEffectsVisitor(behavior.sideEffects) | 461 new SideEffectsVisitor(behavior.sideEffects) |
| 322 .visit(behavior.codeTemplate.ast); | 462 .visit(behavior.codeTemplate.ast); |
| 323 } | 463 } |
| 324 | 464 |
| 465 // TODO(sra): Simplify [throwBehavior] using [sideEffects]. | |
| 466 | |
| 325 return behavior; | 467 return behavior; |
| 326 } | 468 } |
| 327 | 469 |
| 328 static NativeBehavior ofJsEmbeddedGlobalCall(Send jsGlobalCall, | 470 static NativeBehavior ofJsEmbeddedGlobalCall(Send jsGlobalCall, |
| 329 Compiler compiler, | 471 Compiler compiler, |
| 330 resolver) { | 472 resolver) { |
| 331 // The first argument of a JS-embedded global call is a string encoding | 473 // The first argument of a JS-embedded global call is a string encoding |
| 332 // the type of the code. | 474 // the type of the code. |
| 333 // | 475 // |
| 334 // 'Type1|Type2'. A union type. | 476 // 'Type1|Type2'. A union type. |
| 335 // '=Object'. A JavaScript Object, no subtype. | 477 // '=Object'. A JavaScript Object, no subtype. |
| 336 | 478 |
| 337 Link<Node> argNodes = jsGlobalCall.arguments; | 479 Link<Node> argNodes = jsGlobalCall.arguments; |
| 338 if (argNodes.isEmpty) { | 480 if (argNodes.isEmpty) { |
| 339 compiler.internalError(jsGlobalCall, | 481 compiler.internalError(jsGlobalCall, |
| 340 "JS embedded global expression has no type."); | 482 "JS embedded global expression has no type."); |
| 341 } | 483 } |
| 342 | 484 |
| 343 // We don't check the given name. That needs to be done at a later point. | 485 // We don't check the given name. That needs to be done at a later point. |
| 344 // This is, because we want to allow non-literals as names. | 486 // This is, because we want to allow non-literals as names. |
| 345 if (argNodes.tail.isEmpty) { | 487 if (argNodes.tail.isEmpty) { |
| 346 compiler.internalError(jsGlobalCall, 'Embedded Global is missing name'); | 488 compiler.internalError(jsGlobalCall, 'Embedded Global is missing name.'); |
| 347 } | 489 } |
| 348 | 490 |
| 349 if (!argNodes.tail.tail.isEmpty) { | 491 if (!argNodes.tail.tail.isEmpty) { |
| 350 compiler.internalError(argNodes.tail.tail.head, | 492 compiler.internalError(argNodes.tail.tail.head, |
| 351 'Embedded Global has more than 2 arguments'); | 493 'Embedded Global has more than 2 arguments'); |
| 352 } | 494 } |
| 353 | 495 |
| 354 LiteralString specLiteral = argNodes.head.asLiteralString(); | 496 LiteralString specLiteral = argNodes.head.asLiteralString(); |
| 355 if (specLiteral == null) { | 497 if (specLiteral == null) { |
| 356 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It | 498 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It |
| 357 // is not very satisfactory because it does not work for void, dynamic. | 499 // is not very satisfactory because it does not work for void, dynamic. |
| 358 compiler.internalError(argNodes.head, "Unexpected first argument."); | 500 compiler.internalError(argNodes.head, "Unexpected first argument."); |
| 359 } | 501 } |
| 360 | 502 |
| 361 NativeBehavior behavior = new NativeBehavior(); | 503 NativeBehavior behavior = new NativeBehavior(); |
| 362 | 504 |
| 363 String specString = specLiteral.dartString.slowToString(); | 505 String specString = specLiteral.dartString.slowToString(); |
| 364 | 506 |
| 365 dynamic resolveType(String typeString) { | 507 dynamic resolveType(String typeString) { |
| 366 return _parseType( | 508 return _parseType( |
| 367 typeString, | 509 typeString, |
| 368 compiler, | 510 compiler, |
| 369 (name) => resolver.resolveTypeFromString(specLiteral, name), | 511 (name) => resolver.resolveTypeFromString(specLiteral, name), |
| 370 jsGlobalCall); | 512 jsGlobalCall); |
| 371 } | 513 } |
| 372 | 514 |
| 373 void setSideEffects(SideEffects newEffects) { | |
| 374 compiler.internalError(jsGlobalCall, | |
| 375 'Embedded global calls may not have any side-effect overwrites: ' | |
| 376 '$specString'); | |
| 377 } | |
| 378 | |
| 379 processSpecString(compiler, jsGlobalCall, | 515 processSpecString(compiler, jsGlobalCall, |
| 380 specString, | 516 specString, |
| 381 setSideEffects: setSideEffects, | 517 validTags: const ['returns', 'creates'], |
| 382 resolveType: resolveType, | 518 resolveType: resolveType, |
| 383 typesReturned: behavior.typesReturned, | 519 typesReturned: behavior.typesReturned, |
| 384 typesInstantiated: behavior.typesInstantiated, | 520 typesInstantiated: behavior.typesInstantiated, |
| 385 objectType: compiler.objectClass.computeType(compiler), | 521 objectType: compiler.objectClass.computeType(compiler), |
| 386 nullType: compiler.nullClass.computeType(compiler)); | 522 nullType: compiler.nullClass.computeType(compiler)); |
| 387 | 523 |
| 388 return behavior; | 524 return behavior; |
| 389 } | 525 } |
| 390 | 526 |
| 391 static NativeBehavior ofMethod(FunctionElement method, Compiler compiler) { | 527 static NativeBehavior ofMethod(FunctionElement method, Compiler compiler) { |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 546 _errorNode(locationNodeOrElement, compiler), | 682 _errorNode(locationNodeOrElement, compiler), |
| 547 "Type '$typeString' not found."); | 683 "Type '$typeString' not found."); |
| 548 return null; | 684 return null; |
| 549 } | 685 } |
| 550 | 686 |
| 551 static _errorNode(locationNodeOrElement, compiler) { | 687 static _errorNode(locationNodeOrElement, compiler) { |
| 552 if (locationNodeOrElement is Node) return locationNodeOrElement; | 688 if (locationNodeOrElement is Node) return locationNodeOrElement; |
| 553 return locationNodeOrElement.parseNode(compiler); | 689 return locationNodeOrElement.parseNode(compiler); |
| 554 } | 690 } |
| 555 } | 691 } |
| OLD | NEW |