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 library dart2js.js_emitter.program_builder; | 5 library dart2js.js_emitter.program_builder; |
6 | 6 |
7 import '../js_emitter.dart' show computeMixinClass, Emitter; | 7 import '../js_emitter.dart' show computeMixinClass, Emitter; |
8 import '../model.dart'; | 8 import '../model.dart'; |
9 | 9 |
10 import '../../common.dart'; | 10 import '../../common.dart'; |
11 import '../../closure.dart' show ClosureFieldElement; | 11 import '../../closure.dart' show ClosureFieldElement; |
12 import '../../js/js.dart' as js; | 12 import '../../js/js.dart' as js; |
13 | 13 |
14 import '../../js_backend/js_backend.dart' show | 14 import '../../js_backend/js_backend.dart' show |
15 Namer, | 15 Namer, |
16 JavaScriptBackend, | 16 JavaScriptBackend, |
17 JavaScriptConstantCompiler; | 17 JavaScriptConstantCompiler, |
18 StringBackedName; | |
18 | 19 |
19 import '../js_emitter.dart' show | 20 import '../js_emitter.dart' show |
20 ClassStubGenerator, | 21 ClassStubGenerator, |
21 CodeEmitterTask, | 22 CodeEmitterTask, |
22 InterceptorStubGenerator, | 23 InterceptorStubGenerator, |
23 MainCallStubGenerator, | 24 MainCallStubGenerator, |
24 ParameterStubGenerator, | 25 ParameterStubGenerator, |
25 RuntimeTypeGenerator, | 26 RuntimeTypeGenerator, |
26 TypeTestProperties; | 27 TypeTestProperties; |
27 | 28 |
28 import '../../elements/elements.dart' show | 29 import '../../elements/elements.dart' show |
29 FieldElement, | 30 FieldElement, |
30 MethodElement, | 31 MethodElement, |
32 Name, | |
31 ParameterElement; | 33 ParameterElement; |
32 | 34 |
33 import '../../universe/universe.dart' show Universe, ReceiverMaskSet; | 35 import '../../universe/universe.dart' show Universe, ReceiverMaskSet; |
34 import '../../deferred_load.dart' show DeferredLoadTask, OutputUnit; | 36 import '../../deferred_load.dart' show DeferredLoadTask, OutputUnit; |
35 | 37 |
36 part 'collector.dart'; | 38 part 'collector.dart'; |
37 part 'registry.dart'; | 39 part 'registry.dart'; |
38 part 'field_visitor.dart'; | 40 part 'field_visitor.dart'; |
39 | 41 |
40 /// Builds a self-contained representation of the program that can then be | 42 /// Builds a self-contained representation of the program that can then be |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
194 return loadMap; | 196 return loadMap; |
195 } | 197 } |
196 | 198 |
197 js.Expression _buildTypeToInterceptorMap() { | 199 js.Expression _buildTypeToInterceptorMap() { |
198 InterceptorStubGenerator stubGenerator = | 200 InterceptorStubGenerator stubGenerator = |
199 new InterceptorStubGenerator(_compiler, namer, backend); | 201 new InterceptorStubGenerator(_compiler, namer, backend); |
200 return stubGenerator.generateTypeToInterceptorMap(); | 202 return stubGenerator.generateTypeToInterceptorMap(); |
201 } | 203 } |
202 | 204 |
203 MainFragment _buildMainFragment(LibrariesMap librariesMap) { | 205 MainFragment _buildMainFragment(LibrariesMap librariesMap) { |
206 _buildJsInteropStubs(librariesMap); | |
sra1
2015/10/01 20:55:28
_addJsInteropStubs()
By convention _buildXXX retur
Jacob
2015/10/02 20:08:16
Done.
| |
204 // Construct the main output from the libraries and the registered holders. | 207 // Construct the main output from the libraries and the registered holders. |
205 MainFragment result = new MainFragment( | 208 MainFragment result = new MainFragment( |
206 librariesMap.outputUnit, | 209 librariesMap.outputUnit, |
207 "", // The empty string is the name for the main output file. | 210 "", // The empty string is the name for the main output file. |
208 _buildInvokeMain(), | 211 _buildInvokeMain(), |
209 _buildLibraries(librariesMap), | 212 _buildLibraries(librariesMap), |
210 _buildStaticNonFinalFields(librariesMap), | 213 _buildStaticNonFinalFields(librariesMap), |
211 _buildStaticLazilyInitializedFields(librariesMap), | 214 _buildStaticLazilyInitializedFields(librariesMap), |
212 _buildConstants(librariesMap)); | 215 _buildConstants(librariesMap)); |
213 _outputs[librariesMap.outputUnit] = result; | 216 _outputs[librariesMap.outputUnit] = result; |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
312 | 315 |
313 List<Library> _buildLibraries(LibrariesMap librariesMap) { | 316 List<Library> _buildLibraries(LibrariesMap librariesMap) { |
314 List<Library> libraries = new List<Library>(librariesMap.length); | 317 List<Library> libraries = new List<Library>(librariesMap.length); |
315 int count = 0; | 318 int count = 0; |
316 librariesMap.forEach((LibraryElement library, List<Element> elements) { | 319 librariesMap.forEach((LibraryElement library, List<Element> elements) { |
317 libraries[count++] = _buildLibrary(library, elements); | 320 libraries[count++] = _buildLibrary(library, elements); |
318 }); | 321 }); |
319 return libraries; | 322 return libraries; |
320 } | 323 } |
321 | 324 |
325 void _buildJsInteropStubs(LibrariesMap librariesMap) { | |
326 if (_classes.containsKey(_compiler.objectClass)) { | |
327 var toStringInvocation = namer.invocationName(new Selector.call( | |
328 new Name("toString", _compiler.objectClass.library), | |
329 CallStructure.NO_ARGS)); | |
330 // TODO(jacobr): register toString as used so that it is always accessible | |
331 // from JavaScript. | |
332 _classes[_compiler.objectClass].callStubs.add(_buildStubMethod( | |
333 new StringBackedName("toString"), | |
334 js.js('function() { return this.#(this) }', toStringInvocation))); | |
335 } | |
336 | |
337 // We add all members from classes marked with isJsInterop to the base | |
338 // Interceptor class with implementations that directly call the | |
339 // corresponding JavaScript member. We do not attempt to bind this when | |
340 // tearing off JavaScript methods as we cannot distinguish between calling | |
341 // a regular getter that returns a JavaScript function and tearing off | |
342 // a method in the case where there exist multiple JavaScript classes | |
343 // that conflict on whether the member is a getter or a method. | |
344 var interceptorClass = _classes[backend.jsInterceptorClass]; | |
345 var stubNames = new Set<String>(); | |
346 librariesMap.forEach((LibraryElement library, List<Element> elements) { | |
347 for (Element e in elements) { | |
348 if (e is ClassElement && e.isJsInterop) { | |
349 e.declaration.forEachMember((_, Element member) { | |
350 if (!member.isInstanceMember) return; | |
351 if (member.isGetter || member.isField || member.isFunction) { | |
352 var selectors = | |
353 _compiler.codegenWorld.getterInvocationsByName(member.name); | |
354 if (selectors != null && !selectors.isEmpty) { | |
355 for (var selector in selectors.keys) { | |
356 var stubName = namer.invocationName(selector); | |
357 if (stubNames.add(stubName.key)) { | |
358 interceptorClass.callStubs.add(_buildStubMethod( | |
359 stubName, | |
360 js.js( | |
361 'function(obj) { return obj.# }', [member.name]), | |
362 element: member)); | |
363 } | |
364 } | |
365 } | |
366 } | |
367 | |
368 if (member.isSetter || (member.isField && !member.isConst)) { | |
369 var selectors = | |
370 _compiler.codegenWorld.setterInvocationsByName(member.name); | |
371 if (selectors != null && !selectors.isEmpty) { | |
372 var stubName = namer.setterForElement(member); | |
373 if (stubNames.add(stubName.key)) { | |
374 interceptorClass.callStubs.add(_buildStubMethod( | |
375 stubName, | |
376 js.js('function(obj, v) { return obj.# = v }', | |
377 [member.name]), | |
378 element: member)); | |
379 } | |
380 } | |
381 } | |
382 | |
383 if (member.isFunction) { | |
384 var selectors = | |
385 _compiler.codegenWorld.invocationsByName(member.name); | |
386 FunctionElement fn = member; | |
387 // Named arguments are not yet supported. In the future we | |
388 // may want to map named arguments to an object literal containing | |
389 // all named arguments. | |
390 if (selectors != null && !selectors.isEmpty) { | |
391 for (var selector in selectors.keys) { | |
392 // Check whether the arity matches this member. | |
393 var argumentCount = selector.argumentCount; | |
394 if (argumentCount > fn.parameters.length) break; | |
395 if (argumentCount < fn.parameters.length && | |
396 !fn.parameters[argumentCount].isOptional) break; | |
397 var stubName = namer.invocationName(selector); | |
398 if (!stubNames.add(stubName.key)) break; | |
399 var parameters = <js.Parameter>[]; | |
400 for (var i = 0; i < argumentCount; i++) { | |
401 parameters.add( | |
402 new js.Parameter(new String.fromCharCode(97 + i))); | |
403 } | |
sra1
2015/10/01 20:55:28
You can just use strings of names.
var parameters
Jacob
2015/10/02 20:08:16
Done.
| |
404 interceptorClass.callStubs.add(_buildStubMethod( | |
405 stubName, | |
406 js.js('function(obj, #) { return obj.#(#) }', | |
sra1
2015/10/01 20:55:28
obj -> receiver for consistency with other stubs.
Jacob
2015/10/02 20:08:16
Done.
| |
407 [parameters, member.name, parameters]), | |
408 element: member)); | |
sra1
2015/10/01 20:55:28
indentation is off
Jacob
2015/10/02 20:08:15
Done.
| |
409 } | |
410 } | |
411 } | |
412 }); | |
413 } | |
414 } | |
415 }); | |
416 } | |
417 | |
322 // Note that a library-element may have multiple [Library]s, if it is split | 418 // Note that a library-element may have multiple [Library]s, if it is split |
323 // into multiple output units. | 419 // into multiple output units. |
324 Library _buildLibrary(LibraryElement library, List<Element> elements) { | 420 Library _buildLibrary(LibraryElement library, List<Element> elements) { |
325 String uri = library.canonicalUri.toString(); | 421 String uri = library.canonicalUri.toString(); |
326 | 422 |
327 List<StaticMethod> statics = elements | 423 List<StaticMethod> statics = elements |
328 .where((e) => e is FunctionElement) | 424 .where((e) => e is FunctionElement) |
329 .map(_buildStaticMethod) | 425 .map(_buildStaticMethod) |
330 .toList(); | 426 .toList(); |
331 | 427 |
(...skipping 27 matching lines...) Expand all Loading... | |
359 | 455 |
360 return new Class( | 456 return new Class( |
361 element, name, null, [], instanceFields, [], [], [], [], [], [], null, | 457 element, name, null, [], instanceFields, [], [], [], [], [], [], null, |
362 isDirectlyInstantiated: true, | 458 isDirectlyInstantiated: true, |
363 onlyForRti: false, | 459 onlyForRti: false, |
364 isNative: element.isNative); | 460 isNative: element.isNative); |
365 } | 461 } |
366 | 462 |
367 Class _buildClass(ClassElement element) { | 463 Class _buildClass(ClassElement element) { |
368 bool onlyForRti = collector.classesOnlyNeededForRti.contains(element); | 464 bool onlyForRti = collector.classesOnlyNeededForRti.contains(element); |
465 bool onlyJsInterop = element.isJsInterop; | |
466 if (onlyJsInterop) { | |
467 // TODO(jacobr): check whether the class has any active static fields | |
468 // if it does not we can suppress it completelly. | |
469 onlyForRti = true; | |
470 } | |
369 | 471 |
370 List<Method> methods = []; | 472 List<Method> methods = []; |
371 List<StubMethod> callStubs = <StubMethod>[]; | 473 List<StubMethod> callStubs = <StubMethod>[]; |
372 | 474 |
373 ClassStubGenerator classStubGenerator = | 475 ClassStubGenerator classStubGenerator = |
374 new ClassStubGenerator(_compiler, namer, backend); | 476 new ClassStubGenerator(_compiler, namer, backend); |
sra1
2015/10/01 20:55:28
repair indentation
Jacob
2015/10/02 20:08:16
Done.
| |
375 RuntimeTypeGenerator runtimeTypeGenerator = | 477 RuntimeTypeGenerator runtimeTypeGenerator = |
376 new RuntimeTypeGenerator(_compiler, _task, namer); | 478 new RuntimeTypeGenerator(_compiler, _task, namer); |
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:15
Done.
| |
377 | 479 |
378 void visitMember(ClassElement enclosing, Element member) { | 480 void visitMember(ClassElement enclosing, Element member) { |
379 assert(invariant(element, member.isDeclaration)); | 481 assert(invariant(element, member.isDeclaration)); |
380 assert(invariant(element, element == enclosing)); | 482 assert(invariant(element, element == enclosing)); |
381 | 483 |
382 if (Elements.isNonAbstractInstanceMember(member)) { | 484 if (Elements.isNonAbstractInstanceMember(member)) { |
383 // TODO(herhut): Remove once _buildMethod can no longer return null. | 485 // TODO(herhut): Remove once _buildMethod can no longer return null. |
384 Method method = _buildMethod(member); | 486 Method method = _buildMethod(member); |
385 if (method != null) methods.add(method); | 487 if (method != null) methods.add(method); |
386 } | 488 } |
387 if (member.isGetter || member.isField) { | 489 if (member.isGetter || member.isField) { |
388 Map<Selector, ReceiverMaskSet> selectors = | 490 Map<Selector, ReceiverMaskSet> selectors = |
389 _compiler.codegenWorld.invocationsByName(member.name); | 491 _compiler.codegenWorld.invocationsByName(member.name); |
390 if (selectors != null && !selectors.isEmpty) { | 492 if (selectors != null && !selectors.isEmpty) { |
391 | 493 |
392 Map<js.Name, js.Expression> callStubsForMember = | 494 Map<js.Name, js.Expression> callStubsForMember = |
393 classStubGenerator.generateCallStubsForGetter(member, selectors); | 495 classStubGenerator.generateCallStubsForGetter(member, selectors); |
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:15
Done.
| |
394 callStubsForMember.forEach((js.Name name, js.Expression code) { | 496 callStubsForMember.forEach((js.Name name, js.Expression code) { |
395 callStubs.add(_buildStubMethod(name, code, element: member)); | 497 callStubs.add(_buildStubMethod(name, code, element: member)); |
396 }); | 498 }); |
397 } | 499 } |
398 } | 500 } |
399 } | 501 } |
400 | 502 |
401 List<StubMethod> typeVariableReaderStubs = | 503 List<StubMethod> typeVariableReaderStubs = |
402 runtimeTypeGenerator.generateTypeVariableReaderStubs(element); | 504 runtimeTypeGenerator.generateTypeVariableReaderStubs(element); |
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:16
done... arghhh... intelij
| |
403 | 505 |
404 List<StubMethod> noSuchMethodStubs = <StubMethod>[]; | 506 List<StubMethod> noSuchMethodStubs = <StubMethod>[]; |
405 | 507 |
406 if (backend.enabledNoSuchMethod && element == _compiler.objectClass) { | 508 if (backend.enabledNoSuchMethod && element == _compiler.objectClass) { |
407 Map<js.Name, Selector> selectors = | 509 Map<js.Name, Selector> selectors = |
408 classStubGenerator.computeSelectorsForNsmHandlers(); | 510 classStubGenerator.computeSelectorsForNsmHandlers(); |
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:15
Done.
| |
409 selectors.forEach((js.Name name, Selector selector) { | 511 selectors.forEach((js.Name name, Selector selector) { |
410 // If the program contains `const Symbol` names we have to retain them. | 512 // If the program contains `const Symbol` names we have to retain them. |
411 String selectorName = selector.name; | 513 String selectorName = selector.name; |
412 if (selector.isSetter) selectorName = "$selectorName="; | 514 if (selector.isSetter) selectorName = "$selectorName="; |
413 if (backend.symbolsUsed.contains(selectorName)) { | 515 if (backend.symbolsUsed.contains(selectorName)) { |
414 _symbolsMap[name] = selectorName; | 516 _symbolsMap[name] = selectorName; |
415 } | 517 } |
416 noSuchMethodStubs | 518 noSuchMethodStubs |
417 .add(classStubGenerator.generateStubForNoSuchMethod(name, | 519 .add(classStubGenerator.generateStubForNoSuchMethod(name, |
418 selector)); | 520 selector)); |
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:16
Done.
| |
419 }); | 521 }); |
420 } | 522 } |
421 | 523 |
422 if (element == backend.closureClass) { | 524 if (element == backend.closureClass) { |
423 // We add a special getter here to allow for tearing off a closure from | 525 // We add a special getter here to allow for tearing off a closure from |
424 // itself. | 526 // itself. |
425 js.Name name = namer.getterForMember(Selector.CALL_NAME); | 527 js.Name name = namer.getterForMember(Selector.CALL_NAME); |
426 js.Fun function = js.js('function() { return this; }'); | 528 js.Fun function = js.js('function() { return this; }'); |
427 callStubs.add(_buildStubMethod(name, function)); | 529 callStubs.add(_buildStubMethod(name, function)); |
428 } | 530 } |
429 | 531 |
430 ClassElement implementation = element.implementation; | 532 ClassElement implementation = element.implementation; |
431 | 533 |
432 // MixinApplications run through the members of their mixin. Here, we are | 534 // MixinApplications run through the members of their mixin. Here, we are |
433 // only interested in direct members. | 535 // only interested in direct members. |
434 if (!onlyForRti && !element.isMixinApplication) { | 536 if (!onlyForRti && !element.isMixinApplication) { |
435 implementation.forEachMember(visitMember, includeBackendMembers: true); | 537 implementation.forEachMember(visitMember, includeBackendMembers: true); |
436 } | 538 } |
437 | 539 |
438 List<Field> instanceFields = | 540 List<Field> instanceFields = |
439 onlyForRti ? const <Field>[] : _buildFields(element, false); | 541 onlyForRti ? const <Field>[] : _buildFields(element, false); |
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:16
Done.
| |
440 List<Field> staticFieldsForReflection = | 542 List<Field> staticFieldsForReflection = |
441 onlyForRti ? const <Field>[] : _buildFields(element, true); | 543 onlyForRti ? const <Field>[] : _buildFields(element, true); |
442 | 544 |
443 TypeTestProperties typeTests = | 545 TypeTestProperties typeTests = |
444 runtimeTypeGenerator.generateIsTests( | 546 runtimeTypeGenerator.generateIsTests( |
445 element, | 547 element, |
446 storeFunctionTypeInMetadata: _storeFunctionTypesInMetadata); | 548 storeFunctionTypeInMetadata: _storeFunctionTypesInMetadata); |
447 | 549 |
448 List<StubMethod> checkedSetters = <StubMethod>[]; | 550 List<StubMethod> checkedSetters = <StubMethod>[]; |
449 for (Field field in instanceFields) { | 551 List<StubMethod> isChecks = <StubMethod>[]; |
450 if (field.needsCheckedSetter) { | 552 if (!onlyJsInterop) { |
451 assert(!field.needsUncheckedSetter); | 553 for (Field field in instanceFields) { |
452 Element element = field.element; | 554 if (field.needsCheckedSetter) { |
453 js.Expression code = backend.generatedCode[element]; | 555 assert(!field.needsUncheckedSetter); |
454 assert(code != null); | 556 Element element = field.element; |
455 js.Name name = namer.deriveSetterName(field.accessorName); | 557 js.Expression code = backend.generatedCode[element]; |
456 checkedSetters.add(_buildStubMethod(name, code, element: element)); | 558 assert(code != null); |
559 js.Name name = namer.deriveSetterName(field.accessorName); | |
560 checkedSetters.add(_buildStubMethod(name, code, element: element)); | |
561 } | |
457 } | 562 } |
563 | |
564 typeTests.properties.forEach((js.Name name, js.Node code) { | |
565 isChecks.add(_buildStubMethod(name, code)); | |
566 }); | |
458 } | 567 } |
459 | 568 if (element.isJsInterop) { |
sra1
2015/10/01 20:55:28
use one of element.isJsInterop or onlyJsInterop co
Jacob
2015/10/02 20:08:16
Done.
| |
460 List<StubMethod> isChecks = <StubMethod>[]; | 569 // TODO(jacobr): make sure we aren't adding the same is checks multiple ti mes.. |
sra1
2015/10/01 20:55:28
line lengyh
Jacob
2015/10/02 20:08:15
fixedh
| |
461 typeTests.properties.forEach((js.Name name, js.Node code) { | 570 typeTests.properties.forEach((js.Name name, js.Node code) { |
462 isChecks.add(_buildStubMethod(name, code)); | 571 _classes[backend.jsInterceptorClass].isChecks.add(_buildStubMethod(name, code)); |
463 }); | 572 }); |
573 } | |
464 | 574 |
465 js.Name name = namer.className(element); | 575 js.Name name = namer.className(element); |
466 String holderName = namer.globalObjectFor(element); | 576 String holderName = namer.globalObjectFor(element); |
467 // TODO(floitsch): we shouldn't update the registry in the middle of | 577 // TODO(floitsch): we shouldn't update the registry in the middle of |
468 // building a class. | 578 // building a class. |
469 Holder holder = _registry.registerHolder(holderName); | 579 Holder holder = _registry.registerHolder(holderName); |
470 bool isInstantiated = | 580 bool isInstantiated = onlyJsInterop ? false : |
471 _compiler.codegenWorld.directlyInstantiatedClasses.contains(element); | 581 _compiler.codegenWorld.directlyInstantiatedClasses.contains(element); |
472 | 582 |
473 Class result; | 583 Class result; |
474 if (element.isMixinApplication && !onlyForRti) { | 584 if (element.isMixinApplication && !onlyForRti) { |
475 assert(!element.isNative); | 585 assert(!element.isNative); |
476 assert(methods.isEmpty); | 586 assert(methods.isEmpty); |
477 | 587 |
478 result = new MixinApplication(element, | 588 result = new MixinApplication(element, |
479 name, holder, | 589 name, holder, |
480 instanceFields, | 590 instanceFields, |
481 staticFieldsForReflection, | 591 staticFieldsForReflection, |
(...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
829 Constant constant = new Constant(name, holder, constantValue); | 939 Constant constant = new Constant(name, holder, constantValue); |
830 _constants[constantValue] = constant; | 940 _constants[constantValue] = constant; |
831 } | 941 } |
832 } | 942 } |
833 | 943 |
834 Holder _registerStaticStateHolder() { | 944 Holder _registerStaticStateHolder() { |
835 return _registry.registerHolder( | 945 return _registry.registerHolder( |
836 namer.staticStateHolder, isStaticStateHolder: true); | 946 namer.staticStateHolder, isStaticStateHolder: true); |
837 } | 947 } |
838 } | 948 } |
OLD | NEW |