mirror of
https://github.com/dart-lang/sdk
synced 2024-09-20 05:21:28 +00:00
Incremental compiler: Use addStubs for adding methods.
R=johnniwinther@google.com Review URL: https://codereview.chromium.org//764533002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@42042 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
b19abb6151
commit
590e8b06a2
|
@ -34,7 +34,8 @@ class ClassBuilder {
|
|||
fields.add(field);
|
||||
}
|
||||
|
||||
jsAst.ObjectInitializer toObjectInitializer() {
|
||||
jsAst.ObjectInitializer toObjectInitializer(
|
||||
{bool omitClassDescriptor: false}) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
if (superName != null) {
|
||||
buffer.write('$superName');
|
||||
|
@ -52,11 +53,17 @@ class ClassBuilder {
|
|||
classData =
|
||||
new jsAst.ArrayInitializer.from([classData]..addAll(fieldMetadata));
|
||||
}
|
||||
var fieldsAndProperties =
|
||||
[new jsAst.Property(js.string(namer.classDescriptorProperty),
|
||||
classData)]
|
||||
..addAll(properties);
|
||||
List<jsAst.Property> fieldsAndProperties;
|
||||
if (!omitClassDescriptor) {
|
||||
fieldsAndProperties = <jsAst.Property>[];
|
||||
fieldsAndProperties.add(
|
||||
new jsAst.Property(
|
||||
js.string(namer.classDescriptorProperty), classData));
|
||||
fieldsAndProperties
|
||||
..addAll(properties);
|
||||
} else {
|
||||
fieldsAndProperties = properties;
|
||||
}
|
||||
return new jsAst.ObjectInitializer(fieldsAndProperties, isOneLiner: false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1452,8 +1452,12 @@ class OldEmitter implements Emitter {
|
|||
'this.\$dart_unsafe_eval.patch = function(a) { eval(a) }$N');
|
||||
String schemaChange =
|
||||
jsAst.prettyPrint(buildSchemaChangeFunction(), compiler).getText();
|
||||
String addMethod =
|
||||
jsAst.prettyPrint(buildIncrementalAddMethod(), compiler).getText();
|
||||
mainBuffer.add(
|
||||
'this.\$dart_unsafe_eval.schemaChange$_=$_$schemaChange$N');
|
||||
mainBuffer.add(
|
||||
'this.\$dart_unsafe_eval.addMethod$_=$_$addMethod$N');
|
||||
}
|
||||
if (isProgramSplit) {
|
||||
/// We collect all the global state of the, so it can be passed to the
|
||||
|
@ -1682,6 +1686,57 @@ function(newConstructor, oldConstructor, superclass) {
|
|||
}''');
|
||||
}
|
||||
|
||||
/// Used by incremental compilation to patch up an object ([holder]) with a
|
||||
/// new (or updated) method. [arrayOrFunction] is either the new method, or
|
||||
/// an array containing the method (see
|
||||
/// [ContainerBuilder.addMemberMethodFromInfo]). [name] is the name of the
|
||||
/// new method. [isStatic] tells if method is static (or
|
||||
/// top-level). [globalFunctionsAccess] is a reference to
|
||||
/// [embeddedNames.GLOBAL_FUNCTIONS].
|
||||
jsAst.Fun buildIncrementalAddMethod() {
|
||||
return js(r'''
|
||||
function(originalDescriptor, name, holder, isStatic, globalFunctionsAccess) {
|
||||
var arrayOrFunction = originalDescriptor[name];
|
||||
var method;
|
||||
if (arrayOrFunction.constructor === Array) {
|
||||
var existing = holder[name];
|
||||
var array = arrayOrFunction;
|
||||
var descriptor = Object.create(null);
|
||||
this.addStubs(
|
||||
descriptor, arrayOrFunction, name, isStatic, originalDescriptor, []);
|
||||
method = descriptor[name];
|
||||
for (var property in descriptor) {
|
||||
if (!Object.prototype.hasOwnProperty.call(descriptor, property)) continue;
|
||||
var stub = descriptor[property];
|
||||
var existingStub = holder[property];
|
||||
if (stub === method || !existingStub) {
|
||||
// Not replacing an existing stub.
|
||||
holder[property] = method;
|
||||
continue;
|
||||
}
|
||||
if (!stub.$getterStub) {
|
||||
var error = new Error("Unexpected stub.");
|
||||
error.stub = stub;
|
||||
throw error;
|
||||
}
|
||||
// Invoke the existing stub to obtain the tear-off closure.
|
||||
existingStub = existingStub();
|
||||
// A stub already exist. Update all its references to [existing] to
|
||||
// [method].
|
||||
for (var reference in existingStub) {
|
||||
if (existingStub[reference] === existing) {
|
||||
existingStub[reference] = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = arrayOrFunction;
|
||||
holder[name] = method;
|
||||
}
|
||||
if (isStatic) globalFunctionsAccess[name] = method;
|
||||
}''');
|
||||
}
|
||||
|
||||
/// Returns a map from OutputUnit to a hash of its content. The hash uniquely
|
||||
/// identifies the code of the output-unit. It does not include
|
||||
/// boilerplate JS code, like the sourcemap directives or the hash
|
||||
|
|
|
@ -280,15 +280,29 @@ jsAst.Expression getReflectionDataParser(String classesCollector,
|
|||
mangledNamesAccess,
|
||||
mangledGlobalNamesAccess]);
|
||||
|
||||
List<jsAst.Statement> incrementalSupport = <jsAst.Statement>[];
|
||||
if (compiler.hasIncrementalSupport) {
|
||||
incrementalSupport.add(
|
||||
js.statement(
|
||||
r'self.$dart_unsafe_eval.addStubs = addStubs;'));
|
||||
}
|
||||
|
||||
return js('''
|
||||
(function (reflectionData) {
|
||||
"use strict";
|
||||
#; // header
|
||||
#; // processStatics
|
||||
#; // addStubs
|
||||
#; // tearOffCode
|
||||
#; // init
|
||||
})''', [header, processStatics, addStubs, tearOffCode, init]);
|
||||
#header;
|
||||
#processStatics;
|
||||
#addStubs;
|
||||
#tearOffCode;
|
||||
#incrementalSupport;
|
||||
#init;
|
||||
})''', {
|
||||
'header': header,
|
||||
'processStatics': processStatics,
|
||||
'incrementalSupport': incrementalSupport,
|
||||
'addStubs': addStubs,
|
||||
'tearOffCode': tearOffCode,
|
||||
'init': init});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ import 'package:compiler/src/js_emitter/js_emitter.dart' show
|
|||
ClassBuilder,
|
||||
ClassEmitter,
|
||||
CodeEmitterTask,
|
||||
ContainerBuilder,
|
||||
MemberInfo,
|
||||
computeMixinClass;
|
||||
|
||||
|
@ -638,39 +639,36 @@ class LibraryUpdater extends JsFeatures {
|
|||
}
|
||||
|
||||
jsAst.Node computeMemberUpdateJs(Element element) {
|
||||
MemberInfo info = emitter.oldEmitter.containerBuilder
|
||||
.analyzeMemberMethod(element);
|
||||
MemberInfo info = containerBuilder.analyzeMemberMethod(element);
|
||||
if (info == null) {
|
||||
compiler.internalError(element, '${element.runtimeType}');
|
||||
}
|
||||
ClassBuilder builder = new ClassBuilder(element, namer);
|
||||
containerBuilder.addMemberMethodFromInfo(info, builder);
|
||||
jsAst.Node partialDescriptor =
|
||||
builder.toObjectInitializer(omitClassDescriptor: true);
|
||||
|
||||
String name = info.name;
|
||||
jsAst.Node function = info.code;
|
||||
List<jsAst.Statement> statements = <jsAst.Statement>[];
|
||||
bool isStatic = !element.isInstanceMember;
|
||||
|
||||
/// Either a global object (non-instance members) or a prototype (instance
|
||||
/// members).
|
||||
jsAst.Node holder;
|
||||
|
||||
if (element.isInstanceMember) {
|
||||
jsAst.Node elementAccess = namer.elementAccess(element.enclosingClass);
|
||||
statements.add(
|
||||
js.statement('#.prototype.# = f', [elementAccess, name]));
|
||||
holder = js('#.prototype', namer.elementAccess(element.enclosingClass));
|
||||
} else {
|
||||
jsAst.Node elementAccess = namer.elementAccess(element);
|
||||
jsAst.Expression globalFunctionsAccess =
|
||||
emitter.generateEmbeddedGlobalAccess(embeddedNames.GLOBAL_FUNCTIONS);
|
||||
statements.add(
|
||||
js.statement(
|
||||
'#.# = # = f',
|
||||
[globalFunctionsAccess, name, elementAccess]));
|
||||
if (info.canTearOff) {
|
||||
String globalName = namer.globalObjectFor(element);
|
||||
statements.add(
|
||||
js.statement(
|
||||
'#.#().# = f',
|
||||
[globalName, info.tearOffName, callNameFor(element)]));
|
||||
}
|
||||
holder = js('#', namer.globalObjectFor(element));
|
||||
}
|
||||
// Create a scope by creating a new function. The updated function literal
|
||||
// is passed as an argument to this function which ensures that temporary
|
||||
// names in updateScope don't shadow global names.
|
||||
jsAst.Fun updateScope = js('function (f) { # }', [statements]);
|
||||
return js.statement('(#)(#)', [updateScope, function]);
|
||||
|
||||
jsAst.Expression globalFunctionsAccess =
|
||||
emitter.generateEmbeddedGlobalAccess(embeddedNames.GLOBAL_FUNCTIONS);
|
||||
|
||||
return js.statement(
|
||||
r'self.$dart_unsafe_eval.addMethod(#, #, #, #, #)',
|
||||
[partialDescriptor, js.string(name), holder,
|
||||
new jsAst.LiteralBool(isStatic), globalFunctionsAccess]);
|
||||
}
|
||||
|
||||
String prettyPrintJs(jsAst.Node node) {
|
||||
|
@ -1133,6 +1131,8 @@ abstract class JsFeatures {
|
|||
Namer get namer => backend.namer;
|
||||
|
||||
CodeEmitterTask get emitter => backend.emitter;
|
||||
|
||||
ContainerBuilder get containerBuilder => emitter.oldEmitter.containerBuilder;
|
||||
}
|
||||
|
||||
class EmitterHelper extends JsFeatures {
|
||||
|
|
|
@ -866,8 +866,6 @@ main() {
|
|||
const <ProgramResult>[
|
||||
const ProgramResult(
|
||||
r"""
|
||||
import 'dart:collection';
|
||||
|
||||
class C {
|
||||
static m() {
|
||||
print('v1');
|
||||
|
@ -875,8 +873,6 @@ class C {
|
|||
}
|
||||
|
||||
main() {
|
||||
// TODO(ahe): The incremental compiler magter ikke static tear-off closures.
|
||||
new HashMap(equals: identical);
|
||||
try {
|
||||
C.m();
|
||||
} catch (e) {
|
||||
|
|
Loading…
Reference in a new issue