Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(637)

Side by Side Diff: pkg/compiler/lib/src/native/behavior.dart

Issue 1071853002: Improve error handling of JS-calls (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix checked mode Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/compiler/dart2js/js_spec_string_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
169 {Iterable<String> validTags, 169 {Iterable<String> validTags,
170 void setSideEffects(SideEffects newEffects), 170 void setSideEffects(SideEffects newEffects),
171 void setThrows(NativeThrowBehavior throwKind), 171 void setThrows(NativeThrowBehavior throwKind),
172 void setIsAllocation(bool isAllocation), 172 void setIsAllocation(bool isAllocation),
173 void setUseGvn(bool useGvn), 173 void setUseGvn(bool useGvn),
174 dynamic resolveType(String typeString), 174 dynamic resolveType(String typeString),
175 List typesReturned, 175 List typesReturned,
176 List typesInstantiated, 176 List typesInstantiated,
177 objectType, nullType}) { 177 objectType, nullType}) {
178 178
179
180 bool seenError = false;
181
182 void reportError(String message) {
183 seenError = true;
184 listener.reportError(spannable, MessageKind.GENERIC, {'text': message});
185 }
186
179 const List<String> knownTags = const [ 187 const List<String> knownTags = const [
180 'creates', 'returns', 'depends', 'effects', 188 'creates', 'returns', 'depends', 'effects',
181 'throws', 'gvn', 'new']; 189 'throws', 'gvn', 'new'];
182 190
183 /// Resolve a type string of one of the three forms: 191 /// Resolve a type string of one of the three forms:
184 /// * 'void' - in which case [onVoid] is called, 192 /// * 'void' - in which case [onVoid] is called,
185 /// * '' or 'var' - in which case [onVar] is called, 193 /// * '' or 'var' - in which case [onVar] is called,
186 /// * 'T1|...|Tn' - in which case [onType] is called for each resolved Ti. 194 /// * 'T1|...|Tn' - in which case [onType] is called for each resolved Ti.
187 void resolveTypesString(String typesString, 195 void resolveTypesString(String typesString,
188 {onVoid(), onVar(), onType(type)}) { 196 {onVoid(), onVar(), onType(type)}) {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 231
224 assert(validTags == null || 232 assert(validTags == null ||
225 (validTags.toSet()..removeAll(validTags)).isEmpty); 233 (validTags.toSet()..removeAll(validTags)).isEmpty);
226 if (validTags == null) validTags = knownTags; 234 if (validTags == null) validTags = knownTags;
227 235
228 Map<String, String> values = <String, String>{}; 236 Map<String, String> values = <String, String>{};
229 237
230 for (String spec in specs) { 238 for (String spec in specs) {
231 List<String> tagAndValue = spec.split(':'); 239 List<String> tagAndValue = spec.split(':');
232 if (tagAndValue.length != 2) { 240 if (tagAndValue.length != 2) {
233 listener.internalError(spannable, 241 reportError("Invalid <tag>:<value> pair '$spec'.");
234 "Invalid <tag>:<value> pair '$spec'."); 242 continue;
235 } 243 }
236 String tag = tagAndValue[0].trim(); 244 String tag = tagAndValue[0].trim();
237 String value = tagAndValue[1].trim(); 245 String value = tagAndValue[1].trim();
238 246
239 if (validTags.contains(tag)) { 247 if (validTags.contains(tag)) {
240 if (values[tag] == null) { 248 if (values[tag] == null) {
241 values[tag] = value; 249 values[tag] = value;
242 } else { 250 } else {
243 listener.internalError(spannable, "Duplicate tag '$tag'."); 251 reportError("Duplicate tag '$tag'.");
244 } 252 }
245 } else { 253 } else {
246 if (knownTags.contains(tag)) { 254 if (knownTags.contains(tag)) {
247 listener.internalError(spannable, "Tag '$tag' is not valid here."); 255 reportError("Tag '$tag' is not valid here.");
248 } else { 256 } else {
249 listener.internalError(spannable, "Unknown tag '$tag'."); 257 reportError("Unknown tag '$tag'.");
250 } 258 }
251 } 259 }
252 } 260 }
253 261
254 // Enum-like tags are looked up in a map. True signature is: 262 // Enum-like tags are looked up in a map. True signature is:
255 // 263 //
256 // T tagValueLookup<T>(String tag, Map<String, T> map); 264 // T tagValueLookup<T>(String tag, Map<String, T> map);
257 // 265 //
258 dynamic tagValueLookup(String tag, Map<String, dynamic> map) { 266 dynamic tagValueLookup(String tag, Map<String, dynamic> map) {
259 String tagString = values[tag]; 267 String tagString = values[tag];
260 if (tagString == null) return null; 268 if (tagString == null) return null;
261 var value = map[tagString]; 269 var value = map[tagString];
262 if (value == null) { 270 if (value == null) {
263 listener.internalError(spannable, 271 reportError("Unknown '$tag' specification: '$tagString'.");
264 "Unknown '$tag' specification: '$tagString'");
265 } 272 }
266 return value; 273 return value;
267 } 274 }
268 275
269 String returns = values['returns']; 276 String returns = values['returns'];
270 if (returns != null) { 277 if (returns != null) {
271 resolveTypesString(returns, onVar: () { 278 resolveTypesString(returns, onVar: () {
272 typesReturned.add(objectType); 279 typesReturned.add(objectType);
273 typesReturned.add(nullType); 280 typesReturned.add(nullType);
274 }, onType: (type) { 281 }, onType: (type) {
275 typesReturned.add(type); 282 typesReturned.add(type);
276 }); 283 });
277 } 284 }
278 285
279 String creates = values['creates']; 286 String creates = values['creates'];
280 if (creates != null) { 287 if (creates != null) {
281 resolveTypesString(creates, onVoid: () { 288 resolveTypesString(creates, onVoid: () {
282 listener.internalError(spannable, 289 reportError("Invalid type string 'creates:$creates'");
283 "Invalid type string 'creates:$creates'");
284 }, onVar: () { 290 }, onVar: () {
285 listener.internalError(spannable, 291 reportError("Invalid type string 'creates:$creates'");
286 "Invalid type string 'creates:$creates'");
287 }, onType: (type) { 292 }, onType: (type) {
288 typesInstantiated.add(type); 293 typesInstantiated.add(type);
289 }); 294 });
290 } 295 }
291 296
292 const throwsOption = const <String, NativeThrowBehavior>{ 297 const throwsOption = const <String, NativeThrowBehavior>{
293 'never': NativeThrowBehavior.NEVER, 298 'never': NativeThrowBehavior.NEVER,
294 'null(1)': NativeThrowBehavior.MAY_THROW_ONLY_ON_FIRST_ARGUMENT_ACCESS, 299 'null(1)': NativeThrowBehavior.MAY_THROW_ONLY_ON_FIRST_ARGUMENT_ACCESS,
295 'may': NativeThrowBehavior.MAY, 300 'may': NativeThrowBehavior.MAY,
296 'must': NativeThrowBehavior.MUST }; 301 'must': NativeThrowBehavior.MUST };
297 302
298 const boolOptions = const<String, bool>{'true': true, 'false': false}; 303 const boolOptions = const<String, bool>{'true': true, 'false': false};
299 304
300 SideEffects sideEffects = processEffects(listener, spannable, 305 SideEffects sideEffects = processEffects(reportError,
301 values['effects'], values['depends']); 306 values['effects'], values['depends']);
302 NativeThrowBehavior throwsKind = tagValueLookup('throws', throwsOption); 307 NativeThrowBehavior throwsKind = tagValueLookup('throws', throwsOption);
303 bool isAllocation = tagValueLookup('new', boolOptions); 308 bool isAllocation = tagValueLookup('new', boolOptions);
304 bool useGvn = tagValueLookup('gvn', boolOptions); 309 bool useGvn = tagValueLookup('gvn', boolOptions);
305 310
306 if (isAllocation == true && useGvn == true) { 311 if (isAllocation == true && useGvn == true) {
307 listener.internalError(spannable, "'new' and 'gvn' are incompatible"); 312 reportError("'new' and 'gvn' are incompatible");
308 } 313 }
309 314
315 if (seenError) return; // Avoid callbacks.
316
317 // TODO(sra): Simplify [throwBehavior] using [sideEffects].
318
310 if (sideEffects != null) setSideEffects(sideEffects); 319 if (sideEffects != null) setSideEffects(sideEffects);
311 if (throwsKind != null) setThrows(throwsKind); 320 if (throwsKind != null) setThrows(throwsKind);
312 if (isAllocation != null) setIsAllocation(isAllocation); 321 if (isAllocation != null) setIsAllocation(isAllocation);
313 if (useGvn != null) setUseGvn(useGvn); 322 if (useGvn != null) setUseGvn(useGvn);
314 } 323 }
315 324
316 static SideEffects processEffects( 325 static SideEffects processEffects(
317 DiagnosticListener listener, 326 void reportError(String message),
318 Spannable spannable,
319 String effects, 327 String effects,
320 String depends) { 328 String depends) {
321 329
322 if (effects == null && depends == null) return null; 330 if (effects == null && depends == null) return null;
323 331
324 if (effects == null || depends == null) { 332 if (effects == null || depends == null) {
325 listener.internalError(spannable, 333 reportError("'effects' and 'depends' must occur together.");
326 "Invalid JS spec string. "
327 "'effects' and 'depends' must occur together.");
328 return null; 334 return null;
329 } 335 }
330 336
331 SideEffects sideEffects = new SideEffects(); 337 SideEffects sideEffects = new SideEffects();
332 if (effects == "none") { 338 if (effects == "none") {
333 sideEffects.clearAllSideEffects(); 339 sideEffects.clearAllSideEffects();
334 } else if (effects == "all") { 340 } else if (effects == "all") {
335 // Don't do anything. 341 // Don't do anything.
336 } else { 342 } else {
337 List<String> splitEffects = effects.split(","); 343 List<String> splitEffects = effects.split(",");
338 if (splitEffects.isEmpty) { 344 if (splitEffects.isEmpty) {
339 listener.internalError(spannable, "Missing side-effect flag."); 345 reportError("Missing side-effect flag.");
340 } 346 }
341 for (String effect in splitEffects) { 347 for (String effect in splitEffects) {
342 switch (effect) { 348 switch (effect) {
343 case "no-index": 349 case "no-index":
344 sideEffects.clearChangesIndex(); 350 sideEffects.clearChangesIndex();
345 break; 351 break;
346 case "no-instance": 352 case "no-instance":
347 sideEffects.clearChangesInstanceProperty(); 353 sideEffects.clearChangesInstanceProperty();
348 break; 354 break;
349 case "no-static": 355 case "no-static":
350 sideEffects.clearChangesStaticProperty(); 356 sideEffects.clearChangesStaticProperty();
351 break; 357 break;
352 default: 358 default:
353 listener.internalError(spannable, 359 reportError("Unrecognized side-effect flag: '$effect'.");
354 "Unrecognized side-effect flag: '$effect'.");
355 } 360 }
356 } 361 }
357 } 362 }
358 363
359 if (depends == "none") { 364 if (depends == "none") {
360 sideEffects.clearAllDependencies(); 365 sideEffects.clearAllDependencies();
361 } else if (depends == "all") { 366 } else if (depends == "all") {
362 // Don't do anything. 367 // Don't do anything.
363 } else { 368 } else {
364 List<String> splitDependencies = depends.split(","); 369 List<String> splitDependencies = depends.split(",");
365 if (splitDependencies.isEmpty) { 370 if (splitDependencies.isEmpty) {
366 listener.internalError(spannable, 371 reportError("Missing side-effect dependency flag.");
367 "Missing side-effect dependency flag.");
368 } 372 }
369 for (String dependency in splitDependencies) { 373 for (String dependency in splitDependencies) {
370 switch (dependency) { 374 switch (dependency) {
371 case "no-index": 375 case "no-index":
372 sideEffects.clearDependsOnIndexStore(); 376 sideEffects.clearDependsOnIndexStore();
373 break; 377 break;
374 case "no-instance": 378 case "no-instance":
375 sideEffects.clearDependsOnInstancePropertyStore(); 379 sideEffects.clearDependsOnInstancePropertyStore();
376 break; 380 break;
377 case "no-static": 381 case "no-static":
378 sideEffects.clearDependsOnStaticPropertyStore(); 382 sideEffects.clearDependsOnStaticPropertyStore();
379 break; 383 break;
380 default: 384 default:
381 listener.internalError(spannable, 385 reportError("Unrecognized side-effect flag: '$dependency'.");
382 "Unrecognized side-effect flag: '$dependency'.");
383 } 386 }
384 } 387 }
385 } 388 }
386 389
387 return sideEffects; 390 return sideEffects;
388 } 391 }
389 392
390 static NativeBehavior ofJsCall(Send jsCall, Compiler compiler, resolver) { 393 static NativeBehavior ofJsCall(Send jsCall, Compiler compiler, resolver) {
391 // The first argument of a JS-call is a string encoding various attributes 394 // The first argument of a JS-call is a string encoding various attributes
392 // of the code. 395 // of the code.
393 // 396 //
394 // 'Type1|Type2'. A union type. 397 // 'Type1|Type2'. A union type.
395 // '=Object'. A JavaScript Object, no subtype. 398 // '=Object'. A JavaScript Object, no subtype.
396 399
400 NativeBehavior behavior = new NativeBehavior();
401
397 var argNodes = jsCall.arguments; 402 var argNodes = jsCall.arguments;
398 if (argNodes.isEmpty) { 403 if (argNodes.isEmpty || argNodes.tail.isEmpty) {
399 compiler.internalError(jsCall, "JS expression has no type."); 404 compiler.reportError(jsCall, MessageKind.GENERIC,
405 {'text': "JS expression takes two or more arguments."});
406 return behavior;
400 } 407 }
401 408
402 var code = argNodes.tail.head; 409 var specArgument = argNodes.head;
403 if (code is !StringNode || code.isInterpolation) { 410 if (specArgument is !StringNode || specArgument.isInterpolation) {
404 compiler.internalError(code, 'JS code must be a string literal.'); 411 compiler.reportError(specArgument, MessageKind.GENERIC,
412 {'text': "JS first argument must be a string literal."});
413 return behavior;
405 } 414 }
406 415
407 LiteralString specLiteral = argNodes.head.asLiteralString(); 416 var codeArgument = argNodes.tail.head;
408 if (specLiteral == null) { 417 if (codeArgument is !StringNode || codeArgument.isInterpolation) {
409 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It 418 compiler.reportError(codeArgument, MessageKind.GENERIC,
410 // is not very satisfactory because it does not work for void, dynamic. 419 {'text': "JS second argument must be a string literal."});
411 compiler.internalError(argNodes.head, "Unexpected JS first argument."); 420 return behavior;
412 } 421 }
413 422
414 NativeBehavior behavior = new NativeBehavior();
415 behavior.codeTemplate = 423 behavior.codeTemplate =
416 js.js.parseForeignJS(code.dartString.slowToString()); 424 js.js.parseForeignJS(codeArgument.dartString.slowToString());
417 425
418 String specString = specLiteral.dartString.slowToString(); 426 String specString = specArgument.dartString.slowToString();
419 427
420 dynamic resolveType(String typeString) { 428 dynamic resolveType(String typeString) {
421 return _parseType( 429 return _parseType(
422 typeString, 430 typeString,
423 compiler, 431 compiler,
424 (name) => resolver.resolveTypeFromString(specLiteral, name), 432 (name) => resolver.resolveTypeFromString(specArgument, name),
425 jsCall); 433 specArgument);
426 } 434 }
427 435
428 bool sideEffectsAreEncodedInSpecString = false; 436 bool sideEffectsAreEncodedInSpecString = false;
429 437
430 void setSideEffects(SideEffects newEffects) { 438 void setSideEffects(SideEffects newEffects) {
431 sideEffectsAreEncodedInSpecString = true; 439 sideEffectsAreEncodedInSpecString = true;
432 behavior.sideEffects.setTo(newEffects); 440 behavior.sideEffects.setTo(newEffects);
433 } 441 }
434 442
435 bool throwBehaviorFromSpecString = false; 443 bool throwBehaviorFromSpecString = false;
436 void setThrows(NativeThrowBehavior throwBehavior) { 444 void setThrows(NativeThrowBehavior throwBehavior) {
437 throwBehaviorFromSpecString = true; 445 throwBehaviorFromSpecString = true;
438 behavior.throwBehavior = throwBehavior; 446 behavior.throwBehavior = throwBehavior;
439 } 447 }
440 448
441 void setIsAllocation(bool isAllocation) { 449 void setIsAllocation(bool isAllocation) {
442 behavior.isAllocation = isAllocation; 450 behavior.isAllocation = isAllocation;
443 } 451 }
444 452
445 void setUseGvn(bool useGvn) { 453 void setUseGvn(bool useGvn) {
446 behavior.useGvn = useGvn; 454 behavior.useGvn = useGvn;
447 } 455 }
448 456
449 processSpecString(compiler, jsCall, 457 processSpecString(compiler, specArgument,
450 specString, 458 specString,
451 setSideEffects: setSideEffects, 459 setSideEffects: setSideEffects,
452 setThrows: setThrows, 460 setThrows: setThrows,
453 setIsAllocation: setIsAllocation, 461 setIsAllocation: setIsAllocation,
454 setUseGvn: setUseGvn, 462 setUseGvn: setUseGvn,
455 resolveType: resolveType, 463 resolveType: resolveType,
456 typesReturned: behavior.typesReturned, 464 typesReturned: behavior.typesReturned,
457 typesInstantiated: behavior.typesInstantiated, 465 typesInstantiated: behavior.typesInstantiated,
458 objectType: compiler.objectClass.computeType(compiler), 466 objectType: compiler.objectClass.computeType(compiler),
459 nullType: compiler.nullClass.computeType(compiler)); 467 nullType: compiler.nullClass.computeType(compiler));
460 468
461 if (!sideEffectsAreEncodedInSpecString) { 469 if (!sideEffectsAreEncodedInSpecString) {
462 new SideEffectsVisitor(behavior.sideEffects) 470 new SideEffectsVisitor(behavior.sideEffects)
463 .visit(behavior.codeTemplate.ast); 471 .visit(behavior.codeTemplate.ast);
464 } 472 }
465 473
466 // TODO(sra): Simplify [throwBehavior] using [sideEffects].
467
468 return behavior; 474 return behavior;
469 } 475 }
470 476
471 static NativeBehavior ofJsEmbeddedGlobalCall(Send jsGlobalCall, 477 static NativeBehavior ofJsEmbeddedGlobalCall(Send jsGlobalCall,
472 Compiler compiler, 478 Compiler compiler,
473 resolver) { 479 resolver) {
474 // The first argument of a JS-embedded global call is a string encoding 480 // The first argument of a JS-embedded global call is a string encoding
475 // the type of the code. 481 // the type of the code.
476 // 482 //
477 // 'Type1|Type2'. A union type. 483 // 'Type1|Type2'. A union type.
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
663 lookup(name), locationNodeOrElement) { 669 lookup(name), locationNodeOrElement) {
664 if (typeString == '=Object') return SpecialType.JsObject; 670 if (typeString == '=Object') return SpecialType.JsObject;
665 if (typeString == 'dynamic') { 671 if (typeString == 'dynamic') {
666 return const DynamicType(); 672 return const DynamicType();
667 } 673 }
668 var type = lookup(typeString); 674 var type = lookup(typeString);
669 if (type != null) return type; 675 if (type != null) return type;
670 676
671 int index = typeString.indexOf('<'); 677 int index = typeString.indexOf('<');
672 if (index < 1) { 678 if (index < 1) {
673 compiler.internalError( 679 compiler.reportError(
674 _errorNode(locationNodeOrElement, compiler), 680 _errorNode(locationNodeOrElement, compiler),
675 "Type '$typeString' not found."); 681 MessageKind.GENERIC,
682 {'text': "Type '$typeString' not found."});
683 return const DynamicType();
676 } 684 }
677 type = lookup(typeString.substring(0, index)); 685 type = lookup(typeString.substring(0, index));
678 if (type != null) { 686 if (type != null) {
679 // TODO(sra): Parse type parameters. 687 // TODO(sra): Parse type parameters.
680 return type; 688 return type;
681 } 689 }
682 compiler.internalError( 690 compiler.reportError(
683 _errorNode(locationNodeOrElement, compiler), 691 _errorNode(locationNodeOrElement, compiler),
684 "Type '$typeString' not found."); 692 MessageKind.GENERIC,
685 return null; 693 {'text': "Type '$typeString' not found."});
694 return const DynamicType();
686 } 695 }
687 696
688 static _errorNode(locationNodeOrElement, compiler) { 697 static _errorNode(locationNodeOrElement, compiler) {
689 if (locationNodeOrElement is Node) return locationNodeOrElement; 698 if (locationNodeOrElement is Node) return locationNodeOrElement;
690 return locationNodeOrElement.parseNode(compiler); 699 return locationNodeOrElement.parseNode(compiler);
691 } 700 }
692 } 701 }
OLDNEW
« no previous file with comments | « no previous file | tests/compiler/dart2js/js_spec_string_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698