Skip to content

Commit

Permalink
implement user-defined nSM, Object members on functions
Browse files Browse the repository at this point in the history
fix #591 and fix #59

R=vsm@google.com

Review URL: https://codereview.chromium.org/2061373003 .
  • Loading branch information
John Messerly committed Jun 23, 2016
1 parent e8e19b4 commit d27eda4
Show file tree
Hide file tree
Showing 10 changed files with 1,070 additions and 966 deletions.
1,760 changes: 896 additions & 864 deletions pkg/dev_compiler/lib/runtime/dart_sdk.js

Large diffs are not rendered by default.

47 changes: 31 additions & 16 deletions pkg/dev_compiler/lib/src/compiler/code_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ class CodeGenerator extends GeneralizingAstVisitor
_collectElements(unit, nodes);
}
_loader = new ElementLoader(nodes);
if (compilationUnits.isNotEmpty) {
_constField = new ConstFieldVisitor(types,
dummySource: compilationUnits.first.element.source);
}

// Add implicit dart:core dependency so it is first.
emitLibraryName(dartCoreLibrary);
Expand All @@ -267,7 +271,7 @@ class CodeGenerator extends GeneralizingAstVisitor
// NOTE: declarations are not necessarily emitted in this order.
// Order will be changed as needed so the resulting code can execute.
// This is done by forward declaring items.
compilationUnits.forEach(visitCompilationUnit);
compilationUnits.forEach(_finishDeclarationsInUnit);

// Declare imports
_finishImports(items);
Expand Down Expand Up @@ -439,10 +443,12 @@ class CodeGenerator extends GeneralizingAstVisitor
_loader.declareBeforeUse(e, _emitDeclaration);
}

@override
void visitCompilationUnit(CompilationUnit unit) {
_constField = new ConstFieldVisitor(types, unit.element.source);

void _finishDeclarationsInUnit(CompilationUnit unit) {
// NOTE: this method isn't the right place to initialize
// per-compilation-unit state. Declarations can be visited out of order,
// this is only to catch things that haven't been emitted yet.
//
// See _emitDeclaration.
for (var declaration in unit.declarations) {
var element = declaration.element;
if (element != null) {
Expand Down Expand Up @@ -702,8 +708,7 @@ class CodeGenerator extends GeneralizingAstVisitor
var staticFields = <FieldDeclaration>[];
var methods = <MethodDeclaration>[];

// True if a "call" method or getter exists. This can also be
// "noSuchMethod" method, because nSM could implement "call".
// True if a "call" method or getter exists.
bool isCallable = false;
for (var member in node.members) {
if (member is ConstructorDeclaration) {
Expand All @@ -712,15 +717,21 @@ class CodeGenerator extends GeneralizingAstVisitor
(member.isStatic ? staticFields : fields).add(member);
} else if (member is MethodDeclaration) {
methods.add(member);
var name = member.name.name;
if (name == 'call' && !member.isSetter) {
isCallable = true;
} else if (name == 'noSuchMethod' &&
!member.isGetter &&
!member.isSetter &&
// Exclude SDK because we know they don't use nSM to implement call.
!classElem.library.source.isInSystemLibrary) {
isCallable = true;
if (member.name.name == 'call' && !member.isSetter) {
//
// Make sure "call" has a statically known function type:
//
// - if it's a method, then it does because all methods do,
// - if it's a getter, check the return type.
//
// Other cases like a getter returning dynamic/Object/Function will be
// handled at runtime by the dynamic call mechanism. So we only
// concern ourselves with statically known function types.
//
// For the same reason, we can ignore "noSuchMethod".
// call-implemented-by-nSM will be dispatched by dcall at runtime.
//
isCallable = !member.isGetter || member.returnType is FunctionType;
}
}
}
Expand Down Expand Up @@ -792,6 +803,8 @@ class CodeGenerator extends GeneralizingAstVisitor
return js.call('dart.callableClass(#, #)', [ctor, classExpr]);
}

/// Emits a constructor that ensures instances of this class are callable as
/// functions in JavaScript.
JS.Fun _emitCallableClassConstructor(ConstructorElement ctor) {
return js.call(
r'''function (...args) {
Expand Down Expand Up @@ -4302,7 +4315,9 @@ class CodeGenerator extends GeneralizingAstVisitor
// Check if the target could be `null`, is dynamic, or may be an extension
// native type. In all of those cases we need defensive code generation.
var type = getStaticType(target);

return isNullable(target) ||
type is FunctionType ||
type.isDynamic ||
(_extensionTypes.hasNativeSubtype(type) && target is! SuperExpression);
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/dev_compiler/lib/src/compiler/side_effect_analysis.dart
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,12 @@ class _AssignmentFinder extends RecursiveAstVisitor {
class ConstFieldVisitor {
final ConstantVisitor _constantVisitor;

ConstFieldVisitor(TypeProvider types, Source source)
ConstFieldVisitor(TypeProvider types, {Source dummySource})
// TODO(jmesserly): support -D variables on the command line
: _constantVisitor = new ConstantVisitor(
new ConstantEvaluationEngine(types, new DeclaredVariables()),
new ErrorReporter(AnalysisErrorListener.NULL_LISTENER, source));
new ErrorReporter(
AnalysisErrorListener.NULL_LISTENER, dummySource));

// TODO(jmesserly): this is used to determine if the field initialization is
// side effect free. We should make the check more general, as things like
Expand Down
7 changes: 3 additions & 4 deletions pkg/dev_compiler/test/browser/language_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
'deferred_inlined_test': skip_fail,
'deferred_load_inval_code_test': skip_fail,
'deferred_mixin_test': skip_fail,
'deferred_no_such_method_test': skip_fail,
'deferred_no_such_method_test': skip_fail, // deferred libs not implemented
'deferred_not_loaded_check_test': skip_fail,
'deferred_only_constant_test': skip_fail,
'deferred_optimized_test': skip_fail,
Expand All @@ -145,7 +145,7 @@
'f_bounded_quantification3_test': skip_fail,
'factory_type_parameter_test': skip_fail,
'fast_method_extraction_test': skip_fail,
'field_increment_bailout_test': skip_fail,
'field_increment_bailout_test': fail,
'field_optimization3_test': skip_fail,
'final_syntax_test_08_multi': skip_fail,
'first_class_types_test': skip_fail,
Expand Down Expand Up @@ -258,9 +258,8 @@
'named_parameter_clash_test': skip_fail,
'nan_identical_test': skip_fail,
'nested_switch_label_test': skip_fail,
'no_such_method3_test': skip_fail,
'no_such_method_empty_selector_test': fail,
'no_such_method_subtype_test': skip_fail,
'no_such_method_subtype_test': fail,
'number_identifier_test_05_multi': skip_fail,
'number_identity2_test': skip_fail,
'numbers_test': skip_fail,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Dart test program testing that NoSuchMethod is properly called.

import "package:expect/expect.dart";

Invocation invocation;

class C {
noSuchMethod(Invocation i) {
invocation = i;
return 42;
}
}

expectNSME(Object d) {
try {
d.noSuchMethod(invocation);
} on NoSuchMethodError catch (e) {
Expect.isTrue(e.toString().contains('foobar'));
}
}

main() {
dynamic c = new C();
Expect.equals(42, c.foobar(123));
Expect.equals(invocation.memberName, #foobar);
Expect.listEquals(invocation.positionalArguments, [123]);
expectNSME(null);
expectNSME(777);
expectNSME('hello');
// These fail because of https://github.com/dart-lang/dev_compiler/issues/592.
// expectNSME([]);
// expectNSME(['a', 'b', 'c']);
}
15 changes: 0 additions & 15 deletions pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/classes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -337,12 +337,9 @@ void _installPropertiesForObject(jsProto, coreObjProto) {
return;
}


///
/// Copy symbols from the prototype of the source to destination.
/// These are the only properties safe to copy onto an existing public
/// JavaScript class.
///
registerExtension(jsType, dartExtType) => JS('', '''(() => {
// TODO(vsm): Not all registered js types are real.
if (!jsType) return;
Expand Down Expand Up @@ -404,18 +401,6 @@ defineExtensionMembers(type, methodNames) => JS('', '''(() => {
});
})()''');

canonicalMember(obj, name) => JS('', '''(() => {
// Private names are symbols and are already canonical.
if (typeof name === 'symbol') return name;
if ($obj != null && $obj[$_extensionType]) return $dartx[$name];
// Check for certain names that we can't use in JS
if ($name == 'constructor' || $name == 'prototype') {
$name = '+' + $name;
}
return $name;
})()''');

/// Sets the type of `obj` to be `type`
setType(obj, type) {
JS('', '#.__proto__ = #.prototype', obj, type);
Expand Down
Loading

0 comments on commit d27eda4

Please sign in to comment.