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

Side by Side Diff: frog/value.dart

Issue 9110027: Some cleanups to Frog to avoid looking up its builtin types too much (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: updated Created 8 years, 11 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
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, 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 /** 5 /**
6 * Represents a meta-value for code generation. 6 * Represents a meta-value for code generation.
7 */ 7 */
8 class Value { 8 class Value {
9 Type _type; 9 Type _type;
10 10
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after
204 /** Generate a call to an unknown function type. */ 204 /** Generate a call to an unknown function type. */
205 Value _varCall(MethodGenerator context, Node node, Arguments args) { 205 Value _varCall(MethodGenerator context, Node node, Arguments args) {
206 // TODO(jmesserly): calls to unknown functions will bypass type checks, 206 // TODO(jmesserly): calls to unknown functions will bypass type checks,
207 // which normally happen on the caller side, or in the generated stub for 207 // which normally happen on the caller side, or in the generated stub for
208 // dynamic method calls. What should we do? 208 // dynamic method calls. What should we do?
209 var stub = world.functionType.getCallStub(args); 209 var stub = world.functionType.getCallStub(args);
210 return stub.invoke(context, node, this, args); 210 return stub.invoke(context, node, this, args);
211 } 211 }
212 212
213 /** True if convertTo would generate a conversion. */ 213 /** True if convertTo would generate a conversion. */
214 // TODO(jmesserly): I don't like how this is coupled to convertTo.
215 bool needsConversion(Type toType) { 214 bool needsConversion(Type toType) {
216 var callMethod = toType.getCallMethod(); 215 return this != convertTo(null, toType, isDynamic:true);
217 if (callMethod != null) {
218 int arity = callMethod.parameters.length;
219 var myCall = type.getCallMethod();
220 if (myCall == null || myCall.parameters.length != arity) {
221 return true;
222 }
223 }
224 if (options.enableTypeChecks) {
225 Type fromType = type;
226 if (type.isVar && (code != 'null' || !toType.isNullable)) {
227 fromType = world.objectType;
228 }
229 bool bothNum = type.isNum && toType.isNum;
230 return !(fromType.isSubtypeOf(toType) || bothNum);
231 }
232 return false;
233 } 216 }
234 217
235 /** 218 /**
236 * Assign or convert this value to another type. 219 * Assign or convert this value to another type.
237 * This is used for converting between function types, inserting type 220 * This is used for converting between function types, inserting type
238 * checks when --enable_type_checks is enabled, and wrapping callback 221 * checks when --enable_type_checks is enabled, and wrapping callback
239 * functions passed to the dom so we can restore their isolate context. 222 * functions passed to the dom so we can restore their isolate context.
240 */ 223 */
241 // WARNING: this needs to be kept in sync with needsConversion above. 224 Value convertTo(MethodGenerator context, Type toType,
242 Value convertTo(MethodGenerator context, Type toType, Node node,
243 [bool isDynamic=false]) { 225 [bool isDynamic=false]) {
244 226
245 // Issue type warnings unless we are processing a dynamic operation. 227 // Issue type warnings unless we are processing a dynamic operation.
246 bool checked = !isDynamic; 228 bool checked = !isDynamic;
247 229
248 var callMethod = toType.getCallMethod(); 230 var callMethod = toType.getCallMethod();
249 if (callMethod != null) { 231 if (callMethod != null) {
250 if (checked && !toType.isAssignable(type)) { 232 if (checked && !toType.isAssignable(type)) {
251 convertWarning(toType, node); 233 convertWarning(toType);
252 } 234 }
253 235
254 int arity = callMethod.parameters.length; 236 return _maybeWrapFunction(toType, callMethod);
255 var myCall = type.getCallMethod();
256 if (myCall == null || myCall.parameters.length != arity) {
257 final stub = world.functionType.getCallStub(new Arguments.bare(arity));
258 var val = new Value(toType, 'to\$${stub.name}($code)', node.span);
259 // TODO(sigmund): try to remove, see below
260 return _isDomCallback(toType) && !_isDomCallback(type) ?
261 val._wrapDomCallback(toType, arity) : val;
262 } else if (_isDomCallback(toType) && !_isDomCallback(type)) {
263 // TODO(sigmund): try to remove, see below
264 return _wrapDomCallback(toType, arity);
265 }
266 } 237 }
267 238
268 // If we're assigning from a var, pretend it's Object for the purpose of 239 // If we're assigning from a var, pretend it's Object for the purpose of
269 // runtime checks. 240 // runtime checks.
270 241
271 // TODO(jmesserly): I'm a little bothered by the fact that we can't call 242 // TODO(jmesserly): I'm a little bothered by the fact that we can't call
272 // isSubtypeOf directly. If we tracked null literals as the bottom type, 243 // isSubtypeOf directly. If we tracked null literals as the bottom type,
273 // and then only allowed Dynamic to be bottom for generic type args, I think 244 // and then only allowed Dynamic to be bottom for generic type args, I think
274 // we'd get the right behavior from isSubtypeOf. 245 // we'd get the right behavior from isSubtypeOf.
275 Type fromType = type; 246 Type fromType = type;
276 if (type.isVar && (code != 'null' || !toType.isNullable)) { 247 if (type.isVar && (code != 'null' || !toType.isNullable)) {
277 fromType = world.objectType; 248 fromType = world.objectType;
278 } 249 }
279 250
280 // TODO(jmesserly): remove the special case for "num" when our num handling 251 // TODO(jmesserly): remove the special case for "num" when our num handling
281 // is better. 252 // is better.
282 bool bothNum = type.isNum && toType.isNum; 253 bool bothNum = type.isNum && toType.isNum;
283 if (fromType.isSubtypeOf(toType) || bothNum) { 254 if (fromType.isSubtypeOf(toType) || bothNum) {
284 // No checks needed for a widening conversion. 255 // No checks needed for a widening conversion.
285 return this; 256 return this;
286 } 257 }
287 258
288 if (checked && !toType.isSubtypeOf(type)) { 259 if (checked && !toType.isSubtypeOf(type)) {
289 // According to the static types, this conversion can't work. 260 // According to the static types, this conversion can't work.
290 convertWarning(toType, node); 261 convertWarning(toType);
291 } 262 }
292 263
293 // Generate a runtime checks if they're turned on, otherwise skip it. 264 // Generate a runtime checks if they're turned on, otherwise skip it.
294 if (options.enableTypeChecks) { 265 if (options.enableTypeChecks) {
295 return _typeAssert(context, toType, node, isDynamic); 266 if (context == null && isDynamic) {
267 // If we're just testing if we need the conversion, not actually doing
268 // it we don't need a context. Just return something that is != this.
269 // TODO(jmesserly): I don't like using null in this fashion, but it's
270 // better than before where we had two code paths that needed to be
271 // kept in sync.
272 return null;
273 }
274 return _typeAssert(context, toType, isDynamic);
296 } else { 275 } else {
297 return this; 276 return this;
298 } 277 }
299 } 278 }
300 279
301 /** 280 /**
302 * Checks whether [toType] is a callback function, and it is defined in the 281 * Wraps a function with a conversion, so it can be called directly from
303 * dom library. 282 * Dart or JS code with the proper arity. We avoid the wrapping if the target
283 * function has the same arity.
284 *
285 * Also wraps a callback attached to the dom (e.g. event listeners,
286 * setTimeout) so we can restore it's isolate context information. This is
287 * needed so that callbacks are executed within the context of the isolate
288 * that created them in the first place.
304 */ 289 */
305 bool _isDomCallback(toType) { 290 Value _maybeWrapFunction(Type toType, MethodMember callMethod) {
306 return (toType.definition is FunctionTypeDefinition 291 int arity = callMethod.parameters.length;
307 && toType.library == world.dom); 292 var myCall = type.getCallMethod();
293
294 Value result = this;
295 if (myCall == null || myCall.parameters.length != arity) {
296 final stub = world.functionType.getCallStub(new Arguments.bare(arity));
297 result = new Value(toType, 'to\$${stub.name}($code)', span);
298 }
299
300 if (toType.library.isDom && !type.library.isDom) {
301 // TODO(jmesserly): either remove this or make it a more first class
302 // feature of our native interop. We shouldn't be checking for the DOM
303 // library--any host environment (like node.js) might need this feature
304 // for isolates too. But we don't want to wrap every function we send to
305 // native code--many callbacks like List.filter are perfectly safe.
306 if (arity == 0) {
307 world.gen.corejs.useWrap0 = true;
308 } else {
309 world.gen.corejs.useWrap1 = true;
310 }
311
312 result = new Value(toType, '\$wrap_call\$$arity(${result.code})', span);
313 }
314
315 return result;
308 } 316 }
309 317
310 /** 318 /**
311 * Wraps a callback attached to the dom (e.g. event listeners, setTimeout) so
312 * we can restore it's isolate context information. This is needed so that
313 * callbacks are executed within the context of the isolate that created them
314 * in the first place.
315 */
316 // TODO(sigmund): try to remove this specialized logic about isolates
317 // and the dom from the compiler, move into the actual dom library if
318 // possible.
319 Value _wrapDomCallback(Type toType, int arity) {
320 if (arity == 0) {
321 world.gen.corejs.useWrap0 = true;
322 } else {
323 world.gen.corejs.useWrap1 = true;
324 }
325 return new Value(toType, '\$wrap_call\$$arity($code)', span);
326 }
327
328 /**
329 * Generates a run time type assertion for the given value. This works like 319 * Generates a run time type assertion for the given value. This works like
330 * [instanceOf], but it allows null since Dart types are nullable. 320 * [instanceOf], but it allows null since Dart types are nullable.
331 * Also it will throw a TypeError if it gets the wrong type. 321 * Also it will throw a TypeError if it gets the wrong type.
332 */ 322 */
333 Value _typeAssert(MethodGenerator context, Type toType, Node node, 323 Value _typeAssert(MethodGenerator context, Type toType, bool isDynamic) {
334 bool isDynamic) {
335 if (toType is ParameterType) { 324 if (toType is ParameterType) {
336 ParameterType p = toType; 325 ParameterType p = toType;
337 toType = p.extendsType; 326 toType = p.extendsType;
338 } 327 }
339 328
340 if (toType.isObject || toType.isVar) { 329 if (toType.isObject || toType.isVar) {
341 world.internalError( 330 world.internalError(
342 'We thought ${type.name} is not a subtype of ${toType.name}?'); 331 'We thought ${type.name} is not a subtype of ${toType.name}?');
343 } 332 }
344 333
345 // Prevent a stack overflow when forceDynamic and type checks are both 334 // Prevent a stack overflow when forceDynamic and type checks are both
346 // enabled. forceDynamic would cause the TypeError constructor to type check 335 // enabled. forceDynamic would cause the TypeError constructor to type check
347 // its arguments, which in turn invokes the TypeError constructor, ad 336 // its arguments, which in turn invokes the TypeError constructor, ad
348 // infinitum. 337 // infinitum.
349 String throwTypeError(String paramName) => world.withoutForceDynamic(() { 338 String throwTypeError(String paramName) => world.withoutForceDynamic(() {
350 final typeError = world.corelib.types['TypeError']; 339 final typeErrorCtor = world.typeErrorType.getConstructor('_internal');
351 final typeErrorCtor = typeError.getConstructor('_internal');
352 world.gen.corejs.ensureTypeNameOf(); 340 world.gen.corejs.ensureTypeNameOf();
353 final result = typeErrorCtor.invoke(context, node, 341 final result = typeErrorCtor.invoke(context, null,
354 new Value.type(typeError, null), 342 new Value.type(world.typeErrorType, null),
355 new Arguments(null, [ 343 new Arguments(null, [
356 new Value(world.objectType, paramName, null), 344 new Value(world.objectType, paramName, null),
357 new Value(world.stringType, '"${toType.name}"', null)]), 345 new Value(world.stringType, '"${toType.name}"', null)]),
358 isDynamic); 346 isDynamic);
359 world.gen.corejs.useThrow = true; 347 world.gen.corejs.useThrow = true;
360 return '\$throw(${result.code})'; 348 return '\$throw(${result.code})';
361 }); 349 });
362 350
363 // TODO(jmesserly): better assert for integers? 351 // TODO(jmesserly): better assert for integers?
364 if (toType.isNum) toType = world.numType; 352 if (toType.isNum) toType = world.numType;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 387
400 String checkName = 'assert\$' + toType.jsname; 388 String checkName = 'assert\$' + toType.jsname;
401 389
402 // If we track nullability, we could simplify this check. 390 // If we track nullability, we could simplify this check.
403 var temp = context.getTemp(this); 391 var temp = context.getTemp(this);
404 check = '(${context.assignTemp(temp, this).code} == null ? null :'; 392 check = '(${context.assignTemp(temp, this).code} == null ? null :';
405 check += ' ${temp.code}.$checkName())'; 393 check += ' ${temp.code}.$checkName())';
406 if (this != temp) context.freeTemp(temp); 394 if (this != temp) context.freeTemp(temp);
407 395
408 // Generate the fallback on Object (that throws a TypeError) 396 // Generate the fallback on Object (that throws a TypeError)
409 if (!world.objectType.varStubs.containsKey(checkName)) { 397 world.objectType.varStubs.putIfAbsent(checkName,
410 world.objectType.varStubs[checkName] = new VarMethodStub(checkName, 398 () => new VarMethodStub(checkName, null, Arguments.EMPTY,
411 null, Arguments.EMPTY, throwTypeError('this')); 399 throwTypeError('this')));
412 }
413 } 400 }
414 401
415 return new Value(toType, check, span); 402 return new Value(toType, check, span);
416 } 403 }
417 404
418 /** 405 /**
419 * Test to see if value is an instance of this type. 406 * Test to see if value is an instance of this type.
420 * 407 *
421 * - If a primitive type, then uses the JavaScript typeof. 408 * - If a primitive type, then uses the JavaScript typeof.
422 * - If it's a non-generic class, use instanceof. 409 * - If it's a non-generic class, use instanceof.
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
478 465
479 // Generate the fallback on Object (that returns false) 466 // Generate the fallback on Object (that returns false)
480 if (!world.objectType.varStubs.containsKey(checkName)) { 467 if (!world.objectType.varStubs.containsKey(checkName)) {
481 world.objectType.varStubs[checkName] = 468 world.objectType.varStubs[checkName] =
482 new VarMethodStub(checkName, null, Arguments.EMPTY, 'return false'); 469 new VarMethodStub(checkName, null, Arguments.EMPTY, 'return false');
483 } 470 }
484 } 471 }
485 return new Value(world.nonNullBool, testCode, span); 472 return new Value(world.nonNullBool, testCode, span);
486 } 473 }
487 474
488 void convertWarning(Type toType, Node node) { 475 void convertWarning(Type toType) {
489 // TODO(jmesserly): better error messages for type conversion failures 476 // TODO(jmesserly): better error messages for type conversion failures
490 world.warning('type "${type.name}" is not assignable to "${toType.name}"', 477 world.warning('type "${type.name}" is not assignable to "${toType.name}"',
491 node.span); 478 span);
492 } 479 }
493 480
494 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node, 481 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node,
495 [Arguments args]) { 482 [Arguments args]) {
496 var pos = ''; 483 var pos = '';
497 if (args != null) { 484 if (args != null) {
498 var argsCode = []; 485 var argsCode = [];
499 for (int i = 0; i < args.length; i++) { 486 for (int i = 0; i < args.length; i++) {
500 argsCode.add(args.values[i].code); 487 argsCode.add(args.values[i].code);
501 } 488 }
(...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after
751 } 738 }
752 739
753 _ensureCode(); 740 _ensureCode();
754 return null; 741 return null;
755 } 742 }
756 } 743 }
757 744
758 String _escapeForComment(String text) { 745 String _escapeForComment(String text) {
759 return text.replaceAll('/*', '/ *').replaceAll('*/', '* /'); 746 return text.replaceAll('/*', '/ *').replaceAll('*/', '* /');
760 } 747 }
OLDNEW
« no previous file with comments | « frog/type.dart ('k') | frog/var_member.dart » ('j') | utils/apidoc/html_diff.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698