| Index: frog/value.dart
|
| diff --git a/frog/value.dart b/frog/value.dart
|
| index 8ca52851bea992d00c5eaabf16ac98fe637d57fd..7febe6af4ebf0c2b8c27c80c137463dc069a3baa 100644
|
| --- a/frog/value.dart
|
| +++ b/frog/value.dart
|
| @@ -211,25 +211,8 @@ class Value {
|
| }
|
|
|
| /** True if convertTo would generate a conversion. */
|
| - // TODO(jmesserly): I don't like how this is coupled to convertTo.
|
| bool needsConversion(Type toType) {
|
| - var callMethod = toType.getCallMethod();
|
| - if (callMethod != null) {
|
| - int arity = callMethod.parameters.length;
|
| - var myCall = type.getCallMethod();
|
| - if (myCall == null || myCall.parameters.length != arity) {
|
| - return true;
|
| - }
|
| - }
|
| - if (options.enableTypeChecks) {
|
| - Type fromType = type;
|
| - if (type.isVar && (code != 'null' || !toType.isNullable)) {
|
| - fromType = world.objectType;
|
| - }
|
| - bool bothNum = type.isNum && toType.isNum;
|
| - return !(fromType.isSubtypeOf(toType) || bothNum);
|
| - }
|
| - return false;
|
| + return this != convertTo(null, toType, isDynamic:true);
|
| }
|
|
|
| /**
|
| @@ -238,8 +221,7 @@ class Value {
|
| * checks when --enable_type_checks is enabled, and wrapping callback
|
| * functions passed to the dom so we can restore their isolate context.
|
| */
|
| - // WARNING: this needs to be kept in sync with needsConversion above.
|
| - Value convertTo(MethodGenerator context, Type toType, Node node,
|
| + Value convertTo(MethodGenerator context, Type toType,
|
| [bool isDynamic=false]) {
|
|
|
| // Issue type warnings unless we are processing a dynamic operation.
|
| @@ -248,21 +230,10 @@ class Value {
|
| var callMethod = toType.getCallMethod();
|
| if (callMethod != null) {
|
| if (checked && !toType.isAssignable(type)) {
|
| - convertWarning(toType, node);
|
| + convertWarning(toType);
|
| }
|
|
|
| - int arity = callMethod.parameters.length;
|
| - var myCall = type.getCallMethod();
|
| - if (myCall == null || myCall.parameters.length != arity) {
|
| - final stub = world.functionType.getCallStub(new Arguments.bare(arity));
|
| - var val = new Value(toType, 'to\$${stub.name}($code)', node.span);
|
| - // TODO(sigmund): try to remove, see below
|
| - return _isDomCallback(toType) && !_isDomCallback(type) ?
|
| - val._wrapDomCallback(toType, arity) : val;
|
| - } else if (_isDomCallback(toType) && !_isDomCallback(type)) {
|
| - // TODO(sigmund): try to remove, see below
|
| - return _wrapDomCallback(toType, arity);
|
| - }
|
| + return _maybeWrapFunction(toType, callMethod);
|
| }
|
|
|
| // If we're assigning from a var, pretend it's Object for the purpose of
|
| @@ -287,42 +258,61 @@ class Value {
|
|
|
| if (checked && !toType.isSubtypeOf(type)) {
|
| // According to the static types, this conversion can't work.
|
| - convertWarning(toType, node);
|
| + convertWarning(toType);
|
| }
|
|
|
| // Generate a runtime checks if they're turned on, otherwise skip it.
|
| if (options.enableTypeChecks) {
|
| - return _typeAssert(context, toType, node, isDynamic);
|
| + if (context == null && isDynamic) {
|
| + // If we're just testing if we need the conversion, not actually doing
|
| + // it we don't need a context. Just return something that is != this.
|
| + // TODO(jmesserly): I don't like using null in this fashion, but it's
|
| + // better than before where we had two code paths that needed to be
|
| + // kept in sync.
|
| + return null;
|
| + }
|
| + return _typeAssert(context, toType, isDynamic);
|
| } else {
|
| return this;
|
| }
|
| }
|
|
|
| /**
|
| - * Checks whether [toType] is a callback function, and it is defined in the
|
| - * dom library.
|
| + * Wraps a function with a conversion, so it can be called directly from
|
| + * Dart or JS code with the proper arity. We avoid the wrapping if the target
|
| + * function has the same arity.
|
| + *
|
| + * Also wraps a callback attached to the dom (e.g. event listeners,
|
| + * setTimeout) so we can restore it's isolate context information. This is
|
| + * needed so that callbacks are executed within the context of the isolate
|
| + * that created them in the first place.
|
| */
|
| - bool _isDomCallback(toType) {
|
| - return (toType.definition is FunctionTypeDefinition
|
| - && toType.library == world.dom);
|
| - }
|
| + Value _maybeWrapFunction(Type toType, MethodMember callMethod) {
|
| + int arity = callMethod.parameters.length;
|
| + var myCall = type.getCallMethod();
|
| +
|
| + Value result = this;
|
| + if (myCall == null || myCall.parameters.length != arity) {
|
| + final stub = world.functionType.getCallStub(new Arguments.bare(arity));
|
| + result = new Value(toType, 'to\$${stub.name}($code)', span);
|
| + }
|
| +
|
| + if (toType.library.isDom && !type.library.isDom) {
|
| + // TODO(jmesserly): either remove this or make it a more first class
|
| + // feature of our native interop. We shouldn't be checking for the DOM
|
| + // library--any host environment (like node.js) might need this feature
|
| + // for isolates too. But we don't want to wrap every function we send to
|
| + // native code--many callbacks like List.filter are perfectly safe.
|
| + if (arity == 0) {
|
| + world.gen.corejs.useWrap0 = true;
|
| + } else {
|
| + world.gen.corejs.useWrap1 = true;
|
| + }
|
|
|
| - /**
|
| - * Wraps a callback attached to the dom (e.g. event listeners, setTimeout) so
|
| - * we can restore it's isolate context information. This is needed so that
|
| - * callbacks are executed within the context of the isolate that created them
|
| - * in the first place.
|
| - */
|
| - // TODO(sigmund): try to remove this specialized logic about isolates
|
| - // and the dom from the compiler, move into the actual dom library if
|
| - // possible.
|
| - Value _wrapDomCallback(Type toType, int arity) {
|
| - if (arity == 0) {
|
| - world.gen.corejs.useWrap0 = true;
|
| - } else {
|
| - world.gen.corejs.useWrap1 = true;
|
| + result = new Value(toType, '\$wrap_call\$$arity(${result.code})', span);
|
| }
|
| - return new Value(toType, '\$wrap_call\$$arity($code)', span);
|
| +
|
| + return result;
|
| }
|
|
|
| /**
|
| @@ -330,8 +320,7 @@ class Value {
|
| * [instanceOf], but it allows null since Dart types are nullable.
|
| * Also it will throw a TypeError if it gets the wrong type.
|
| */
|
| - Value _typeAssert(MethodGenerator context, Type toType, Node node,
|
| - bool isDynamic) {
|
| + Value _typeAssert(MethodGenerator context, Type toType, bool isDynamic) {
|
| if (toType is ParameterType) {
|
| ParameterType p = toType;
|
| toType = p.extendsType;
|
| @@ -347,11 +336,10 @@ class Value {
|
| // its arguments, which in turn invokes the TypeError constructor, ad
|
| // infinitum.
|
| String throwTypeError(String paramName) => world.withoutForceDynamic(() {
|
| - final typeError = world.corelib.types['TypeError'];
|
| - final typeErrorCtor = typeError.getConstructor('_internal');
|
| + final typeErrorCtor = world.typeErrorType.getConstructor('_internal');
|
| world.gen.corejs.ensureTypeNameOf();
|
| - final result = typeErrorCtor.invoke(context, node,
|
| - new Value.type(typeError, null),
|
| + final result = typeErrorCtor.invoke(context, null,
|
| + new Value.type(world.typeErrorType, null),
|
| new Arguments(null, [
|
| new Value(world.objectType, paramName, null),
|
| new Value(world.stringType, '"${toType.name}"', null)]),
|
| @@ -406,10 +394,9 @@ function \$assert_${toType.name}(x) {
|
| if (this != temp) context.freeTemp(temp);
|
|
|
| // Generate the fallback on Object (that throws a TypeError)
|
| - if (!world.objectType.varStubs.containsKey(checkName)) {
|
| - world.objectType.varStubs[checkName] = new VarMethodStub(checkName,
|
| - null, Arguments.EMPTY, throwTypeError('this'));
|
| - }
|
| + world.objectType.varStubs.putIfAbsent(checkName,
|
| + () => new VarMethodStub(checkName, null, Arguments.EMPTY,
|
| + throwTypeError('this')));
|
| }
|
|
|
| return new Value(toType, check, span);
|
| @@ -485,10 +472,10 @@ function \$assert_${toType.name}(x) {
|
| return new Value(world.nonNullBool, testCode, span);
|
| }
|
|
|
| - void convertWarning(Type toType, Node node) {
|
| + void convertWarning(Type toType) {
|
| // TODO(jmesserly): better error messages for type conversion failures
|
| world.warning('type "${type.name}" is not assignable to "${toType.name}"',
|
| - node.span);
|
| + span);
|
| }
|
|
|
| Value invokeNoSuchMethod(MethodGenerator context, String name, Node node,
|
|
|