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

Side by Side Diff: pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart

Issue 1318043005: Support user generated custom native JS classes. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 3 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
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 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
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);
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
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 }
404 interceptorClass.callStubs.add(_buildStubMethod(
405 stubName,
406 js.js('function(obj, #) { return obj.#(#) }',
407 [parameters, member.name, parameters]),
408 element: member));
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
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; // XXX is this right?
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);
alexandre.ardhuin 2015/09/03 20:14:11 There are some strange indentations in the rest of
375 RuntimeTypeGenerator runtimeTypeGenerator = 477 RuntimeTypeGenerator runtimeTypeGenerator =
376 new RuntimeTypeGenerator(_compiler, _task, namer); 478 new RuntimeTypeGenerator(_compiler, _task, namer);
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);
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);
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();
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));
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);
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) {
460 List<StubMethod> isChecks = <StubMethod>[]; 569 // TODO(jacobr): make sure we aren't adding the same is checks multiple ti mes..
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698