| 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); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 53 /// [DartType]s or [SpecialType]s instantiated by the native element. | 53 /// [DartType]s or [SpecialType]s instantiated by the native element. |
| 54 final List typesInstantiated = []; | 54 final List typesInstantiated = []; |
| 55 | 55 |
| 56 // If this behavior is for a JS expression, [codeTemplate] contains the | 56 // If this behavior is for a JS expression, [codeTemplate] contains the |
| 57 // parsed tree. | 57 // parsed tree. |
| 58 js.Template codeTemplate; | 58 js.Template codeTemplate; |
| 59 | 59 |
| 60 final SideEffects sideEffects = new SideEffects.empty(); | 60 final SideEffects sideEffects = new SideEffects.empty(); |
| 61 | 61 |
| 62 /// Processes the type specification string of a call to JS and stores the | 62 /// Processes the type specification string of a call to JS and stores the |
| 63 /// result in the [typesReturned] and [typesInstantiated]. | 63 /// result in the [typesReturned] and [typesInstantiated]. It furthermore |
| 64 /// computes the side effects, and, if given, invokes [setSideEffects] with |
| 65 /// the computed effects. If no side effects are encoded in the [specString] |
| 66 /// the [setSideEffects] method is not invoked. |
| 64 /// | 67 /// |
| 65 /// Two forms of the string is supported: | 68 /// Two forms of the string is supported: |
| 66 /// 1) A single type string of the form 'void', '', 'var' or 'T1|...|Tn' | 69 /// 1) A single type string of the form 'void', '', 'var' or 'T1|...|Tn' |
| 67 /// which defines the types returned and for the later form also created by | 70 /// which defines the types returned and for the later form also created by |
| 68 /// the call to JS. | 71 /// the call to JS. |
| 69 /// 2) A sequence of the form '<tag>:<type-string>;' where <tag> is either | 72 /// 2) A sequence of the form |
| 70 /// 'returns' or 'creates' and where <type-string> is a type string like in | 73 /// '<type-tag>:<type-string>;<effect-tag>:<effect-string>' |
| 71 /// 1). The type string marked by 'returns' defines the types returned and | 74 /// where <type-tag> is either 'returns' or 'creates' and where |
| 72 /// 'creates' defines the types created by the call to JS. Each tag kind | 75 /// <type-string> is a type string like in 1). The type string marked by |
| 73 /// can only occur once in the sequence. | 76 /// 'returns' defines the types returned and 'creates' defines the types |
| 77 /// created by the call to JS. |
| 78 /// |
| 79 /// The <effect-tag> is either 'effects' or 'depends' and |
| 80 /// <effect-string> is either 'all', 'none' or a comma-separated list of |
| 81 /// 'no-index', 'no-instance', 'no-static'. |
| 82 /// |
| 83 /// The flag 'all' indicates that the call affects/depends on every |
| 84 /// side-effect. The flag 'none' indicates that the call does not affect |
| 85 /// (resp. depends on) anything. |
| 86 /// |
| 87 /// 'no-index' indicates that the call does *not* do any array index-store |
| 88 /// (for 'effects'), or depends on any value in an array (for 'depends'). |
| 89 /// The flag 'no-instance' indicates that the call does not modify (resp. |
| 90 /// depends on) any instance variable. Similarly static variables are |
| 91 /// indicated with 'no-static'. The flags 'effects' and 'depends' must be |
| 92 /// used in unison (either both are present or none is). |
| 93 /// |
| 94 /// Each tag kind (including the 'type-tag's) can only occur once in the |
| 95 /// sequence. |
| 74 /// | 96 /// |
| 75 /// [specString] is the specification string, [resolveType] resolves named | 97 /// [specString] is the specification string, [resolveType] resolves named |
| 76 /// types into type values, [typesReturned] and [typesInstantiated] collects | 98 /// types into type values, [typesReturned] and [typesInstantiated] collects |
| 77 /// the types defined by the specification string, and [objectType] and | 99 /// the types defined by the specification string, and [objectType] and |
| 78 /// [nullType] define the types for `Object` and `Null`, respectively. The | 100 /// [nullType] define the types for `Object` and `Null`, respectively. The |
| 79 /// latter is used for the type strings of the form '' and 'var'. | 101 /// latter is used for the type strings of the form '' and 'var'. |
| 80 // TODO(johnniwinther): Use ';' as a separator instead of a terminator. | 102 // TODO(johnniwinther): Use ';' as a separator instead of a terminator. |
| 81 static void processSpecString( | 103 static void processSpecString( |
| 82 DiagnosticListener listener, | 104 DiagnosticListener listener, |
| 83 Spannable spannable, | 105 Spannable spannable, |
| 84 String specString, | 106 String specString, |
| 85 {dynamic resolveType(String typeString), | 107 {void setSideEffects(SideEffects newEffects), |
| 108 dynamic resolveType(String typeString), |
| 86 List typesReturned, List typesInstantiated, | 109 List typesReturned, List typesInstantiated, |
| 87 objectType, nullType}) { | 110 objectType, nullType}) { |
| 88 | 111 |
| 89 /// Resolve a type string of one of the three forms: | 112 /// Resolve a type string of one of the three forms: |
| 90 /// * 'void' - in which case [onVoid] is called, | 113 /// * 'void' - in which case [onVoid] is called, |
| 91 /// * '' or 'var' - in which case [onVar] is called, | 114 /// * '' or 'var' - in which case [onVar] is called, |
| 92 /// * 'T1|...|Tn' - in which case [onType] is called for each Ti. | 115 /// * 'T1|...|Tn' - in which case [onType] is called for each Ti. |
| 93 void resolveTypesString(String typesString, | 116 void resolveTypesString(String typesString, |
| 94 {onVoid(), onVar(), onType(type)}) { | 117 {onVoid(), onVar(), onType(type)}) { |
| 95 // Various things that are not in fact types. | 118 // Various things that are not in fact types. |
| 96 if (typesString == 'void') { | 119 if (typesString == 'void') { |
| 97 if (onVoid != null) { | 120 if (onVoid != null) { |
| 98 onVoid(); | 121 onVoid(); |
| 99 } | 122 } |
| 100 return; | 123 return; |
| 101 } | 124 } |
| 102 if (typesString == '' || typesString == 'var') { | 125 if (typesString == '' || typesString == 'var') { |
| 103 if (onVar != null) { | 126 if (onVar != null) { |
| 104 onVar(); | 127 onVar(); |
| 105 } | 128 } |
| 106 return; | 129 return; |
| 107 } | 130 } |
| 108 for (final typeString in typesString.split('|')) { | 131 for (final typeString in typesString.split('|')) { |
| 109 onType(resolveType(typeString)); | 132 onType(resolveType(typeString)); |
| 110 } | 133 } |
| 111 } | 134 } |
| 112 | 135 |
| 136 |
| 113 if (specString.contains(':')) { | 137 if (specString.contains(':')) { |
| 114 /// Find and remove a substring of the form 'tag:<type-string>;' from | 138 /// Find and remove a substring of the form 'tag:<string>;' from |
| 115 /// [specString]. | 139 /// [specString]. |
| 116 String getTypesString(String tag) { | 140 String getTagString(String tag) { |
| 117 String marker = '$tag:'; | 141 String marker = '$tag:'; |
| 118 int startPos = specString.indexOf(marker); | 142 int startPos = specString.indexOf(marker); |
| 119 if (startPos == -1) return null; | 143 if (startPos == -1) return null; |
| 120 int endPos = specString.indexOf(';', startPos); | 144 int endPos = specString.indexOf(';', startPos); |
| 121 if (endPos == -1) return null; | 145 if (endPos == -1) return null; |
| 122 String typeString = | 146 String typeString = |
| 123 specString.substring(startPos + marker.length, endPos); | 147 specString.substring(startPos + marker.length, endPos); |
| 124 specString = '${specString.substring(0, startPos)}' | 148 specString = '${specString.substring(0, startPos)}' |
| 125 '${specString.substring(endPos + 1)}'.trim(); | 149 '${specString.substring(endPos + 1)}'.trim(); |
| 126 return typeString; | 150 return typeString; |
| 127 } | 151 } |
| 128 | 152 |
| 129 String returns = getTypesString('returns'); | 153 String returns = getTagString('returns'); |
| 130 if (returns != null) { | 154 if (returns != null) { |
| 131 resolveTypesString(returns, onVar: () { | 155 resolveTypesString(returns, onVar: () { |
| 132 typesReturned.add(objectType); | 156 typesReturned.add(objectType); |
| 133 typesReturned.add(nullType); | 157 typesReturned.add(nullType); |
| 134 }, onType: (type) { | 158 }, onType: (type) { |
| 135 typesReturned.add(type); | 159 typesReturned.add(type); |
| 136 }); | 160 }); |
| 137 } | 161 } |
| 138 | 162 |
| 139 String creates = getTypesString('creates'); | 163 String creates = getTagString('creates'); |
| 140 if (creates != null) { | 164 if (creates != null) { |
| 141 resolveTypesString(creates, onVoid: () { | 165 resolveTypesString(creates, onVoid: () { |
| 142 listener.internalError(spannable, | 166 listener.internalError(spannable, |
| 143 "Invalid type string 'creates:$creates'"); | 167 "Invalid type string 'creates:$creates'"); |
| 144 }, onVar: () { | 168 }, onVar: () { |
| 145 listener.internalError(spannable, | 169 listener.internalError(spannable, |
| 146 "Invalid type string 'creates:$creates'"); | 170 "Invalid type string 'creates:$creates'"); |
| 147 }, onType: (type) { | 171 }, onType: (type) { |
| 148 typesInstantiated.add(type); | 172 typesInstantiated.add(type); |
| 149 }); | 173 }); |
| 150 } | 174 } |
| 151 | 175 |
| 176 String effects = getTagString('effects'); |
| 177 String depends = getTagString('depends'); |
| 178 if (effects != null && depends == null || |
| 179 effects == null && depends != null) { |
| 180 listener.internalError(spannable, |
| 181 "Invalid JS spec string. " |
| 182 "'effects' and 'depends' must occur together."); |
| 183 } |
| 184 |
| 185 if (effects != null) { |
| 186 SideEffects sideEffects = new SideEffects(); |
| 187 if (effects == "none") { |
| 188 sideEffects.clearAllSideEffects(); |
| 189 } else if (effects == "all") { |
| 190 // Don't do anything. |
| 191 } else { |
| 192 List<String> splitEffects = effects.split(","); |
| 193 if (splitEffects.isEmpty) { |
| 194 listener.internalError(spannable, "Missing side-effect flag."); |
| 195 } |
| 196 for (String effect in splitEffects) { |
| 197 switch (effect) { |
| 198 case "no-index": |
| 199 sideEffects.clearChangesIndex(); |
| 200 break; |
| 201 case "no-instance": |
| 202 sideEffects.clearChangesInstanceProperty(); |
| 203 break; |
| 204 case "no-static": |
| 205 sideEffects.clearChangesStaticProperty(); |
| 206 break; |
| 207 default: |
| 208 listener.internalError(spannable, |
| 209 "Unrecognized side-effect flag: $effect."); |
| 210 } |
| 211 } |
| 212 } |
| 213 |
| 214 if (depends == "none") { |
| 215 sideEffects.clearAllDependencies(); |
| 216 } else if (depends == "all") { |
| 217 // Don't do anything. |
| 218 } else { |
| 219 List<String> splitDependencies = depends.split(","); |
| 220 if (splitDependencies.isEmpty) { |
| 221 listener.internalError(spannable, |
| 222 "Missing side-effect dependency flag."); |
| 223 } |
| 224 for (String dependency in splitDependencies) { |
| 225 switch (dependency) { |
| 226 case "no-index": |
| 227 sideEffects.clearDependsOnIndexStore(); |
| 228 break; |
| 229 case "no-instance": |
| 230 sideEffects.clearDependsOnInstancePropertyStore(); |
| 231 break; |
| 232 case "no-static": |
| 233 sideEffects.clearDependsOnStaticPropertyStore(); |
| 234 break; |
| 235 default: |
| 236 listener.internalError(spannable, |
| 237 "Unrecognized side-effect flag: $dependency."); |
| 238 } |
| 239 } |
| 240 } |
| 241 |
| 242 setSideEffects(sideEffects); |
| 243 } |
| 244 |
| 152 if (!specString.isEmpty) { | 245 if (!specString.isEmpty) { |
| 153 listener.internalError(spannable, "Invalid JS type string."); | 246 listener.internalError(spannable, "Invalid JS spec string."); |
| 154 } | 247 } |
| 155 } else { | 248 } else { |
| 156 resolveTypesString(specString, onVar: () { | 249 resolveTypesString(specString, onVar: () { |
| 157 typesReturned.add(objectType); | 250 typesReturned.add(objectType); |
| 158 typesReturned.add(nullType); | 251 typesReturned.add(nullType); |
| 159 }, onType: (type) { | 252 }, onType: (type) { |
| 160 typesInstantiated.add(type); | 253 typesInstantiated.add(type); |
| 161 typesReturned.add(type); | 254 typesReturned.add(type); |
| 162 }); | 255 }); |
| 163 } | 256 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 183 LiteralString specLiteral = argNodes.head.asLiteralString(); | 276 LiteralString specLiteral = argNodes.head.asLiteralString(); |
| 184 if (specLiteral == null) { | 277 if (specLiteral == null) { |
| 185 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It | 278 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It |
| 186 // is not very satisfactory because it does not work for void, dynamic. | 279 // is not very satisfactory because it does not work for void, dynamic. |
| 187 compiler.internalError(argNodes.head, "Unexpected JS first argument."); | 280 compiler.internalError(argNodes.head, "Unexpected JS first argument."); |
| 188 } | 281 } |
| 189 | 282 |
| 190 NativeBehavior behavior = new NativeBehavior(); | 283 NativeBehavior behavior = new NativeBehavior(); |
| 191 behavior.codeTemplate = | 284 behavior.codeTemplate = |
| 192 js.js.parseForeignJS(code.dartString.slowToString()); | 285 js.js.parseForeignJS(code.dartString.slowToString()); |
| 193 new SideEffectsVisitor(behavior.sideEffects) | |
| 194 .visit(behavior.codeTemplate.ast); | |
| 195 | 286 |
| 196 String specString = specLiteral.dartString.slowToString(); | 287 String specString = specLiteral.dartString.slowToString(); |
| 197 | 288 |
| 198 resolveType(String typeString) { | 289 dynamic resolveType(String typeString) { |
| 199 return _parseType( | 290 return _parseType( |
| 200 typeString, | 291 typeString, |
| 201 compiler, | 292 compiler, |
| 202 (name) => resolver.resolveTypeFromString(specLiteral, name), | 293 (name) => resolver.resolveTypeFromString(specLiteral, name), |
| 203 jsCall); | 294 jsCall); |
| 204 } | 295 } |
| 205 | 296 |
| 297 bool sideEffectsAreEncodedInSpecString = false; |
| 298 |
| 299 void setSideEffects(SideEffects newEffects) { |
| 300 sideEffectsAreEncodedInSpecString = true; |
| 301 behavior.sideEffects.setTo(newEffects); |
| 302 } |
| 303 |
| 206 processSpecString(compiler, jsCall, | 304 processSpecString(compiler, jsCall, |
| 207 specString, | 305 specString, |
| 306 setSideEffects: setSideEffects, |
| 208 resolveType: resolveType, | 307 resolveType: resolveType, |
| 209 typesReturned: behavior.typesReturned, | 308 typesReturned: behavior.typesReturned, |
| 210 typesInstantiated: behavior.typesInstantiated, | 309 typesInstantiated: behavior.typesInstantiated, |
| 211 objectType: compiler.objectClass.computeType(compiler), | 310 objectType: compiler.objectClass.computeType(compiler), |
| 212 nullType: compiler.nullClass.computeType(compiler)); | 311 nullType: compiler.nullClass.computeType(compiler)); |
| 213 | 312 |
| 313 if (!sideEffectsAreEncodedInSpecString) { |
| 314 new SideEffectsVisitor(behavior.sideEffects) |
| 315 .visit(behavior.codeTemplate.ast); |
| 316 } |
| 317 |
| 214 return behavior; | 318 return behavior; |
| 215 } | 319 } |
| 216 | 320 |
| 217 static NativeBehavior ofJsEmbeddedGlobalCall(Send jsGlobalCall, | 321 static NativeBehavior ofJsEmbeddedGlobalCall(Send jsGlobalCall, |
| 218 Compiler compiler, | 322 Compiler compiler, |
| 219 resolver) { | 323 resolver) { |
| 220 // The first argument of a JS-embedded global call is a string encoding | 324 // The first argument of a JS-embedded global call is a string encoding |
| 221 // the type of the code. | 325 // the type of the code. |
| 222 // | 326 // |
| 223 // 'Type1|Type2'. A union type. | 327 // 'Type1|Type2'. A union type. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 244 if (specLiteral == null) { | 348 if (specLiteral == null) { |
| 245 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It | 349 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It |
| 246 // is not very satisfactory because it does not work for void, dynamic. | 350 // is not very satisfactory because it does not work for void, dynamic. |
| 247 compiler.internalError(argNodes.head, "Unexpected first argument."); | 351 compiler.internalError(argNodes.head, "Unexpected first argument."); |
| 248 } | 352 } |
| 249 | 353 |
| 250 NativeBehavior behavior = new NativeBehavior(); | 354 NativeBehavior behavior = new NativeBehavior(); |
| 251 | 355 |
| 252 String specString = specLiteral.dartString.slowToString(); | 356 String specString = specLiteral.dartString.slowToString(); |
| 253 | 357 |
| 254 resolveType(String typeString) { | 358 dynamic resolveType(String typeString) { |
| 255 return _parseType( | 359 return _parseType( |
| 256 typeString, | 360 typeString, |
| 257 compiler, | 361 compiler, |
| 258 (name) => resolver.resolveTypeFromString(specLiteral, name), | 362 (name) => resolver.resolveTypeFromString(specLiteral, name), |
| 259 jsGlobalCall); | 363 jsGlobalCall); |
| 260 } | 364 } |
| 261 | 365 |
| 366 void setSideEffects(SideEffects newEffects) { |
| 367 compiler.internalError(jsGlobalCall, |
| 368 'Embedded global calls may not have any side-effect overwrites: ' |
| 369 '$specString'); |
| 370 } |
| 371 |
| 262 processSpecString(compiler, jsGlobalCall, | 372 processSpecString(compiler, jsGlobalCall, |
| 263 specString, | 373 specString, |
| 374 setSideEffects: setSideEffects, |
| 264 resolveType: resolveType, | 375 resolveType: resolveType, |
| 265 typesReturned: behavior.typesReturned, | 376 typesReturned: behavior.typesReturned, |
| 266 typesInstantiated: behavior.typesInstantiated, | 377 typesInstantiated: behavior.typesInstantiated, |
| 267 objectType: compiler.objectClass.computeType(compiler), | 378 objectType: compiler.objectClass.computeType(compiler), |
| 268 nullType: compiler.nullClass.computeType(compiler)); | 379 nullType: compiler.nullClass.computeType(compiler)); |
| 269 | 380 |
| 270 return behavior; | 381 return behavior; |
| 271 } | 382 } |
| 272 | 383 |
| 273 static NativeBehavior ofMethod(FunctionElement method, Compiler compiler) { | 384 static NativeBehavior ofMethod(FunctionElement method, Compiler compiler) { |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 397 FunctionType functionType = type; | 508 FunctionType functionType = type; |
| 398 _capture(functionType.returnType, compiler); | 509 _capture(functionType.returnType, compiler); |
| 399 for (DartType parameter in functionType.parameterTypes) { | 510 for (DartType parameter in functionType.parameterTypes) { |
| 400 _escape(parameter, compiler); | 511 _escape(parameter, compiler); |
| 401 } | 512 } |
| 402 } else { | 513 } else { |
| 403 typesInstantiated.add(type); | 514 typesInstantiated.add(type); |
| 404 } | 515 } |
| 405 } | 516 } |
| 406 | 517 |
| 407 static _parseType(String typeString, Compiler compiler, | 518 static dynamic _parseType(String typeString, Compiler compiler, |
| 408 lookup(name), locationNodeOrElement) { | 519 lookup(name), locationNodeOrElement) { |
| 409 if (typeString == '=Object') return SpecialType.JsObject; | 520 if (typeString == '=Object') return SpecialType.JsObject; |
| 410 if (typeString == 'dynamic') { | 521 if (typeString == 'dynamic') { |
| 411 return const DynamicType(); | 522 return const DynamicType(); |
| 412 } | 523 } |
| 413 DartType type = lookup(typeString); | 524 var type = lookup(typeString); |
| 414 if (type != null) return type; | 525 if (type != null) return type; |
| 415 | 526 |
| 416 int index = typeString.indexOf('<'); | 527 int index = typeString.indexOf('<'); |
| 417 if (index < 1) { | 528 if (index < 1) { |
| 418 compiler.internalError( | 529 compiler.internalError( |
| 419 _errorNode(locationNodeOrElement, compiler), | 530 _errorNode(locationNodeOrElement, compiler), |
| 420 "Type '$typeString' not found."); | 531 "Type '$typeString' not found."); |
| 421 } | 532 } |
| 422 type = lookup(typeString.substring(0, index)); | 533 type = lookup(typeString.substring(0, index)); |
| 423 if (type != null) { | 534 if (type != null) { |
| 424 // TODO(sra): Parse type parameters. | 535 // TODO(sra): Parse type parameters. |
| 425 return type; | 536 return type; |
| 426 } | 537 } |
| 427 compiler.internalError( | 538 compiler.internalError( |
| 428 _errorNode(locationNodeOrElement, compiler), | 539 _errorNode(locationNodeOrElement, compiler), |
| 429 "Type '$typeString' not found."); | 540 "Type '$typeString' not found."); |
| 541 return null; |
| 430 } | 542 } |
| 431 | 543 |
| 432 static _errorNode(locationNodeOrElement, compiler) { | 544 static _errorNode(locationNodeOrElement, compiler) { |
| 433 if (locationNodeOrElement is Node) return locationNodeOrElement; | 545 if (locationNodeOrElement is Node) return locationNodeOrElement; |
| 434 return locationNodeOrElement.parseNode(compiler); | 546 return locationNodeOrElement.parseNode(compiler); |
| 435 } | 547 } |
| 436 } | 548 } |
| OLD | NEW |