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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_emitter/old_emitter/type_test_emitter.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
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.
4
5 part of dart2js.js_emitter;
6
7 class TypeTestEmitter extends CodeEmitterHelper {
8 static const int MAX_FUNCTION_TYPE_PREDICATES = 10;
9
10 /**
11 * Raw ClassElement symbols occuring in is-checks and type assertions. If the
12 * program contains parameterized checks `x is Set<int>` and
13 * `x is Set<String>` then the ClassElement `Set` will occur once in
14 * [checkedClasses].
15 */
16 Set<ClassElement> checkedClasses;
17
18 /**
19 * The set of function types that checked, both explicity through tests of
20 * typedefs and implicitly through type annotations in checked mode.
21 */
22 Set<FunctionType> checkedFunctionTypes;
23
24 Map<ClassElement, Set<FunctionType>> checkedGenericFunctionTypes =
25 new Map<ClassElement, Set<FunctionType>>();
26
27 Set<FunctionType> checkedNonGenericFunctionTypes =
28 new Set<FunctionType>();
29
30 final Set<ClassElement> rtiNeededClasses = new Set<ClassElement>();
31
32 Iterable<ClassElement> cachedClassesUsingTypeVariableTests;
33
34 Iterable<ClassElement> get classesUsingTypeVariableTests {
35 if (cachedClassesUsingTypeVariableTests == null) {
36 cachedClassesUsingTypeVariableTests = compiler.codegenWorld.isChecks
37 .where((DartType t) => t is TypeVariableType)
38 .map((TypeVariableType v) => v.element.enclosingClass)
39 .toList();
40 }
41 return cachedClassesUsingTypeVariableTests;
42 }
43
44 void emitIsTests(ClassElement classElement, ClassBuilder builder) {
45 assert(invariant(classElement, classElement.isDeclaration));
46
47 void generateIsTest(Element other) {
48 if (other == compiler.objectClass && other != classElement) {
49 // Avoid emitting [:$isObject:] on all classes but [Object].
50 return;
51 }
52 builder.addProperty(namer.operatorIs(other), js('true'));
53 }
54
55 void generateFunctionTypeSignature(FunctionElement method,
56 FunctionType type) {
57 assert(method.isImplementation);
58 jsAst.Expression thisAccess = new jsAst.This();
59 Node node = method.node;
60 ClosureClassMap closureData =
61 compiler.closureToClassMapper.closureMappingCache[node];
62 if (closureData != null) {
63 ClosureFieldElement thisLocal =
64 closureData.getFreeVariableElement(closureData.thisLocal);
65 if (thisLocal != null) {
66 String thisName = namer.instanceFieldPropertyName(thisLocal);
67 thisAccess = js('this.#', thisName);
68 }
69 }
70 RuntimeTypes rti = backend.rti;
71 jsAst.Expression encoding = rti.getSignatureEncoding(type, thisAccess);
72 String operatorSignature = namer.operatorSignature();
73 if (!type.containsTypeVariables) {
74 builder.functionType = '${emitter.metadataEmitter.reifyType(type)}';
75 } else {
76 builder.addProperty(operatorSignature, encoding);
77 }
78 }
79
80 void generateSubstitution(ClassElement cls, {bool emitNull: false}) {
81 if (cls.typeVariables.isEmpty) return;
82 RuntimeTypes rti = backend.rti;
83 jsAst.Expression expression;
84 bool needsNativeCheck = emitter.nativeEmitter.requiresNativeIsCheck(cls);
85 expression = rti.getSupertypeSubstitution(
86 classElement, cls, alwaysGenerateFunction: true);
87 if (expression == null && (emitNull || needsNativeCheck)) {
88 expression = new jsAst.LiteralNull();
89 }
90 if (expression != null) {
91 builder.addProperty(namer.substitutionName(cls), expression);
92 }
93 }
94
95 generateIsTestsOn(classElement, generateIsTest,
96 generateFunctionTypeSignature,
97 generateSubstitution);
98 }
99
100 /**
101 * Generate "is tests" for [cls]: itself, and the "is tests" for the
102 * classes it implements and type argument substitution functions for these
103 * tests. We don't need to add the "is tests" of the super class because
104 * they will be inherited at runtime, but we may need to generate the
105 * substitutions, because they may have changed.
106 */
107 void generateIsTestsOn(ClassElement cls,
108 void emitIsTest(Element element),
109 FunctionTypeSignatureEmitter emitFunctionTypeSignature,
110 SubstitutionEmitter emitSubstitution) {
111 if (checkedClasses.contains(cls)) {
112 emitIsTest(cls);
113 emitSubstitution(cls);
114 }
115
116 RuntimeTypes rti = backend.rti;
117 ClassElement superclass = cls.superclass;
118
119 bool haveSameTypeVariables(ClassElement a, ClassElement b) {
120 if (a.isClosure) return true;
121 return backend.rti.isTrivialSubstitution(a, b);
122 }
123
124 if (superclass != null && superclass != compiler.objectClass &&
125 !haveSameTypeVariables(cls, superclass)) {
126 // We cannot inherit the generated substitutions, because the type
127 // variable layout for this class is different. Instead we generate
128 // substitutions for all checks and make emitSubstitution a NOP for the
129 // rest of this function.
130 Set<ClassElement> emitted = new Set<ClassElement>();
131 // TODO(karlklose): move the computation of these checks to
132 // RuntimeTypeInformation.
133 while (superclass != null) {
134 if (backend.classNeedsRti(superclass)) {
135 emitSubstitution(superclass, emitNull: true);
136 emitted.add(superclass);
137 }
138 superclass = superclass.superclass;
139 }
140 for (DartType supertype in cls.allSupertypes) {
141 ClassElement superclass = supertype.element;
142 if (classesUsingTypeVariableTests.contains(superclass)) {
143 emitSubstitution(superclass, emitNull: true);
144 emitted.add(superclass);
145 }
146 for (ClassElement check in checkedClasses) {
147 if (supertype.element == check && !emitted.contains(check)) {
148 // Generate substitution. If no substitution is necessary, emit
149 // [:null:] to overwrite a (possibly) existing substitution from the
150 // super classes.
151 emitSubstitution(check, emitNull: true);
152 emitted.add(check);
153 }
154 }
155 }
156 void emitNothing(_, {emitNull}) {};
157 emitSubstitution = emitNothing;
158 }
159
160 Set<Element> generated = new Set<Element>();
161 // A class that defines a [:call:] method implicitly implements
162 // [Function] and needs checks for all typedefs that are used in is-checks.
163 if (checkedClasses.contains(compiler.functionClass) ||
164 !checkedFunctionTypes.isEmpty) {
165 Element call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME);
166 if (call == null) {
167 // If [cls] is a closure, it has a synthetic call operator method.
168 call = cls.lookupBackendMember(Compiler.CALL_OPERATOR_NAME);
169 }
170 if (call != null && call.isFunction) {
171 generateInterfacesIsTests(compiler.functionClass,
172 emitIsTest,
173 emitSubstitution,
174 generated);
175 FunctionType callType = call.computeType(compiler);
176 Map<FunctionType, bool> functionTypeChecks =
177 getFunctionTypeChecksOn(callType);
178 generateFunctionTypeTests(
179 call, callType, functionTypeChecks,
180 emitFunctionTypeSignature);
181 }
182 }
183
184 for (DartType interfaceType in cls.interfaces) {
185 generateInterfacesIsTests(interfaceType.element, emitIsTest,
186 emitSubstitution, generated);
187 }
188 }
189
190 /**
191 * Generate "is tests" where [cls] is being implemented.
192 */
193 void generateInterfacesIsTests(ClassElement cls,
194 void emitIsTest(ClassElement element),
195 SubstitutionEmitter emitSubstitution,
196 Set<Element> alreadyGenerated) {
197 void tryEmitTest(ClassElement check) {
198 if (!alreadyGenerated.contains(check) && checkedClasses.contains(check)) {
199 alreadyGenerated.add(check);
200 emitIsTest(check);
201 emitSubstitution(check);
202 }
203 };
204
205 tryEmitTest(cls);
206
207 for (DartType interfaceType in cls.interfaces) {
208 Element element = interfaceType.element;
209 tryEmitTest(element);
210 generateInterfacesIsTests(element, emitIsTest, emitSubstitution,
211 alreadyGenerated);
212 }
213
214 // We need to also emit "is checks" for the superclass and its supertypes.
215 ClassElement superclass = cls.superclass;
216 if (superclass != null) {
217 tryEmitTest(superclass);
218 generateInterfacesIsTests(superclass, emitIsTest, emitSubstitution,
219 alreadyGenerated);
220 }
221 }
222
223 /**
224 * Returns a mapping containing all checked function types for which [type]
225 * can be a subtype. A function type is mapped to [:true:] if [type] is
226 * statically known to be a subtype of it and to [:false:] if [type] might
227 * be a subtype, provided with the right type arguments.
228 */
229 // TODO(johnniwinther): Change to return a mapping from function types to
230 // a set of variable points and use this to detect statically/dynamically
231 // known subtype relations.
232 Map<FunctionType, bool> getFunctionTypeChecksOn(DartType type) {
233 Map<FunctionType, bool> functionTypeMap = new Map<FunctionType, bool>();
234 for (FunctionType functionType in checkedFunctionTypes) {
235 int maybeSubtype =
236 compiler.types.computeSubtypeRelation(type, functionType);
237 if (maybeSubtype == Types.IS_SUBTYPE) {
238 functionTypeMap[functionType] = true;
239 } else if (maybeSubtype == Types.MAYBE_SUBTYPE) {
240 functionTypeMap[functionType] = false;
241 }
242 }
243 // TODO(johnniwinther): Ensure stable ordering of the keys.
244 return functionTypeMap;
245 }
246
247 /**
248 * Generates function type checks on [method] with type [methodType] against
249 * the function type checks in [functionTypeChecks].
250 */
251 void generateFunctionTypeTests(
252 Element method,
253 FunctionType methodType,
254 Map<FunctionType, bool> functionTypeChecks,
255 FunctionTypeSignatureEmitter emitFunctionTypeSignature) {
256
257 // TODO(ahe): We should be able to remove this forEach loop.
258 functionTypeChecks.forEach((FunctionType functionType, bool knownSubtype) {
259 registerDynamicFunctionTypeCheck(functionType);
260 });
261
262 emitFunctionTypeSignature(method, methodType);
263 }
264
265 void registerDynamicFunctionTypeCheck(FunctionType functionType) {
266 ClassElement classElement = Types.getClassContext(functionType);
267 if (classElement != null) {
268 checkedGenericFunctionTypes.putIfAbsent(classElement,
269 () => new Set<FunctionType>()).add(functionType);
270 } else {
271 checkedNonGenericFunctionTypes.add(functionType);
272 }
273 }
274
275 void emitRuntimeTypeSupport(CodeBuffer buffer, OutputUnit outputUnit) {
276 emitter.addComment('Runtime type support', buffer);
277 RuntimeTypes rti = backend.rti;
278 TypeChecks typeChecks = rti.requiredChecks;
279
280 // Add checks to the constructors of instantiated classes.
281 // TODO(sigurdm): We should avoid running through this list for each
282 // output unit.
283
284 jsAst.Statement variables = js.statement('var TRUE = !0, _;');
285 List<jsAst.Statement> statements = <jsAst.Statement>[];
286
287 for (ClassElement cls in typeChecks) {
288 OutputUnit destination =
289 compiler.deferredLoadTask.outputUnitForElement(cls);
290 if (destination != outputUnit) continue;
291 // TODO(9556). The properties added to 'holder' should be generated
292 // directly as properties of the class object, not added later.
293
294 // Each element is a pair: [propertyName, valueExpression]
295 List<List> properties = <List>[];
296
297 for (TypeCheck check in typeChecks[cls]) {
298 ClassElement checkedClass = check.cls;
299 properties.add([namer.operatorIs(checkedClass), js('TRUE')]);
300 Substitution substitution = check.substitution;
301 if (substitution != null) {
302 jsAst.Expression body = substitution.getCode(rti, false);
303 properties.add([namer.substitutionName(checkedClass), body]);
304 }
305 }
306
307 jsAst.Expression holder = namer.elementAccess(cls);
308 if (properties.length > 1) {
309 // Use temporary shortened reference.
310 statements.add(js.statement('_ = #;', holder));
311 holder = js('#', '_');
312 }
313 for (List nameAndValue in properties) {
314 statements.add(
315 js.statement('#.# = #',
316 [holder, nameAndValue[0], nameAndValue[1]]));
317 }
318 }
319
320 if (statements.isNotEmpty) {
321 buffer.write(';');
322 buffer.write(
323 jsAst.prettyPrint(
324 js.statement('(function() { #; #; })()', [variables, statements]),
325 compiler));
326 buffer.write('$N');
327 }
328 }
329
330 /**
331 * Returns the classes with constructors used as a 'holder' in
332 * [emitRuntimeTypeSupport].
333 * TODO(9556): Some cases will go away when the class objects are created as
334 * complete. Not all classes will go away while constructors are referenced
335 * from type substitutions.
336 */
337 Set<ClassElement> classesModifiedByEmitRuntimeTypeSupport() {
338 TypeChecks typeChecks = backend.rti.requiredChecks;
339 Set<ClassElement> result = new Set<ClassElement>();
340 for (ClassElement cls in typeChecks) {
341 for (TypeCheck check in typeChecks[cls]) {
342 result.add(cls);
343 break;
344 }
345 }
346 return result;
347 }
348
349 Set<ClassElement> computeRtiNeededClasses() {
350 void addClassWithSuperclasses(ClassElement cls) {
351 rtiNeededClasses.add(cls);
352 for (ClassElement superclass = cls.superclass;
353 superclass != null;
354 superclass = superclass.superclass) {
355 rtiNeededClasses.add(superclass);
356 }
357 }
358
359 void addClassesWithSuperclasses(Iterable<ClassElement> classes) {
360 for (ClassElement cls in classes) {
361 addClassWithSuperclasses(cls);
362 }
363 }
364
365 // 1. Add classes that are referenced by type arguments or substitutions in
366 // argument checks.
367 // TODO(karlklose): merge this case with 2 when unifying argument and
368 // object checks.
369 RuntimeTypes rti = backend.rti;
370 rti.getRequiredArgumentClasses(backend)
371 .forEach(addClassWithSuperclasses);
372
373 // 2. Add classes that are referenced by substitutions in object checks and
374 // their superclasses.
375 TypeChecks requiredChecks =
376 rti.computeChecks(rtiNeededClasses, checkedClasses);
377 Set<ClassElement> classesUsedInSubstitutions =
378 rti.getClassesUsedInSubstitutions(backend, requiredChecks);
379 addClassesWithSuperclasses(classesUsedInSubstitutions);
380
381 // 3. Add classes that contain checked generic function types. These are
382 // needed to store the signature encoding.
383 for (FunctionType type in checkedFunctionTypes) {
384 ClassElement contextClass = Types.getClassContext(type);
385 if (contextClass != null) {
386 rtiNeededClasses.add(contextClass);
387 }
388 }
389
390 bool canTearOff(Element function) {
391 if (!function.isFunction ||
392 function.isConstructor ||
393 function.isAccessor) {
394 return false;
395 } else if (function.isInstanceMember) {
396 if (!function.enclosingClass.isClosure) {
397 return compiler.codegenWorld.hasInvokedGetter(
398 function, compiler.world);
399 }
400 }
401 return false;
402 }
403
404 bool canBeReflectedAsFunction(Element element) {
405 return element.kind == ElementKind.FUNCTION ||
406 element.kind == ElementKind.GETTER ||
407 element.kind == ElementKind.SETTER ||
408 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR;
409 }
410
411 bool canBeReified(Element element) {
412 return (canTearOff(element) || backend.isAccessibleByReflection(element));
413 }
414
415 // Find all types referenced from the types of elements that can be
416 // reflected on 'as functions'.
417 backend.generatedCode.keys.where((element) {
418 return canBeReflectedAsFunction(element) && canBeReified(element);
419 }).forEach((FunctionElement function) {
420 DartType type = function.computeType(compiler);
421 for (ClassElement cls in backend.rti.getReferencedClasses(type)) {
422 while (cls != null) {
423 rtiNeededClasses.add(cls);
424 cls = cls.superclass;
425 }
426 }
427 });
428
429 return rtiNeededClasses;
430 }
431
432 void computeRequiredTypeChecks() {
433 assert(checkedClasses == null && checkedFunctionTypes == null);
434
435 backend.rti.addImplicitChecks(compiler.codegenWorld,
436 classesUsingTypeVariableTests);
437
438 checkedClasses = new Set<ClassElement>();
439 checkedFunctionTypes = new Set<FunctionType>();
440 compiler.codegenWorld.isChecks.forEach((DartType t) {
441 if (t is InterfaceType) {
442 checkedClasses.add(t.element);
443 } else if (t is FunctionType) {
444 checkedFunctionTypes.add(t);
445 }
446 });
447 }
448 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698