diff --git a/Chapter 1/bank_terminal/build/web/bank_terminal_s5.css b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.css new file mode 100644 index 0000000..1a4d4c0 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.css @@ -0,0 +1,35 @@ +body { + background-color: #F8F8F8; + font-family: 'Open Sans', sans-serif; + font-size: 14px; + font-weight: normal; + line-height: 1.2em; + margin: 15px; + padding: 20px; +} + +form { + background: #9cbc2c; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 20px; + width: 400px; +} + +h1, p { + color: #333; +} + +.auto-style1 { + width: 25%; + border: 1px solid #0000FF; +} + +.auto-style2 { + width: 107px; + } + +.btns { + width: 127px; +} \ No newline at end of file diff --git a/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart new file mode 100644 index 0000000..630c00c --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart @@ -0,0 +1,100 @@ +import 'dart:html'; +import 'package:bank_terminal_s5/bank_terminal.dart'; + +LabelElement owner, balance, error; +InputElement number, amount; +ButtonElement btn_other, btn_deposit, btn_withdrawal, btn_interest; +BankAccount bac; + +void main() { + bind_elements(); + attach_event_handlers(); + disable_transactions(true); +} + +bind_elements() { + owner = querySelector('#owner'); + balance = querySelector('#balance'); + number = querySelector('#number'); + btn_other = querySelector('#btn_other'); + amount = querySelector('#amount'); + btn_deposit = querySelector('#btn_deposit'); + btn_interest = querySelector('#btn_interest'); + error = querySelector('#error'); +} + +attach_event_handlers() { + number.onInput.listen(readData); + amount.onChange.listen(nonNegative); + amount.onBlur.listen(nonNegative); + btn_other.onClick.listen(clearData); + btn_deposit.onClick.listen(changeBalance); + btn_interest.onClick.listen(interest); +} + +readData(Event e) { + // show data: + var key = 'Bankaccount:${number.value}'; + if (!window.localStorage.containsKey(key)) { + error.innerHtml = "Unknown bank account!"; + number.focus(); + return; + } + error.innerHtml = ""; + // read data from local storage: + String acc_json = window.localStorage[key]; + bac = new BankAccount.fromJsonString(acc_json); + // show owner and balance: + owner.innerHtml = "${bac.owner.name}"; + balance.innerHtml = "${bac.balance.toStringAsFixed(2)}"; + // enable transactions part: + disable_transactions(false); +} + +clearData(Event e) => clear(); + +clear() { + number.value = ""; + owner.innerHtml = "----------"; + balance.innerHtml = "0.0"; + number.focus(); + disable_transactions(true); +} + +disable_transactions(bool off) { + amount.disabled = off; + btn_deposit.disabled = off; + btn_interest.disabled = off; +} + +nonNegative(Event e) { + num input; + try { + input = double.parse(amount.value); + } on FormatException catch(e) { + window.alert("This is not a valid amount!"); + amount.focus(); + } +} + +changeBalance(Event e) { + // read amount: + double money_amount = double.parse(amount.value); + // call deposit on BankAccount object: + if (money_amount >= 0) bac.deposit(money_amount); + else bac.withdraw(money_amount); + window.localStorage["Bankaccount:${bac.number}"] = bac.toJson(); + // show new amount: + balance.innerHtml = "${bac.balance.toStringAsFixed(2)}"; + // disable refresh screen: + e.preventDefault(); + e.stopPropagation(); +} + +interest(Event e) { + bac.interest(); + window.localStorage["Bankaccount:${bac.number}"] = bac.toJson(); + balance.innerHtml = "${bac.balance.toStringAsFixed(2)}"; + e.preventDefault(); + e.stopPropagation(); +} diff --git a/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart.js b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart.js new file mode 100644 index 0000000..91dcaa2 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart.js @@ -0,0 +1,8200 @@ +// Generated by dart2js, the Dart to JavaScript compiler. +// The code supports the following hooks: +// dartPrint(message): +// if this function is defined it is called instead of the Dart [print] +// method. +// +// dartMainRunner(main, args): +// if this function is defined, the Dart [main] method will not be invoked +// directly. Instead, a closure that will invoke [main], and its arguments +// [args] is passed to [dartMainRunner]. +(function($) { +function dart(){ this.x = 0 }var A = new dart; +delete A.x; +var B = new dart; +delete B.x; +var C = new dart; +delete C.x; +var D = new dart; +delete D.x; +var E = new dart; +delete E.x; +var F = new dart; +delete F.x; +var G = new dart; +delete G.x; +var H = new dart; +delete H.x; +var J = new dart; +delete J.x; +var K = new dart; +delete K.x; +var L = new dart; +delete L.x; +var M = new dart; +delete M.x; +var N = new dart; +delete N.x; +var O = new dart; +delete O.x; +var P = new dart; +delete P.x; +var Q = new dart; +delete Q.x; +var R = new dart; +delete R.x; +var S = new dart; +delete S.x; +var T = new dart; +delete T.x; +var U = new dart; +delete U.x; +var V = new dart; +delete V.x; +var W = new dart; +delete W.x; +var X = new dart; +delete X.x; +var Y = new dart; +delete Y.x; +var Z = new dart; +delete Z.x; +function Isolate() {} +init(); + +$ = Isolate.$isolateProperties; +var $$ = {}; + +// Native classes +(function (reflectionData) { + "use strict"; + function map(x){x={x:x};delete x.x;return x} + function processStatics(descriptor) { + for (var property in descriptor) { + if (!hasOwnProperty.call(descriptor, property)) continue; + if (property === "^") continue; + var element = descriptor[property]; + var firstChar = property.substring(0, 1); + var previousProperty; + if (firstChar === "+") { + mangledGlobalNames[previousProperty] = property.substring(1); + var flag = descriptor[property]; + if (flag > 0) descriptor[previousProperty].$reflectable = flag; + if (element && element.length) init.typeInformation[previousProperty] = element; + } else if (firstChar === "@") { + property = property.substring(1); + $[property]["@"] = element; + } else if (firstChar === "*") { + globalObject[previousProperty].$defaultValues = element; + var optionalMethods = descriptor.$methodsWithOptionalArguments; + if (!optionalMethods) { + descriptor.$methodsWithOptionalArguments = optionalMethods = {} + } + optionalMethods[property] = previousProperty; + } else if (typeof element === "function") { + globalObject[previousProperty = property] = element; + functions.push(property); + init.globalFunctions[property] = element; + } else if (element.constructor === Array) { + addStubs(globalObject, element, property, true, descriptor, functions); + } else { + previousProperty = property; + var newDesc = {}; + var previousProp; + for (var prop in element) { + if (!hasOwnProperty.call(element, prop)) continue; + firstChar = prop.substring(0, 1); + if (prop === "static") { + processStatics(init.statics[property] = element[prop]); + } else if (firstChar === "+") { + mangledNames[previousProp] = prop.substring(1); + var flag = element[prop]; + if (flag > 0) element[previousProp].$reflectable = flag; + } else if (firstChar === "@" && prop !== "@") { + newDesc[prop.substring(1)]["@"] = element[prop]; + } else if (firstChar === "*") { + newDesc[previousProp].$defaultValues = element[prop]; + var optionalMethods = newDesc.$methodsWithOptionalArguments; + if (!optionalMethods) { + newDesc.$methodsWithOptionalArguments = optionalMethods={} + } + optionalMethods[prop] = previousProp; + } else { + var elem = element[prop]; + if (prop !== "^" && elem != null && elem.constructor === Array && prop !== "<>") { + addStubs(newDesc, elem, prop, false, element, []); + } else { + newDesc[previousProp = prop] = elem; + } + } + } + $$[property] = [globalObject, newDesc]; + classes.push(property); + } + } + } + function addStubs(descriptor, array, name, isStatic, originalDescriptor, functions) { + var f, funcs = [originalDescriptor[name] = descriptor[name] = f = array[0]]; + f.$stubName = name; + functions.push(name); + for (var index = 0; index < array.length; index += 2) { + f = array[index + 1]; + if (typeof f != "function") break; + f.$stubName = array[index + 2]; + funcs.push(f); + if (f.$stubName) { + originalDescriptor[f.$stubName] = descriptor[f.$stubName] = f; + functions.push(f.$stubName); + } + } + for (var i = 0; i < funcs.length; index++, i++) { + funcs[i].$callName = array[index + 1]; + } + var getterStubName = array[++index]; + array = array.slice(++index); + var requiredParameterInfo = array[0]; + var requiredParameterCount = requiredParameterInfo >> 1; + var isAccessor = (requiredParameterInfo & 1) === 1; + var isSetter = requiredParameterInfo === 3; + var isGetter = requiredParameterInfo === 1; + var optionalParameterInfo = array[1]; + var optionalParameterCount = optionalParameterInfo >> 1; + var optionalParametersAreNamed = (optionalParameterInfo & 1) === 1; + var isIntercepted = requiredParameterCount + optionalParameterCount != funcs[0].length; + var functionTypeIndex = array[2]; + var unmangledNameIndex = 2 * optionalParameterCount + requiredParameterCount + 3; + var isReflectable = array.length > unmangledNameIndex; + + if (getterStubName) { + f = tearOff(funcs, array, isStatic, name, isIntercepted); + f.getterStub = true; + if (isStatic) init.globalFunctions[name] = f; + originalDescriptor[getterStubName] = descriptor[getterStubName] = f; + funcs.push(f); + if (getterStubName) functions.push(getterStubName); + f.$stubName = getterStubName; + f.$callName = null; + if (isIntercepted) init.interceptedNames[getterStubName] = true; + } + if (isReflectable) { + for (var i = 0; i < funcs.length; i++) { + funcs[i].$reflectable = 1; + funcs[i].$reflectionInfo = array; + } + var mangledNames = isStatic ? init.mangledGlobalNames : init.mangledNames; + var unmangledName = array[unmangledNameIndex]; + var reflectionName = unmangledName; + if (getterStubName) mangledNames[getterStubName] = reflectionName; + if (isSetter) { + reflectionName += "="; + } else if (!isGetter) { + reflectionName += ":" + requiredParameterCount + ":" + optionalParameterCount; + } + mangledNames[name] = reflectionName; + funcs[0].$reflectionName = reflectionName; + funcs[0].$metadataIndex = unmangledNameIndex + 1; + if (optionalParameterCount) descriptor[unmangledName + "*"] = funcs[0]; + } + } + function tearOffGetterNoCsp(funcs, reflectionInfo, name, isIntercepted) { + return isIntercepted + ? new Function("funcs", "reflectionInfo", "name", "H", "c", + "return function tearOff_" + name + (functionCounter++)+ "(x) {" + + "if (c === null) c = H.closureFromTearOff(" + + "this, funcs, reflectionInfo, false, [x], name);" + + "return new c(this, funcs[0], x, name);" + + "}")(funcs, reflectionInfo, name, H, null) + : new Function("funcs", "reflectionInfo", "name", "H", "c", + "return function tearOff_" + name + (functionCounter++)+ "() {" + + "if (c === null) c = H.closureFromTearOff(" + + "this, funcs, reflectionInfo, false, [], name);" + + "return new c(this, funcs[0], null, name);" + + "}")(funcs, reflectionInfo, name, H, null) + } + function tearOffGetterCsp(funcs, reflectionInfo, name, isIntercepted) { + var cache = null; + return isIntercepted + ? function(x) { + if (cache === null) cache = H.closureFromTearOff(this, funcs, reflectionInfo, false, [x], name); + return new cache(this, funcs[0], x, name) + } + : function() { + if (cache === null) cache = H.closureFromTearOff(this, funcs, reflectionInfo, false, [], name); + return new cache(this, funcs[0], null, name) + } + } + function tearOff(funcs, reflectionInfo, isStatic, name, isIntercepted) { + var cache; + return isStatic + ? function() { + if (cache === void 0) cache = H.closureFromTearOff(this, funcs, reflectionInfo, true, [], name).prototype; + return cache; + } + : tearOffGetter(funcs, reflectionInfo, name, isIntercepted); + } + var functionCounter = 0; + var tearOffGetter = (typeof dart_precompiled == "function") + ? tearOffGetterCsp : tearOffGetterNoCsp; + if (!init.libraries) init.libraries = []; + if (!init.mangledNames) init.mangledNames = map(); + if (!init.mangledGlobalNames) init.mangledGlobalNames = map(); + if (!init.statics) init.statics = map(); + if (!init.typeInformation) init.typeInformation = map(); + if (!init.globalFunctions) init.globalFunctions = map(); + if (!init.interceptedNames) init.interceptedNames = map(); + var libraries = init.libraries; + var mangledNames = init.mangledNames; + var mangledGlobalNames = init.mangledGlobalNames; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var length = reflectionData.length; + for (var i = 0; i < length; i++) { + var data = reflectionData[i]; + var name = data[0]; + var uri = data[1]; + var metadata = data[2]; + var globalObject = data[3]; + var descriptor = data[4]; + var isRoot = !!data[5]; + var fields = descriptor && descriptor["^"]; + var classes = []; + var functions = []; + processStatics(descriptor); + libraries.push([name, uri, classes, functions, metadata, fields, isRoot, + globalObject]); + } +}) +([ +["_foreign_helper", "dart:_foreign_helper", , H, { + "^": "", + JS_CONST: { + "^": "Object;code" + } +}], +["_interceptors", "dart:_interceptors", , J, { + "^": "", + getInterceptor: function(object) { + return void 0; + }, + makeDispatchRecord: function(interceptor, proto, extension, indexability) { + return {i: interceptor, p: proto, e: extension, x: indexability}; + }, + getNativeInterceptor: function(object) { + var record, proto, objectProto, interceptor; + record = object[init.dispatchPropertyName]; + if (record == null) + if ($.initNativeDispatchFlag == null) { + H.initNativeDispatch(); + record = object[init.dispatchPropertyName]; + } + if (record != null) { + proto = record.p; + if (false === proto) + return record.i; + if (true === proto) + return object; + objectProto = Object.getPrototypeOf(object); + if (proto === objectProto) + return record.i; + if (record.e === objectProto) + throw H.wrapException(P.UnimplementedError$("Return interceptor for " + H.S(proto(object, record)))); + } + interceptor = H.lookupAndCacheInterceptor(object); + if (interceptor == null) { + proto = Object.getPrototypeOf(object); + if (proto == null || proto === Object.prototype) + return C.PlainJavaScriptObject_methods; + else + return C.UnknownJavaScriptObject_methods; + } + return interceptor; + }, + Interceptor: { + "^": "Object;", + $eq: function(receiver, other) { + return receiver === other; + }, + get$hashCode: function(receiver) { + return H.Primitives_objectHashCode(receiver); + }, + toString$0: function(receiver) { + return H.Primitives_objectToString(receiver); + }, + "%": "DOMError|DOMImplementation|FileError|MediaError|MediaKeyError|Navigator|NavigatorUserMediaError|PositionError|SQLError|SVGAnimatedNumberList" + }, + JSBool: { + "^": "bool/Interceptor;", + toString$0: function(receiver) { + return String(receiver); + }, + get$hashCode: function(receiver) { + return receiver ? 519018 : 218159; + }, + $isbool: true + }, + JSNull: { + "^": "Null/Interceptor;", + $eq: function(receiver, other) { + return null == other; + }, + toString$0: function(receiver) { + return "null"; + }, + get$hashCode: function(receiver) { + return 0; + } + }, + JavaScriptObject: { + "^": "Interceptor;", + get$hashCode: function(_) { + return 0; + } + }, + PlainJavaScriptObject: { + "^": "JavaScriptObject;" + }, + UnknownJavaScriptObject: { + "^": "JavaScriptObject;" + }, + JSArray: { + "^": "List/Interceptor;", + remove$1: function(receiver, element) { + var i; + if (!!receiver.fixed$length) + H.throwExpression(P.UnsupportedError$("remove")); + for (i = 0; i < receiver.length; ++i) + if (J.$eq(receiver[i], element)) { + receiver.splice(i, 1); + return true; + } + return false; + }, + forEach$1: function(receiver, f) { + return H.IterableMixinWorkaround_forEach(receiver, f); + }, + elementAt$1: function(receiver, index) { + if (index < 0 || index >= receiver.length) + return H.ioore(receiver, index); + return receiver[index]; + }, + contains$1: function(receiver, other) { + var i; + for (i = 0; i < receiver.length; ++i) + if (J.$eq(receiver[i], other)) + return true; + return false; + }, + get$isEmpty: function(receiver) { + return receiver.length === 0; + }, + toString$0: function(receiver) { + return H.IterableMixinWorkaround_toStringIterable(receiver, "[", "]"); + }, + toList$1$growable: function(receiver, growable) { + var t1; + if (growable) + return H.setRuntimeTypeInfo(receiver.slice(), [H.getTypeArgumentByIndex(receiver, 0)]); + else { + t1 = H.setRuntimeTypeInfo(receiver.slice(), [H.getTypeArgumentByIndex(receiver, 0)]); + t1.fixed$length = init; + return t1; + } + }, + toList$0: function($receiver) { + return this.toList$1$growable($receiver, true); + }, + get$iterator: function(receiver) { + return new H.ListIterator(receiver, receiver.length, 0, null); + }, + get$hashCode: function(receiver) { + return H.Primitives_objectHashCode(receiver); + }, + get$length: function(receiver) { + return receiver.length; + }, + set$length: function(receiver, newLength) { + if (newLength < 0) + throw H.wrapException(P.RangeError$value(newLength)); + if (!!receiver.fixed$length) + H.throwExpression(P.UnsupportedError$("set length")); + receiver.length = newLength; + }, + $index: function(receiver, index) { + if (typeof index !== "number" || Math.floor(index) !== index) + throw H.wrapException(new P.ArgumentError(index)); + if (index >= receiver.length || index < 0) + throw H.wrapException(P.RangeError$value(index)); + return receiver[index]; + }, + $indexSet: function(receiver, index, value) { + if (!!receiver.immutable$list) + H.throwExpression(P.UnsupportedError$("indexed set")); + if (typeof index !== "number" || Math.floor(index) !== index) + throw H.wrapException(new P.ArgumentError(index)); + if (index >= receiver.length || index < 0) + throw H.wrapException(P.RangeError$value(index)); + receiver[index] = value; + }, + $isList: true, + $isList: true, + $asList: null, + $isEfficientLength: true, + static: {JSArray_JSArray$fixed: function($length, $E) { + var t1; + if (typeof $length !== "number" || Math.floor($length) !== $length || $length < 0) + throw H.wrapException(P.ArgumentError$("Length must be a non-negative integer: " + H.S($length))); + t1 = H.setRuntimeTypeInfo(new Array($length), [$E]); + t1.fixed$length = init; + return t1; + }} + }, + JSNumber: { + "^": "num/Interceptor;", + get$isFinite: function(receiver) { + return isFinite(receiver); + }, + remainder$1: function(receiver, b) { + return receiver % b; + }, + toInt$0: function(receiver) { + var t1; + if (receiver >= -2147483648 && receiver <= 2147483647) + return receiver | 0; + if (isFinite(receiver)) { + t1 = receiver < 0 ? Math.ceil(receiver) : Math.floor(receiver); + return t1 + 0; + } + throw H.wrapException(P.UnsupportedError$('' + receiver)); + }, + round$0: function(receiver) { + return this.toInt$0(this.roundToDouble$0(receiver)); + }, + roundToDouble$0: function(receiver) { + if (receiver < 0) + return -Math.round(-receiver); + else + return Math.round(receiver); + }, + toStringAsFixed$1: function(receiver, fractionDigits) { + var result, t1; + if (fractionDigits > 20) + throw H.wrapException(P.RangeError$(fractionDigits)); + result = receiver.toFixed(fractionDigits); + if (receiver === 0) + t1 = 1 / receiver < 0; + else + t1 = false; + if (t1) + return "-" + result; + return result; + }, + toString$0: function(receiver) { + if (receiver === 0 && 1 / receiver < 0) + return "-0.0"; + else + return "" + receiver; + }, + get$hashCode: function(receiver) { + return receiver & 0x1FFFFFFF; + }, + $add: function(receiver, other) { + if (typeof other !== "number") + throw H.wrapException(new P.ArgumentError(other)); + return receiver + other; + }, + $sub: function(receiver, other) { + return receiver - other; + }, + $mul: function(receiver, other) { + if (typeof other !== "number") + throw H.wrapException(new P.ArgumentError(other)); + return receiver * other; + }, + _tdivFast$1: function(receiver, other) { + return (receiver | 0) === receiver ? receiver / other | 0 : this.toInt$0(receiver / other); + }, + _shrOtherPositive$1: function(receiver, other) { + var t1; + if (receiver > 0) + t1 = other > 31 ? 0 : receiver >>> other; + else { + t1 = other > 31 ? 31 : other; + t1 = receiver >> t1 >>> 0; + } + return t1; + }, + $lt: function(receiver, other) { + if (typeof other !== "number") + throw H.wrapException(P.ArgumentError$(other)); + return receiver < other; + }, + $le: function(receiver, other) { + if (typeof other !== "number") + throw H.wrapException(new P.ArgumentError(other)); + return receiver <= other; + }, + $ge: function(receiver, other) { + if (typeof other !== "number") + throw H.wrapException(P.ArgumentError$(other)); + return receiver >= other; + }, + $isnum: true, + static: {"^": "JSNumber__MIN_INT32,JSNumber__MAX_INT32"} + }, + JSInt: { + "^": "int/JSNumber;", + $isnum: true, + $isint: true + }, + JSDouble: { + "^": "double/JSNumber;", + $isnum: true + }, + JSString: { + "^": "String/Interceptor;", + codeUnitAt$1: function(receiver, index) { + if (index < 0) + throw H.wrapException(P.RangeError$value(index)); + if (index >= receiver.length) + throw H.wrapException(P.RangeError$value(index)); + return receiver.charCodeAt(index); + }, + $add: function(receiver, other) { + if (typeof other !== "string") + throw H.wrapException(new P.ArgumentError(other)); + return receiver + other; + }, + startsWith$2: function(receiver, pattern, index) { + var endIndex; + if (index > receiver.length) + throw H.wrapException(P.RangeError$range(index, 0, receiver.length)); + endIndex = index + pattern.length; + if (endIndex > receiver.length) + return false; + return pattern === receiver.substring(index, endIndex); + }, + startsWith$1: function($receiver, pattern) { + return this.startsWith$2($receiver, pattern, 0); + }, + substring$2: function(receiver, startIndex, endIndex) { + if (endIndex == null) + endIndex = receiver.length; + if (typeof endIndex !== "number" || Math.floor(endIndex) !== endIndex) + H.throwExpression(P.ArgumentError$(endIndex)); + if (startIndex < 0) + throw H.wrapException(P.RangeError$value(startIndex)); + if (typeof endIndex !== "number") + return H.iae(endIndex); + if (startIndex > endIndex) + throw H.wrapException(P.RangeError$value(startIndex)); + if (endIndex > receiver.length) + throw H.wrapException(P.RangeError$value(endIndex)); + return receiver.substring(startIndex, endIndex); + }, + substring$1: function($receiver, startIndex) { + return this.substring$2($receiver, startIndex, null); + }, + toLowerCase$0: function(receiver) { + return receiver.toLowerCase(); + }, + trim$0: function(receiver) { + var result, endIndex, startIndex, t1, endIndex0; + result = receiver.trim(); + endIndex = result.length; + if (endIndex === 0) + return result; + if (this.codeUnitAt$1(result, 0) === 133) { + startIndex = J.JSString__skipLeadingWhitespace(result, 1); + if (startIndex === endIndex) + return ""; + } else + startIndex = 0; + t1 = endIndex - 1; + endIndex0 = this.codeUnitAt$1(result, t1) === 133 ? J.JSString__skipTrailingWhitespace(result, t1) : endIndex; + if (startIndex === 0 && endIndex0 === endIndex) + return result; + return result.substring(startIndex, endIndex0); + }, + $mul: function(receiver, times) { + var s, result; + if (0 >= times) + return ""; + if (times === 1 || receiver.length === 0) + return receiver; + if (times !== times >>> 0) + throw H.wrapException(C.C_OutOfMemoryError); + for (s = receiver, result = ""; true;) { + if ((times & 1) === 1) + result = s + result; + times = times >>> 1; + if (times === 0) + break; + s += s; + } + return result; + }, + get$isEmpty: function(receiver) { + return receiver.length === 0; + }, + toString$0: function(receiver) { + return receiver; + }, + get$hashCode: function(receiver) { + var t1, hash, i; + for (t1 = receiver.length, hash = 0, i = 0; i < t1; ++i) { + hash = 536870911 & hash + receiver.charCodeAt(i); + hash = 536870911 & hash + ((524287 & hash) << 10 >>> 0); + hash ^= hash >> 6; + } + hash = 536870911 & hash + ((67108863 & hash) << 3 >>> 0); + hash ^= hash >> 11; + return 536870911 & hash + ((16383 & hash) << 15 >>> 0); + }, + get$length: function(receiver) { + return receiver.length; + }, + $index: function(receiver, index) { + if (typeof index !== "number" || Math.floor(index) !== index) + throw H.wrapException(new P.ArgumentError(index)); + if (index >= receiver.length || index < 0) + throw H.wrapException(P.RangeError$value(index)); + return receiver[index]; + }, + $isString: true, + static: {JSString__isWhitespace: function(codeUnit) { + if (codeUnit < 256) + switch (codeUnit) { + case 9: + case 10: + case 11: + case 12: + case 13: + case 32: + case 133: + case 160: + return true; + default: + return false; + } + switch (codeUnit) { + case 5760: + case 6158: + case 8192: + case 8193: + case 8194: + case 8195: + case 8196: + case 8197: + case 8198: + case 8199: + case 8200: + case 8201: + case 8202: + case 8232: + case 8233: + case 8239: + case 8287: + case 12288: + case 65279: + return true; + default: + return false; + } + }, JSString__skipLeadingWhitespace: function(string, index) { + var t1, codeUnit; + for (t1 = string.length; index < t1;) { + if (index >= t1) + H.throwExpression(P.RangeError$value(index)); + codeUnit = string.charCodeAt(index); + if (codeUnit !== 32 && codeUnit !== 13 && !J.JSString__isWhitespace(codeUnit)) + break; + ++index; + } + return index; + }, JSString__skipTrailingWhitespace: function(string, index) { + var t1, index0, codeUnit; + for (t1 = string.length; index > 0; index = index0) { + index0 = index - 1; + if (index0 >= t1) + H.throwExpression(P.RangeError$value(index0)); + codeUnit = string.charCodeAt(index0); + if (codeUnit !== 32 && codeUnit !== 13 && !J.JSString__isWhitespace(codeUnit)) + break; + } + return index; + }} + } +}], +["_isolate_helper", "dart:_isolate_helper", , H, { + "^": "", + _callInIsolate: function(isolate, $function) { + var result = isolate.eval$1($function); + init.globalState.topEventLoop.run$0(); + return result; + }, + leaveJsAsync: function() { + var t1 = init.globalState.topEventLoop; + t1._activeJsAsyncCount = t1._activeJsAsyncCount - 1; + }, + startRootIsolate: function(entry, args) { + var t1, t2, t3, t4, t5, rootContext; + t1 = {}; + t1.args_0 = args; + args = args; + t1.args_0 = args; + if (args == null) { + args = []; + t1.args_0 = args; + t2 = args; + } else + t2 = args; + if (!J.getInterceptor(t2).$isList) + throw H.wrapException(new P.ArgumentError("Arguments to main must be a List: " + H.S(t2))); + t2 = new H._Manager(0, 0, 1, null, null, null, null, null, null, null, null, null, entry); + t2._Manager$1(entry); + init.globalState = t2; + if (init.globalState.isWorker === true) + return; + t2 = init.globalState; + t3 = t2.nextIsolateId; + t2.nextIsolateId = t3 + 1; + t2 = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSInt, H.RawReceivePortImpl); + t4 = P.LinkedHashSet_LinkedHashSet(null, null, null, J.JSInt); + t5 = new H.RawReceivePortImpl(0, null, false); + rootContext = new H._IsolateContext(t3, t2, t4, new Isolate(), t5, P.Capability_Capability(), P.Capability_Capability(), false, [], P.LinkedHashSet_LinkedHashSet(null, null, null, null), null, false); + t4.add$1(0, 0); + rootContext._addRegistration$2(0, t5); + init.globalState.rootContext = rootContext; + init.globalState.currentContext = rootContext; + t2 = H.getDynamicRuntimeType(); + t3 = H.buildFunctionType(t2, [t2])._isTest$1(entry); + if (t3) + rootContext.eval$1(new H.startRootIsolate_closure(t1, entry)); + else { + t2 = H.buildFunctionType(t2, [t2, t2])._isTest$1(entry); + if (t2) + rootContext.eval$1(new H.startRootIsolate_closure0(t1, entry)); + else + rootContext.eval$1(entry); + } + init.globalState.topEventLoop.run$0(); + }, + IsolateNatives_computeThisScript: function() { + var currentScript = init.currentScript; + if (currentScript != null) + return String(currentScript.src); + if (typeof version == "function" && typeof os == "object" && "system" in os) + return H.IsolateNatives_computeThisScriptFromTrace(); + if (typeof version == "function" && typeof system == "function") + return thisFilename(); + if (init.globalState.isWorker === true) + return H.IsolateNatives_computeThisScriptFromTrace(); + return; + }, + IsolateNatives_computeThisScriptFromTrace: function() { + var stack, matches; + stack = new Error().stack; + if (stack == null) { + stack = (function() {try { throw new Error() } catch(e) { return e.stack }})(); + if (stack == null) + throw H.wrapException(P.UnsupportedError$("No stack trace")); + } + matches = stack.match(new RegExp("^ *at [^(]*\\((.*):[0-9]*:[0-9]*\\)$", "m")); + if (matches != null) + return matches[1]; + matches = stack.match(new RegExp("^[^@]*@(.*):[0-9]*$", "m")); + if (matches != null) + return matches[1]; + throw H.wrapException(P.UnsupportedError$("Cannot extract URI from \"" + H.S(stack) + "\"")); + }, + IsolateNatives__processWorkerMessage: function(sender, e) { + var msg, t1, functionName, entryPoint, args, message, isSpawnUri, startPaused, replyTo, t2, t3, t4, context, uri, t5, t6, worker, t7, workerId; + msg = H._deserializeMessage(e.data); + t1 = J.getInterceptor$asx(msg); + switch (t1.$index(msg, "command")) { + case "start": + init.globalState.currentManagerId = t1.$index(msg, "id"); + functionName = t1.$index(msg, "functionName"); + entryPoint = functionName == null ? init.globalState.entry : init.globalFunctions[functionName](); + args = t1.$index(msg, "args"); + message = H._deserializeMessage(t1.$index(msg, "msg")); + isSpawnUri = t1.$index(msg, "isSpawnUri"); + startPaused = t1.$index(msg, "startPaused"); + replyTo = H._deserializeMessage(t1.$index(msg, "replyTo")); + t1 = init.globalState; + t2 = t1.nextIsolateId; + t1.nextIsolateId = t2 + 1; + t1 = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSInt, H.RawReceivePortImpl); + t3 = P.LinkedHashSet_LinkedHashSet(null, null, null, J.JSInt); + t4 = new H.RawReceivePortImpl(0, null, false); + context = new H._IsolateContext(t2, t1, t3, new Isolate(), t4, P.Capability_Capability(), P.Capability_Capability(), false, [], P.LinkedHashSet_LinkedHashSet(null, null, null, null), null, false); + t3.add$1(0, 0); + context._addRegistration$2(0, t4); + init.globalState.topEventLoop.events._add$1(new H._IsolateEvent(context, new H.IsolateNatives__processWorkerMessage_closure(entryPoint, args, message, isSpawnUri, startPaused, replyTo), "worker-start")); + init.globalState.currentContext = context; + init.globalState.topEventLoop.run$0(); + break; + case "spawn-worker": + t2 = t1.$index(msg, "functionName"); + uri = t1.$index(msg, "uri"); + t3 = t1.$index(msg, "args"); + t4 = t1.$index(msg, "msg"); + t5 = t1.$index(msg, "isSpawnUri"); + t6 = t1.$index(msg, "startPaused"); + t1 = t1.$index(msg, "replyPort"); + if (uri == null) + uri = $.get$IsolateNatives_thisScript(); + worker = new Worker(uri); + worker.onmessage = function(e) { H.IsolateNatives__processWorkerMessage(worker, e); }; + t7 = init.globalState; + workerId = t7.nextManagerId; + t7.nextManagerId = workerId + 1; + $.get$IsolateNatives_workerIds().$indexSet(0, worker, workerId); + init.globalState.managers.$indexSet(0, workerId, worker); + worker.postMessage(H._serializeMessage(H.fillLiteralMap(["command", "start", "id", workerId, "replyTo", H._serializeMessage(t1), "args", t3, "msg", H._serializeMessage(t4), "isSpawnUri", t5, "startPaused", t6, "functionName", t2], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null)))); + break; + case "message": + if (t1.$index(msg, "port") != null) + J.send$1$x(t1.$index(msg, "port"), t1.$index(msg, "msg")); + init.globalState.topEventLoop.run$0(); + break; + case "close": + init.globalState.managers.remove$1(0, $.get$IsolateNatives_workerIds().$index(0, sender)); + sender.terminate(); + init.globalState.topEventLoop.run$0(); + break; + case "log": + H.IsolateNatives__log(t1.$index(msg, "msg")); + break; + case "print": + if (init.globalState.isWorker === true) { + t1 = init.globalState.mainManager; + t2 = H._serializeMessage(H.fillLiteralMap(["command", "print", "msg", msg], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null))); + t1.toString; + self.postMessage(t2); + } else + P.print(t1.$index(msg, "msg")); + break; + case "error": + throw H.wrapException(t1.$index(msg, "msg")); + } + }, + IsolateNatives__log: function(msg) { + var trace, t1, t2, exception; + if (init.globalState.isWorker === true) { + t1 = init.globalState.mainManager; + t2 = H._serializeMessage(H.fillLiteralMap(["command", "log", "msg", msg], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null))); + t1.toString; + self.postMessage(t2); + } else + try { + $.get$globalThis().console.log(msg); + } catch (exception) { + H.unwrapException(exception); + trace = new H._StackTrace(exception, null); + throw H.wrapException(P.Exception_Exception(trace)); + } + + }, + IsolateNatives__startIsolate: function(topLevel, args, message, isSpawnUri, startPaused, replyTo) { + var context, t1, t2, t3; + context = init.globalState.currentContext; + t1 = context.id; + $.Primitives_mirrorFunctionCacheName = $.Primitives_mirrorFunctionCacheName + ("_" + t1); + $.Primitives_mirrorInvokeCacheName = $.Primitives_mirrorInvokeCacheName + ("_" + t1); + t1 = context.controlPort; + t2 = init.globalState.currentContext.id; + t3 = context.pauseCapability; + J.send$1$x(replyTo, ["spawned", new H._NativeJsSendPort(t1, t2), t3, context.terminateCapability]); + t2 = new H.IsolateNatives__startIsolate_runStartFunction(topLevel, args, message, isSpawnUri); + if (startPaused === true) { + context.addPause$2(t3, t3); + init.globalState.topEventLoop.events._add$1(new H._IsolateEvent(context, t2, "start isolate")); + } else + t2.call$0(); + }, + _serializeMessage: function(message) { + var t1; + if (init.globalState.supportsWorkers === true) { + t1 = new H._JsSerializer(0, new H._MessageTraverserVisitedMap()); + t1._visited = new H._JsVisitedMap(null); + return t1.traverse$1(message); + } else { + t1 = new H._JsCopier(new H._MessageTraverserVisitedMap()); + t1._visited = new H._JsVisitedMap(null); + return t1.traverse$1(message); + } + }, + _deserializeMessage: function(message) { + if (init.globalState.supportsWorkers === true) + return new H._JsDeserializer(null).deserialize$1(message); + else + return message; + }, + _MessageTraverser_isPrimitive: function(x) { + return x == null || typeof x === "string" || typeof x === "number" || typeof x === "boolean"; + }, + _Deserializer_isPrimitive: function(x) { + return x == null || typeof x === "string" || typeof x === "number" || typeof x === "boolean"; + }, + startRootIsolate_closure: { + "^": "Closure:9;box_0,entry_1", + call$0: function() { + this.entry_1.call$1(this.box_0.args_0); + } + }, + startRootIsolate_closure0: { + "^": "Closure:9;box_0,entry_2", + call$0: function() { + this.entry_2.call$2(this.box_0.args_0, null); + } + }, + _Manager: { + "^": "Object;nextIsolateId,currentManagerId,nextManagerId,currentContext,rootContext,topEventLoop,fromCommandLine,isWorker,supportsWorkers,isolates,mainManager,managers,entry", + _Manager$1: function(entry) { + var t1, t2, t3, $function; + t1 = $.get$globalWindow() == null; + t2 = $.get$globalWorker(); + t3 = t1 && $.get$globalPostMessageDefined() === true; + this.isWorker = t3; + if (!t3) + t2 = t2 != null && $.get$IsolateNatives_thisScript() != null; + else + t2 = true; + this.supportsWorkers = t2; + this.fromCommandLine = t1 && !t3; + t2 = H._IsolateEvent; + t3 = H.setRuntimeTypeInfo(new P.ListQueue(null, 0, 0, 0), [t2]); + t3.ListQueue$1(null, t2); + this.topEventLoop = new H._EventLoop(t3, 0); + this.isolates = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSInt, H._IsolateContext); + this.managers = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSInt, null); + if (this.isWorker === true) { + t1 = new H._MainManagerStub(); + this.mainManager = t1; + $function = function (e) { H.IsolateNatives__processWorkerMessage(t1, e); }; + $.get$globalThis().onmessage = $function; + $.get$globalThis().dartPrint = function (object) {}; + } + } + }, + _IsolateContext: { + "^": "Object;id,ports,weakPorts,isolateStatics<,controlPort<,pauseCapability,terminateCapability,isPaused,delayedEvents,pauseTokens,doneHandlers,errorsAreFatal", + addPause$2: function(authentification, resume) { + if (!this.pauseCapability.$eq(0, authentification)) + return; + if (this.pauseTokens.add$1(0, resume) && !this.isPaused) + this.isPaused = true; + this._updateGlobalState$0(); + }, + removePause$1: function(resume) { + var t1, t2, $event, t3, t4, t5; + if (!this.isPaused) + return; + t1 = this.pauseTokens; + t1.remove$1(0, resume); + if (t1._collection$_length === 0) { + for (t1 = this.delayedEvents; t2 = t1.length, t2 !== 0;) { + if (0 >= t2) + return H.ioore(t1, 0); + $event = t1.pop(); + t2 = init.globalState.topEventLoop.events; + t3 = t2._head; + t4 = t2._table; + t5 = t4.length; + t3 = (t3 - 1 & t5 - 1) >>> 0; + t2._head = t3; + if (t3 < 0 || t3 >= t5) + return H.ioore(t4, t3); + t4[t3] = $event; + if (t3 === t2._tail) + t2._grow$0(); + t2._modificationCount = t2._modificationCount + 1; + } + this.isPaused = false; + } + this._updateGlobalState$0(); + }, + addDoneListener$1: function(responsePort) { + var t1 = this.doneHandlers; + if (t1 == null) { + t1 = []; + this.doneHandlers = t1; + } + if (J.contains$1$asx(t1, responsePort)) + return; + this.doneHandlers.push(responsePort); + }, + removeDoneListener$1: function(responsePort) { + var t1 = this.doneHandlers; + if (t1 == null) + return; + J.remove$1$ax(t1, responsePort); + }, + setErrorsFatal$2: function(authentification, errorsAreFatal) { + if (!this.terminateCapability.$eq(0, authentification)) + return; + this.errorsAreFatal = errorsAreFatal; + }, + handlePing$2: function(responsePort, pingType) { + if (J.$eq(pingType, 2)) + init.globalState.topEventLoop.events._add$1(new H._IsolateEvent(this, new H._IsolateContext_handlePing_closure(responsePort), "ping")); + else + J.send$1$x(responsePort, null); + }, + eval$1: function(code) { + var old, result; + old = init.globalState.currentContext; + init.globalState.currentContext = this; + $ = this.isolateStatics; + result = null; + try { + result = code.call$0(); + } finally { + init.globalState.currentContext = old; + if (old != null) + $ = old.get$isolateStatics(); + } + return result; + }, + lookup$1: function(portId) { + return this.ports.$index(0, portId); + }, + _addRegistration$2: function(portId, port) { + var t1 = this.ports; + if (t1.containsKey$1(0, portId)) + throw H.wrapException(P.Exception_Exception("Registry: ports must be registered only once.")); + t1.$indexSet(0, portId, port); + }, + _updateGlobalState$0: function() { + if (this.ports._collection$_length - this.weakPorts._collection$_length > 0 || this.isPaused) + init.globalState.isolates.$indexSet(0, this.id, this); + else + this._shutdown$0(); + }, + _shutdown$0: function() { + init.globalState.isolates.remove$1(0, this.id); + var t1 = this.doneHandlers; + if (t1 != null) + for (t1 = new H.ListIterator(t1, t1.length, 0, null); t1.moveNext$0();) + J.send$1$x(t1._current, null); + } + }, + _IsolateContext_handlePing_closure: { + "^": "Closure:9;responsePort_0", + call$0: function() { + J.send$1$x(this.responsePort_0, null); + } + }, + _EventLoop: { + "^": "Object;events,_activeJsAsyncCount", + dequeue$0: function() { + var t1, t2, t3, t4, result; + t1 = this.events; + t2 = t1._head; + if (t2 === t1._tail) + return; + t1._modificationCount = t1._modificationCount + 1; + t3 = t1._table; + t4 = t3.length; + if (t2 >= t4) + return H.ioore(t3, t2); + result = t3[t2]; + t3[t2] = null; + t1._head = (t2 + 1 & t4 - 1) >>> 0; + return result; + }, + runIteration$0: function() { + var $event, t1, t2; + $event = this.dequeue$0(); + if ($event == null) { + if (init.globalState.rootContext != null && init.globalState.isolates.containsKey$1(0, init.globalState.rootContext.id) && init.globalState.fromCommandLine === true && init.globalState.rootContext.ports._collection$_length === 0) + H.throwExpression(P.Exception_Exception("Program exited with open ReceivePorts.")); + t1 = init.globalState; + if (t1.isWorker === true && t1.isolates._collection$_length === 0 && t1.topEventLoop._activeJsAsyncCount === 0) { + t1 = t1.mainManager; + t2 = H._serializeMessage(H.fillLiteralMap(["command", "close"], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null))); + t1.toString; + self.postMessage(t2); + } + return false; + } + $event.process$0(); + return true; + }, + _runHelper$0: function() { + if ($.get$globalWindow() != null) + new H._EventLoop__runHelper_next(this).call$0(); + else + for (; this.runIteration$0();) + ; + }, + run$0: function() { + var e, trace, exception, t1, t2; + if (init.globalState.isWorker !== true) + this._runHelper$0(); + else + try { + this._runHelper$0(); + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + trace = new H._StackTrace(exception, null); + t1 = init.globalState.mainManager; + t2 = H._serializeMessage(H.fillLiteralMap(["command", "error", "msg", H.S(e) + "\n" + H.S(trace)], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null))); + t1.toString; + self.postMessage(t2); + } + + } + }, + _EventLoop__runHelper_next: { + "^": "Closure:1;this_0", + call$0: function() { + if (!this.this_0.runIteration$0()) + return; + P.Timer_Timer(C.Duration_0, this); + } + }, + _IsolateEvent: { + "^": "Object;isolate,fn,message", + process$0: function() { + var t1 = this.isolate; + if (t1.isPaused) { + t1.delayedEvents.push(this); + return; + } + t1.eval$1(this.fn); + } + }, + _MainManagerStub: { + "^": "Object;" + }, + IsolateNatives__processWorkerMessage_closure: { + "^": "Closure:9;entryPoint_0,args_1,message_2,isSpawnUri_3,startPaused_4,replyTo_5", + call$0: function() { + H.IsolateNatives__startIsolate(this.entryPoint_0, this.args_1, this.message_2, this.isSpawnUri_3, this.startPaused_4, this.replyTo_5); + } + }, + IsolateNatives__startIsolate_runStartFunction: { + "^": "Closure:1;topLevel_0,args_1,message_2,isSpawnUri_3", + call$0: function() { + var t1, t2, t3; + if (this.isSpawnUri_3 !== true) + this.topLevel_0.call$1(this.message_2); + else { + t1 = this.topLevel_0; + t2 = H.getDynamicRuntimeType(); + t3 = H.buildFunctionType(t2, [t2, t2])._isTest$1(t1); + if (t3) + t1.call$2(this.args_1, this.message_2); + else { + t2 = H.buildFunctionType(t2, [t2])._isTest$1(t1); + if (t2) + t1.call$1(this.args_1); + else + t1.call$0(); + } + } + } + }, + _BaseSendPort: { + "^": "Object;", + $isSendPort: true, + $isCapability: true + }, + _NativeJsSendPort: { + "^": "_BaseSendPort;_receivePort,_isolateId", + send$1: function(_, message) { + var t1, t2, isolate, t3, shouldSerialize, msg; + t1 = {}; + t2 = this._isolateId; + isolate = init.globalState.isolates.$index(0, t2); + if (isolate == null) + return; + t3 = this._receivePort; + if (t3.get$_isClosed()) + return; + shouldSerialize = init.globalState.currentContext != null && init.globalState.currentContext.id !== t2; + t1.msg_0 = message; + if (shouldSerialize) { + msg = H._serializeMessage(message); + t1.msg_0 = msg; + t2 = msg; + } else + t2 = message; + if (isolate.get$controlPort() === t3) { + t1 = J.getInterceptor$asx(t2); + switch (t1.$index(t2, 0)) { + case "pause": + isolate.addPause$2(t1.$index(t2, 1), t1.$index(t2, 2)); + break; + case "resume": + isolate.removePause$1(t1.$index(t2, 1)); + break; + case "add-ondone": + isolate.addDoneListener$1(t1.$index(t2, 1)); + break; + case "remove-ondone": + isolate.removeDoneListener$1(t1.$index(t2, 1)); + break; + case "set-errors-fatal": + isolate.setErrorsFatal$2(t1.$index(t2, 1), t1.$index(t2, 2)); + break; + case "ping": + isolate.handlePing$2(t1.$index(t2, 1), t1.$index(t2, 2)); + break; + default: + P.print("UNKNOWN MESSAGE: " + H.S(t2)); + } + return; + } + t2 = init.globalState.topEventLoop; + t3 = "receive " + H.S(message); + t2.events._add$1(new H._IsolateEvent(isolate, new H._NativeJsSendPort_send_closure(t1, this, shouldSerialize), t3)); + }, + $eq: function(_, other) { + if (other == null) + return false; + return !!J.getInterceptor(other).$is_NativeJsSendPort && J.$eq(this._receivePort, other._receivePort); + }, + get$hashCode: function(_) { + return this._receivePort.get$_id(); + }, + $is_NativeJsSendPort: true, + $isSendPort: true, + $isCapability: true + }, + _NativeJsSendPort_send_closure: { + "^": "Closure:9;box_0,this_1,shouldSerialize_2", + call$0: function() { + var t1, t2; + t1 = this.this_1._receivePort; + if (!t1.get$_isClosed()) { + if (this.shouldSerialize_2) { + t2 = this.box_0; + t2.msg_0 = H._deserializeMessage(t2.msg_0); + } + t1.__isolate_helper$_add$1(this.box_0.msg_0); + } + } + }, + _WorkerSendPort: { + "^": "_BaseSendPort;_workerId,_receivePortId,_isolateId", + send$1: function(_, message) { + var workerMessage, manager; + workerMessage = H._serializeMessage(H.fillLiteralMap(["command", "message", "port", this, "msg", message], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null))); + if (init.globalState.isWorker === true) { + init.globalState.mainManager.toString; + self.postMessage(workerMessage); + } else { + manager = init.globalState.managers.$index(0, this._workerId); + if (manager != null) + manager.postMessage(workerMessage); + } + }, + $eq: function(_, other) { + if (other == null) + return false; + return !!J.getInterceptor(other).$is_WorkerSendPort && J.$eq(this._workerId, other._workerId) && J.$eq(this._isolateId, other._isolateId) && J.$eq(this._receivePortId, other._receivePortId); + }, + get$hashCode: function(_) { + var t1, t2, t3; + t1 = this._workerId; + if (typeof t1 !== "number") + return t1.$shl(); + t2 = this._isolateId; + if (typeof t2 !== "number") + return t2.$shl(); + t3 = this._receivePortId; + if (typeof t3 !== "number") + return H.iae(t3); + return (t1 << 16 ^ t2 << 8 ^ t3) >>> 0; + }, + $is_WorkerSendPort: true, + $isSendPort: true, + $isCapability: true + }, + RawReceivePortImpl: { + "^": "Object;_id<,_handler,_isClosed<", + _handler$1: function(arg0) { + return this._handler.call$1(arg0); + }, + __isolate_helper$_add$1: function(dataEvent) { + if (this._isClosed) + return; + this._handler$1(dataEvent); + }, + static: {"^": "RawReceivePortImpl__nextFreeId"} + }, + _JsSerializer: { + "^": "_Serializer;_nextFreeRefId,_visited", + visitSendPort$1: function(x) { + if (!!x.$is_NativeJsSendPort) + return ["sendport", init.globalState.currentManagerId, x._isolateId, x._receivePort.get$_id()]; + if (!!x.$is_WorkerSendPort) + return ["sendport", x._workerId, x._isolateId, x._receivePortId]; + throw H.wrapException("Illegal underlying port " + H.S(x)); + }, + visitCapability$1: function(x) { + if (!!x.$isCapabilityImpl) + return ["capability", x._id]; + throw H.wrapException("Capability not serializable: " + H.S(x)); + } + }, + _JsCopier: { + "^": "_Copier;_visited", + visitSendPort$1: function(x) { + if (!!x.$is_NativeJsSendPort) + return new H._NativeJsSendPort(x._receivePort, x._isolateId); + if (!!x.$is_WorkerSendPort) + return new H._WorkerSendPort(x._workerId, x._receivePortId, x._isolateId); + throw H.wrapException("Illegal underlying port " + H.S(x)); + }, + visitCapability$1: function(x) { + if (!!x.$isCapabilityImpl) + return new H.CapabilityImpl(x._id); + throw H.wrapException("Capability not serializable: " + H.S(x)); + } + }, + _JsDeserializer: { + "^": "_Deserializer;_deserialized", + deserializeSendPort$1: function(list) { + var t1, managerId, isolateId, receivePortId, isolate, receivePort; + t1 = J.getInterceptor$asx(list); + managerId = t1.$index(list, 1); + isolateId = t1.$index(list, 2); + receivePortId = t1.$index(list, 3); + if (J.$eq(managerId, init.globalState.currentManagerId)) { + isolate = init.globalState.isolates.$index(0, isolateId); + if (isolate == null) + return; + receivePort = isolate.lookup$1(receivePortId); + if (receivePort == null) + return; + return new H._NativeJsSendPort(receivePort, isolateId); + } else + return new H._WorkerSendPort(managerId, receivePortId, isolateId); + }, + deserializeCapability$1: function(list) { + return new H.CapabilityImpl(J.$index$asx(list, 1)); + } + }, + _JsVisitedMap: { + "^": "Object;tagged", + $index: function(_, object) { + return object.__MessageTraverser__attached_info__; + }, + $indexSet: function(_, object, info) { + this.tagged.push(object); + object.__MessageTraverser__attached_info__ = info; + }, + reset$0: function(_) { + this.tagged = []; + }, + cleanup$0: function() { + var $length, i, t1; + for ($length = this.tagged.length, i = 0; i < $length; ++i) { + t1 = this.tagged; + if (i >= t1.length) + return H.ioore(t1, i); + t1[i].__MessageTraverser__attached_info__ = null; + } + this.tagged = null; + } + }, + _MessageTraverserVisitedMap: { + "^": "Object;", + $index: function(_, object) { + return; + }, + $indexSet: function(_, object, info) { + }, + reset$0: function(_) { + }, + cleanup$0: function() { + } + }, + _MessageTraverser: { + "^": "Object;", + traverse$1: function(x) { + var result; + if (H._MessageTraverser_isPrimitive(x)) + return this.visitPrimitive$1(x); + this._visited.reset$0(0); + result = null; + try { + result = this._dispatch$1(x); + } finally { + this._visited.cleanup$0(); + } + return result; + }, + _dispatch$1: function(x) { + var t1; + if (x == null || typeof x === "string" || typeof x === "number" || typeof x === "boolean") + return this.visitPrimitive$1(x); + t1 = J.getInterceptor(x); + if (!!t1.$isList) + return this.visitList$1(x); + if (!!t1.$isMap) + return this.visitMap$1(x); + if (!!t1.$isSendPort) + return this.visitSendPort$1(x); + if (!!t1.$isCapability) + return this.visitCapability$1(x); + return this.visitObject$1(x); + }, + visitObject$1: function(x) { + throw H.wrapException("Message serialization: Illegal value " + H.S(x) + " passed"); + } + }, + _Copier: { + "^": "_MessageTraverser;", + visitPrimitive$1: function(x) { + return x; + }, + visitList$1: function(list) { + var copy, t1, len, i; + copy = this._visited.$index(0, list); + if (copy != null) + return copy; + t1 = J.getInterceptor$asx(list); + len = t1.get$length(list); + copy = Array(len); + copy.fixed$length = init; + this._visited.$indexSet(0, list, copy); + for (i = 0; i < len; ++i) + copy[i] = this._dispatch$1(t1.$index(list, i)); + return copy; + }, + visitMap$1: function(map) { + var t1, copy; + t1 = {}; + copy = this._visited.$index(0, map); + t1.copy_0 = copy; + if (copy != null) + return copy; + copy = P.LinkedHashMap_LinkedHashMap(null, null, null, null, null); + t1.copy_0 = copy; + this._visited.$indexSet(0, map, copy); + J.forEach$1$ax(map, new H._Copier_visitMap_closure(t1, this)); + return t1.copy_0; + }, + visitSendPort$1: function(x) { + return H.throwExpression(P.UnimplementedError$(null)); + }, + visitCapability$1: function(x) { + return H.throwExpression(P.UnimplementedError$(null)); + } + }, + _Copier_visitMap_closure: { + "^": "Closure:10;box_0,this_1", + call$2: function(key, val) { + var t1 = this.this_1; + J.$indexSet$ax(this.box_0.copy_0, t1._dispatch$1(key), t1._dispatch$1(val)); + } + }, + _Serializer: { + "^": "_MessageTraverser;", + visitPrimitive$1: function(x) { + return x; + }, + visitList$1: function(list) { + var copyId, id; + copyId = this._visited.$index(0, list); + if (copyId != null) + return ["ref", copyId]; + id = this._nextFreeRefId; + this._nextFreeRefId = id + 1; + this._visited.$indexSet(0, list, id); + return ["list", id, this._serializeList$1(list)]; + }, + visitMap$1: function(map) { + var copyId, id, t1; + copyId = this._visited.$index(0, map); + if (copyId != null) + return ["ref", copyId]; + id = this._nextFreeRefId; + this._nextFreeRefId = id + 1; + this._visited.$indexSet(0, map, id); + t1 = J.getInterceptor$x(map); + return ["map", id, this._serializeList$1(J.toList$0$ax(t1.get$keys(map))), this._serializeList$1(J.toList$0$ax(t1.get$values(map)))]; + }, + _serializeList$1: function(list) { + var t1, len, result, i, t2; + t1 = J.getInterceptor$asx(list); + len = t1.get$length(list); + result = []; + C.JSArray_methods.set$length(result, len); + for (i = 0; i < len; ++i) { + t2 = this._dispatch$1(t1.$index(list, i)); + if (i >= result.length) + return H.ioore(result, i); + result[i] = t2; + } + return result; + }, + visitSendPort$1: function(x) { + return H.throwExpression(P.UnimplementedError$(null)); + }, + visitCapability$1: function(x) { + return H.throwExpression(P.UnimplementedError$(null)); + } + }, + _Deserializer: { + "^": "Object;", + deserialize$1: function(x) { + if (H._Deserializer_isPrimitive(x)) + return x; + this._deserialized = P.HashMap_HashMap(null, null, null, null, null); + return this._deserializeHelper$1(x); + }, + _deserializeHelper$1: function(x) { + var t1, id; + if (x == null || typeof x === "string" || typeof x === "number" || typeof x === "boolean") + return x; + t1 = J.getInterceptor$asx(x); + switch (t1.$index(x, 0)) { + case "ref": + id = t1.$index(x, 1); + return this._deserialized.$index(0, id); + case "list": + return this._deserializeList$1(x); + case "map": + return this._deserializeMap$1(x); + case "sendport": + return this.deserializeSendPort$1(x); + case "capability": + return this.deserializeCapability$1(x); + default: + return this.deserializeObject$1(x); + } + }, + _deserializeList$1: function(x) { + var t1, id, dartList, len, i; + t1 = J.getInterceptor$asx(x); + id = t1.$index(x, 1); + dartList = t1.$index(x, 2); + this._deserialized.$indexSet(0, id, dartList); + t1 = J.getInterceptor$asx(dartList); + len = t1.get$length(dartList); + if (typeof len !== "number") + return H.iae(len); + i = 0; + for (; i < len; ++i) + t1.$indexSet(dartList, i, this._deserializeHelper$1(t1.$index(dartList, i))); + return dartList; + }, + _deserializeMap$1: function(x) { + var result, t1, id, keys, values, len, t2, i; + result = P.LinkedHashMap_LinkedHashMap(null, null, null, null, null); + t1 = J.getInterceptor$asx(x); + id = t1.$index(x, 1); + this._deserialized.$indexSet(0, id, result); + keys = t1.$index(x, 2); + values = t1.$index(x, 3); + t1 = J.getInterceptor$asx(keys); + len = t1.get$length(keys); + if (typeof len !== "number") + return H.iae(len); + t2 = J.getInterceptor$asx(values); + i = 0; + for (; i < len; ++i) + result.$indexSet(0, this._deserializeHelper$1(t1.$index(keys, i)), this._deserializeHelper$1(t2.$index(values, i))); + return result; + }, + deserializeObject$1: function(x) { + throw H.wrapException("Unexpected serialized object"); + } + }, + TimerImpl: { + "^": "Object;_once,_inEventLoop,_handle", + TimerImpl$2: function(milliseconds, callback) { + var t1, t2; + if (milliseconds === 0) + t1 = $.get$globalThis().setTimeout == null || init.globalState.isWorker === true; + else + t1 = false; + if (t1) { + this._handle = 1; + t1 = init.globalState.topEventLoop; + t2 = init.globalState.currentContext; + t1.events._add$1(new H._IsolateEvent(t2, new H.TimerImpl_internalCallback(this, callback), "timer")); + this._inEventLoop = true; + } else { + t1 = $.get$globalThis(); + if (t1.setTimeout != null) { + t2 = init.globalState.topEventLoop; + t2._activeJsAsyncCount = t2._activeJsAsyncCount + 1; + this._handle = t1.setTimeout(H.convertDartClosureToJS(new H.TimerImpl_internalCallback0(this, callback), 0), milliseconds); + } else + throw H.wrapException(P.UnsupportedError$("Timer greater than 0.")); + } + }, + static: {TimerImpl$: function(milliseconds, callback) { + var t1 = new H.TimerImpl(true, false, null); + t1.TimerImpl$2(milliseconds, callback); + return t1; + }} + }, + TimerImpl_internalCallback: { + "^": "Closure:1;this_0,callback_1", + call$0: function() { + this.this_0._handle = null; + this.callback_1.call$0(); + } + }, + TimerImpl_internalCallback0: { + "^": "Closure:1;this_2,callback_3", + call$0: function() { + this.this_2._handle = null; + H.leaveJsAsync(); + this.callback_3.call$0(); + } + }, + CapabilityImpl: { + "^": "Object;_id<", + get$hashCode: function(_) { + var hash = this._id; + if (typeof hash !== "number") + return hash.$shr(); + hash = C.JSNumber_methods._shrOtherPositive$1(hash, 0) ^ C.JSNumber_methods._tdivFast$1(hash, 4294967296); + hash = (~hash >>> 0) + (hash << 15 >>> 0) & 4294967295; + hash = ((hash ^ hash >>> 12) >>> 0) * 5 & 4294967295; + hash = ((hash ^ hash >>> 4) >>> 0) * 2057 & 4294967295; + return (hash ^ hash >>> 16) >>> 0; + }, + $eq: function(_, other) { + var t1, t2; + if (other == null) + return false; + if (other === this) + return true; + if (!!J.getInterceptor(other).$isCapabilityImpl) { + t1 = this._id; + t2 = other._id; + return t1 == null ? t2 == null : t1 === t2; + } + return false; + }, + $isCapabilityImpl: true, + $isCapability: true + } +}], +["_js_helper", "dart:_js_helper", , H, { + "^": "", + isJsIndexable: function(object, record) { + var result; + if (record != null) { + result = record.x; + if (result != null) + return result; + } + return !!J.getInterceptor(object).$isJavaScriptIndexingBehavior; + }, + S: function(value) { + var res; + if (typeof value === "string") + return value; + if (typeof value === "number") { + if (value !== 0) + return "" + value; + } else if (true === value) + return "true"; + else if (false === value) + return "false"; + else if (value == null) + return "null"; + res = J.toString$0(value); + if (typeof res !== "string") + throw H.wrapException(P.ArgumentError$(value)); + return res; + }, + Primitives_objectHashCode: function(object) { + var hash = object.$identityHash; + if (hash == null) { + hash = Math.random() * 0x3fffffff | 0; + object.$identityHash = hash; + } + return hash; + }, + Primitives__throwFormatException: [function(string) { + throw H.wrapException(P.FormatException$(string)); + }, "call$1", "Primitives__throwFormatException$closure", 2, 0, 0], + Primitives_parseInt: function(source, radix, handleError) { + var match, t1; + handleError = H.Primitives__throwFormatException$closure(); + if (typeof source !== "string") + H.throwExpression(new P.ArgumentError(source)); + match = /^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i.exec(source); + if (match != null) { + t1 = match.length; + if (2 >= t1) + return H.ioore(match, 2); + if (match[2] != null) + return parseInt(source, 16); + if (3 >= t1) + return H.ioore(match, 3); + if (match[3] != null) + return parseInt(source, 10); + return handleError.call$1(source); + } + if (match == null) + return handleError.call$1(source); + return parseInt(source, 10); + }, + Primitives_parseDouble: function(source, handleError) { + var result, trimmed; + if (typeof source !== "string") + H.throwExpression(new P.ArgumentError(source)); + handleError = H.Primitives__throwFormatException$closure(); + if (!/^\s*[+-]?(?:Infinity|NaN|(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(source)) + return handleError.call$1(source); + result = parseFloat(source); + if (isNaN(result)) { + trimmed = J.trim$0$s(source); + if (trimmed === "NaN" || trimmed === "+NaN" || trimmed === "-NaN") + return result; + return handleError.call$1(source); + } + return result; + }, + Primitives_objectTypeName: function(object) { + var $name, decompiled; + $name = C.JS_CONST_IX5(J.getInterceptor(object)); + if ($name === "Object") { + decompiled = String(object.constructor).match(/^\s*function\s*(\S*)\s*\(/)[1]; + if (typeof decompiled === "string") + $name = decompiled; + } + if (J.getInterceptor$s($name).codeUnitAt$1($name, 0) === 36) + $name = C.JSString_methods.substring$1($name, 1); + return $name + H.joinArguments(H.getRuntimeTypeInfo(object), 0, null); + }, + Primitives_objectToString: function(object) { + return "Instance of '" + H.Primitives_objectTypeName(object) + "'"; + }, + Primitives__fromCharCodeApply: function(array) { + var end, t1, result, i, subarray, t2; + end = array.length; + for (t1 = end <= 500, result = "", i = 0; i < end; i += 500) { + if (t1) + subarray = array; + else { + t2 = i + 500; + t2 = t2 < end ? t2 : end; + subarray = array.slice(i, t2); + } + result += String.fromCharCode.apply(null, subarray); + } + return result; + }, + Primitives_stringFromCodePoints: function(codePoints) { + var a, t1, i; + a = []; + a.$builtinTypeInfo = [J.JSInt]; + for (t1 = new H.ListIterator(codePoints, codePoints.length, 0, null); t1.moveNext$0();) { + i = t1._current; + if (typeof i !== "number" || Math.floor(i) !== i) + throw H.wrapException(P.ArgumentError$(i)); + if (i <= 65535) + a.push(i); + else if (i <= 1114111) { + a.push(55296 + (C.JSInt_methods._shrOtherPositive$1(i - 65536, 10) & 1023)); + a.push(56320 + (i & 1023)); + } else + throw H.wrapException(P.ArgumentError$(i)); + } + return H.Primitives__fromCharCodeApply(a); + }, + Primitives_stringFromCharCodes: function(charCodes) { + var t1, i; + for (t1 = new H.ListIterator(charCodes, charCodes.length, 0, null); t1.moveNext$0();) { + i = t1._current; + if (typeof i !== "number" || Math.floor(i) !== i) + throw H.wrapException(P.ArgumentError$(i)); + if (i < 0) + throw H.wrapException(P.ArgumentError$(i)); + if (i > 65535) + return H.Primitives_stringFromCodePoints(charCodes); + } + return H.Primitives__fromCharCodeApply(charCodes); + }, + Primitives_valueFromDecomposedDate: function(years, month, day, hours, minutes, seconds, milliseconds, isUtc) { + var jsMonth, value, t1, date; + if (typeof years !== "number" || Math.floor(years) !== years) + H.throwExpression(new P.ArgumentError(years)); + if (typeof month !== "number" || Math.floor(month) !== month) + H.throwExpression(new P.ArgumentError(month)); + if (typeof day !== "number" || Math.floor(day) !== day) + H.throwExpression(new P.ArgumentError(day)); + if (typeof hours !== "number" || Math.floor(hours) !== hours) + H.throwExpression(new P.ArgumentError(hours)); + if (typeof minutes !== "number" || Math.floor(minutes) !== minutes) + H.throwExpression(new P.ArgumentError(minutes)); + if (typeof seconds !== "number" || Math.floor(seconds) !== seconds) + H.throwExpression(new P.ArgumentError(seconds)); + jsMonth = J.$sub$n(month, 1); + value = isUtc ? Date.UTC(years, jsMonth, day, hours, minutes, seconds, milliseconds) : new Date(years, jsMonth, day, hours, minutes, seconds, milliseconds).valueOf(); + if (isNaN(value) || value < -8640000000000000 || value > 8640000000000000) + throw H.wrapException(new P.ArgumentError(null)); + t1 = J.getInterceptor$n(years); + if (t1.$le(years, 0) || t1.$lt(years, 100)) { + date = new Date(value); + if (isUtc) + date.setUTCFullYear(years); + else + date.setFullYear(years); + return date.valueOf(); + } + return value; + }, + Primitives_lazyAsJsDate: function(receiver) { + if (receiver.date === void 0) + receiver.date = new Date(receiver.millisecondsSinceEpoch); + return receiver.date; + }, + Primitives_getProperty: function(object, key) { + if (object == null || typeof object === "boolean" || typeof object === "number" || typeof object === "string") + throw H.wrapException(new P.ArgumentError(object)); + return object[key]; + }, + Primitives_setProperty: function(object, key, value) { + if (object == null || typeof object === "boolean" || typeof object === "number" || typeof object === "string") + throw H.wrapException(new P.ArgumentError(object)); + object[key] = value; + }, + iae: function(argument) { + throw H.wrapException(P.ArgumentError$(argument)); + }, + ioore: function(receiver, index) { + if (receiver == null) + J.get$length$asx(receiver); + if (typeof index !== "number" || Math.floor(index) !== index) + H.iae(index); + throw H.wrapException(P.RangeError$value(index)); + }, + wrapException: function(ex) { + var wrapper; + if (ex == null) + ex = new P.NullThrownError(); + wrapper = new Error(); + wrapper.dartException = ex; + if ("defineProperty" in Object) { + Object.defineProperty(wrapper, "message", { get: H.toStringWrapper }); + wrapper.name = ""; + } else + wrapper.toString = H.toStringWrapper; + return wrapper; + }, + toStringWrapper: function() { + return J.toString$0(this.dartException); + }, + throwExpression: function(ex) { + var wrapper; + if (ex == null) + ex = new P.NullThrownError(); + wrapper = new Error(); + wrapper.dartException = ex; + if ("defineProperty" in Object) { + Object.defineProperty(wrapper, "message", { get: H.toStringWrapper }); + wrapper.name = ""; + } else + wrapper.toString = H.toStringWrapper; + throw wrapper; + }, + unwrapException: function(ex) { + var t1, message, number, ieErrorCode, t2, t3, t4, nullLiteralCall, t5, t6, t7, t8, t9, match; + t1 = new H.unwrapException_saveStackTrace(ex); + if (ex == null) + return; + if (typeof ex !== "object") + return ex; + if ("dartException" in ex) + return t1.call$1(ex.dartException); + else if (!("message" in ex)) + return ex; + message = ex.message; + if ("number" in ex && typeof ex.number == "number") { + number = ex.number; + ieErrorCode = number & 65535; + if ((C.JSInt_methods._shrOtherPositive$1(number, 16) & 8191) === 10) + switch (ieErrorCode) { + case 438: + return t1.call$1(H.JsNoSuchMethodError$(H.S(message) + " (Error " + ieErrorCode + ")", null)); + case 445: + case 5007: + t2 = H.S(message) + " (Error " + ieErrorCode + ")"; + return t1.call$1(new H.NullError(t2, null)); + } + } + if (ex instanceof TypeError) { + t2 = $.get$TypeErrorDecoder_noSuchMethodPattern(); + t3 = $.get$TypeErrorDecoder_notClosurePattern(); + t4 = $.get$TypeErrorDecoder_nullCallPattern(); + nullLiteralCall = $.get$TypeErrorDecoder_nullLiteralCallPattern(); + t5 = $.get$TypeErrorDecoder_undefinedCallPattern(); + t6 = $.get$TypeErrorDecoder_undefinedLiteralCallPattern(); + t7 = $.get$TypeErrorDecoder_nullPropertyPattern(); + $.get$TypeErrorDecoder_nullLiteralPropertyPattern(); + t8 = $.get$TypeErrorDecoder_undefinedPropertyPattern(); + t9 = $.get$TypeErrorDecoder_undefinedLiteralPropertyPattern(); + match = t2.matchTypeError$1(message); + if (match != null) + return t1.call$1(H.JsNoSuchMethodError$(message, match)); + else { + match = t3.matchTypeError$1(message); + if (match != null) { + match.method = "call"; + return t1.call$1(H.JsNoSuchMethodError$(message, match)); + } else { + match = t4.matchTypeError$1(message); + if (match == null) { + match = nullLiteralCall.matchTypeError$1(message); + if (match == null) { + match = t5.matchTypeError$1(message); + if (match == null) { + match = t6.matchTypeError$1(message); + if (match == null) { + match = t7.matchTypeError$1(message); + if (match == null) { + match = nullLiteralCall.matchTypeError$1(message); + if (match == null) { + match = t8.matchTypeError$1(message); + if (match == null) { + match = t9.matchTypeError$1(message); + t2 = match != null; + } else + t2 = true; + } else + t2 = true; + } else + t2 = true; + } else + t2 = true; + } else + t2 = true; + } else + t2 = true; + } else + t2 = true; + if (t2) { + t2 = match == null ? null : match.method; + return t1.call$1(new H.NullError(message, t2)); + } + } + } + t2 = typeof message === "string" ? message : ""; + return t1.call$1(new H.UnknownJsTypeError(t2)); + } + if (ex instanceof RangeError) { + if (typeof message === "string" && message.indexOf("call stack") !== -1) + return new P.StackOverflowError(); + return t1.call$1(new P.ArgumentError(null)); + } + if (typeof InternalError == "function" && ex instanceof InternalError) + if (typeof message === "string" && message === "too much recursion") + return new P.StackOverflowError(); + return ex; + }, + objectHashCode: function(object) { + if (object == null || typeof object != 'object') + return J.get$hashCode$(object); + else + return H.Primitives_objectHashCode(object); + }, + fillLiteralMap: function(keyValuePairs, result) { + var $length, index, index0, index1; + $length = keyValuePairs.length; + for (index = 0; index < $length; index = index1) { + index0 = index + 1; + index1 = index0 + 1; + result.$indexSet(0, keyValuePairs[index], keyValuePairs[index0]); + } + return result; + }, + invokeClosure: function(closure, isolate, numberOfArguments, arg1, arg2, arg3, arg4) { + var t1 = J.getInterceptor(numberOfArguments); + if (t1.$eq(numberOfArguments, 0)) + return H._callInIsolate(isolate, new H.invokeClosure_closure(closure)); + else if (t1.$eq(numberOfArguments, 1)) + return H._callInIsolate(isolate, new H.invokeClosure_closure0(closure, arg1)); + else if (t1.$eq(numberOfArguments, 2)) + return H._callInIsolate(isolate, new H.invokeClosure_closure1(closure, arg1, arg2)); + else if (t1.$eq(numberOfArguments, 3)) + return H._callInIsolate(isolate, new H.invokeClosure_closure2(closure, arg1, arg2, arg3)); + else if (t1.$eq(numberOfArguments, 4)) + return H._callInIsolate(isolate, new H.invokeClosure_closure3(closure, arg1, arg2, arg3, arg4)); + else + throw H.wrapException(P.Exception_Exception("Unsupported number of arguments for wrapped closure")); + }, + convertDartClosureToJS: function(closure, arity) { + var $function; + if (closure == null) + return; + $function = closure.$identity; + if (!!$function) + return $function; + $function = (function(closure, arity, context, invoke) { return function(a1, a2, a3, a4) { return invoke(closure, context, arity, a1, a2, a3, a4); };})(closure,arity,init.globalState.currentContext,H.invokeClosure); + closure.$identity = $function; + return $function; + }, + Closure_fromTearOff: function(receiver, functions, reflectionInfo, isStatic, jsArguments, propertyName) { + var $function, callName, functionType, $prototype, $constructor, t1, isIntercepted, trampoline, signatureFunction, getReceiver, i, stub, stubCallName, t2; + $function = functions[0]; + $function.$stubName; + callName = $function.$callName; + $function.$reflectionInfo = reflectionInfo; + functionType = H.ReflectionInfo_ReflectionInfo($function).functionType; + $prototype = isStatic ? Object.create(new H.TearOffClosure().constructor.prototype) : Object.create(new H.BoundClosure(null, null, null, null).constructor.prototype); + $prototype.$initialize = $prototype.constructor; + if (isStatic) + $constructor = function(){this.$initialize()}; + else if (typeof dart_precompiled == "function") { + t1 = function(a,b,c,d) {this.$initialize(a,b,c,d)}; + $constructor = t1; + } else { + t1 = $.Closure_functionCounter; + $.Closure_functionCounter = J.$add$ns(t1, 1); + t1 = new Function("a", "b", "c", "d", "this.$initialize(a,b,c,d);" + t1); + $constructor = t1; + } + $prototype.constructor = $constructor; + $constructor.prototype = $prototype; + t1 = !isStatic; + if (t1) { + isIntercepted = jsArguments.length == 1 && true; + trampoline = H.Closure_forwardCallTo(receiver, $function, isIntercepted); + trampoline.$reflectionInfo = reflectionInfo; + } else { + $prototype.$name = propertyName; + trampoline = $function; + isIntercepted = false; + } + if (typeof functionType == "number") + signatureFunction = (function(s){return function(){return init.metadata[s]}})(functionType); + else if (t1 && typeof functionType == "function") { + getReceiver = isIntercepted ? H.BoundClosure_receiverOf : H.BoundClosure_selfOf; + signatureFunction = function(f,r){return function(){return f.apply({$receiver:r(this)},arguments)}}(functionType,getReceiver); + } else + throw H.wrapException("Error in reflectionInfo."); + $prototype.$signature = signatureFunction; + $prototype[callName] = trampoline; + for (t1 = functions.length, i = 1; i < t1; ++i) { + stub = functions[i]; + stubCallName = stub.$callName; + if (stubCallName != null) { + t2 = isStatic ? stub : H.Closure_forwardCallTo(receiver, stub, isIntercepted); + $prototype[stubCallName] = t2; + } + } + $prototype["call*"] = trampoline; + return $constructor; + }, + Closure_cspForwardCall: function(arity, isSuperCall, stubName, $function) { + var getSelf = H.BoundClosure_selfOf; + switch (isSuperCall ? -1 : arity) { + case 0: + return function(n,S){return function(){return S(this)[n]()}}(stubName,getSelf); + case 1: + return function(n,S){return function(a){return S(this)[n](a)}}(stubName,getSelf); + case 2: + return function(n,S){return function(a,b){return S(this)[n](a,b)}}(stubName,getSelf); + case 3: + return function(n,S){return function(a,b,c){return S(this)[n](a,b,c)}}(stubName,getSelf); + case 4: + return function(n,S){return function(a,b,c,d){return S(this)[n](a,b,c,d)}}(stubName,getSelf); + case 5: + return function(n,S){return function(a,b,c,d,e){return S(this)[n](a,b,c,d,e)}}(stubName,getSelf); + default: + return function(f,s){return function(){return f.apply(s(this),arguments)}}($function,getSelf); + } + }, + Closure_forwardCallTo: function(receiver, $function, isIntercepted) { + var stubName, arity, lookedUpFunction, t1, t2, $arguments; + if (isIntercepted) + return H.Closure_forwardInterceptedCallTo(receiver, $function); + stubName = $function.$stubName; + arity = $function.length; + lookedUpFunction = receiver[stubName]; + t1 = $function == null ? lookedUpFunction == null : $function === lookedUpFunction; + if (typeof dart_precompiled == "function" || !t1 || arity >= 27) + return H.Closure_cspForwardCall(arity, !t1, stubName, $function); + if (arity === 0) { + t1 = $.BoundClosure_selfFieldNameCache; + if (t1 == null) { + t1 = H.BoundClosure_computeFieldNamed("self"); + $.BoundClosure_selfFieldNameCache = t1; + } + t1 = "return function(){return this." + H.S(t1) + "." + H.S(stubName) + "();"; + t2 = $.Closure_functionCounter; + $.Closure_functionCounter = J.$add$ns(t2, 1); + return new Function(t1 + H.S(t2) + "}")(); + } + $arguments = "abcdefghijklmnopqrstuvwxyz".split("").splice(0, arity).join(","); + t1 = "return function(" + $arguments + "){return this."; + t2 = $.BoundClosure_selfFieldNameCache; + if (t2 == null) { + t2 = H.BoundClosure_computeFieldNamed("self"); + $.BoundClosure_selfFieldNameCache = t2; + } + t2 = t1 + H.S(t2) + "." + H.S(stubName) + "(" + $arguments + ");"; + t1 = $.Closure_functionCounter; + $.Closure_functionCounter = J.$add$ns(t1, 1); + return new Function(t2 + H.S(t1) + "}")(); + }, + Closure_cspForwardInterceptedCall: function(arity, isSuperCall, $name, $function) { + var getSelf, getReceiver; + getSelf = H.BoundClosure_selfOf; + getReceiver = H.BoundClosure_receiverOf; + switch (isSuperCall ? -1 : arity) { + case 0: + throw H.wrapException(H.RuntimeError$("Intercepted function with no arguments.")); + case 1: + return function(n,s,r){return function(){return s(this)[n](r(this))}}($name,getSelf,getReceiver); + case 2: + return function(n,s,r){return function(a){return s(this)[n](r(this),a)}}($name,getSelf,getReceiver); + case 3: + return function(n,s,r){return function(a,b){return s(this)[n](r(this),a,b)}}($name,getSelf,getReceiver); + case 4: + return function(n,s,r){return function(a,b,c){return s(this)[n](r(this),a,b,c)}}($name,getSelf,getReceiver); + case 5: + return function(n,s,r){return function(a,b,c,d){return s(this)[n](r(this),a,b,c,d)}}($name,getSelf,getReceiver); + case 6: + return function(n,s,r){return function(a,b,c,d,e){return s(this)[n](r(this),a,b,c,d,e)}}($name,getSelf,getReceiver); + default: + return function(f,s,r,a){return function(){a=[r(this)];Array.prototype.push.apply(a,arguments);return f.apply(s(this),a)}}($function,getSelf,getReceiver); + } + }, + Closure_forwardInterceptedCallTo: function(receiver, $function) { + var selfField, t1, stubName, arity, isCsp, lookedUpFunction, t2, $arguments; + selfField = H.BoundClosure_selfFieldName(); + t1 = $.BoundClosure_receiverFieldNameCache; + if (t1 == null) { + t1 = H.BoundClosure_computeFieldNamed("receiver"); + $.BoundClosure_receiverFieldNameCache = t1; + } + stubName = $function.$stubName; + arity = $function.length; + isCsp = typeof dart_precompiled == "function"; + lookedUpFunction = receiver[stubName]; + t2 = $function == null ? lookedUpFunction == null : $function === lookedUpFunction; + if (isCsp || !t2 || arity >= 28) + return H.Closure_cspForwardInterceptedCall(arity, !t2, stubName, $function); + if (arity === 1) { + t1 = "return function(){return this." + H.S(selfField) + "." + H.S(stubName) + "(this." + H.S(t1) + ");"; + t2 = $.Closure_functionCounter; + $.Closure_functionCounter = J.$add$ns(t2, 1); + return new Function(t1 + H.S(t2) + "}")(); + } + $arguments = "abcdefghijklmnopqrstuvwxyz".split("").splice(0, arity - 1).join(","); + t1 = "return function(" + $arguments + "){return this." + H.S(selfField) + "." + H.S(stubName) + "(this." + H.S(t1) + ", " + $arguments + ");"; + t2 = $.Closure_functionCounter; + $.Closure_functionCounter = J.$add$ns(t2, 1); + return new Function(t1 + H.S(t2) + "}")(); + }, + closureFromTearOff: function(receiver, functions, reflectionInfo, isStatic, jsArguments, $name) { + functions.fixed$length = init; + reflectionInfo.fixed$length = init; + return H.Closure_fromTearOff(receiver, functions, reflectionInfo, !!isStatic, jsArguments, $name); + }, + throwCyclicInit: function(staticName) { + throw H.wrapException(P.CyclicInitializationError$("Cyclic initialization for static " + H.S(staticName))); + }, + buildFunctionType: function(returnType, parameterTypes, optionalParameterTypes) { + return new H.RuntimeFunctionType(returnType, parameterTypes, optionalParameterTypes, null); + }, + getDynamicRuntimeType: function() { + return C.C_DynamicRuntimeType; + }, + setRuntimeTypeInfo: function(target, typeInfo) { + if (target != null) + target.$builtinTypeInfo = typeInfo; + return target; + }, + getRuntimeTypeInfo: function(target) { + if (target == null) + return; + return target.$builtinTypeInfo; + }, + getRuntimeTypeArguments: function(target, substitutionName) { + return H.substitute(target["$as" + H.S(substitutionName)], H.getRuntimeTypeInfo(target)); + }, + getRuntimeTypeArgument: function(target, substitutionName, index) { + var $arguments = H.getRuntimeTypeArguments(target, substitutionName); + return $arguments == null ? null : $arguments[index]; + }, + getTypeArgumentByIndex: function(target, index) { + var rti = H.getRuntimeTypeInfo(target); + return rti == null ? null : rti[index]; + }, + runtimeTypeToString: function(type, onTypeVariable) { + if (type == null) + return "dynamic"; + else if (typeof type === "object" && type !== null && type.constructor === Array) + return type[0].builtin$cls + H.joinArguments(type, 1, onTypeVariable); + else if (typeof type == "function") + return type.builtin$cls; + else if (typeof type === "number" && Math.floor(type) === type) + return C.JSInt_methods.toString$0(type); + else + return; + }, + joinArguments: function(types, startIndex, onTypeVariable) { + var buffer, index, firstArgument, allDynamic, argument, str; + if (types == null) + return ""; + buffer = P.StringBuffer$(""); + for (index = startIndex, firstArgument = true, allDynamic = true; index < types.length; ++index) { + if (firstArgument) + firstArgument = false; + else + buffer._contents = buffer._contents + ", "; + argument = types[index]; + if (argument != null) + allDynamic = false; + str = H.runtimeTypeToString(argument, onTypeVariable); + str = typeof str === "string" ? str : H.S(str); + buffer._contents = buffer._contents + str; + } + return allDynamic ? "" : "<" + H.S(buffer) + ">"; + }, + substitute: function(substitution, $arguments) { + if (typeof substitution === "object" && substitution !== null && substitution.constructor === Array) + $arguments = substitution; + else if (typeof substitution == "function") { + substitution = H.invokeOn(substitution, null, $arguments); + if (typeof substitution === "object" && substitution !== null && substitution.constructor === Array) + $arguments = substitution; + else if (typeof substitution == "function") + $arguments = H.invokeOn(substitution, null, $arguments); + } + return $arguments; + }, + areSubtypes: function(s, t) { + var len, i; + if (s == null || t == null) + return true; + len = s.length; + for (i = 0; i < len; ++i) + if (!H.isSubtype(s[i], t[i])) + return false; + return true; + }, + computeSignature: function(signature, context, contextName) { + return H.invokeOn(signature, context, H.getRuntimeTypeArguments(context, contextName)); + }, + isSubtype: function(s, t) { + var targetSignatureFunction, t1, typeOfS, t2, typeOfT, $name, substitution; + if (s === t) + return true; + if (s == null || t == null) + return true; + if ("func" in t) { + if (!("func" in s)) { + if ("$is_" + H.S(t.func) in s) + return true; + targetSignatureFunction = s.$signature; + if (targetSignatureFunction == null) + return false; + s = targetSignatureFunction.apply(s, null); + } + return H.isFunctionSubtype(s, t); + } + if (t.builtin$cls === "Function" && "func" in s) + return true; + t1 = typeof s === "object" && s !== null && s.constructor === Array; + typeOfS = t1 ? s[0] : s; + t2 = typeof t === "object" && t !== null && t.constructor === Array; + typeOfT = t2 ? t[0] : t; + $name = H.runtimeTypeToString(typeOfT, null); + if (typeOfT !== typeOfS) { + if (!("$is" + H.S($name) in typeOfS)) + return false; + substitution = typeOfS["$as" + H.S(H.runtimeTypeToString(typeOfT, null))]; + } else + substitution = null; + if (!t1 && substitution == null || !t2) + return true; + t1 = t1 ? s.slice(1) : null; + t2 = t2 ? t.slice(1) : null; + return H.areSubtypes(H.substitute(substitution, t1), t2); + }, + areAssignable: function(s, t, allowShorter) { + var sLength, tLength, i, t1, t2; + if (t == null && s == null) + return true; + if (t == null) + return allowShorter; + if (s == null) + return false; + sLength = s.length; + tLength = t.length; + if (allowShorter) { + if (sLength < tLength) + return false; + } else if (sLength !== tLength) + return false; + for (i = 0; i < tLength; ++i) { + t1 = s[i]; + t2 = t[i]; + if (!(H.isSubtype(t1, t2) || H.isSubtype(t2, t1))) + return false; + } + return true; + }, + areAssignableMaps: function(s, t) { + var t1, names, i, $name, tType, sType; + if (t == null) + return true; + if (s == null) + return false; + t1 = Object.getOwnPropertyNames(t); + t1.fixed$length = init; + names = t1; + for (t1 = names.length, i = 0; i < t1; ++i) { + $name = names[i]; + if (!Object.hasOwnProperty.call(s, $name)) + return false; + tType = t[$name]; + sType = s[$name]; + if (!(H.isSubtype(tType, sType) || H.isSubtype(sType, tType))) + return false; + } + return true; + }, + isFunctionSubtype: function(s, t) { + var sReturnType, tReturnType, sParameterTypes, tParameterTypes, sOptionalParameterTypes, tOptionalParameterTypes, sParametersLen, tParametersLen, sOptionalParametersLen, tOptionalParametersLen, pos, t1, t2, tPos, sPos; + if (!("func" in s)) + return false; + if ("void" in s) { + if (!("void" in t) && "ret" in t) + return false; + } else if (!("void" in t)) { + sReturnType = s.ret; + tReturnType = t.ret; + if (!(H.isSubtype(sReturnType, tReturnType) || H.isSubtype(tReturnType, sReturnType))) + return false; + } + sParameterTypes = s.args; + tParameterTypes = t.args; + sOptionalParameterTypes = s.opt; + tOptionalParameterTypes = t.opt; + sParametersLen = sParameterTypes != null ? sParameterTypes.length : 0; + tParametersLen = tParameterTypes != null ? tParameterTypes.length : 0; + sOptionalParametersLen = sOptionalParameterTypes != null ? sOptionalParameterTypes.length : 0; + tOptionalParametersLen = tOptionalParameterTypes != null ? tOptionalParameterTypes.length : 0; + if (sParametersLen > tParametersLen) + return false; + if (sParametersLen + sOptionalParametersLen < tParametersLen + tOptionalParametersLen) + return false; + if (sParametersLen === tParametersLen) { + if (!H.areAssignable(sParameterTypes, tParameterTypes, false)) + return false; + if (!H.areAssignable(sOptionalParameterTypes, tOptionalParameterTypes, true)) + return false; + } else { + for (pos = 0; pos < sParametersLen; ++pos) { + t1 = sParameterTypes[pos]; + t2 = tParameterTypes[pos]; + if (!(H.isSubtype(t1, t2) || H.isSubtype(t2, t1))) + return false; + } + for (tPos = pos, sPos = 0; tPos < tParametersLen; ++sPos, ++tPos) { + t1 = sOptionalParameterTypes[sPos]; + t2 = tParameterTypes[tPos]; + if (!(H.isSubtype(t1, t2) || H.isSubtype(t2, t1))) + return false; + } + for (tPos = 0; tPos < tOptionalParametersLen; ++sPos, ++tPos) { + t1 = sOptionalParameterTypes[sPos]; + t2 = tOptionalParameterTypes[tPos]; + if (!(H.isSubtype(t1, t2) || H.isSubtype(t2, t1))) + return false; + } + } + return H.areAssignableMaps(s.named, t.named); + }, + invokeOn: function($function, receiver, $arguments) { + return $function.apply(receiver, $arguments); + }, + toStringForNativeObject: function(obj) { + var t1 = $.getTagFunction; + return "Instance of " + (t1 == null ? "" : t1.call$1(obj)); + }, + hashCodeForNativeObject: function(object) { + return H.Primitives_objectHashCode(object); + }, + defineProperty: function(obj, property, value) { + Object.defineProperty(obj, property, {value: value, enumerable: false, writable: true, configurable: true}); + }, + lookupAndCacheInterceptor: function(obj) { + var tag, record, interceptor, interceptorClass, mark, t1; + tag = $.getTagFunction.call$1(obj); + record = $.dispatchRecordsForInstanceTags[tag]; + if (record != null) { + Object.defineProperty(obj, init.dispatchPropertyName, {value: record, enumerable: false, writable: true, configurable: true}); + return record.i; + } + interceptor = $.interceptorsForUncacheableTags[tag]; + if (interceptor != null) + return interceptor; + interceptorClass = init.interceptorsByTag[tag]; + if (interceptorClass == null) { + tag = $.alternateTagFunction.call$2(obj, tag); + if (tag != null) { + record = $.dispatchRecordsForInstanceTags[tag]; + if (record != null) { + Object.defineProperty(obj, init.dispatchPropertyName, {value: record, enumerable: false, writable: true, configurable: true}); + return record.i; + } + interceptor = $.interceptorsForUncacheableTags[tag]; + if (interceptor != null) + return interceptor; + interceptorClass = init.interceptorsByTag[tag]; + } + } + if (interceptorClass == null) + return; + interceptor = interceptorClass.prototype; + mark = tag[0]; + if (mark === "!") { + record = H.makeLeafDispatchRecord(interceptor); + $.dispatchRecordsForInstanceTags[tag] = record; + Object.defineProperty(obj, init.dispatchPropertyName, {value: record, enumerable: false, writable: true, configurable: true}); + return record.i; + } + if (mark === "~") { + $.interceptorsForUncacheableTags[tag] = interceptor; + return interceptor; + } + if (mark === "-") { + t1 = H.makeLeafDispatchRecord(interceptor); + Object.defineProperty(Object.getPrototypeOf(obj), init.dispatchPropertyName, {value: t1, enumerable: false, writable: true, configurable: true}); + return t1.i; + } + if (mark === "+") + return H.patchInteriorProto(obj, interceptor); + if (mark === "*") + throw H.wrapException(P.UnimplementedError$(tag)); + if (init.leafTags[tag] === true) { + t1 = H.makeLeafDispatchRecord(interceptor); + Object.defineProperty(Object.getPrototypeOf(obj), init.dispatchPropertyName, {value: t1, enumerable: false, writable: true, configurable: true}); + return t1.i; + } else + return H.patchInteriorProto(obj, interceptor); + }, + patchInteriorProto: function(obj, interceptor) { + var proto, record; + proto = Object.getPrototypeOf(obj); + record = J.makeDispatchRecord(interceptor, proto, null, null); + Object.defineProperty(proto, init.dispatchPropertyName, {value: record, enumerable: false, writable: true, configurable: true}); + return interceptor; + }, + makeLeafDispatchRecord: function(interceptor) { + return J.makeDispatchRecord(interceptor, false, null, !!interceptor.$isJavaScriptIndexingBehavior); + }, + makeDefaultDispatchRecord: function(tag, interceptorClass, proto) { + var interceptor = interceptorClass.prototype; + if (init.leafTags[tag] === true) + return J.makeDispatchRecord(interceptor, false, null, !!interceptor.$isJavaScriptIndexingBehavior); + else + return J.makeDispatchRecord(interceptor, proto, null, null); + }, + initNativeDispatch: function() { + if (true === $.initNativeDispatchFlag) + return; + $.initNativeDispatchFlag = true; + H.initNativeDispatchContinue(); + }, + initNativeDispatchContinue: function() { + var map, tags, i, tag, proto, record, interceptorClass; + $.dispatchRecordsForInstanceTags = Object.create(null); + $.interceptorsForUncacheableTags = Object.create(null); + H.initHooks(); + map = init.interceptorsByTag; + tags = Object.getOwnPropertyNames(map); + if (typeof window != "undefined") { + window; + for (i = 0; i < tags.length; ++i) { + tag = tags[i]; + proto = $.prototypeForTagFunction.call$1(tag); + if (proto != null) { + record = H.makeDefaultDispatchRecord(tag, map[tag], proto); + if (record != null) + Object.defineProperty(proto, init.dispatchPropertyName, {value: record, enumerable: false, writable: true, configurable: true}); + } + } + } + for (i = 0; i < tags.length; ++i) { + tag = tags[i]; + if (/^[A-Za-z_]/.test(tag)) { + interceptorClass = map[tag]; + map["!" + tag] = interceptorClass; + map["~" + tag] = interceptorClass; + map["-" + tag] = interceptorClass; + map["+" + tag] = interceptorClass; + map["*" + tag] = interceptorClass; + } + } + }, + initHooks: function() { + var hooks, transformers, i, transformer, getTag, getUnknownTag, prototypeForTag; + hooks = C.JS_CONST_aQP(); + hooks = H.applyHooksTransformer(C.JS_CONST_0, H.applyHooksTransformer(C.JS_CONST_rr7, H.applyHooksTransformer(C.JS_CONST_Fs4, H.applyHooksTransformer(C.JS_CONST_Fs4, H.applyHooksTransformer(C.JS_CONST_gkc, H.applyHooksTransformer(C.JS_CONST_U4w, H.applyHooksTransformer(C.JS_CONST_QJm(C.JS_CONST_IX5), hooks))))))); + if (typeof dartNativeDispatchHooksTransformer != "undefined") { + transformers = dartNativeDispatchHooksTransformer; + if (typeof transformers == "function") + transformers = [transformers]; + if (transformers.constructor == Array) + for (i = 0; i < transformers.length; ++i) { + transformer = transformers[i]; + if (typeof transformer == "function") + hooks = transformer(hooks) || hooks; + } + } + getTag = hooks.getTag; + getUnknownTag = hooks.getUnknownTag; + prototypeForTag = hooks.prototypeForTag; + $.getTagFunction = new H.initHooks_closure(getTag); + $.alternateTagFunction = new H.initHooks_closure0(getUnknownTag); + $.prototypeForTagFunction = new H.initHooks_closure1(prototypeForTag); + }, + applyHooksTransformer: function(transformer, hooks) { + return transformer(hooks) || hooks; + }, + ReflectionInfo: { + "^": "Object;jsFunction,data,isAccessor,requiredParameterCount,optionalParameterCount,areOptionalParametersNamed,functionType,cachedSortedIndices", + static: {"^": "ReflectionInfo_REQUIRED_PARAMETERS_INFO,ReflectionInfo_OPTIONAL_PARAMETERS_INFO,ReflectionInfo_FUNCTION_TYPE_INDEX,ReflectionInfo_FIRST_DEFAULT_ARGUMENT", ReflectionInfo_ReflectionInfo: function(jsFunction) { + var data, requiredParametersInfo, requiredParameterCount, optionalParametersInfo; + data = jsFunction.$reflectionInfo; + if (data == null) + return; + data.fixed$length = init; + data = data; + requiredParametersInfo = data[0]; + requiredParameterCount = requiredParametersInfo >> 1; + optionalParametersInfo = data[1]; + return new H.ReflectionInfo(jsFunction, data, (requiredParametersInfo & 1) === 1, requiredParameterCount, optionalParametersInfo >> 1, (optionalParametersInfo & 1) === 1, data[2], null); + }} + }, + TypeErrorDecoder: { + "^": "Object;_pattern,_arguments,_argumentsExpr,_expr,_method,_receiver", + matchTypeError$1: function(message) { + var match, result, t1; + match = new RegExp(this._pattern).exec(message); + if (match == null) + return; + result = {}; + t1 = this._arguments; + if (t1 !== -1) + result.arguments = match[t1 + 1]; + t1 = this._argumentsExpr; + if (t1 !== -1) + result.argumentsExpr = match[t1 + 1]; + t1 = this._expr; + if (t1 !== -1) + result.expr = match[t1 + 1]; + t1 = this._method; + if (t1 !== -1) + result.method = match[t1 + 1]; + t1 = this._receiver; + if (t1 !== -1) + result.receiver = match[t1 + 1]; + return result; + }, + static: {"^": "TypeErrorDecoder_noSuchMethodPattern,TypeErrorDecoder_notClosurePattern,TypeErrorDecoder_nullCallPattern,TypeErrorDecoder_nullLiteralCallPattern,TypeErrorDecoder_undefinedCallPattern,TypeErrorDecoder_undefinedLiteralCallPattern,TypeErrorDecoder_nullPropertyPattern,TypeErrorDecoder_nullLiteralPropertyPattern,TypeErrorDecoder_undefinedPropertyPattern,TypeErrorDecoder_undefinedLiteralPropertyPattern", TypeErrorDecoder_extractPattern: function(message) { + var match, $arguments, argumentsExpr, expr, method, receiver; + message = message.replace(String({}), '$receiver$').replace(new RegExp("[[\\]{}()*+?.\\\\^$|]", 'g'), '\\$&'); + match = message.match(/\\\$[a-zA-Z]+\\\$/g); + if (match == null) + match = []; + $arguments = match.indexOf("\\$arguments\\$"); + argumentsExpr = match.indexOf("\\$argumentsExpr\\$"); + expr = match.indexOf("\\$expr\\$"); + method = match.indexOf("\\$method\\$"); + receiver = match.indexOf("\\$receiver\\$"); + return new H.TypeErrorDecoder(message.replace('\\$arguments\\$', '((?:x|[^x])*)').replace('\\$argumentsExpr\\$', '((?:x|[^x])*)').replace('\\$expr\\$', '((?:x|[^x])*)').replace('\\$method\\$', '((?:x|[^x])*)').replace('\\$receiver\\$', '((?:x|[^x])*)'), $arguments, argumentsExpr, expr, method, receiver); + }, TypeErrorDecoder_provokeCallErrorOn: function(expression) { + return function($expr$) { + var $argumentsExpr$ = '$arguments$' + try { + $expr$.$method$($argumentsExpr$); + } catch (e) { + return e.message; + } +}(expression); + }, TypeErrorDecoder_provokePropertyErrorOn: function(expression) { + return function($expr$) { + try { + $expr$.$method$; + } catch (e) { + return e.message; + } +}(expression); + }} + }, + NullError: { + "^": "Error;_message,_method", + toString$0: function(_) { + var t1 = this._method; + if (t1 == null) + return "NullError: " + H.S(this._message); + return "NullError: Cannot call \"" + H.S(t1) + "\" on null"; + }, + $isError: true + }, + JsNoSuchMethodError: { + "^": "Error;_message,_method,_receiver", + toString$0: function(_) { + var t1, t2; + t1 = this._method; + if (t1 == null) + return "NoSuchMethodError: " + H.S(this._message); + t2 = this._receiver; + if (t2 == null) + return "NoSuchMethodError: Cannot call \"" + H.S(t1) + "\" (" + H.S(this._message) + ")"; + return "NoSuchMethodError: Cannot call \"" + H.S(t1) + "\" on \"" + H.S(t2) + "\" (" + H.S(this._message) + ")"; + }, + $isError: true, + static: {JsNoSuchMethodError$: function(_message, match) { + var t1, t2; + t1 = match == null; + t2 = t1 ? null : match.method; + t1 = t1 ? null : match.receiver; + return new H.JsNoSuchMethodError(_message, t2, t1); + }} + }, + UnknownJsTypeError: { + "^": "Error;_message", + toString$0: function(_) { + var t1 = this._message; + return C.JSString_methods.get$isEmpty(t1) ? "Error" : "Error: " + t1; + } + }, + unwrapException_saveStackTrace: { + "^": "Closure:11;ex_0", + call$1: function(error) { + if (!!J.getInterceptor(error).$isError) + if (error.$thrownJsError == null) + error.$thrownJsError = this.ex_0; + return error; + } + }, + _StackTrace: { + "^": "Object;_exception,_trace", + toString$0: function(_) { + var t1, trace; + t1 = this._trace; + if (t1 != null) + return t1; + t1 = this._exception; + trace = typeof t1 === "object" ? t1.stack : null; + t1 = trace == null ? "" : trace; + this._trace = t1; + return t1; + } + }, + invokeClosure_closure: { + "^": "Closure:9;closure_0", + call$0: function() { + return this.closure_0.call$0(); + } + }, + invokeClosure_closure0: { + "^": "Closure:9;closure_1,arg1_2", + call$0: function() { + return this.closure_1.call$1(this.arg1_2); + } + }, + invokeClosure_closure1: { + "^": "Closure:9;closure_3,arg1_4,arg2_5", + call$0: function() { + return this.closure_3.call$2(this.arg1_4, this.arg2_5); + } + }, + invokeClosure_closure2: { + "^": "Closure:9;closure_6,arg1_7,arg2_8,arg3_9", + call$0: function() { + return this.closure_6.call$3(this.arg1_7, this.arg2_8, this.arg3_9); + } + }, + invokeClosure_closure3: { + "^": "Closure:9;closure_10,arg1_11,arg2_12,arg3_13,arg4_14", + call$0: function() { + return this.closure_10.call$4(this.arg1_11, this.arg2_12, this.arg3_13, this.arg4_14); + } + }, + Closure: { + "^": "Object;", + toString$0: function(_) { + return "Closure"; + } + }, + TearOffClosure: { + "^": "Closure;" + }, + BoundClosure: { + "^": "TearOffClosure;_self,__js_helper$_target,_receiver,__js_helper$_name", + $eq: function(_, other) { + if (other == null) + return false; + if (this === other) + return true; + if (!J.getInterceptor(other).$isBoundClosure) + return false; + return this._self === other._self && this.__js_helper$_target === other.__js_helper$_target && this._receiver === other._receiver; + }, + get$hashCode: function(_) { + var t1, receiverHashCode; + t1 = this._receiver; + if (t1 == null) + receiverHashCode = H.Primitives_objectHashCode(this._self); + else + receiverHashCode = typeof t1 !== "object" ? J.get$hashCode$(t1) : H.Primitives_objectHashCode(t1); + t1 = H.Primitives_objectHashCode(this.__js_helper$_target); + if (typeof receiverHashCode !== "number") + return receiverHashCode.$xor(); + return (receiverHashCode ^ t1) >>> 0; + }, + $isBoundClosure: true, + static: {"^": "BoundClosure_selfFieldNameCache,BoundClosure_receiverFieldNameCache", BoundClosure_selfOf: function(closure) { + return closure._self; + }, BoundClosure_receiverOf: function(closure) { + return closure._receiver; + }, BoundClosure_selfFieldName: function() { + var t1 = $.BoundClosure_selfFieldNameCache; + if (t1 == null) { + t1 = H.BoundClosure_computeFieldNamed("self"); + $.BoundClosure_selfFieldNameCache = t1; + } + return t1; + }, BoundClosure_computeFieldNamed: function(fieldName) { + var template, t1, names, i, $name; + template = new H.BoundClosure("self", "target", "receiver", "name"); + t1 = Object.getOwnPropertyNames(template); + t1.fixed$length = init; + names = t1; + for (t1 = names.length, i = 0; i < t1; ++i) { + $name = names[i]; + if (template[$name] === fieldName) + return $name; + } + }} + }, + RuntimeError: { + "^": "Error;message", + toString$0: function(_) { + return "RuntimeError: " + H.S(this.message); + }, + static: {RuntimeError$: function(message) { + return new H.RuntimeError(message); + }} + }, + RuntimeType: { + "^": "Object;" + }, + RuntimeFunctionType: { + "^": "RuntimeType;returnType,parameterTypes,optionalParameterTypes,namedParameters", + _isTest$1: function(expression) { + var functionTypeObject = this._extractFunctionTypeObjectFrom$1(expression); + return functionTypeObject == null ? false : H.isFunctionSubtype(functionTypeObject, this.toRti$0()); + }, + _extractFunctionTypeObjectFrom$1: function(o) { + var interceptor = J.getInterceptor(o); + return "$signature" in interceptor ? interceptor.$signature() : null; + }, + toRti$0: function() { + var result, t1, t2, namedRti, keys, i, $name; + result = { "func": "dynafunc" }; + t1 = this.returnType; + t2 = J.getInterceptor(t1); + if (!!t2.$isVoidRuntimeType) + result.void = true; + else if (!t2.$isDynamicRuntimeType) + result.ret = t1.toRti$0(); + t1 = this.parameterTypes; + if (t1 != null && t1.length !== 0) + result.args = H.RuntimeFunctionType_listToRti(t1); + t1 = this.optionalParameterTypes; + if (t1 != null && t1.length !== 0) + result.opt = H.RuntimeFunctionType_listToRti(t1); + t1 = this.namedParameters; + if (t1 != null) { + namedRti = {}; + keys = H.extractKeys(t1); + for (t2 = keys.length, i = 0; i < t2; ++i) { + $name = keys[i]; + namedRti[$name] = t1[$name].toRti$0(); + } + result.named = namedRti; + } + return result; + }, + toString$0: function(_) { + var t1, t2, result, needsComma, i, type, keys, $name; + t1 = this.parameterTypes; + if (t1 != null) + for (t2 = t1.length, result = "(", needsComma = false, i = 0; i < t2; ++i, needsComma = true) { + type = t1[i]; + if (needsComma) + result += ", "; + result += H.S(type); + } + else { + result = "("; + needsComma = false; + } + t1 = this.optionalParameterTypes; + if (t1 != null && t1.length !== 0) { + result = (needsComma ? result + ", " : result) + "["; + for (t2 = t1.length, needsComma = false, i = 0; i < t2; ++i, needsComma = true) { + type = t1[i]; + if (needsComma) + result += ", "; + result += H.S(type); + } + result += "]"; + } else { + t1 = this.namedParameters; + if (t1 != null) { + result = (needsComma ? result + ", " : result) + "{"; + keys = H.extractKeys(t1); + for (t2 = keys.length, needsComma = false, i = 0; i < t2; ++i, needsComma = true) { + $name = keys[i]; + if (needsComma) + result += ", "; + result += H.S(t1[$name].toRti$0()) + " " + $name; + } + result += "}"; + } + } + return result + (") -> " + H.S(this.returnType)); + }, + static: {"^": "RuntimeFunctionType_inAssert", RuntimeFunctionType_listToRti: function(list) { + var result, t1, i; + list = list; + result = []; + for (t1 = list.length, i = 0; i < t1; ++i) + result.push(list[i].toRti$0()); + return result; + }} + }, + DynamicRuntimeType: { + "^": "RuntimeType;", + toString$0: function(_) { + return "dynamic"; + }, + toRti$0: function() { + return; + }, + $isDynamicRuntimeType: true + }, + initHooks_closure: { + "^": "Closure:11;getTag_0", + call$1: function(o) { + return this.getTag_0(o); + } + }, + initHooks_closure0: { + "^": "Closure:12;getUnknownTag_1", + call$2: function(o, tag) { + return this.getUnknownTag_1(o, tag); + } + }, + initHooks_closure1: { + "^": "Closure:0;prototypeForTag_2", + call$1: function(tag) { + return this.prototypeForTag_2(tag); + } + }, + JSSyntaxRegExp: { + "^": "Object;_nativeRegExp,_nativeGlobalRegExp,_nativeAnchoredRegExp", + firstMatch$1: function(str) { + var m; + if (typeof str !== "string") + H.throwExpression(new P.ArgumentError(str)); + m = this._nativeRegExp.exec(str); + if (m == null) + return; + return H._MatchImplementation$(this, m); + }, + static: {JSSyntaxRegExp_makeNative: function(pattern, multiLine, caseSensitive, global) { + var m, i, g, regexp, errorMessage; + m = multiLine ? "m" : ""; + i = caseSensitive ? "" : "i"; + g = global ? "g" : ""; + regexp = (function() {try {return new RegExp(pattern, m + i + g);} catch (e) {return e;}})(); + if (regexp instanceof RegExp) + return regexp; + errorMessage = String(regexp); + throw H.wrapException(P.FormatException$("Illegal RegExp pattern: " + pattern + ", " + errorMessage)); + }} + }, + _MatchImplementation: { + "^": "Object;pattern,_match", + $index: function(_, index) { + var t1 = this._match; + if (index >>> 0 !== index || index >= t1.length) + return H.ioore(t1, index); + return t1[index]; + }, + _MatchImplementation$2: function(pattern, _match) { + }, + static: {_MatchImplementation$: function(pattern, _match) { + var t1 = new H._MatchImplementation(pattern, _match); + t1._MatchImplementation$2(pattern, _match); + return t1; + }} + } +}], +["bank_terminal", "package:bank_terminal_s5/bank_terminal.dart", , D, { + "^": "", + BankAccount: { + "^": "Object;_number,owner,_balance,_pin_code,date_created,date_modified", + set$number: function(value) { + var t1; + if (value == null || J.get$isEmpty$asx(value) === true) + return; + t1 = H.JSSyntaxRegExp_makeNative("[0-9]{3}-[0-9]{7}-[0-9]{2}", false, true, false); + if (typeof value !== "string") + H.throwExpression(new P.ArgumentError(value)); + if (t1.test(value)) + this._number = value; + }, + toString$0: function(_) { + return "Bank account from " + H.S(this.owner) + " with number " + H.S(this._number) + " and balance " + H.S(this._balance); + }, + toJson$0: function() { + var acc = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSString, P.Object); + acc.$indexSet(0, "number", this._number); + acc.$indexSet(0, "owner", this.owner.toJson$0()); + acc.$indexSet(0, "balance", this._balance); + acc.$indexSet(0, "pin_code", this._pin_code); + acc.$indexSet(0, "creation_date", this.date_created.toString$0(0)); + acc.$indexSet(0, "modified_date", J.toString$0(this.date_modified)); + return C.JsonCodec_null_null.encode$1(acc); + }, + BankAccount$fromJson$1: function(json) { + var t1, t2, t3; + t1 = J.getInterceptor$asx(json); + this.set$number(t1.$index(json, "number")); + t2 = new D.Person(null, null, null, null, null); + t2.Person$fromJson$1(t1.$index(json, "owner")); + this.owner = t2; + t2 = t1.$index(json, "balance"); + if (J.$ge$n(t2, 0)) + this._balance = t2; + t2 = t1.$index(json, "pin_code"); + t3 = J.getInterceptor$n(t2); + if (t3.$ge(t2, 1) && t3.$le(t2, 999999)) + this._pin_code = t2; + this.date_modified = P.DateTime_parse(t1.$index(json, "modified_date")); + }, + static: {"^": "BankAccount_INTEREST"} + }, + Person: { + "^": "Object;_bank_terminal$_name,address,_email,_gender,_date_birth", + toString$0: function(_) { + return "Person: " + H.S(this._bank_terminal$_name) + ", " + H.S(this._gender); + }, + toJson$0: function() { + var per = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSString, P.Object); + per.$indexSet(0, "name", this._bank_terminal$_name); + per.$indexSet(0, "address", this.address); + per.$indexSet(0, "email", this._email); + per.$indexSet(0, "gender", this._gender); + per.$indexSet(0, "birthdate", J.toString$0(this._date_birth)); + return per; + }, + Person$fromJson$1: function(json) { + var t1, t2, t3; + t1 = J.getInterceptor$asx(json); + t2 = t1.$index(json, "name"); + if (t2 != null && J.get$isEmpty$asx(t2) !== true) + this._bank_terminal$_name = t2; + this.address = t1.$index(json, "address"); + t2 = t1.$index(json, "email"); + if (t2 != null && J.get$isEmpty$asx(t2) !== true) + this._email = t2; + t2 = t1.$index(json, "gender"); + t3 = J.getInterceptor(t2); + if (t3.$eq(t2, "M") || t3.$eq(t2, "F")) + this._gender = t2; + t1 = P.DateTime_parse(t1.$index(json, "birthdate")); + t2 = Date.now(); + new P.DateTime(t2, false).DateTime$_now$0(); + if (t1.millisecondsSinceEpoch < t2) + this._date_birth = t1; + } + } +}], +["", "bank_terminal_s5.dart", , M, { + "^": "", + main: [function() { + $.owner = document.querySelector("#owner"); + $.balance = document.querySelector("#balance"); + $.number = document.querySelector("#number"); + $.btn_other = document.querySelector("#btn_other"); + $.amount = document.querySelector("#amount"); + $.btn_deposit = document.querySelector("#btn_deposit"); + $.btn_interest = document.querySelector("#btn_interest"); + $.error = document.querySelector("#error"); + var t1 = J.get$onInput$x($.number); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.readData$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + t1 = J.get$onChange$x($.amount); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.nonNegative$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + t1 = J.get$onBlur$x($.amount); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.nonNegative$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + t1 = J.get$onClick$x($.btn_other); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.clearData$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + t1 = J.get$onClick$x($.btn_deposit); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.changeBalance$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + t1 = J.get$onClick$x($.btn_interest); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.interest$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + M.disable_transactions(true); + }, "call$0", "main$closure", 0, 0, 1], + readData: [function(e) { + var key, t1, t2; + key = "Bankaccount:" + H.S(J.get$value$x($.number)); + if (window.localStorage.getItem(key) == null) { + J.set$innerHtml$x($.error, "Unknown bank account!"); + $.number.focus(); + return; + } + J.set$innerHtml$x($.error, ""); + t1 = C.JsonCodec_null_null.decode$1(window.localStorage.getItem(key)); + t2 = new D.BankAccount(null, null, null, null, P.DateTime_parse(J.$index$asx(t1, "creation_date")), null); + t2.BankAccount$fromJson$1(t1); + $.bac = t2; + J.set$innerHtml$x($.owner, "" + H.S(t2.owner._bank_terminal$_name) + ""); + J.set$innerHtml$x($.balance, "" + J.toStringAsFixed$1$n($.bac._balance, 2) + ""); + M.disable_transactions(false); + }, "call$1", "readData$closure", 2, 0, 2], + clearData: [function(e) { + J.set$value$x($.number, ""); + J.set$innerHtml$x($.owner, "----------"); + J.set$innerHtml$x($.balance, "0.0"); + $.number.focus(); + M.disable_transactions(true); + return; + }, "call$1", "clearData$closure", 2, 0, 2], + disable_transactions: function(off) { + J.set$disabled$x($.amount, off); + J.set$disabled$x($.btn_deposit, off); + J.set$disabled$x($.btn_interest, off); + }, + nonNegative: [function(e) { + var input, exception; + input = null; + try { + input = H.Primitives_parseDouble(J.get$value$x($.amount), null); + } catch (exception) { + if (!!J.getInterceptor(H.unwrapException(exception)).$isFormatException) { + window.alert("This is not a valid amount!"); + $.amount.focus(); + } else + throw exception; + } + + }, "call$1", "nonNegative$closure", 2, 0, 2], + changeBalance: [function(e) { + var money_amount, t1, t2; + money_amount = H.Primitives_parseDouble(J.get$value$x($.amount), null); + t1 = J.$ge$n(money_amount, 0); + t2 = $.bac; + if (t1) { + t1 = J.$add$ns(t2._balance, money_amount); + if (J.$ge$n(t1, 0)) + t2._balance = t1; + t1 = new P.DateTime(Date.now(), false); + t1.DateTime$_now$0(); + t2.date_modified = t1; + } else { + t1 = J.$add$ns(t2._balance, money_amount); + if (J.$ge$n(t1, 0)) + t2._balance = t1; + t1 = new P.DateTime(Date.now(), false); + t1.DateTime$_now$0(); + t2.date_modified = t1; + } + window.localStorage.setItem("Bankaccount:" + H.S($.bac._number), $.bac.toJson$0()); + J.set$innerHtml$x($.balance, "" + J.toStringAsFixed$1$n($.bac._balance, 2) + ""); + J.preventDefault$0$x(e); + e.stopPropagation(); + }, "call$1", "changeBalance$closure", 2, 0, 2], + interest: [function(e) { + var t1, t2, t3, t4; + t1 = $.bac; + t2 = t1._balance; + t3 = J.getInterceptor$ns(t2); + t4 = t3.$mul(t2, 5); + if (typeof t4 !== "number") + return t4.$div(); + t4 = t3.$add(t2, t4 / 100); + if (J.$ge$n(t4, 0)) + t1._balance = t4; + window.localStorage.setItem("Bankaccount:" + H.S($.bac._number), $.bac.toJson$0()); + J.set$innerHtml$x($.balance, "" + J.toStringAsFixed$1$n($.bac._balance, 2) + ""); + J.preventDefault$0$x(e); + e.stopPropagation(); + }, "call$1", "interest$closure", 2, 0, 2] +}, +1], +["dart._internal", "dart:_internal", , H, { + "^": "", + IterableMixinWorkaround_forEach: function(iterable, f) { + var t1; + for (t1 = new H.ListIterator(iterable, iterable.length, 0, null); t1.moveNext$0();) + f.call$1(t1._current); + }, + IterableMixinWorkaround_any: function(iterable, f) { + var t1; + for (t1 = new H.ListIterator(iterable, iterable.length, 0, null); t1.moveNext$0();) + if (f.call$1(t1._current) === true) + return true; + return false; + }, + IterableMixinWorkaround_toStringIterable: function(iterable, leftDelimiter, rightDelimiter) { + var result, i, t1; + for (i = 0; t1 = $.get$IterableMixinWorkaround__toStringList(), i < t1.length; ++i) + if (t1[i] === iterable) + return H.S(leftDelimiter) + "..." + H.S(rightDelimiter); + result = P.StringBuffer$(""); + try { + $.get$IterableMixinWorkaround__toStringList().push(iterable); + result.write$1(leftDelimiter); + result.writeAll$2(iterable, ", "); + result.write$1(rightDelimiter); + } finally { + t1 = $.get$IterableMixinWorkaround__toStringList(); + if (0 >= t1.length) + return H.ioore(t1, 0); + t1.pop(); + } + return result.get$_contents(); + }, + IterableMixinWorkaround_setRangeList: function(list, start, end, from, skipCount) { + var $length; + if (start < 0 || start > list.length) + H.throwExpression(P.RangeError$range(start, 0, list.length)); + if (end < start || end > list.length) + H.throwExpression(P.RangeError$range(end, start, list.length)); + $length = end - start; + if ($length === 0) + return; + if (skipCount < 0) + throw H.wrapException(new P.ArgumentError(skipCount)); + if (skipCount + $length > from.length) + throw H.wrapException(P.StateError$("Not enough elements")); + H.Lists_copy(from, skipCount, list, start, $length); + }, + Lists_copy: function(src, srcStart, dst, dstStart, count) { + var i, j, t1, t2; + if (srcStart < dstStart) + for (i = srcStart + count - 1, j = dstStart + count - 1, t1 = src.length; i >= srcStart; --i, --j) { + if (i < 0 || i >= t1) + return H.ioore(src, i); + C.JSArray_methods.$indexSet(dst, j, src[i]); + } + else + for (t1 = srcStart + count, t2 = src.length, j = dstStart, i = srcStart; i < t1; ++i, ++j) { + if (i < 0 || i >= t2) + return H.ioore(src, i); + C.JSArray_methods.$indexSet(dst, j, src[i]); + } + }, + Symbol_getName: function(symbol) { + return symbol.get$_name(); + }, + ListIterable: { + "^": "IterableBase;", + get$iterator: function(_) { + return new H.ListIterator(this, this.get$length(this), 0, null); + }, + forEach$1: function(_, action) { + var $length, i; + $length = this.get$length(this); + for (i = 0; i < $length; ++i) { + action.call$1(this.elementAt$1(0, i)); + if ($length !== this.get$length(this)) + throw H.wrapException(P.ConcurrentModificationError$(this)); + } + }, + get$isEmpty: function(_) { + return this.get$length(this) === 0; + }, + $isEfficientLength: true + }, + ListIterator: { + "^": "Object;_iterable,_length,_index,_current", + get$current: function() { + return this._current; + }, + moveNext$0: function() { + var t1, t2, $length, t3; + t1 = this._iterable; + t2 = J.getInterceptor$asx(t1); + $length = t2.get$length(t1); + if (this._length !== $length) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + t3 = this._index; + if (t3 >= $length) { + this._current = null; + return false; + } + this._current = t2.elementAt$1(t1, t3); + this._index = this._index + 1; + return true; + } + }, + MappedIterable: { + "^": "IterableBase;_iterable,_f", + get$iterator: function(_) { + var t1 = this._iterable; + t1 = new H.MappedIterator(null, t1.get$iterator(t1), this._f); + t1.$builtinTypeInfo = this.$builtinTypeInfo; + return t1; + }, + get$length: function(_) { + var t1 = this._iterable; + return t1.get$length(t1); + }, + get$isEmpty: function(_) { + var t1 = this._iterable; + return t1.get$isEmpty(t1); + }, + $asIterableBase: function($S, $T) { + return [$T]; + }, + static: {MappedIterable_MappedIterable: function(iterable, $function, $S, $T) { + if (!!iterable.$isEfficientLength) + return H.setRuntimeTypeInfo(new H.EfficientLengthMappedIterable(iterable, $function), [$S, $T]); + return H.setRuntimeTypeInfo(new H.MappedIterable(iterable, $function), [$S, $T]); + }} + }, + EfficientLengthMappedIterable: { + "^": "MappedIterable;_iterable,_f", + $isEfficientLength: true + }, + MappedIterator: { + "^": "Iterator;_current,_iterator,_f", + _f$1: function(arg0) { + return this._f.call$1(arg0); + }, + moveNext$0: function() { + var t1 = this._iterator; + if (t1.moveNext$0()) { + this._current = this._f$1(t1.get$current()); + return true; + } + this._current = null; + return false; + }, + get$current: function() { + return this._current; + } + }, + MappedListIterable: { + "^": "ListIterable;_source,_f", + _f$1: function(arg0) { + return this._f.call$1(arg0); + }, + get$length: function(_) { + return J.get$length$asx(this._source); + }, + elementAt$1: function(_, index) { + return this._f$1(J.elementAt$1$ax(this._source, index)); + }, + $asListIterable: function($S, $T) { + return [$T]; + }, + $asIterableBase: function($S, $T) { + return [$T]; + }, + $isEfficientLength: true + }, + WhereIterable: { + "^": "IterableBase;_iterable,_f", + get$iterator: function(_) { + var t1 = new H.WhereIterator(J.get$iterator$ax(this._iterable), this._f); + t1.$builtinTypeInfo = this.$builtinTypeInfo; + return t1; + } + }, + WhereIterator: { + "^": "Iterator;_iterator,_f", + _f$1: function(arg0) { + return this._f.call$1(arg0); + }, + moveNext$0: function() { + for (var t1 = this._iterator; t1.moveNext$0();) + if (this._f$1(t1.get$current()) === true) + return true; + return false; + }, + get$current: function() { + return this._iterator.get$current(); + } + }, + FixedLengthListMixin: { + "^": "Object;" + } +}], +["dart._js_names", "dart:_js_names", , H, { + "^": "", + extractKeys: function(victim) { + var t1 = H.setRuntimeTypeInfo((function(victim, hasOwnProperty) { + var result = []; + for (var key in victim) { + if (hasOwnProperty.call(victim, key)) result.push(key); + } + return result; +})(victim, Object.prototype.hasOwnProperty), [null]); + t1.fixed$length = init; + return t1; + } +}], +["dart.async", "dart:async", , P, { + "^": "", + _registerErrorHandler: function(errorHandler, zone) { + var t1 = H.getDynamicRuntimeType(); + t1 = H.buildFunctionType(t1, [t1, t1])._isTest$1(errorHandler); + if (t1) { + zone.toString; + return errorHandler; + } else { + zone.toString; + return errorHandler; + } + }, + _asyncRunCallbackLoop: function() { + var entry = $._nextCallback; + for (; entry != null;) { + entry.callback$0(); + entry = entry.next; + $._nextCallback = entry; + } + $._lastCallback = null; + }, + _asyncRunCallback: [function() { + var exception; + try { + P._asyncRunCallbackLoop(); + } catch (exception) { + H.unwrapException(exception); + P._createTimer(C.Duration_0, P._asyncRunCallback$closure()); + $._nextCallback = $._nextCallback.next; + throw exception; + } + + }, "call$0", "_asyncRunCallback$closure", 0, 0, 1], + _scheduleAsyncCallback: function(callback) { + var t1, t2; + t1 = $._lastCallback; + if (t1 == null) { + t1 = new P._AsyncCallbackEntry(callback, null); + $._lastCallback = t1; + $._nextCallback = t1; + P._createTimer(C.Duration_0, P._asyncRunCallback$closure()); + } else { + t2 = new P._AsyncCallbackEntry(callback, null); + t1.next = t2; + $._lastCallback = t2; + } + }, + _runUserCode: function(userCode, onSuccess, onError) { + var e, s, exception, t1; + try { + onSuccess.call$1(userCode.call$0()); + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + s = new H._StackTrace(exception, null); + onError.call$2(e, s); + } + + }, + _cancelAndError: function(subscription, future, error, stackTrace) { + subscription.cancel$0(); + future._completeError$2(error, stackTrace); + }, + _cancelAndErrorClosure: function(subscription, future) { + return new P._cancelAndErrorClosure_closure(subscription, future); + }, + _cancelAndValue: function(subscription, future, value) { + subscription.cancel$0(); + future._complete$1(value); + }, + Timer_Timer: function(duration, callback) { + var t1 = $.Zone__current; + if (t1 === C.C__RootZone) { + t1.toString; + return P._rootCreateTimer(t1, null, t1, duration, callback); + } + return P._rootCreateTimer(t1, null, t1, duration, t1.bindCallback$2$runGuarded(callback, true)); + }, + _createTimer: function(duration, callback) { + var milliseconds = C.JSInt_methods._tdivFast$1(duration._duration, 1000); + return H.TimerImpl$(milliseconds < 0 ? 0 : milliseconds, callback); + }, + Zone__enter: function(zone) { + var previous = $.Zone__current; + $.Zone__current = zone; + return previous; + }, + _rootHandleUncaughtError: function($self, $parent, zone, error, stackTrace) { + P._rootRun($self, null, $self, new P._rootHandleUncaughtError_closure(error, stackTrace)); + }, + _rootRun: function($self, $parent, zone, f) { + var old, t1; + if ($.Zone__current === zone) + return f.call$0(); + old = P.Zone__enter(zone); + try { + t1 = f.call$0(); + return t1; + } finally { + $.Zone__current = old; + } + }, + _rootRunUnary: function($self, $parent, zone, f, arg) { + var old, t1; + if ($.Zone__current === zone) + return f.call$1(arg); + old = P.Zone__enter(zone); + try { + t1 = f.call$1(arg); + return t1; + } finally { + $.Zone__current = old; + } + }, + _rootRunBinary: function($self, $parent, zone, f, arg1, arg2) { + var old, t1; + if ($.Zone__current === zone) + return f.call$2(arg1, arg2); + old = P.Zone__enter(zone); + try { + t1 = f.call$2(arg1, arg2); + return t1; + } finally { + $.Zone__current = old; + } + }, + _rootScheduleMicrotask: function($self, $parent, zone, f) { + P._scheduleAsyncCallback(C.C__RootZone !== zone ? zone.bindCallback$1(f) : f); + }, + _rootCreateTimer: function($self, $parent, zone, duration, callback) { + return P._createTimer(duration, C.C__RootZone !== zone ? zone.bindCallback$1(callback) : callback); + }, + _AsyncError: { + "^": "Object;error>,stackTrace<", + $isError: true + }, + _Future: { + "^": "Object;_state,_zone<,_resultOrListeners,_nextListener<,_onValueCallback,_errorTestCallback,_onErrorCallback,_whenCompleteActionCallback", + get$_isComplete: function() { + return this._state >= 4; + }, + get$_hasValue: function() { + return this._state === 4; + }, + get$_hasError: function() { + return this._state === 8; + }, + set$_isChained: function(value) { + if (value) + this._state = 2; + else + this._state = 0; + }, + then$2$onError: function(f, onError) { + var t1, result; + t1 = $.Zone__current; + t1.toString; + result = H.setRuntimeTypeInfo(new P._Future(0, t1, null, null, f, null, P._registerErrorHandler(onError, t1), null), [null]); + this._addListener$1(result); + return result; + }, + get$_async$_value: function() { + return this._resultOrListeners; + }, + get$_error: function() { + return this._resultOrListeners; + }, + _setValue$1: function(value) { + this._state = 4; + this._resultOrListeners = value; + }, + _setError$2: function(error, stackTrace) { + this._state = 8; + this._resultOrListeners = new P._AsyncError(error, stackTrace); + }, + _addListener$1: function(listener) { + var t1; + if (this._state >= 4) { + t1 = this._zone; + t1.toString; + P._rootScheduleMicrotask(t1, null, t1, new P._Future__addListener_closure(this, listener)); + } else { + listener._nextListener = this._resultOrListeners; + this._resultOrListeners = listener; + } + }, + _removeListeners$0: function() { + var current, prev, next; + current = this._resultOrListeners; + this._resultOrListeners = null; + for (prev = null; current != null; prev = current, current = next) { + next = current.get$_nextListener(); + current._nextListener = prev; + } + return prev; + }, + _complete$1: function(value) { + var t1, listeners; + t1 = J.getInterceptor(value); + if (!!t1.$isFuture) + if (!!t1.$is_Future) + P._Future__chainCoreFuture(value, this); + else + P._Future__chainForeignFuture(value, this); + else { + listeners = this._removeListeners$0(); + this._setValue$1(value); + P._Future__propagateToListeners(this, listeners); + } + }, + _completeError$2: [function(error, stackTrace) { + var listeners = this._removeListeners$0(); + this._setError$2(error, stackTrace); + P._Future__propagateToListeners(this, listeners); + }, function(error) { + return this._completeError$2(error, null); + }, "_completeError$1", "call$2", "call$1", "get$_completeError", 2, 2, 13, 14], + $is_Future: true, + $isFuture: true, + static: {"^": "_Future__INCOMPLETE,_Future__PENDING_COMPLETE,_Future__CHAINED,_Future__VALUE,_Future__ERROR", _Future$: function($T) { + return H.setRuntimeTypeInfo(new P._Future(0, $.Zone__current, null, null, null, null, null, null), [$T]); + }, _Future__chainForeignFuture: function(source, target) { + target._state = 2; + source.then$2$onError(new P._Future__chainForeignFuture_closure(target), new P._Future__chainForeignFuture_closure0(target)); + }, _Future__chainCoreFuture: function(source, target) { + target._state = 2; + if (source._state >= 4) + P._Future__propagateToListeners(source, target); + else + source._addListener$1(target); + }, _Future__propagateMultipleListeners: function(source, listeners) { + var listeners0; + do { + listeners0 = listeners.get$_nextListener(); + listeners._nextListener = null; + P._Future__propagateToListeners(source, listeners); + if (listeners0 != null) { + listeners = listeners0; + continue; + } else + break; + } while (true); + }, _Future__propagateToListeners: function(source, listeners) { + var t1, t2, t3, hasError, asyncError, t4, sourceValue, t5, zone, oldZone, chainSource, listeners0; + t1 = {}; + t1.source_4 = source; + for (t2 = source; true;) { + t3 = {}; + if (!t2.get$_isComplete()) + return; + hasError = t1.source_4.get$_hasError(); + if (hasError && listeners == null) { + t2 = t1.source_4; + asyncError = t2.get$_error(); + t2 = t2._zone; + t3 = J.get$error$x(asyncError); + t4 = asyncError.get$stackTrace(); + t2.toString; + P._rootHandleUncaughtError(t2, null, t2, t3, t4); + return; + } + if (listeners == null) + return; + if (listeners._nextListener != null) { + P._Future__propagateMultipleListeners(t1.source_4, listeners); + return; + } + t3.listenerHasValue_1 = true; + sourceValue = t1.source_4.get$_hasValue() ? t1.source_4.get$_async$_value() : null; + t3.listenerValueOrError_2 = sourceValue; + t3.isPropagationAborted_3 = false; + t2 = !hasError; + if (t2) { + t4 = listeners._state === 2; + if ((t4 ? null : listeners._onValueCallback) == null) { + t5 = (t4 ? null : listeners._whenCompleteActionCallback) != null; + t4 = t5; + } else + t4 = true; + } else + t4 = true; + if (t4) { + zone = listeners._zone; + if (hasError) { + t4 = t1.source_4.get$_zone(); + t4.toString; + zone.toString; + t4 = zone == null ? t4 != null : zone !== t4; + } else + t4 = false; + if (t4) { + t2 = t1.source_4; + asyncError = t2.get$_error(); + t2 = t2._zone; + t3 = J.get$error$x(asyncError); + t4 = asyncError.get$stackTrace(); + t2.toString; + P._rootHandleUncaughtError(t2, null, t2, t3, t4); + return; + } + oldZone = $.Zone__current; + if (oldZone == null ? zone != null : oldZone !== zone) + $.Zone__current = zone; + else + oldZone = null; + if (t2) { + if ((listeners._state === 2 ? null : listeners._onValueCallback) != null) + t3.listenerHasValue_1 = new P._Future__propagateToListeners_handleValueCallback(t3, listeners, sourceValue, zone).call$0(); + } else + new P._Future__propagateToListeners_handleError(t1, t3, listeners, zone).call$0(); + if ((listeners._state === 2 ? null : listeners._whenCompleteActionCallback) != null) + new P._Future__propagateToListeners_handleWhenCompleteCallback(t1, t3, hasError, listeners, zone).call$0(); + if (oldZone != null) + $.Zone__current = oldZone; + if (t3.isPropagationAborted_3) + return; + if (t3.listenerHasValue_1 === true) { + t2 = t3.listenerValueOrError_2; + t2 = (sourceValue == null ? t2 != null : sourceValue !== t2) && !!J.getInterceptor(t2).$isFuture; + } else + t2 = false; + if (t2) { + chainSource = t3.listenerValueOrError_2; + if (!!J.getInterceptor(chainSource).$is_Future) + if (chainSource._state >= 4) { + listeners._state = 2; + t1.source_4 = chainSource; + t2 = chainSource; + continue; + } else + P._Future__chainCoreFuture(chainSource, listeners); + else + P._Future__chainForeignFuture(chainSource, listeners); + return; + } + } + if (t3.listenerHasValue_1 === true) { + listeners0 = listeners._removeListeners$0(); + t2 = t3.listenerValueOrError_2; + listeners._state = 4; + listeners._resultOrListeners = t2; + } else { + listeners0 = listeners._removeListeners$0(); + asyncError = t3.listenerValueOrError_2; + t2 = J.get$error$x(asyncError); + t3 = asyncError.get$stackTrace(); + listeners._state = 8; + listeners._resultOrListeners = new P._AsyncError(t2, t3); + } + t1.source_4 = listeners; + t2 = listeners; + listeners = listeners0; + } + }} + }, + _Future__addListener_closure: { + "^": "Closure:9;this_0,listener_1", + call$0: function() { + P._Future__propagateToListeners(this.this_0, this.listener_1); + } + }, + _Future__chainForeignFuture_closure: { + "^": "Closure:11;target_0", + call$1: function(value) { + var t1, listeners; + t1 = this.target_0; + listeners = t1._removeListeners$0(); + t1._setValue$1(value); + P._Future__propagateToListeners(t1, listeners); + } + }, + _Future__chainForeignFuture_closure0: { + "^": "Closure:15;target_1", + call$2: function(error, stackTrace) { + this.target_1._completeError$2(error, stackTrace); + }, + call$1: function(error) { + return this.call$2(error, null); + } + }, + _Future__propagateToListeners_handleValueCallback: { + "^": "Closure:16;box_1,listener_3,sourceValue_4,zone_5", + call$0: function() { + var e, s, t1, t2, exception; + try { + t1 = this.zone_5; + t2 = this.listener_3; + t2 = t2._state === 2 ? null : t2._onValueCallback; + t1.toString; + this.box_1.listenerValueOrError_2 = P._rootRunUnary(t1, null, t1, t2, this.sourceValue_4); + return true; + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + s = new H._StackTrace(exception, null); + this.box_1.listenerValueOrError_2 = new P._AsyncError(e, s); + return false; + } + + } + }, + _Future__propagateToListeners_handleError: { + "^": "Closure:1;box_2,box_1,listener_6,zone_7", + call$0: function() { + var asyncError, test, matchesTest, e, s, errorCallback, e0, s0, t1, t2, t3, exception, listenerValueOrError, t4; + asyncError = this.box_2.source_4.get$_error(); + t1 = this.listener_6; + test = t1._state === 2 ? null : t1._errorTestCallback; + matchesTest = true; + if (test != null) + try { + t2 = this.zone_7; + t3 = J.get$error$x(asyncError); + t2.toString; + matchesTest = P._rootRunUnary(t2, null, t2, test, t3); + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + s = new H._StackTrace(exception, null); + t1 = J.get$error$x(asyncError); + t2 = e; + listenerValueOrError = (t1 == null ? t2 == null : t1 === t2) ? asyncError : new P._AsyncError(e, s); + t1 = this.box_1; + t1.listenerValueOrError_2 = listenerValueOrError; + t1.listenerHasValue_1 = false; + return; + } + + errorCallback = t1._state === 2 ? null : t1._onErrorCallback; + if (matchesTest === true && errorCallback != null) { + try { + t1 = errorCallback; + t2 = H.getDynamicRuntimeType(); + t2 = H.buildFunctionType(t2, [t2, t2])._isTest$1(t1); + t3 = this.zone_7; + t4 = this.box_1; + if (t2) { + t1 = J.get$error$x(asyncError); + t2 = asyncError.get$stackTrace(); + t3.toString; + t4.listenerValueOrError_2 = P._rootRunBinary(t3, null, t3, errorCallback, t1, t2); + } else { + t1 = J.get$error$x(asyncError); + t3.toString; + t4.listenerValueOrError_2 = P._rootRunUnary(t3, null, t3, errorCallback, t1); + } + } catch (exception) { + t1 = H.unwrapException(exception); + e0 = t1; + s0 = new H._StackTrace(exception, null); + t1 = J.get$error$x(asyncError); + t2 = e0; + listenerValueOrError = (t1 == null ? t2 == null : t1 === t2) ? asyncError : new P._AsyncError(e0, s0); + t1 = this.box_1; + t1.listenerValueOrError_2 = listenerValueOrError; + t1.listenerHasValue_1 = false; + return; + } + + this.box_1.listenerHasValue_1 = true; + } else { + t1 = this.box_1; + t1.listenerValueOrError_2 = asyncError; + t1.listenerHasValue_1 = false; + } + } + }, + _Future__propagateToListeners_handleWhenCompleteCallback: { + "^": "Closure:1;box_2,box_1,hasError_8,listener_9,zone_10", + call$0: function() { + var t1, e, s, t2, t3, exception; + t1 = {}; + t1.completeResult_0 = null; + try { + t2 = this.zone_10; + t3 = this.listener_9; + t3 = t3._state === 2 ? null : t3._whenCompleteActionCallback; + t2.toString; + t1.completeResult_0 = P._rootRun(t2, null, t2, t3); + } catch (exception) { + t2 = H.unwrapException(exception); + e = t2; + s = new H._StackTrace(exception, null); + if (this.hasError_8) { + t2 = J.get$error$x(this.box_2.source_4.get$_error()); + t3 = e; + t3 = t2 == null ? t3 == null : t2 === t3; + t2 = t3; + } else + t2 = false; + t3 = this.box_1; + if (t2) + t3.listenerValueOrError_2 = this.box_2.source_4.get$_error(); + else + t3.listenerValueOrError_2 = new P._AsyncError(e, s); + t3.listenerHasValue_1 = false; + } + + if (!!J.getInterceptor(t1.completeResult_0).$isFuture) { + t2 = this.listener_9; + t2.set$_isChained(true); + this.box_1.isPropagationAborted_3 = true; + t1.completeResult_0.then$2$onError(new P._Future__propagateToListeners_handleWhenCompleteCallback_closure(this.box_2, t2), new P._Future__propagateToListeners_handleWhenCompleteCallback_closure0(t1, t2)); + } + } + }, + _Future__propagateToListeners_handleWhenCompleteCallback_closure: { + "^": "Closure:11;box_2,listener_11", + call$1: function(ignored) { + P._Future__propagateToListeners(this.box_2.source_4, this.listener_11); + } + }, + _Future__propagateToListeners_handleWhenCompleteCallback_closure0: { + "^": "Closure:15;box_0,listener_12", + call$2: function(error, stackTrace) { + var t1, completeResult; + t1 = this.box_0; + if (!J.getInterceptor(t1.completeResult_0).$is_Future) { + completeResult = P._Future$(null); + t1.completeResult_0 = completeResult; + completeResult._setError$2(error, stackTrace); + } + P._Future__propagateToListeners(t1.completeResult_0, this.listener_12); + }, + call$1: function(error) { + return this.call$2(error, null); + } + }, + _AsyncCallbackEntry: { + "^": "Object;callback,next", + callback$0: function() { + return this.callback.call$0(); + } + }, + Stream: { + "^": "Object;", + forEach$1: function(_, action) { + var t1, future; + t1 = {}; + future = P._Future$(null); + t1.subscription_0 = null; + t1.subscription_0 = this.listen$4$cancelOnError$onDone$onError(new P.Stream_forEach_closure(t1, this, action, future), true, new P.Stream_forEach_closure0(future), future.get$_completeError()); + return future; + }, + get$length: function(_) { + var t1, future; + t1 = {}; + future = P._Future$(J.JSInt); + t1.count_0 = 0; + this.listen$4$cancelOnError$onDone$onError(new P.Stream_length_closure(t1), true, new P.Stream_length_closure0(t1, future), future.get$_completeError()); + return future; + }, + get$isEmpty: function(_) { + var t1, future; + t1 = {}; + future = P._Future$(J.JSBool); + t1.subscription_0 = null; + t1.subscription_0 = this.listen$4$cancelOnError$onDone$onError(new P.Stream_isEmpty_closure(t1, future), true, new P.Stream_isEmpty_closure0(future), future.get$_completeError()); + return future; + } + }, + Stream_forEach_closure: { + "^": "Closure;box_0,this_1,action_2,future_3", + call$1: function(element) { + P._runUserCode(new P.Stream_forEach__closure(this.action_2, element), new P.Stream_forEach__closure0(), P._cancelAndErrorClosure(this.box_0.subscription_0, this.future_3)); + }, + $signature: function() { + return H.computeSignature(function(T) { + return {func: "dynamic__T", args: [T]}; + }, this.this_1, "Stream"); + } + }, + Stream_forEach__closure: { + "^": "Closure:9;action_4,element_5", + call$0: function() { + return this.action_4.call$1(this.element_5); + } + }, + Stream_forEach__closure0: { + "^": "Closure:11;", + call$1: function(_) { + } + }, + Stream_forEach_closure0: { + "^": "Closure:9;future_6", + call$0: function() { + this.future_6._complete$1(null); + } + }, + Stream_length_closure: { + "^": "Closure:11;box_0", + call$1: function(_) { + var t1 = this.box_0; + t1.count_0 = t1.count_0 + 1; + } + }, + Stream_length_closure0: { + "^": "Closure:9;box_0,future_1", + call$0: function() { + this.future_1._complete$1(this.box_0.count_0); + } + }, + Stream_isEmpty_closure: { + "^": "Closure:11;box_0,future_1", + call$1: function(_) { + P._cancelAndValue(this.box_0.subscription_0, this.future_1, false); + } + }, + Stream_isEmpty_closure0: { + "^": "Closure:9;future_2", + call$0: function() { + this.future_2._complete$1(true); + } + }, + StreamSubscription: { + "^": "Object;" + }, + _EventSink: { + "^": "Object;" + }, + _cancelAndError_closure: { + "^": "Closure:9;future_0,error_1,stackTrace_2", + call$0: function() { + return this.future_0._completeError$2(this.error_1, this.stackTrace_2); + } + }, + _cancelAndErrorClosure_closure: { + "^": "Closure:17;subscription_0,future_1", + call$2: function(error, stackTrace) { + return P._cancelAndError(this.subscription_0, this.future_1, error, stackTrace); + } + }, + _cancelAndValue_closure: { + "^": "Closure:9;future_0,value_1", + call$0: function() { + return this.future_0._complete$1(this.value_1); + } + }, + _BaseZone: { + "^": "Object;", + runGuarded$1: function(f) { + var e, s, t1, exception; + try { + t1 = this.run$1(f); + return t1; + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + s = new H._StackTrace(exception, null); + return this.handleUncaughtError$2(e, s); + } + + }, + runUnaryGuarded$2: function(f, arg) { + var e, s, t1, exception; + try { + t1 = this.runUnary$2(f, arg); + return t1; + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + s = new H._StackTrace(exception, null); + return this.handleUncaughtError$2(e, s); + } + + }, + bindCallback$2$runGuarded: function(f, runGuarded) { + var registered = this.registerCallback$1(f); + if (runGuarded) + return new P._BaseZone_bindCallback_closure(this, registered); + else + return new P._BaseZone_bindCallback_closure0(this, registered); + }, + bindCallback$1: function(f) { + return this.bindCallback$2$runGuarded(f, true); + }, + bindUnaryCallback$2$runGuarded: function(f, runGuarded) { + var registered = this.registerUnaryCallback$1(f); + if (runGuarded) + return new P._BaseZone_bindUnaryCallback_closure(this, registered); + else + return new P._BaseZone_bindUnaryCallback_closure0(this, registered); + } + }, + _BaseZone_bindCallback_closure: { + "^": "Closure:9;this_0,registered_1", + call$0: function() { + return this.this_0.runGuarded$1(this.registered_1); + } + }, + _BaseZone_bindCallback_closure0: { + "^": "Closure:9;this_2,registered_3", + call$0: function() { + return this.this_2.run$1(this.registered_3); + } + }, + _BaseZone_bindUnaryCallback_closure: { + "^": "Closure:11;this_0,registered_1", + call$1: function(arg) { + return this.this_0.runUnaryGuarded$2(this.registered_1, arg); + } + }, + _BaseZone_bindUnaryCallback_closure0: { + "^": "Closure:11;this_2,registered_3", + call$1: function(arg) { + return this.this_2.runUnary$2(this.registered_3, arg); + } + }, + _rootHandleUncaughtError_closure: { + "^": "Closure:9;error_0,stackTrace_1", + call$0: function() { + P._scheduleAsyncCallback(new P._rootHandleUncaughtError__closure(this.error_0, this.stackTrace_1)); + } + }, + _rootHandleUncaughtError__closure: { + "^": "Closure:9;error_2,stackTrace_3", + call$0: function() { + var t1, trace; + t1 = this.error_2; + P.print("Uncaught Error: " + H.S(t1)); + trace = this.stackTrace_3; + if (trace == null && !!J.getInterceptor(t1).$isError) + trace = t1.get$stackTrace(); + if (trace != null) + P.print("Stack Trace: \n" + H.S(trace) + "\n"); + throw H.wrapException(t1); + } + }, + _RootZone: { + "^": "_BaseZone;", + $index: function(_, key) { + return; + }, + handleUncaughtError$2: function(error, stackTrace) { + return P._rootHandleUncaughtError(this, null, this, error, stackTrace); + }, + run$1: function(f) { + return P._rootRun(this, null, this, f); + }, + runUnary$2: function(f, arg) { + return P._rootRunUnary(this, null, this, f, arg); + }, + registerCallback$1: function(f) { + return f; + }, + registerUnaryCallback$1: function(f) { + return f; + } + } +}], +["dart.collection", "dart:collection", , P, { + "^": "", + _defaultEquals: [function(a, b) { + return J.$eq(a, b); + }, "call$2", "_defaultEquals$closure", 4, 0, 3], + _defaultHashCode: [function(a) { + return J.get$hashCode$(a); + }, "call$1", "_defaultHashCode$closure", 2, 0, 4], + HashMap_HashMap: function(equals, hashCode, isValidKey, $K, $V) { + return H.setRuntimeTypeInfo(new P._HashMap(0, null, null, null, null), [$K, $V]); + }, + HashSet_HashSet$identity: function($E) { + return H.setRuntimeTypeInfo(new P._IdentityHashSet(0, null, null, null, null), [$E]); + }, + _iterableToString: function(iterable) { + var parts, t1; + if ($.get$_toStringVisiting().contains$1(0, iterable)) + return "(...)"; + $.get$_toStringVisiting().add$1(0, iterable); + parts = []; + try { + P._iterablePartsToStrings(iterable, parts); + } finally { + $.get$_toStringVisiting().remove$1(0, iterable); + } + t1 = P.StringBuffer$("("); + t1.writeAll$2(parts, ", "); + t1.write$1(")"); + return t1._contents; + }, + _iterablePartsToStrings: function(iterable, parts) { + var it, $length, count, next, ultimateString, penultimateString, penultimate, ultimate, ultimate0, elision; + it = iterable.get$iterator(iterable); + $length = 0; + count = 0; + while (true) { + if (!($length < 80 || count < 3)) + break; + if (!it.moveNext$0()) + return; + next = H.S(it.get$current()); + parts.push(next); + $length += next.length + 2; + ++count; + } + if (!it.moveNext$0()) { + if (count <= 5) + return; + if (0 >= parts.length) + return H.ioore(parts, 0); + ultimateString = parts.pop(); + if (0 >= parts.length) + return H.ioore(parts, 0); + penultimateString = parts.pop(); + } else { + penultimate = it.get$current(); + ++count; + if (!it.moveNext$0()) { + if (count <= 4) { + parts.push(H.S(penultimate)); + return; + } + ultimateString = H.S(penultimate); + if (0 >= parts.length) + return H.ioore(parts, 0); + penultimateString = parts.pop(); + $length += ultimateString.length + 2; + } else { + ultimate = it.get$current(); + ++count; + for (; it.moveNext$0(); penultimate = ultimate, ultimate = ultimate0) { + ultimate0 = it.get$current(); + ++count; + if (count > 100) { + while (true) { + if (!($length > 75 && count > 3)) + break; + if (0 >= parts.length) + return H.ioore(parts, 0); + $length -= parts.pop().length + 2; + --count; + } + parts.push("..."); + return; + } + } + penultimateString = H.S(penultimate); + ultimateString = H.S(ultimate); + $length += ultimateString.length + penultimateString.length + 4; + } + } + if (count > parts.length + 2) { + $length += 5; + elision = "..."; + } else + elision = null; + while (true) { + if (!($length > 80 && parts.length > 3)) + break; + if (0 >= parts.length) + return H.ioore(parts, 0); + $length -= parts.pop().length + 2; + if (elision == null) { + $length += 5; + elision = "..."; + } + } + if (elision != null) + parts.push(elision); + parts.push(penultimateString); + parts.push(ultimateString); + }, + LinkedHashMap_LinkedHashMap: function(equals, hashCode, isValidKey, $K, $V) { + return H.setRuntimeTypeInfo(new P._LinkedHashMap(0, null, null, null, null, null, 0), [$K, $V]); + }, + LinkedHashSet_LinkedHashSet: function(equals, hashCode, isValidKey, $E) { + return H.setRuntimeTypeInfo(new P._LinkedHashSet(0, null, null, null, null, null, 0), [$E]); + }, + Maps_mapToString: function(m) { + var t1, result, i, t2; + t1 = {}; + for (i = 0; t2 = $.get$Maps__toStringList(), i < t2.length; ++i) + if (t2[i] === m) + return "{...}"; + result = P.StringBuffer$(""); + try { + $.get$Maps__toStringList().push(m); + result.write$1("{"); + t1.first_0 = true; + J.forEach$1$ax(m, new P.Maps_mapToString_closure(t1, result)); + result.write$1("}"); + } finally { + t1 = $.get$Maps__toStringList(); + if (0 >= t1.length) + return H.ioore(t1, 0); + t1.pop(); + } + return result.get$_contents(); + }, + _HashMap: { + "^": "Object;_collection$_length,_strings,_nums,_rest,_keys", + get$length: function(_) { + return this._collection$_length; + }, + get$isEmpty: function(_) { + return this._collection$_length === 0; + }, + get$keys: function(_) { + return H.setRuntimeTypeInfo(new P.HashMapKeyIterable(this), [H.getTypeArgumentByIndex(this, 0)]); + }, + get$values: function(_) { + return H.MappedIterable_MappedIterable(H.setRuntimeTypeInfo(new P.HashMapKeyIterable(this), [H.getTypeArgumentByIndex(this, 0)]), new P._HashMap_values_closure(this), H.getTypeArgumentByIndex(this, 0), H.getTypeArgumentByIndex(this, 1)); + }, + $index: function(_, key) { + var strings, t1, entry, nums, rest, bucket, index; + if (typeof key === "string" && key !== "__proto__") { + strings = this._strings; + if (strings == null) + t1 = null; + else { + entry = strings[key]; + t1 = entry === strings ? null : entry; + } + return t1; + } else if (typeof key === "number" && (key & 0x3ffffff) === key) { + nums = this._nums; + if (nums == null) + t1 = null; + else { + entry = nums[key]; + t1 = entry === nums ? null : entry; + } + return t1; + } else { + rest = this._rest; + if (rest == null) + return; + bucket = rest[this._computeHashCode$1(key)]; + index = this._findBucketIndex$2(bucket, key); + return index < 0 ? null : bucket[index + 1]; + } + }, + $indexSet: function(_, key, value) { + var strings, nums, rest, hash, bucket, index; + if (typeof key === "string" && key !== "__proto__") { + strings = this._strings; + if (strings == null) { + strings = P._HashMap__newHashTable(); + this._strings = strings; + } + this._addHashTableEntry$3(strings, key, value); + } else if (typeof key === "number" && (key & 0x3ffffff) === key) { + nums = this._nums; + if (nums == null) { + nums = P._HashMap__newHashTable(); + this._nums = nums; + } + this._addHashTableEntry$3(nums, key, value); + } else { + rest = this._rest; + if (rest == null) { + rest = P._HashMap__newHashTable(); + this._rest = rest; + } + hash = this._computeHashCode$1(key); + bucket = rest[hash]; + if (bucket == null) { + P._HashMap__setTableEntry(rest, hash, [key, value]); + this._collection$_length = this._collection$_length + 1; + this._keys = null; + } else { + index = this._findBucketIndex$2(bucket, key); + if (index >= 0) + bucket[index + 1] = value; + else { + bucket.push(key, value); + this._collection$_length = this._collection$_length + 1; + this._keys = null; + } + } + } + }, + forEach$1: function(_, action) { + var keys, $length, i, key; + keys = this._computeKeys$0(); + for ($length = keys.length, i = 0; i < $length; ++i) { + key = keys[i]; + action.call$2(key, this.$index(0, key)); + if (keys !== this._keys) + throw H.wrapException(P.ConcurrentModificationError$(this)); + } + }, + _computeKeys$0: function() { + var t1, result, strings, names, entries, index, i, nums, rest, bucket, $length, i0; + t1 = this._keys; + if (t1 != null) + return t1; + result = Array(this._collection$_length); + result.fixed$length = init; + strings = this._strings; + if (strings != null) { + names = Object.getOwnPropertyNames(strings); + entries = names.length; + for (index = 0, i = 0; i < entries; ++i) { + result[index] = names[i]; + ++index; + } + } else + index = 0; + nums = this._nums; + if (nums != null) { + names = Object.getOwnPropertyNames(nums); + entries = names.length; + for (i = 0; i < entries; ++i) { + result[index] = +names[i]; + ++index; + } + } + rest = this._rest; + if (rest != null) { + names = Object.getOwnPropertyNames(rest); + entries = names.length; + for (i = 0; i < entries; ++i) { + bucket = rest[names[i]]; + $length = bucket.length; + for (i0 = 0; i0 < $length; i0 += 2) { + result[index] = bucket[i0]; + ++index; + } + } + } + this._keys = result; + return result; + }, + _addHashTableEntry$3: function(table, key, value) { + if (table[key] == null) { + this._collection$_length = this._collection$_length + 1; + this._keys = null; + } + P._HashMap__setTableEntry(table, key, value); + }, + _computeHashCode$1: function(key) { + return J.get$hashCode$(key) & 0x3ffffff; + }, + _findBucketIndex$2: function(bucket, key) { + var $length, i; + if (bucket == null) + return -1; + $length = bucket.length; + for (i = 0; i < $length; i += 2) + if (J.$eq(bucket[i], key)) + return i; + return -1; + }, + $isMap: true, + $asMap: null, + static: {_HashMap__setTableEntry: function(table, key, value) { + if (value == null) + table[key] = table; + else + table[key] = value; + }, _HashMap__newHashTable: function() { + var table = Object.create(null); + P._HashMap__setTableEntry(table, "", table); + delete table[""]; + return table; + }} + }, + _HashMap_values_closure: { + "^": "Closure:11;this_0", + call$1: function(each) { + return this.this_0.$index(0, each); + } + }, + HashMapKeyIterable: { + "^": "IterableBase;_map", + get$length: function(_) { + return this._map._collection$_length; + }, + get$isEmpty: function(_) { + return this._map._collection$_length === 0; + }, + get$iterator: function(_) { + var t1 = this._map; + return new P.HashMapKeyIterator(t1, t1._computeKeys$0(), 0, null); + }, + forEach$1: function(_, f) { + var t1, keys, $length, i; + t1 = this._map; + keys = t1._computeKeys$0(); + for ($length = keys.length, i = 0; i < $length; ++i) { + f.call$1(keys[i]); + if (keys !== t1._keys) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + } + }, + $isEfficientLength: true + }, + HashMapKeyIterator: { + "^": "Object;_map,_keys,_offset,_collection$_current", + get$current: function() { + return this._collection$_current; + }, + moveNext$0: function() { + var keys, offset, t1; + keys = this._keys; + offset = this._offset; + t1 = this._map; + if (keys !== t1._keys) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + else if (offset >= keys.length) { + this._collection$_current = null; + return false; + } else { + this._collection$_current = keys[offset]; + this._offset = offset + 1; + return true; + } + } + }, + _LinkedHashMap: { + "^": "Object;_collection$_length,_strings,_nums,_rest,_first,_last,_modifications", + get$length: function(_) { + return this._collection$_length; + }, + get$isEmpty: function(_) { + return this._collection$_length === 0; + }, + get$keys: function(_) { + return H.setRuntimeTypeInfo(new P.LinkedHashMapKeyIterable(this), [H.getTypeArgumentByIndex(this, 0)]); + }, + get$values: function(_) { + return H.MappedIterable_MappedIterable(H.setRuntimeTypeInfo(new P.LinkedHashMapKeyIterable(this), [H.getTypeArgumentByIndex(this, 0)]), new P._LinkedHashMap_values_closure(this), H.getTypeArgumentByIndex(this, 0), H.getTypeArgumentByIndex(this, 1)); + }, + containsKey$1: function(_, key) { + var nums, rest; + if ((key & 0x3ffffff) === key) { + nums = this._nums; + if (nums == null) + return false; + return nums[key] != null; + } else { + rest = this._rest; + if (rest == null) + return false; + return this._findBucketIndex$2(rest[this._computeHashCode$1(key)], key) >= 0; + } + }, + $index: function(_, key) { + var strings, cell, nums, rest, bucket, index; + if (typeof key === "string" && key !== "__proto__") { + strings = this._strings; + if (strings == null) + return; + cell = strings[key]; + return cell == null ? null : cell.get$_value(); + } else if (typeof key === "number" && (key & 0x3ffffff) === key) { + nums = this._nums; + if (nums == null) + return; + cell = nums[key]; + return cell == null ? null : cell.get$_value(); + } else { + rest = this._rest; + if (rest == null) + return; + bucket = rest[this._computeHashCode$1(key)]; + index = this._findBucketIndex$2(bucket, key); + if (index < 0) + return; + return bucket[index].get$_value(); + } + }, + $indexSet: function(_, key, value) { + var strings, nums, rest, hash, bucket, index; + if (typeof key === "string" && key !== "__proto__") { + strings = this._strings; + if (strings == null) { + strings = P._LinkedHashMap__newHashTable(); + this._strings = strings; + } + this._addHashTableEntry$3(strings, key, value); + } else if (typeof key === "number" && (key & 0x3ffffff) === key) { + nums = this._nums; + if (nums == null) { + nums = P._LinkedHashMap__newHashTable(); + this._nums = nums; + } + this._addHashTableEntry$3(nums, key, value); + } else { + rest = this._rest; + if (rest == null) { + rest = P._LinkedHashMap__newHashTable(); + this._rest = rest; + } + hash = this._computeHashCode$1(key); + bucket = rest[hash]; + if (bucket == null) + rest[hash] = [this._newLinkedCell$2(key, value)]; + else { + index = this._findBucketIndex$2(bucket, key); + if (index >= 0) + bucket[index].set$_value(value); + else + bucket.push(this._newLinkedCell$2(key, value)); + } + } + }, + remove$1: function(_, key) { + var rest, bucket, index, cell; + if (typeof key === "string" && key !== "__proto__") + return this._removeHashTableEntry$2(this._strings, key); + else if (typeof key === "number" && (key & 0x3ffffff) === key) + return this._removeHashTableEntry$2(this._nums, key); + else { + rest = this._rest; + if (rest == null) + return; + bucket = rest[this._computeHashCode$1(key)]; + index = this._findBucketIndex$2(bucket, key); + if (index < 0) + return; + cell = bucket.splice(index, 1)[0]; + this._unlinkCell$1(cell); + return cell.get$_value(); + } + }, + forEach$1: function(_, action) { + var cell, modifications; + cell = this._first; + modifications = this._modifications; + for (; cell != null;) { + action.call$2(cell.get$_key(cell), cell._value); + if (modifications !== this._modifications) + throw H.wrapException(P.ConcurrentModificationError$(this)); + cell = cell._next; + } + }, + _addHashTableEntry$3: function(table, key, value) { + var cell = table[key]; + if (cell == null) + table[key] = this._newLinkedCell$2(key, value); + else + cell.set$_value(value); + }, + _removeHashTableEntry$2: function(table, key) { + var cell; + if (table == null) + return; + cell = table[key]; + if (cell == null) + return; + this._unlinkCell$1(cell); + delete table[key]; + return cell.get$_value(); + }, + _newLinkedCell$2: function(key, value) { + var cell, last; + cell = new P.LinkedHashMapCell(key, value, null, null); + if (this._first == null) { + this._last = cell; + this._first = cell; + } else { + last = this._last; + cell._previous = last; + last.set$_next(cell); + this._last = cell; + } + this._collection$_length = this._collection$_length + 1; + this._modifications = this._modifications + 1 & 67108863; + return cell; + }, + _unlinkCell$1: function(cell) { + var previous, next; + previous = cell.get$_previous(); + next = cell.get$_next(); + if (previous == null) + this._first = next; + else + previous.set$_next(next); + if (next == null) + this._last = previous; + else + next.set$_previous(previous); + this._collection$_length = this._collection$_length - 1; + this._modifications = this._modifications + 1 & 67108863; + }, + _computeHashCode$1: function(key) { + return J.get$hashCode$(key) & 0x3ffffff; + }, + _findBucketIndex$2: function(bucket, key) { + var $length, i; + if (bucket == null) + return -1; + $length = bucket.length; + for (i = 0; i < $length; ++i) + if (J.$eq(J.get$_key$x(bucket[i]), key)) + return i; + return -1; + }, + toString$0: function(_) { + return P.Maps_mapToString(this); + }, + $isMap: true, + $asMap: null, + static: {_LinkedHashMap__newHashTable: function() { + var table = Object.create(null); + table[""] = table; + delete table[""]; + return table; + }} + }, + _LinkedHashMap_values_closure: { + "^": "Closure:11;this_0", + call$1: function(each) { + return this.this_0.$index(0, each); + } + }, + LinkedHashMapCell: { + "^": "Object;_key>,_value@,_next@,_previous@" + }, + LinkedHashMapKeyIterable: { + "^": "IterableBase;_map", + get$length: function(_) { + return this._map._collection$_length; + }, + get$isEmpty: function(_) { + return this._map._collection$_length === 0; + }, + get$iterator: function(_) { + var t1, t2; + t1 = this._map; + t2 = new P.LinkedHashMapKeyIterator(t1, t1._modifications, null, null); + t2._cell = t1._first; + return t2; + }, + forEach$1: function(_, f) { + var t1, cell, modifications; + t1 = this._map; + cell = t1._first; + modifications = t1._modifications; + for (; cell != null;) { + f.call$1(cell.get$_key(cell)); + if (modifications !== t1._modifications) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + cell = cell._next; + } + }, + $isEfficientLength: true + }, + LinkedHashMapKeyIterator: { + "^": "Object;_map,_modifications,_cell,_collection$_current", + get$current: function() { + return this._collection$_current; + }, + moveNext$0: function() { + var t1 = this._map; + if (this._modifications !== t1._modifications) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + else { + t1 = this._cell; + if (t1 == null) { + this._collection$_current = null; + return false; + } else { + this._collection$_current = t1.get$_key(t1); + this._cell = t1._next; + return true; + } + } + } + }, + _HashSet: { + "^": "_HashSetBase;", + get$iterator: function(_) { + return new P.HashSetIterator(this, this._computeElements$0(), 0, null); + }, + get$length: function(_) { + return this._collection$_length; + }, + get$isEmpty: function(_) { + return this._collection$_length === 0; + }, + contains$1: function(_, object) { + var strings, nums, rest; + if (typeof object === "string" && object !== "__proto__") { + strings = this._strings; + return strings == null ? false : strings[object] != null; + } else if (typeof object === "number" && (object & 0x3ffffff) === object) { + nums = this._nums; + return nums == null ? false : nums[object] != null; + } else { + rest = this._rest; + if (rest == null) + return false; + return this._findBucketIndex$2(rest[this._computeHashCode$1(object)], object) >= 0; + } + }, + lookup$1: function(object) { + var t1, rest, bucket, index; + if (!(typeof object === "string" && object !== "__proto__")) + t1 = typeof object === "number" && (object & 0x3ffffff) === object; + else + t1 = true; + if (t1) + return this.contains$1(0, object) ? object : null; + rest = this._rest; + if (rest == null) + return; + bucket = rest[this._computeHashCode$1(object)]; + index = this._findBucketIndex$2(bucket, object); + if (index < 0) + return; + return J.$index$asx(bucket, index); + }, + add$1: function(_, element) { + var rest, table, hash, bucket; + rest = this._rest; + if (rest == null) { + table = Object.create(null); + table[""] = table; + delete table[""]; + this._rest = table; + rest = table; + } + hash = this._computeHashCode$1(element); + bucket = rest[hash]; + if (bucket == null) + rest[hash] = [element]; + else { + if (this._findBucketIndex$2(bucket, element) >= 0) + return false; + bucket.push(element); + } + this._collection$_length = this._collection$_length + 1; + this._elements = null; + return true; + }, + remove$1: function(_, object) { + var rest, bucket, index; + rest = this._rest; + if (rest == null) + return false; + bucket = rest[this._computeHashCode$1(object)]; + index = this._findBucketIndex$2(bucket, object); + if (index < 0) + return false; + this._collection$_length = this._collection$_length - 1; + this._elements = null; + bucket.splice(index, 1); + return true; + }, + _computeElements$0: function() { + var t1, result, strings, names, entries, index, i, nums, rest, bucket, $length, i0; + t1 = this._elements; + if (t1 != null) + return t1; + result = Array(this._collection$_length); + result.fixed$length = init; + strings = this._strings; + if (strings != null) { + names = Object.getOwnPropertyNames(strings); + entries = names.length; + for (index = 0, i = 0; i < entries; ++i) { + result[index] = names[i]; + ++index; + } + } else + index = 0; + nums = this._nums; + if (nums != null) { + names = Object.getOwnPropertyNames(nums); + entries = names.length; + for (i = 0; i < entries; ++i) { + result[index] = +names[i]; + ++index; + } + } + rest = this._rest; + if (rest != null) { + names = Object.getOwnPropertyNames(rest); + entries = names.length; + for (i = 0; i < entries; ++i) { + bucket = rest[names[i]]; + $length = bucket.length; + for (i0 = 0; i0 < $length; ++i0) { + result[index] = bucket[i0]; + ++index; + } + } + } + this._elements = result; + return result; + }, + _computeHashCode$1: function(element) { + return J.get$hashCode$(element) & 0x3ffffff; + }, + _findBucketIndex$2: function(bucket, element) { + var $length, i; + if (bucket == null) + return -1; + $length = bucket.length; + for (i = 0; i < $length; ++i) + if (J.$eq(bucket[i], element)) + return i; + return -1; + }, + $isEfficientLength: true + }, + _IdentityHashSet: { + "^": "_HashSet;_collection$_length,_strings,_nums,_rest,_elements", + _computeHashCode$1: function(key) { + return H.objectHashCode(key) & 0x3ffffff; + }, + _findBucketIndex$2: function(bucket, element) { + var $length, i, t1; + if (bucket == null) + return -1; + $length = bucket.length; + for (i = 0; i < $length; ++i) { + t1 = bucket[i]; + if (t1 == null ? element == null : t1 === element) + return i; + } + return -1; + } + }, + HashSetIterator: { + "^": "Object;_set,_elements,_offset,_collection$_current", + get$current: function() { + return this._collection$_current; + }, + moveNext$0: function() { + var elements, offset, t1; + elements = this._elements; + offset = this._offset; + t1 = this._set; + if (elements !== t1._elements) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + else if (offset >= elements.length) { + this._collection$_current = null; + return false; + } else { + this._collection$_current = elements[offset]; + this._offset = offset + 1; + return true; + } + } + }, + _LinkedHashSet: { + "^": "_HashSetBase;_collection$_length,_strings,_nums,_rest,_first,_last,_modifications", + get$iterator: function(_) { + var t1 = new P.LinkedHashSetIterator(this, this._modifications, null, null); + t1._cell = this._first; + return t1; + }, + get$length: function(_) { + return this._collection$_length; + }, + get$isEmpty: function(_) { + return this._collection$_length === 0; + }, + contains$1: function(_, object) { + var strings, nums, rest; + if (typeof object === "string" && object !== "__proto__") { + strings = this._strings; + if (strings == null) + return false; + return strings[object] != null; + } else if (typeof object === "number" && (object & 0x3ffffff) === object) { + nums = this._nums; + if (nums == null) + return false; + return nums[object] != null; + } else { + rest = this._rest; + if (rest == null) + return false; + return this._findBucketIndex$2(rest[this._computeHashCode$1(object)], object) >= 0; + } + }, + lookup$1: function(object) { + var t1, rest, bucket, index; + if (!(typeof object === "string" && object !== "__proto__")) + t1 = typeof object === "number" && (object & 0x3ffffff) === object; + else + t1 = true; + if (t1) + return this.contains$1(0, object) ? object : null; + else { + rest = this._rest; + if (rest == null) + return; + bucket = rest[this._computeHashCode$1(object)]; + index = this._findBucketIndex$2(bucket, object); + if (index < 0) + return; + return J.$index$asx(bucket, index).get$_collection$_element(); + } + }, + forEach$1: function(_, action) { + var cell, modifications; + cell = this._first; + modifications = this._modifications; + for (; cell != null;) { + action.call$1(cell.get$_collection$_element()); + if (modifications !== this._modifications) + throw H.wrapException(P.ConcurrentModificationError$(this)); + cell = cell._next; + } + }, + add$1: function(_, element) { + var strings, table, nums, rest, hash, bucket; + if (typeof element === "string" && element !== "__proto__") { + strings = this._strings; + if (strings == null) { + table = Object.create(null); + table[""] = table; + delete table[""]; + this._strings = table; + strings = table; + } + return this._addHashTableEntry$2(strings, element); + } else if (typeof element === "number" && (element & 0x3ffffff) === element) { + nums = this._nums; + if (nums == null) { + table = Object.create(null); + table[""] = table; + delete table[""]; + this._nums = table; + nums = table; + } + return this._addHashTableEntry$2(nums, element); + } else { + rest = this._rest; + if (rest == null) { + table = Object.create(null); + table[""] = table; + delete table[""]; + this._rest = table; + rest = table; + } + hash = this._computeHashCode$1(element); + bucket = rest[hash]; + if (bucket == null) + rest[hash] = [this._newLinkedCell$1(element)]; + else { + if (this._findBucketIndex$2(bucket, element) >= 0) + return false; + bucket.push(this._newLinkedCell$1(element)); + } + return true; + } + }, + addAll$1: function(_, objects) { + var t1; + for (t1 = J.get$iterator$ax(objects); t1.moveNext$0();) + this.add$1(0, t1._current); + }, + remove$1: function(_, object) { + var rest, bucket, index; + if (typeof object === "string" && object !== "__proto__") + return this._removeHashTableEntry$2(this._strings, object); + else if (typeof object === "number" && (object & 0x3ffffff) === object) + return this._removeHashTableEntry$2(this._nums, object); + else { + rest = this._rest; + if (rest == null) + return false; + bucket = rest[this._computeHashCode$1(object)]; + index = this._findBucketIndex$2(bucket, object); + if (index < 0) + return false; + this._unlinkCell$1(bucket.splice(index, 1)[0]); + return true; + } + }, + _addHashTableEntry$2: function(table, element) { + if (table[element] != null) + return false; + table[element] = this._newLinkedCell$1(element); + return true; + }, + _removeHashTableEntry$2: function(table, element) { + var cell; + if (table == null) + return false; + cell = table[element]; + if (cell == null) + return false; + this._unlinkCell$1(cell); + delete table[element]; + return true; + }, + _newLinkedCell$1: function(element) { + var cell, last; + cell = new P.LinkedHashSetCell(element, null, null); + if (this._first == null) { + this._last = cell; + this._first = cell; + } else { + last = this._last; + cell._previous = last; + last.set$_next(cell); + this._last = cell; + } + this._collection$_length = this._collection$_length + 1; + this._modifications = this._modifications + 1 & 67108863; + return cell; + }, + _unlinkCell$1: function(cell) { + var previous, next; + previous = cell.get$_previous(); + next = cell.get$_next(); + if (previous == null) + this._first = next; + else + previous.set$_next(next); + if (next == null) + this._last = previous; + else + next.set$_previous(previous); + this._collection$_length = this._collection$_length - 1; + this._modifications = this._modifications + 1 & 67108863; + }, + _computeHashCode$1: function(element) { + return J.get$hashCode$(element) & 0x3ffffff; + }, + _findBucketIndex$2: function(bucket, element) { + var $length, i; + if (bucket == null) + return -1; + $length = bucket.length; + for (i = 0; i < $length; ++i) + if (J.$eq(bucket[i].get$_collection$_element(), element)) + return i; + return -1; + }, + $isEfficientLength: true + }, + LinkedHashSetCell: { + "^": "Object;_collection$_element<,_next@,_previous@" + }, + LinkedHashSetIterator: { + "^": "Object;_set,_modifications,_cell,_collection$_current", + get$current: function() { + return this._collection$_current; + }, + moveNext$0: function() { + var t1 = this._set; + if (this._modifications !== t1._modifications) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + else { + t1 = this._cell; + if (t1 == null) { + this._collection$_current = null; + return false; + } else { + this._collection$_current = t1.get$_collection$_element(); + this._cell = t1._next; + return true; + } + } + } + }, + _HashSetBase: { + "^": "IterableBase;", + toString$0: function(_) { + return H.IterableMixinWorkaround_toStringIterable(this, "{", "}"); + }, + $isEfficientLength: true + }, + IterableBase: { + "^": "Object;", + forEach$1: function(_, f) { + var t1; + for (t1 = this.get$iterator(this); t1.moveNext$0();) + f.call$1(t1.get$current()); + }, + toList$1$growable: function(_, growable) { + return P.List_List$from(this, growable, H.getRuntimeTypeArgument(this, "IterableBase", 0)); + }, + toList$0: function($receiver) { + return this.toList$1$growable($receiver, true); + }, + get$length: function(_) { + var it, count; + it = this.get$iterator(this); + for (count = 0; it.moveNext$0();) + ++count; + return count; + }, + get$isEmpty: function(_) { + return !this.get$iterator(this).moveNext$0(); + }, + get$single: function(_) { + var it, result; + it = this.get$iterator(this); + if (!it.moveNext$0()) + throw H.wrapException(P.StateError$("No elements")); + result = it.get$current(); + if (it.moveNext$0()) + throw H.wrapException(P.StateError$("More than one element")); + return result; + }, + elementAt$1: function(_, index) { + var t1, remaining, element; + if (index < 0) + throw H.wrapException(P.RangeError$value(index)); + for (t1 = this.get$iterator(this), remaining = index; t1.moveNext$0();) { + element = t1.get$current(); + if (remaining === 0) + return element; + --remaining; + } + throw H.wrapException(P.RangeError$value(index)); + }, + toString$0: function(_) { + return P._iterableToString(this); + } + }, + ListBase: { + "^": "Object+ListMixin;", + $isList: true, + $asList: null, + $isEfficientLength: true + }, + ListMixin: { + "^": "Object;", + get$iterator: function(receiver) { + return new H.ListIterator(receiver, this.get$length(receiver), 0, null); + }, + elementAt$1: function(receiver, index) { + return this.$index(receiver, index); + }, + forEach$1: function(receiver, action) { + var $length, i; + $length = this.get$length(receiver); + for (i = 0; i < $length; ++i) { + action.call$1(this.$index(receiver, i)); + if ($length !== this.get$length(receiver)) + throw H.wrapException(P.ConcurrentModificationError$(receiver)); + } + }, + get$isEmpty: function(receiver) { + return this.get$length(receiver) === 0; + }, + where$1: function(receiver, test) { + return H.setRuntimeTypeInfo(new H.WhereIterable(receiver, test), [H.getRuntimeTypeArgument(receiver, "ListMixin", 0)]); + }, + toString$0: function(receiver) { + var result; + if ($.get$_toStringVisiting().contains$1(0, receiver)) + return "[...]"; + result = P.StringBuffer$(""); + try { + $.get$_toStringVisiting().add$1(0, receiver); + result.write$1("["); + result.writeAll$2(receiver, ", "); + result.write$1("]"); + } finally { + $.get$_toStringVisiting().remove$1(0, receiver); + } + return result.get$_contents(); + }, + $isList: true, + $asList: null, + $isEfficientLength: true + }, + Maps_mapToString_closure: { + "^": "Closure:10;box_0,result_1", + call$2: function(k, v) { + var t1 = this.box_0; + if (!t1.first_0) + this.result_1.write$1(", "); + t1.first_0 = false; + t1 = this.result_1; + t1.write$1(k); + t1.write$1(": "); + t1.write$1(v); + } + }, + ListQueue: { + "^": "IterableBase;_table,_head,_tail,_modificationCount", + get$iterator: function(_) { + return new P._ListQueueIterator(this, this._tail, this._modificationCount, this._head, null); + }, + forEach$1: function(_, action) { + var modificationCount, i, t1; + modificationCount = this._modificationCount; + for (i = this._head; i !== this._tail; i = (i + 1 & this._table.length - 1) >>> 0) { + t1 = this._table; + if (i < 0 || i >= t1.length) + return H.ioore(t1, i); + action.call$1(t1[i]); + if (modificationCount !== this._modificationCount) + H.throwExpression(P.ConcurrentModificationError$(this)); + } + }, + get$isEmpty: function(_) { + return this._head === this._tail; + }, + get$length: function(_) { + return (this._tail - this._head & this._table.length - 1) >>> 0; + }, + toString$0: function(_) { + return H.IterableMixinWorkaround_toStringIterable(this, "{", "}"); + }, + _add$1: function(element) { + var t1, t2, t3; + t1 = this._table; + t2 = this._tail; + t3 = t1.length; + if (t2 < 0 || t2 >= t3) + return H.ioore(t1, t2); + t1[t2] = element; + t3 = (t2 + 1 & t3 - 1) >>> 0; + this._tail = t3; + if (this._head === t3) + this._grow$0(); + this._modificationCount = this._modificationCount + 1; + }, + _grow$0: function() { + var t1, newTable, t2, split; + t1 = Array(this._table.length * 2); + t1.fixed$length = init; + newTable = H.setRuntimeTypeInfo(t1, [H.getTypeArgumentByIndex(this, 0)]); + t1 = this._table; + t2 = this._head; + split = t1.length - t2; + H.IterableMixinWorkaround_setRangeList(newTable, 0, split, t1, t2); + t1 = this._head; + t2 = this._table; + H.IterableMixinWorkaround_setRangeList(newTable, split, split + t1, t2, 0); + this._head = 0; + this._tail = this._table.length; + this._table = newTable; + }, + ListQueue$1: function(initialCapacity, $E) { + var t1 = Array(8); + t1.fixed$length = init; + this._table = H.setRuntimeTypeInfo(t1, [$E]); + }, + $isEfficientLength: true, + static: {"^": "ListQueue__INITIAL_CAPACITY"} + }, + _ListQueueIterator: { + "^": "Object;_queue,_end,_modificationCount,_collection$_position,_collection$_current", + get$current: function() { + return this._collection$_current; + }, + moveNext$0: function() { + var t1, t2, t3; + t1 = this._queue; + if (this._modificationCount !== t1._modificationCount) + H.throwExpression(P.ConcurrentModificationError$(t1)); + t2 = this._collection$_position; + if (t2 === this._end) { + this._collection$_current = null; + return false; + } + t1 = t1._table; + t3 = t1.length; + if (t2 >= t3) + return H.ioore(t1, t2); + this._collection$_current = t1[t2]; + this._collection$_position = (t2 + 1 & t3 - 1) >>> 0; + return true; + } + } +}], +["dart.convert", "dart:convert", , P, { + "^": "", + _convertJsonToDart: function(json, reviver) { + var revive = new P._convertJsonToDart_closure(); + return revive.call$2(null, new P._convertJsonToDart_walk(revive).call$1(json)); + }, + _parseJson: function(source, reviver) { + var parsed, e, t1, exception; + t1 = source; + if (typeof t1 !== "string") + throw H.wrapException(new P.ArgumentError(source)); + parsed = null; + try { + parsed = JSON.parse(source); + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + throw H.wrapException(P.FormatException$(String(e))); + } + + return P._convertJsonToDart(parsed, reviver); + }, + _defaultToEncodable: [function(object) { + return object.toJson$0(); + }, "call$1", "_defaultToEncodable$closure", 2, 0, 5], + _convertJsonToDart_closure: { + "^": "Closure:10;", + call$2: function(key, value) { + return value; + } + }, + _convertJsonToDart_walk: { + "^": "Closure:11;revive_0", + call$1: function(e) { + var list, t1, i, keys, map, key, proto; + if (e == null || typeof e != "object") + return e; + if (Object.getPrototypeOf(e) === Array.prototype) { + list = e; + for (t1 = this.revive_0, i = 0; i < list.length; ++i) + list[i] = t1.call$2(i, this.call$1(list[i])); + return list; + } + keys = Object.keys(e); + map = H.fillLiteralMap([], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null)); + for (t1 = this.revive_0, i = 0; i < keys.length; ++i) { + key = keys[i]; + map.$indexSet(0, key, t1.call$2(key, this.call$1(e[key]))); + } + proto = e.__proto__; + if (typeof proto !== "undefined" && proto !== Object.prototype) + map.$indexSet(0, "__proto__", t1.call$2("__proto__", this.call$1(proto))); + return map; + } + }, + Codec: { + "^": "Object;" + }, + Converter: { + "^": "Object;" + }, + JsonUnsupportedObjectError: { + "^": "Error;unsupportedObject,cause", + toString$0: function(_) { + if (this.cause != null) + return "Converting object to an encodable object failed."; + else + return "Converting object did not return an encodable object."; + }, + static: {JsonUnsupportedObjectError$: function(unsupportedObject, cause) { + return new P.JsonUnsupportedObjectError(unsupportedObject, cause); + }} + }, + JsonCyclicError: { + "^": "JsonUnsupportedObjectError;unsupportedObject,cause", + toString$0: function(_) { + return "Cyclic error in JSON stringify"; + }, + static: {JsonCyclicError$: function(object) { + return new P.JsonCyclicError(object, null); + }} + }, + JsonCodec: { + "^": "Codec;_reviver,_toEncodable", + decode$2$reviver: function(source, reviver) { + return P._parseJson(source, this.get$decoder()._reviver); + }, + decode$1: function(source) { + return this.decode$2$reviver(source, null); + }, + encode$2$toEncodable: function(value, toEncodable) { + return P._JsonStringifier_stringify(value, this.get$encoder()._toEncodableFunction); + }, + encode$1: function(value) { + return this.encode$2$toEncodable(value, null); + }, + get$encoder: function() { + return C.JsonEncoder_null; + }, + get$decoder: function() { + return C.JsonDecoder_null; + } + }, + JsonEncoder: { + "^": "Converter;_toEncodableFunction" + }, + JsonDecoder: { + "^": "Converter;_reviver" + }, + _JsonStringifier: { + "^": "Object;_toEncodable,_sink,_seen", + _toEncodable$1: function(arg0) { + return this._toEncodable.call$1(arg0); + }, + escape$1: function(s) { + var t1, $length, t2, offset, i, charCode, t3, charCodes, str; + t1 = J.getInterceptor$asx(s); + $length = t1.get$length(s); + if (typeof $length !== "number") + return H.iae($length); + t2 = this._sink; + offset = 0; + i = 0; + for (; i < $length; ++i) { + charCode = t1.codeUnitAt$1(s, i); + if (charCode > 92) + continue; + if (charCode < 32) { + if (i > offset) { + t3 = C.JSString_methods.substring$2(s, offset, i); + t2._contents = t2._contents + t3; + } + offset = i + 1; + charCodes = P.List_List$filled(1, 92, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + switch (charCode) { + case 8: + charCodes = P.List_List$filled(1, 98, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + case 9: + charCodes = P.List_List$filled(1, 116, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + case 10: + charCodes = P.List_List$filled(1, 110, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + case 12: + charCodes = P.List_List$filled(1, 102, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + case 13: + charCodes = P.List_List$filled(1, 114, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + default: + charCodes = P.List_List$filled(1, 117, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + charCodes = P.List_List$filled(1, 48, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + charCodes = P.List_List$filled(1, 48, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + t3 = charCode >>> 4 & 15; + t3 = t3 < 10 ? 48 + t3 : 87 + t3; + charCodes = P.List_List$filled(1, t3, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + t3 = charCode & 15; + t3 = t3 < 10 ? 48 + t3 : 87 + t3; + charCodes = P.List_List$filled(1, t3, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + } + } else if (charCode === 34 || charCode === 92) { + if (i > offset) { + t3 = C.JSString_methods.substring$2(s, offset, i); + t2._contents = t2._contents + t3; + } + offset = i + 1; + charCodes = P.List_List$filled(1, 92, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + charCodes = P.List_List$filled(1, charCode, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + } + } + if (offset === 0) { + str = typeof s === "string" ? s : H.S(s); + t2._contents = t2._contents + str; + } else if (offset < $length) { + t1 = t1.substring$2(s, offset, $length); + t2._contents = t2._contents + t1; + } + }, + checkCycle$1: function(object) { + var t1, t2, i, t3; + for (t1 = this._seen, t2 = t1.length, i = 0; i < t2; ++i) { + t3 = t1[i]; + if (object == null ? t3 == null : object === t3) + throw H.wrapException(P.JsonCyclicError$(object)); + } + t1.push(object); + }, + stringifyValue$1: function(object) { + var customJson, e, t1, exception; + if (!this.stringifyJsonValue$1(object)) { + this.checkCycle$1(object); + try { + customJson = this._toEncodable$1(object); + if (!this.stringifyJsonValue$1(customJson)) { + t1 = P.JsonUnsupportedObjectError$(object, null); + throw H.wrapException(t1); + } + t1 = this._seen; + if (0 >= t1.length) + return H.ioore(t1, 0); + t1.pop(); + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + throw H.wrapException(P.JsonUnsupportedObjectError$(object, e)); + } + + } + }, + stringifyJsonValue$1: function(object) { + var t1, t2, i, t3, separator, key; + if (typeof object === "number") { + if (!C.JSNumber_methods.get$isFinite(object)) + return false; + this._sink.write$1(C.JSNumber_methods.toString$0(object)); + return true; + } else if (object === true) { + this._sink.write$1("true"); + return true; + } else if (object === false) { + this._sink.write$1("false"); + return true; + } else if (object == null) { + this._sink.write$1("null"); + return true; + } else if (typeof object === "string") { + t1 = this._sink; + t1.write$1("\""); + this.escape$1(object); + t1.write$1("\""); + return true; + } else { + t1 = J.getInterceptor(object); + if (!!t1.$isList) { + this.checkCycle$1(object); + t2 = this._sink; + t2.write$1("["); + if (t1.get$length(object) > 0) { + this.stringifyValue$1(t1.$index(object, 0)); + for (i = 1; i < t1.get$length(object); ++i) { + t2._contents = t2._contents + ","; + this.stringifyValue$1(t1.$index(object, i)); + } + } + t2.write$1("]"); + this._removeSeen$1(object); + return true; + } else if (!!t1.$isMap) { + this.checkCycle$1(object); + t2 = this._sink; + t2.write$1("{"); + for (t3 = J.get$iterator$ax(t1.get$keys(object)), separator = "\""; t3.moveNext$0(); separator = ",\"") { + key = t3.get$current(); + t2._contents = t2._contents + separator; + this.escape$1(key); + t2._contents = t2._contents + "\":"; + this.stringifyValue$1(t1.$index(object, key)); + } + t2.write$1("}"); + this._removeSeen$1(object); + return true; + } else + return false; + } + }, + _removeSeen$1: function(object) { + var t1 = this._seen; + if (0 >= t1.length) + return H.ioore(t1, 0); + t1.pop(); + }, + static: {"^": "_JsonStringifier_BACKSPACE,_JsonStringifier_TAB,_JsonStringifier_NEWLINE,_JsonStringifier_CARRIAGE_RETURN,_JsonStringifier_FORM_FEED,_JsonStringifier_QUOTE,_JsonStringifier_CHAR_0,_JsonStringifier_BACKSLASH,_JsonStringifier_CHAR_b,_JsonStringifier_CHAR_f,_JsonStringifier_CHAR_n,_JsonStringifier_CHAR_r,_JsonStringifier_CHAR_t,_JsonStringifier_CHAR_u", _JsonStringifier_stringify: function(object, toEncodable) { + var output; + toEncodable = P._defaultToEncodable$closure(); + output = P.StringBuffer$(""); + new P._JsonStringifier(toEncodable, output, []).stringifyValue$1(object); + return output._contents; + }} + } +}], +["dart.core", "dart:core", , P, { + "^": "", + _symbolToString: function(symbol) { + return H.Symbol_getName(symbol); + }, + Error_safeToString: function(object) { + var buffer, t1, i, t2, codeUnit, charCodes; + if (typeof object === "number" || typeof object === "boolean" || null == object) + return J.toString$0(object); + if (typeof object === "string") { + buffer = new P.StringBuffer(""); + buffer._contents = "\""; + for (t1 = object.length, i = 0, t2 = "\""; i < t1; ++i) { + codeUnit = C.JSString_methods.codeUnitAt$1(object, i); + if (codeUnit <= 31) + if (codeUnit === 10) { + t2 = buffer._contents + "\\n"; + buffer._contents = t2; + } else if (codeUnit === 13) { + t2 = buffer._contents + "\\r"; + buffer._contents = t2; + } else if (codeUnit === 9) { + t2 = buffer._contents + "\\t"; + buffer._contents = t2; + } else { + t2 = buffer._contents + "\\x"; + buffer._contents = t2; + if (codeUnit < 16) + buffer._contents = t2 + "0"; + else { + buffer._contents = t2 + "1"; + codeUnit -= 16; + } + t2 = codeUnit < 10 ? 48 + codeUnit : 87 + codeUnit; + charCodes = P.List_List$filled(1, t2, J.JSInt); + t2 = H.Primitives_stringFromCharCodes(charCodes); + t2 = buffer._contents + t2; + buffer._contents = t2; + } + else if (codeUnit === 92) { + t2 = buffer._contents + "\\\\"; + buffer._contents = t2; + } else if (codeUnit === 34) { + t2 = buffer._contents + "\\\""; + buffer._contents = t2; + } else { + charCodes = P.List_List$filled(1, codeUnit, J.JSInt); + t2 = H.Primitives_stringFromCharCodes(charCodes); + t2 = buffer._contents + t2; + buffer._contents = t2; + } + } + t1 = t2 + "\""; + buffer._contents = t1; + return t1; + } + return "Instance of '" + H.Primitives_objectTypeName(object) + "'"; + }, + Exception_Exception: function(message) { + return new P._ExceptionImplementation(message); + }, + identical: [function(a, b) { + return a == null ? b == null : a === b; + }, "call$2", "identical$closure", 4, 0, 6], + identityHashCode: [function(object) { + return H.objectHashCode(object); + }, "call$1", "identityHashCode$closure", 2, 0, 7], + List_List$filled: function($length, fill, $E) { + var result, t1, i; + result = J.JSArray_JSArray$fixed($length, $E); + if ($length !== 0 && true) + for (t1 = result.length, i = 0; i < t1; ++i) + result[i] = fill; + return result; + }, + List_List$from: function(other, growable, $E) { + var list, t1; + list = H.setRuntimeTypeInfo([], [$E]); + for (t1 = J.get$iterator$ax(other); t1.moveNext$0();) + list.push(t1.get$current()); + if (growable) + return list; + list.fixed$length = init; + return list; + }, + print: function(object) { + var line = H.S(object); + H.printString(line); + }, + NoSuchMethodError_toString_closure: { + "^": "Closure:18;box_0", + call$2: function(key, value) { + var t1 = this.box_0; + if (t1.i_1 > 0) + t1.sb_0.write$1(", "); + t1.sb_0.write$1(P._symbolToString(key)); + } + }, + DateTime: { + "^": "Object;millisecondsSinceEpoch,isUtc", + $eq: function(_, other) { + if (other == null) + return false; + if (!J.getInterceptor(other).$isDateTime) + return false; + return this.millisecondsSinceEpoch === other.millisecondsSinceEpoch && this.isUtc === other.isUtc; + }, + get$hashCode: function(_) { + return this.millisecondsSinceEpoch; + }, + toString$0: function(_) { + var t1, y, m, d, h, min, sec, ms; + t1 = this.isUtc; + y = P.DateTime__fourDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCFullYear() + 0 : H.Primitives_lazyAsJsDate(this).getFullYear() + 0); + m = P.DateTime__twoDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCMonth() + 1 : H.Primitives_lazyAsJsDate(this).getMonth() + 1); + d = P.DateTime__twoDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCDate() + 0 : H.Primitives_lazyAsJsDate(this).getDate() + 0); + h = P.DateTime__twoDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCHours() + 0 : H.Primitives_lazyAsJsDate(this).getHours() + 0); + min = P.DateTime__twoDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCMinutes() + 0 : H.Primitives_lazyAsJsDate(this).getMinutes() + 0); + sec = P.DateTime__twoDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCSeconds() + 0 : H.Primitives_lazyAsJsDate(this).getSeconds() + 0); + ms = P.DateTime__threeDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCMilliseconds() + 0 : H.Primitives_lazyAsJsDate(this).getMilliseconds() + 0); + if (t1) + return y + "-" + m + "-" + d + " " + h + ":" + min + ":" + sec + "." + ms + "Z"; + else + return y + "-" + m + "-" + d + " " + h + ":" + min + ":" + sec + "." + ms; + }, + DateTime$fromMillisecondsSinceEpoch$2$isUtc: function(millisecondsSinceEpoch, isUtc) { + if (Math.abs(millisecondsSinceEpoch) > 8640000000000000) + throw H.wrapException(new P.ArgumentError(millisecondsSinceEpoch)); + }, + DateTime$_now$0: function() { + H.Primitives_lazyAsJsDate(this); + }, + $isDateTime: true, + static: {"^": "DateTime_MONDAY,DateTime_TUESDAY,DateTime_WEDNESDAY,DateTime_THURSDAY,DateTime_FRIDAY,DateTime_SATURDAY,DateTime_SUNDAY,DateTime_DAYS_PER_WEEK,DateTime_JANUARY,DateTime_FEBRUARY,DateTime_MARCH,DateTime_APRIL,DateTime_MAY,DateTime_JUNE,DateTime_JULY,DateTime_AUGUST,DateTime_SEPTEMBER,DateTime_OCTOBER,DateTime_NOVEMBER,DateTime_DECEMBER,DateTime_MONTHS_PER_YEAR,DateTime__MAX_MILLISECONDS_SINCE_EPOCH", DateTime_parse: function(formattedString) { + var match, t1, t2, years, month, day, hour, minute, second, millisecond, addOneMillisecond, t3, sign, hourDifference, minuteDifference, isUtc, millisecondsSinceEpoch; + match = new H.JSSyntaxRegExp(H.JSSyntaxRegExp_makeNative("^([+-]?\\d{4,5})-?(\\d\\d)-?(\\d\\d)(?:[ T](\\d\\d)(?::?(\\d\\d)(?::?(\\d\\d)(.\\d{1,6})?)?)?( ?[zZ]| ?([-+])(\\d\\d)(?::?(\\d\\d))?)?)?$", false, true, false), null, null).firstMatch$1(formattedString); + if (match != null) { + t1 = new P.DateTime_parse_parseIntOrZero(); + t2 = match._match; + if (1 >= t2.length) + return H.ioore(t2, 1); + years = H.Primitives_parseInt(t2[1], null, null); + if (2 >= t2.length) + return H.ioore(t2, 2); + month = H.Primitives_parseInt(t2[2], null, null); + if (3 >= t2.length) + return H.ioore(t2, 3); + day = H.Primitives_parseInt(t2[3], null, null); + if (4 >= t2.length) + return H.ioore(t2, 4); + hour = t1.call$1(t2[4]); + if (5 >= t2.length) + return H.ioore(t2, 5); + minute = t1.call$1(t2[5]); + if (6 >= t2.length) + return H.ioore(t2, 6); + second = t1.call$1(t2[6]); + if (7 >= t2.length) + return H.ioore(t2, 7); + millisecond = J.round$0$n(J.$mul$ns(new P.DateTime_parse_parseDoubleOrZero().call$1(t2[7]), 1000)); + if (millisecond === 1000) { + addOneMillisecond = true; + millisecond = 999; + } else + addOneMillisecond = false; + t3 = t2.length; + if (8 >= t3) + return H.ioore(t2, 8); + if (t2[8] != null) { + if (9 >= t3) + return H.ioore(t2, 9); + t3 = t2[9]; + if (t3 != null) { + sign = J.$eq(t3, "-") ? -1 : 1; + if (10 >= t2.length) + return H.ioore(t2, 10); + hourDifference = H.Primitives_parseInt(t2[10], null, null); + if (11 >= t2.length) + return H.ioore(t2, 11); + minuteDifference = t1.call$1(t2[11]); + if (typeof hourDifference !== "number") + return H.iae(hourDifference); + minuteDifference = J.$add$ns(minuteDifference, 60 * hourDifference); + if (typeof minuteDifference !== "number") + return H.iae(minuteDifference); + minute = J.$sub$n(minute, sign * minuteDifference); + } + isUtc = true; + } else + isUtc = false; + millisecondsSinceEpoch = H.Primitives_valueFromDecomposedDate(years, month, day, hour, minute, second, millisecond, isUtc); + return P.DateTime$fromMillisecondsSinceEpoch(addOneMillisecond ? millisecondsSinceEpoch + 1 : millisecondsSinceEpoch, isUtc); + } else + throw H.wrapException(P.FormatException$(formattedString)); + }, DateTime$fromMillisecondsSinceEpoch: function(millisecondsSinceEpoch, isUtc) { + var t1 = new P.DateTime(millisecondsSinceEpoch, isUtc); + t1.DateTime$fromMillisecondsSinceEpoch$2$isUtc(millisecondsSinceEpoch, isUtc); + return t1; + }, DateTime__fourDigits: function(n) { + var absN, sign; + absN = Math.abs(n); + sign = n < 0 ? "-" : ""; + if (absN >= 1000) + return "" + n; + if (absN >= 100) + return sign + "0" + H.S(absN); + if (absN >= 10) + return sign + "00" + H.S(absN); + return sign + "000" + H.S(absN); + }, DateTime__threeDigits: function(n) { + if (n >= 100) + return "" + n; + if (n >= 10) + return "0" + n; + return "00" + n; + }, DateTime__twoDigits: function(n) { + if (n >= 10) + return "" + n; + return "0" + n; + }} + }, + DateTime_parse_parseIntOrZero: { + "^": "Closure:19;", + call$1: function(matched) { + if (matched == null) + return 0; + return H.Primitives_parseInt(matched, null, null); + } + }, + DateTime_parse_parseDoubleOrZero: { + "^": "Closure:20;", + call$1: function(matched) { + if (matched == null) + return 0; + return H.Primitives_parseDouble(matched, null); + } + }, + Duration: { + "^": "Object;_duration<", + $add: function(_, other) { + return P.Duration$(0, 0, this._duration + other.get$_duration(), 0, 0, 0); + }, + $sub: function(_, other) { + return P.Duration$(0, 0, C.JSInt_methods.$sub(this._duration, other.get$_duration()), 0, 0, 0); + }, + $mul: function(_, factor) { + return P.Duration$(0, 0, C.JSNumber_methods.toInt$0(C.JSInt_methods.roundToDouble$0(this._duration * factor)), 0, 0, 0); + }, + $lt: function(_, other) { + return C.JSInt_methods.$lt(this._duration, other.get$_duration()); + }, + $le: function(_, other) { + return C.JSInt_methods.$le(this._duration, other.get$_duration()); + }, + $ge: function(_, other) { + return C.JSInt_methods.$ge(this._duration, other.get$_duration()); + }, + $eq: function(_, other) { + if (other == null) + return false; + if (!J.getInterceptor(other).$isDuration) + return false; + return this._duration === other._duration; + }, + get$hashCode: function(_) { + return this._duration & 0x1FFFFFFF; + }, + toString$0: function(_) { + var t1, t2, twoDigitMinutes, twoDigitSeconds, sixDigitUs; + t1 = new P.Duration_toString_twoDigits(); + t2 = this._duration; + if (t2 < 0) + return "-" + H.S(P.Duration$(0, 0, -t2, 0, 0, 0)); + twoDigitMinutes = t1.call$1(C.JSInt_methods.remainder$1(C.JSInt_methods._tdivFast$1(t2, 60000000), 60)); + twoDigitSeconds = t1.call$1(C.JSInt_methods.remainder$1(C.JSInt_methods._tdivFast$1(t2, 1000000), 60)); + sixDigitUs = new P.Duration_toString_sixDigits().call$1(C.JSInt_methods.remainder$1(t2, 1000000)); + return "" + C.JSInt_methods._tdivFast$1(t2, 3600000000) + ":" + H.S(twoDigitMinutes) + ":" + H.S(twoDigitSeconds) + "." + H.S(sixDigitUs); + }, + $isDuration: true, + static: {"^": "Duration_MICROSECONDS_PER_MILLISECOND,Duration_MILLISECONDS_PER_SECOND,Duration_SECONDS_PER_MINUTE,Duration_MINUTES_PER_HOUR,Duration_HOURS_PER_DAY,Duration_MICROSECONDS_PER_SECOND,Duration_MICROSECONDS_PER_MINUTE,Duration_MICROSECONDS_PER_HOUR,Duration_MICROSECONDS_PER_DAY,Duration_MILLISECONDS_PER_MINUTE,Duration_MILLISECONDS_PER_HOUR,Duration_MILLISECONDS_PER_DAY,Duration_SECONDS_PER_HOUR,Duration_SECONDS_PER_DAY,Duration_MINUTES_PER_DAY,Duration_ZERO", Duration$: function(days, hours, microseconds, milliseconds, minutes, seconds) { + return new P.Duration(days * 86400000000 + hours * 3600000000 + minutes * 60000000 + seconds * 1000000 + milliseconds * 1000 + microseconds); + }} + }, + Duration_toString_sixDigits: { + "^": "Closure:21;", + call$1: function(n) { + if (n >= 100000) + return "" + n; + if (n >= 10000) + return "0" + n; + if (n >= 1000) + return "00" + n; + if (n >= 100) + return "000" + n; + if (n >= 10) + return "0000" + n; + return "00000" + n; + } + }, + Duration_toString_twoDigits: { + "^": "Closure:21;", + call$1: function(n) { + if (n >= 10) + return "" + n; + return "0" + n; + } + }, + Error: { + "^": "Object;", + get$stackTrace: function() { + return new H._StackTrace(this.$thrownJsError, null); + }, + $isError: true + }, + NullThrownError: { + "^": "Error;", + toString$0: function(_) { + return "Throw of null."; + } + }, + ArgumentError: { + "^": "Error;message", + toString$0: function(_) { + var t1 = this.message; + if (t1 != null) + return "Illegal argument(s): " + H.S(t1); + return "Illegal argument(s)"; + }, + static: {ArgumentError$: function(message) { + return new P.ArgumentError(message); + }} + }, + RangeError: { + "^": "ArgumentError;message", + toString$0: function(_) { + return "RangeError: " + H.S(this.message); + }, + static: {RangeError$: function(message) { + return new P.RangeError(message); + }, RangeError$value: function(value) { + return new P.RangeError("value " + H.S(value)); + }, RangeError$range: function(value, start, end) { + return new P.RangeError("value " + H.S(value) + " not in range " + start + ".." + H.S(end)); + }} + }, + UnsupportedError: { + "^": "Error;message", + toString$0: function(_) { + return "Unsupported operation: " + this.message; + }, + static: {UnsupportedError$: function(message) { + return new P.UnsupportedError(message); + }} + }, + UnimplementedError: { + "^": "Error;message", + toString$0: function(_) { + var t1 = this.message; + return t1 != null ? "UnimplementedError: " + H.S(t1) : "UnimplementedError"; + }, + $isError: true, + static: {UnimplementedError$: function(message) { + return new P.UnimplementedError(message); + }} + }, + StateError: { + "^": "Error;message", + toString$0: function(_) { + return "Bad state: " + this.message; + }, + static: {StateError$: function(message) { + return new P.StateError(message); + }} + }, + ConcurrentModificationError: { + "^": "Error;modifiedObject", + toString$0: function(_) { + var t1 = this.modifiedObject; + if (t1 == null) + return "Concurrent modification during iteration."; + return "Concurrent modification during iteration: " + H.S(P.Error_safeToString(t1)) + "."; + }, + static: {ConcurrentModificationError$: function(modifiedObject) { + return new P.ConcurrentModificationError(modifiedObject); + }} + }, + OutOfMemoryError: { + "^": "Object;", + toString$0: function(_) { + return "Out of Memory"; + }, + get$stackTrace: function() { + return; + }, + $isError: true + }, + StackOverflowError: { + "^": "Object;", + toString$0: function(_) { + return "Stack Overflow"; + }, + get$stackTrace: function() { + return; + }, + $isError: true + }, + CyclicInitializationError: { + "^": "Error;variableName", + toString$0: function(_) { + return "Reading static variable '" + this.variableName + "' during its initialization"; + }, + static: {CyclicInitializationError$: function(variableName) { + return new P.CyclicInitializationError(variableName); + }} + }, + _ExceptionImplementation: { + "^": "Object;message", + toString$0: function(_) { + var t1 = this.message; + if (t1 == null) + return "Exception"; + return "Exception: " + H.S(t1); + } + }, + FormatException: { + "^": "Object;message", + toString$0: function(_) { + return "FormatException: " + H.S(this.message); + }, + $isFormatException: true, + static: {FormatException$: function(message) { + return new P.FormatException(message); + }} + }, + Expando: { + "^": "Object;name", + toString$0: function(_) { + return "Expando:" + H.S(this.name); + }, + $index: function(_, object) { + var values = H.Primitives_getProperty(object, "expando$values"); + return values == null ? null : H.Primitives_getProperty(values, this._getKey$0()); + }, + $indexSet: function(_, object, value) { + var values = H.Primitives_getProperty(object, "expando$values"); + if (values == null) { + values = new P.Object(); + H.Primitives_setProperty(object, "expando$values", values); + } + H.Primitives_setProperty(values, this._getKey$0(), value); + }, + _getKey$0: function() { + var key, t1; + key = H.Primitives_getProperty(this, "expando$key"); + if (key == null) { + t1 = $.Expando__keyCount; + $.Expando__keyCount = t1 + 1; + key = "expando$key$" + t1; + H.Primitives_setProperty(this, "expando$key", key); + } + return key; + }, + static: {"^": "Expando__KEY_PROPERTY_NAME,Expando__EXPANDO_PROPERTY_NAME,Expando__keyCount"} + }, + Iterator: { + "^": "Object;" + }, + Null: { + "^": "Object;", + toString$0: function(_) { + return "null"; + } + }, + Object: { + "^": ";", + $eq: function(_, other) { + return this === other; + }, + get$hashCode: function(_) { + return H.Primitives_objectHashCode(this); + }, + toString$0: function(_) { + return H.Primitives_objectToString(this); + } + }, + StackTrace: { + "^": "Object;" + }, + StringBuffer: { + "^": "Object;_contents<", + get$length: function(_) { + return this._contents.length; + }, + get$isEmpty: function(_) { + return this._contents.length === 0; + }, + write$1: function(obj) { + var str = typeof obj === "string" ? obj : H.S(obj); + this._contents = this._contents + str; + }, + writeAll$2: function(objects, separator) { + var iterator, str; + iterator = J.get$iterator$ax(objects); + if (!iterator.moveNext$0()) + return; + if (separator.length === 0) + do { + str = iterator.get$current(); + str = typeof str === "string" ? str : H.S(str); + this._contents = this._contents + str; + } while (iterator.moveNext$0()); + else { + this.write$1(iterator.get$current()); + for (; iterator.moveNext$0();) { + this._contents = this._contents + separator; + str = iterator.get$current(); + str = typeof str === "string" ? str : H.S(str); + this._contents = this._contents + str; + } + } + }, + toString$0: function(_) { + return this._contents; + }, + StringBuffer$1: function($content) { + this._contents = $content; + }, + static: {StringBuffer$: function($content) { + var t1 = new P.StringBuffer(""); + t1.StringBuffer$1($content); + return t1; + }} + }, + Symbol: { + "^": "Object;" + } +}], +["dart.dom.html", "dart:html", , W, { + "^": "", + Element_Element$html: function(html, treeSanitizer, validator) { + var fragment, t1; + fragment = J.createFragment$3$treeSanitizer$validator$x(document.body, html, treeSanitizer, validator); + fragment.toString; + t1 = new W._ChildNodeListLazy(fragment); + t1 = t1.where$1(t1, new W.Element_Element$html_closure()); + return t1.get$single(t1); + }, + Window__isDartLocation: function(thing) { + var exception; + try { + return !!J.getInterceptor(thing).$isLocation; + } catch (exception) { + H.unwrapException(exception); + return false; + } + + }, + _wrapZone: function(callback) { + var t1 = $.Zone__current; + if (t1 === C.C__RootZone) + return callback; + return t1.bindUnaryCallback$2$runGuarded(callback, true); + }, + HtmlElement: { + "^": "Element;", + "%": "HTMLAppletElement|HTMLBRElement|HTMLCanvasElement|HTMLContentElement|HTMLDListElement|HTMLDataListElement|HTMLDetailsElement|HTMLDialogElement|HTMLDirectoryElement|HTMLDivElement|HTMLFontElement|HTMLFrameElement|HTMLFrameSetElement|HTMLHRElement|HTMLHeadElement|HTMLHeadingElement|HTMLHtmlElement|HTMLImageElement|HTMLLabelElement|HTMLLegendElement|HTMLMarqueeElement|HTMLMenuElement|HTMLModElement|HTMLOListElement|HTMLParagraphElement|HTMLPreElement|HTMLQuoteElement|HTMLScriptElement|HTMLShadowElement|HTMLSourceElement|HTMLSpanElement|HTMLTableCaptionElement|HTMLTableCellElement|HTMLTableColElement|HTMLTableDataCellElement|HTMLTableHeaderCellElement|HTMLTitleElement|HTMLTrackElement|HTMLUListElement|HTMLUnknownElement;HTMLElement" + }, + AnchorElement: { + "^": "HtmlElement;hostname=,href},port=,protocol=", + toString$0: function(receiver) { + return receiver.toString(); + }, + "%": "HTMLAnchorElement" + }, + AreaElement: { + "^": "HtmlElement;hostname=,href},port=,protocol=", + toString$0: function(receiver) { + return receiver.toString(); + }, + "%": "HTMLAreaElement" + }, + BaseElement: { + "^": "HtmlElement;href}", + "%": "HTMLBaseElement" + }, + BodyElement: { + "^": "HtmlElement;", + get$onBlur: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_blur._eventType, false), [null]); + }, + $isBodyElement: true, + "%": "HTMLBodyElement" + }, + ButtonElement: { + "^": "HtmlElement;disabled},name=,value%", + "%": "HTMLButtonElement" + }, + CharacterData: { + "^": "Node;length=", + "%": "CDATASection|CharacterData|Comment|ProcessingInstruction|Text" + }, + DomException: { + "^": "Interceptor;", + toString$0: function(receiver) { + return receiver.toString(); + }, + "%": "DOMException" + }, + Element: { + "^": "Node;", + get$attributes: function(receiver) { + return new W._ElementAttributeMap(receiver); + }, + toString$0: function(receiver) { + return receiver.localName; + }, + createFragment$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + var t1, t2, base, contextElement, fragment; + if (treeSanitizer == null) { + t1 = $.Element__defaultValidator; + if (t1 == null) { + t1 = H.setRuntimeTypeInfo([], [W.NodeValidator]); + t2 = new W.NodeValidatorBuilder(t1); + t1.push(W._Html5NodeValidator$(null)); + t1.push(W._TemplatingNodeValidator$()); + $.Element__defaultValidator = t2; + validator = t2; + } else + validator = t1; + t1 = $.Element__defaultSanitizer; + if (t1 == null) { + t1 = new W._ValidatingTreeSanitizer(validator); + $.Element__defaultSanitizer = t1; + treeSanitizer = t1; + } else { + t1.validator = validator; + treeSanitizer = t1; + } + } + if ($.Element__parseDocument == null) { + t1 = document.implementation.createHTMLDocument(""); + $.Element__parseDocument = t1; + $.Element__parseRange = t1.createRange(); + base = $.Element__parseDocument.createElement("base", null); + J.set$href$x(base, document.baseURI); + $.Element__parseDocument.head.appendChild(base); + } + t1 = $.Element__parseDocument; + if (!!this.$isBodyElement) + contextElement = t1.body; + else { + contextElement = t1.createElement(receiver.tagName, null); + $.Element__parseDocument.body.appendChild(contextElement); + } + if ("createContextualFragment" in window.Range.prototype) { + $.Element__parseRange.selectNodeContents(contextElement); + fragment = $.Element__parseRange.createContextualFragment(html); + } else { + contextElement.innerHTML = html; + fragment = $.Element__parseDocument.createDocumentFragment(); + for (; t1 = contextElement.firstChild, t1 != null;) + fragment.appendChild(t1); + } + t1 = $.Element__parseDocument.body; + if (contextElement == null ? t1 != null : contextElement !== t1) + J.remove$0$ax(contextElement); + treeSanitizer.sanitizeTree$1(fragment); + document.adoptNode(fragment); + return fragment; + }, + createFragment$2$treeSanitizer: function($receiver, html, treeSanitizer) { + return this.createFragment$3$treeSanitizer$validator($receiver, html, treeSanitizer, null); + }, + set$innerHtml: function(receiver, html) { + this.setInnerHtml$1(receiver, html); + }, + setInnerHtml$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + receiver.textContent = null; + receiver.appendChild(this.createFragment$3$treeSanitizer$validator(receiver, html, treeSanitizer, validator)); + }, + setInnerHtml$1: function($receiver, html) { + return this.setInnerHtml$3$treeSanitizer$validator($receiver, html, null, null); + }, + get$onBlur: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_blur._eventType, false), [null]); + }, + get$onChange: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_change._eventType, false), [null]); + }, + get$onClick: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_click._eventType, false), [null]); + }, + get$onInput: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_input._eventType, false), [null]); + }, + $isElement: true, + "%": ";Element" + }, + EmbedElement: { + "^": "HtmlElement;name=", + "%": "HTMLEmbedElement" + }, + ErrorEvent: { + "^": "Event;error=", + "%": "ErrorEvent" + }, + Event: { + "^": "Interceptor;", + preventDefault$0: function(receiver) { + return receiver.preventDefault(); + }, + "%": "AudioProcessingEvent|AutocompleteErrorEvent|BeforeLoadEvent|BeforeUnloadEvent|CSSFontFaceLoadEvent|CloseEvent|CustomEvent|DeviceMotionEvent|DeviceOrientationEvent|HashChangeEvent|IDBVersionChangeEvent|MIDIConnectionEvent|MIDIMessageEvent|MediaKeyEvent|MediaKeyMessageEvent|MediaKeyNeededEvent|MediaStreamEvent|MediaStreamTrackEvent|MessageEvent|MutationEvent|OfflineAudioCompletionEvent|OverflowEvent|PageTransitionEvent|PopStateEvent|ProgressEvent|RTCDTMFToneChangeEvent|RTCDataChannelEvent|RTCIceCandidateEvent|ResourceProgressEvent|SecurityPolicyViolationEvent|SpeechInputEvent|SpeechRecognitionEvent|SpeechSynthesisEvent|StorageEvent|TrackEvent|TransitionEvent|WebGLContextEvent|WebKitAnimationEvent|WebKitTransitionEvent|XMLHttpRequestProgressEvent;Event" + }, + EventTarget: { + "^": "Interceptor;", + addEventListener$3: function(receiver, type, listener, useCapture) { + return receiver.addEventListener(type, H.convertDartClosureToJS(listener, 1), useCapture); + }, + removeEventListener$3: function(receiver, type, listener, useCapture) { + return receiver.removeEventListener(type, H.convertDartClosureToJS(listener, 1), useCapture); + }, + "%": ";EventTarget" + }, + FieldSetElement: { + "^": "HtmlElement;disabled},name=", + "%": "HTMLFieldSetElement" + }, + FormElement: { + "^": "HtmlElement;length=,name=", + "%": "HTMLFormElement" + }, + IFrameElement: { + "^": "HtmlElement;name=", + "%": "HTMLIFrameElement" + }, + InputElement: { + "^": "HtmlElement;disabled},name=,value%", + $isElement: true, + "%": "HTMLInputElement" + }, + KeygenElement: { + "^": "HtmlElement;disabled},name=", + "%": "HTMLKeygenElement" + }, + LIElement: { + "^": "HtmlElement;value%", + "%": "HTMLLIElement" + }, + LinkElement: { + "^": "HtmlElement;disabled},href}", + "%": "HTMLLinkElement" + }, + Location: { + "^": "Interceptor;hostname=,port=,protocol=", + toString$0: function(receiver) { + return receiver.toString(); + }, + $isLocation: true, + "%": "Location" + }, + MapElement: { + "^": "HtmlElement;name=", + "%": "HTMLMapElement" + }, + MediaElement: { + "^": "HtmlElement;error=", + "%": "HTMLAudioElement|HTMLMediaElement|HTMLVideoElement" + }, + MetaElement: { + "^": "HtmlElement;name=", + "%": "HTMLMetaElement" + }, + MeterElement: { + "^": "HtmlElement;value%", + "%": "HTMLMeterElement" + }, + MidiOutput: { + "^": "MidiPort;", + send$2: function(receiver, data, timestamp) { + return receiver.send(data, timestamp); + }, + send$1: function($receiver, data) { + return $receiver.send(data); + }, + "%": "MIDIOutput" + }, + MidiPort: { + "^": "EventTarget;", + "%": "MIDIInput;MIDIPort" + }, + MouseEvent: { + "^": "UIEvent;", + "%": "DragEvent|MSPointerEvent|MouseEvent|MouseScrollEvent|MouseWheelEvent|PointerEvent|WheelEvent" + }, + Node: { + "^": "EventTarget;", + get$nodes: function(receiver) { + return new W._ChildNodeListLazy(receiver); + }, + remove$0: function(receiver) { + var t1 = receiver.parentNode; + if (t1 != null) + t1.removeChild(receiver); + }, + toString$0: function(receiver) { + var t1 = receiver.nodeValue; + return t1 == null ? J.Interceptor.prototype.toString$0.call(this, receiver) : t1; + }, + "%": "Document|DocumentFragment|DocumentType|HTMLDocument|Notation|ShadowRoot|XMLDocument;Node" + }, + NodeList: { + "^": "Interceptor_ListMixin_ImmutableListMixin;", + get$length: function(receiver) { + return receiver.length; + }, + $index: function(receiver, index) { + var t1 = receiver.length; + if (index >>> 0 !== index || index >= t1) + throw H.wrapException(P.RangeError$range(index, 0, t1)); + return receiver[index]; + }, + $indexSet: function(receiver, index, value) { + throw H.wrapException(P.UnsupportedError$("Cannot assign element of immutable List.")); + }, + elementAt$1: function(receiver, index) { + if (index < 0 || index >= receiver.length) + return H.ioore(receiver, index); + return receiver[index]; + }, + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true, + $isJavaScriptIndexingBehavior: true, + "%": "NodeList|RadioNodeList" + }, + ObjectElement: { + "^": "HtmlElement;name=", + "%": "HTMLObjectElement" + }, + OptGroupElement: { + "^": "HtmlElement;disabled}", + "%": "HTMLOptGroupElement" + }, + OptionElement: { + "^": "HtmlElement;disabled},value%", + "%": "HTMLOptionElement" + }, + OutputElement: { + "^": "HtmlElement;name=,value%", + "%": "HTMLOutputElement" + }, + ParamElement: { + "^": "HtmlElement;name=,value%", + "%": "HTMLParamElement" + }, + ProgressElement: { + "^": "HtmlElement;value%", + "%": "HTMLProgressElement" + }, + Range: { + "^": "Interceptor;", + toString$0: function(receiver) { + return receiver.toString(); + }, + "%": "Range" + }, + SelectElement: { + "^": "HtmlElement;disabled},length=,name=,value%", + "%": "HTMLSelectElement" + }, + SpeechRecognitionError: { + "^": "Event;error=", + "%": "SpeechRecognitionError" + }, + Storage: { + "^": "Interceptor;", + $index: function(receiver, key) { + return receiver.getItem(key); + }, + $indexSet: function(receiver, key, value) { + receiver.setItem(key, value); + }, + forEach$1: function(receiver, f) { + var i, key; + for (i = 0; true; ++i) { + key = receiver.key(i); + if (key == null) + return; + f.call$2(key, receiver.getItem(key)); + } + }, + get$keys: function(receiver) { + var keys = []; + this.forEach$1(receiver, new W.Storage_keys_closure(keys)); + return keys; + }, + get$values: function(receiver) { + var values = []; + this.forEach$1(receiver, new W.Storage_values_closure(values)); + return values; + }, + get$length: function(receiver) { + return receiver.length; + }, + get$isEmpty: function(receiver) { + return receiver.key(0) == null; + }, + $isMap: true, + $asMap: function() { + return [J.JSString, J.JSString]; + }, + "%": "Storage" + }, + StyleElement: { + "^": "HtmlElement;disabled}", + "%": "HTMLStyleElement" + }, + TableElement: { + "^": "HtmlElement;", + createFragment$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + var table, fragment; + if ("createContextualFragment" in window.Range.prototype) + return W.Element.prototype.createFragment$3$treeSanitizer$validator.call(this, receiver, html, treeSanitizer, validator); + table = W.Element_Element$html("" + html + "
", treeSanitizer, validator); + fragment = document.createDocumentFragment(); + fragment.toString; + new W._ChildNodeListLazy(fragment).addAll$1(0, J.get$nodes$x(table)); + return fragment; + }, + "%": "HTMLTableElement" + }, + TableRowElement: { + "^": "HtmlElement;", + createFragment$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + var fragment, t1, section, row; + if ("createContextualFragment" in window.Range.prototype) + return W.Element.prototype.createFragment$3$treeSanitizer$validator.call(this, receiver, html, treeSanitizer, validator); + fragment = document.createDocumentFragment(); + t1 = J.createFragment$3$treeSanitizer$validator$x(document.createElement("table", null), html, treeSanitizer, validator); + t1.toString; + t1 = new W._ChildNodeListLazy(t1); + section = t1.get$single(t1); + section.toString; + t1 = new W._ChildNodeListLazy(section); + row = t1.get$single(t1); + fragment.toString; + row.toString; + new W._ChildNodeListLazy(fragment).addAll$1(0, new W._ChildNodeListLazy(row)); + return fragment; + }, + "%": "HTMLTableRowElement" + }, + TableSectionElement: { + "^": "HtmlElement;", + createFragment$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + var fragment, t1, section; + if ("createContextualFragment" in window.Range.prototype) + return W.Element.prototype.createFragment$3$treeSanitizer$validator.call(this, receiver, html, treeSanitizer, validator); + fragment = document.createDocumentFragment(); + t1 = J.createFragment$3$treeSanitizer$validator$x(document.createElement("table", null), html, treeSanitizer, validator); + t1.toString; + t1 = new W._ChildNodeListLazy(t1); + section = t1.get$single(t1); + fragment.toString; + section.toString; + new W._ChildNodeListLazy(fragment).addAll$1(0, new W._ChildNodeListLazy(section)); + return fragment; + }, + "%": "HTMLTableSectionElement" + }, + TemplateElement: { + "^": "HtmlElement;", + setInnerHtml$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + var fragment; + receiver.textContent = null; + fragment = this.createFragment$3$treeSanitizer$validator(receiver, html, treeSanitizer, validator); + receiver.content.appendChild(fragment); + }, + setInnerHtml$1: function($receiver, html) { + return this.setInnerHtml$3$treeSanitizer$validator($receiver, html, null, null); + }, + $isTemplateElement: true, + "%": "HTMLTemplateElement" + }, + TextAreaElement: { + "^": "HtmlElement;disabled},name=,value%", + "%": "HTMLTextAreaElement" + }, + UIEvent: { + "^": "Event;", + "%": "CompositionEvent|FocusEvent|KeyboardEvent|SVGZoomEvent|TextEvent|TouchEvent;UIEvent" + }, + Window: { + "^": "EventTarget;", + get$location: function(receiver) { + var result = receiver.location; + if (W.Window__isDartLocation(result) === true) + return result; + if (null == receiver._location_wrapper) + receiver._location_wrapper = new W._LocationWrapper(result); + return receiver._location_wrapper; + }, + toString$0: function(receiver) { + return receiver.toString(); + }, + "%": "DOMWindow|Window" + }, + _Attr: { + "^": "Node;name=,value=", + "%": "Attr" + }, + _NamedNodeMap: { + "^": "Interceptor_ListMixin_ImmutableListMixin0;", + get$length: function(receiver) { + return receiver.length; + }, + $index: function(receiver, index) { + var t1 = receiver.length; + if (index >>> 0 !== index || index >= t1) + throw H.wrapException(P.RangeError$range(index, 0, t1)); + return receiver[index]; + }, + $indexSet: function(receiver, index, value) { + throw H.wrapException(P.UnsupportedError$("Cannot assign element of immutable List.")); + }, + elementAt$1: function(receiver, index) { + if (index < 0 || index >= receiver.length) + return H.ioore(receiver, index); + return receiver[index]; + }, + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true, + $isJavaScriptIndexingBehavior: true, + "%": "MozNamedAttrMap|NamedNodeMap" + }, + Element_Element$html_closure: { + "^": "Closure:11;", + call$1: function(e) { + return !!J.getInterceptor(e).$isElement; + } + }, + _ChildNodeListLazy: { + "^": "ListBase;_this", + get$single: function(_) { + var t1, l; + t1 = this._this; + l = t1.childNodes.length; + if (l === 0) + throw H.wrapException(P.StateError$("No elements")); + if (l > 1) + throw H.wrapException(P.StateError$("More than one element")); + return t1.firstChild; + }, + addAll$1: function(_, iterable) { + var t1, t2, len, i; + t1 = iterable._this; + t2 = this._this; + if (t1 !== t2) + for (len = t1.childNodes.length, i = 0; i < len; ++i) + t2.appendChild(t1.firstChild); + return; + }, + $indexSet: function(_, index, value) { + var t1, t2; + t1 = this._this; + t2 = t1.childNodes; + if (index >>> 0 !== index || index >= t2.length) + return H.ioore(t2, index); + t1.replaceChild(value, t2[index]); + }, + get$iterator: function(_) { + return C.NodeList_methods.get$iterator(this._this.childNodes); + }, + get$length: function(_) { + return this._this.childNodes.length; + }, + $index: function(_, index) { + var t1 = this._this.childNodes; + if (index >>> 0 !== index || index >= t1.length) + return H.ioore(t1, index); + return t1[index]; + }, + $asListBase: function() { + return [W.Node]; + }, + $asList: function() { + return [W.Node]; + } + }, + Interceptor_ListMixin: { + "^": "Interceptor+ListMixin;", + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true + }, + Interceptor_ListMixin_ImmutableListMixin: { + "^": "Interceptor_ListMixin+ImmutableListMixin;", + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true + }, + Storage_keys_closure: { + "^": "Closure:10;keys_0", + call$2: function(k, v) { + return this.keys_0.push(k); + } + }, + Storage_values_closure: { + "^": "Closure:10;values_0", + call$2: function(k, v) { + return this.values_0.push(v); + } + }, + Interceptor_ListMixin0: { + "^": "Interceptor+ListMixin;", + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true + }, + Interceptor_ListMixin_ImmutableListMixin0: { + "^": "Interceptor_ListMixin0+ImmutableListMixin;", + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true + }, + _AttributeMap: { + "^": "Object;", + forEach$1: function(_, f) { + var t1, key; + for (t1 = this.get$keys(this), t1 = new H.ListIterator(t1, t1.length, 0, null); t1.moveNext$0();) { + key = t1._current; + f.call$2(key, this.$index(0, key)); + } + }, + get$keys: function(_) { + var attributes, keys, len, i, t1; + attributes = this._element.attributes; + keys = H.setRuntimeTypeInfo([], [J.JSString]); + for (len = attributes.length, i = 0; i < len; ++i) { + if (i >= attributes.length) + return H.ioore(attributes, i); + t1 = attributes[i]; + if (this._matches$1(t1)) + keys.push(J.get$name$x(t1)); + } + return keys; + }, + get$values: function(_) { + var attributes, values, len, i, t1; + attributes = this._element.attributes; + values = H.setRuntimeTypeInfo([], [J.JSString]); + for (len = attributes.length, i = 0; i < len; ++i) { + if (i >= attributes.length) + return H.ioore(attributes, i); + t1 = attributes[i]; + if (this._matches$1(t1)) + values.push(J.get$value$x(t1)); + } + return values; + }, + get$isEmpty: function(_) { + return this.get$length(this) === 0; + }, + $isMap: true, + $asMap: function() { + return [J.JSString, J.JSString]; + } + }, + _ElementAttributeMap: { + "^": "_AttributeMap;_element", + $index: function(_, key) { + return this._element.getAttribute(key); + }, + $indexSet: function(_, key, value) { + this._element.setAttribute(key, value); + }, + get$length: function(_) { + return this.get$keys(this).length; + }, + _matches$1: function(node) { + return node.namespaceURI == null; + } + }, + EventStreamProvider: { + "^": "Object;_eventType" + }, + _EventStream: { + "^": "Stream;", + listen$4$cancelOnError$onDone$onError: function(onData, cancelOnError, onDone, onError) { + var t1 = new W._EventStreamSubscription(0, this._target, this._eventType, W._wrapZone(onData), this._useCapture); + t1.$builtinTypeInfo = this.$builtinTypeInfo; + t1._tryResume$0(); + return t1; + } + }, + _ElementEventStreamImpl: { + "^": "_EventStream;_target,_eventType,_useCapture" + }, + _EventStreamSubscription: { + "^": "StreamSubscription;_pauseCount,_target,_eventType,_onData,_useCapture", + cancel$0: function() { + if (this._target == null) + return; + this._unlisten$0(); + this._target = null; + this._onData = null; + return; + }, + _tryResume$0: function() { + var t1 = this._onData; + if (t1 != null && this._pauseCount <= 0) + J.addEventListener$3$x(this._target, this._eventType, t1, this._useCapture); + }, + _unlisten$0: function() { + var t1 = this._onData; + if (t1 != null) + J.removeEventListener$3$x(this._target, this._eventType, t1, this._useCapture); + } + }, + _Html5NodeValidator: { + "^": "Object;uriPolicy<", + allowsElement$1: function(element) { + return $.get$_Html5NodeValidator__allowedElements().contains$1(0, element.tagName); + }, + allowsAttribute$3: function(element, attributeName, value) { + var tagName, t1, validator; + tagName = element.tagName; + t1 = $.get$_Html5NodeValidator__attributeValidators(); + validator = t1.$index(0, H.S(tagName) + "::" + attributeName); + if (validator == null) + validator = t1.$index(0, "*::" + attributeName); + if (validator == null) + return false; + return validator.call$4(element, attributeName, value, this); + }, + _Html5NodeValidator$1$uriPolicy: function(uriPolicy) { + var t1, t2; + t1 = $.get$_Html5NodeValidator__attributeValidators(); + if (t1.get$isEmpty(t1)) { + for (t2 = new H.ListIterator(C.List_1GN, 261, 0, null); t2.moveNext$0();) + t1.$indexSet(0, t2._current, W._Html5NodeValidator__standardAttributeValidator$closure()); + for (t2 = new H.ListIterator(C.List_yrN, 12, 0, null); t2.moveNext$0();) + t1.$indexSet(0, t2._current, W._Html5NodeValidator__uriAttributeValidator$closure()); + } + }, + static: {"^": "_Html5NodeValidator__allowedElements,_Html5NodeValidator__standardAttributes,_Html5NodeValidator__uriAttributes,_Html5NodeValidator__attributeValidators", _Html5NodeValidator$: function(uriPolicy) { + var e, t1; + e = document.createElement("a", null); + t1 = new W._SameOriginUriPolicy(e, C.Window_methods.get$location(window)); + t1 = new W._Html5NodeValidator(t1); + t1._Html5NodeValidator$1$uriPolicy(uriPolicy); + return t1; + }, _Html5NodeValidator__standardAttributeValidator: [function(element, attributeName, value, context) { + return true; + }, "call$4", "_Html5NodeValidator__standardAttributeValidator$closure", 8, 0, 8], _Html5NodeValidator__uriAttributeValidator: [function(element, attributeName, value, context) { + var t1, t2, t3, t4, t5, t6; + t1 = context.get$uriPolicy(); + t2 = t1._hiddenAnchor; + t3 = J.getInterceptor$x(t2); + t3.set$href(t2, value); + t4 = t3.get$hostname(t2); + t1 = t1._loc; + t5 = J.getInterceptor$x(t1); + t6 = t5.get$hostname(t1); + if (t4 == null ? t6 == null : t4 === t6) { + t4 = t3.get$port(t2); + t6 = t5.get$port(t1); + if (t4 == null ? t6 == null : t4 === t6) { + t4 = t3.get$protocol(t2); + t1 = t5.get$protocol(t1); + t1 = t4 == null ? t1 == null : t4 === t1; + } else + t1 = false; + } else + t1 = false; + if (!t1) + t1 = t3.get$hostname(t2) === "" && t3.get$port(t2) === "" && t3.get$protocol(t2) === ":"; + else + t1 = true; + return t1; + }, "call$4", "_Html5NodeValidator__uriAttributeValidator$closure", 8, 0, 8]} + }, + ImmutableListMixin: { + "^": "Object;", + get$iterator: function(receiver) { + return new W.FixedSizeListIterator(receiver, this.get$length(receiver), -1, null); + }, + $isList: true, + $asList: null, + $isEfficientLength: true + }, + NodeValidatorBuilder: { + "^": "Object;_validators", + allowsElement$1: function(element) { + return H.IterableMixinWorkaround_any(this._validators, new W.NodeValidatorBuilder_allowsElement_closure(element)); + }, + allowsAttribute$3: function(element, attributeName, value) { + return H.IterableMixinWorkaround_any(this._validators, new W.NodeValidatorBuilder_allowsAttribute_closure(element, attributeName, value)); + } + }, + NodeValidatorBuilder_allowsElement_closure: { + "^": "Closure:11;element_0", + call$1: function(v) { + return v.allowsElement$1(this.element_0); + } + }, + NodeValidatorBuilder_allowsAttribute_closure: { + "^": "Closure:11;element_0,attributeName_1,value_2", + call$1: function(v) { + return v.allowsAttribute$3(this.element_0, this.attributeName_1, this.value_2); + } + }, + _SimpleNodeValidator: { + "^": "Object;uriPolicy<", + allowsElement$1: function(element) { + return this.allowedElements.contains$1(0, element.tagName); + }, + allowsAttribute$3: function(element, attributeName, value) { + var tagName, t1; + tagName = element.tagName; + t1 = this.allowedUriAttributes; + if (t1.contains$1(0, H.S(tagName) + "::" + attributeName)) + return this.uriPolicy.allowsUri$1(value); + else if (t1.contains$1(0, "*::" + attributeName)) + return this.uriPolicy.allowsUri$1(value); + else { + t1 = this.allowedAttributes; + if (t1.contains$1(0, H.S(tagName) + "::" + attributeName)) + return true; + else if (t1.contains$1(0, "*::" + attributeName)) + return true; + else if (t1.contains$1(0, H.S(tagName) + "::*")) + return true; + else if (t1.contains$1(0, "*::*")) + return true; + } + return false; + } + }, + _TemplatingNodeValidator: { + "^": "_SimpleNodeValidator;_templateAttrs,allowedElements,allowedAttributes,allowedUriAttributes,uriPolicy", + allowsAttribute$3: function(element, attributeName, value) { + if (W._SimpleNodeValidator.prototype.allowsAttribute$3.call(this, element, attributeName, value)) + return true; + if (attributeName === "template" && value === "") + return true; + if (element.getAttribute("template") === "") + return this._templateAttrs.contains$1(0, attributeName); + return false; + }, + static: {"^": "_TemplatingNodeValidator__TEMPLATE_ATTRS", _TemplatingNodeValidator$: function() { + var t1, t2, t3, t4; + t1 = H.setRuntimeTypeInfo(new H.MappedListIterable(C.List_wSV, new W._TemplatingNodeValidator_closure()), [null, null]); + t2 = P.LinkedHashSet_LinkedHashSet(null, null, null, null); + t2.addAll$1(0, ["TEMPLATE"]); + t3 = P.LinkedHashSet_LinkedHashSet(null, null, null, null); + t3.addAll$1(0, t1); + t1 = t3; + t3 = P.LinkedHashSet_LinkedHashSet(null, null, null, null); + t4 = P.LinkedHashSet_LinkedHashSet(null, null, null, J.JSString); + t4.addAll$1(0, C.List_wSV); + return new W._TemplatingNodeValidator(t4, t2, t1, t3, null); + }} + }, + _TemplatingNodeValidator_closure: { + "^": "Closure:11;", + call$1: function(attr) { + return "TEMPLATE::" + H.S(attr); + } + }, + _SvgNodeValidator: { + "^": "Object;", + allowsElement$1: function(element) { + var t1 = J.getInterceptor(element); + if (!!t1.$isScriptElement) + return false; + if (!!t1.$isSvgElement) + return true; + return false; + }, + allowsAttribute$3: function(element, attributeName, value) { + if (attributeName === "is" || C.JSString_methods.startsWith$1(attributeName, "on")) + return false; + return this.allowsElement$1(element); + } + }, + FixedSizeListIterator: { + "^": "Object;_array,_html$_length,_position,_html$_current", + moveNext$0: function() { + var nextPosition, t1; + nextPosition = this._position + 1; + t1 = this._html$_length; + if (nextPosition < t1) { + this._html$_current = J.$index$asx(this._array, nextPosition); + this._position = nextPosition; + return true; + } + this._html$_current = null; + this._position = t1; + return false; + }, + get$current: function() { + return this._html$_current; + } + }, + _LocationWrapper: { + "^": "Object;_ptr", + get$hostname: function(_) { + return this._ptr.hostname; + }, + get$port: function(_) { + return this._ptr.port; + }, + get$protocol: function(_) { + return this._ptr.protocol; + }, + toString$0: function(_) { + return this._ptr.toString(); + }, + $isLocation: true + }, + NodeValidator: { + "^": "Object;" + }, + _SameOriginUriPolicy: { + "^": "Object;_hiddenAnchor,_loc" + }, + _ValidatingTreeSanitizer: { + "^": "Object;validator", + sanitizeTree$1: function(node) { + new W._ValidatingTreeSanitizer_sanitizeTree_walk(this).call$1(node); + }, + sanitizeNode$1: function(node) { + var t1, attrs, t2, isAttr, keys, i, $name, t3; + switch (node.nodeType) { + case 1: + t1 = J.getInterceptor$x(node); + attrs = t1.get$attributes(node); + if (!this.validator.allowsElement$1(node)) { + window; + t2 = "Removing disallowed element <" + H.S(node.tagName) + ">"; + if (typeof console != "undefined") + console.warn(t2); + t1.remove$0(node); + break; + } + t2 = attrs._element; + isAttr = t2.getAttribute("is"); + if (isAttr != null) + if (!this.validator.allowsAttribute$3(node, "is", isAttr)) { + window; + t2 = "Removing disallowed type extension <" + H.S(node.tagName) + " is=\"" + isAttr + "\">"; + if (typeof console != "undefined") + console.warn(t2); + t1.remove$0(node); + break; + } + keys = C.JSArray_methods.toList$0(attrs.get$keys(attrs)); + for (i = attrs.get$keys(attrs).length - 1; i >= 0; --i) { + if (i >= keys.length) + return H.ioore(keys, i); + $name = keys[i]; + if (!this.validator.allowsAttribute$3(node, J.toLowerCase$0$s($name), t2.getAttribute($name))) { + window; + t3 = "Removing disallowed attribute <" + H.S(node.tagName) + " " + $name + "=\"" + H.S(t2.getAttribute($name)) + "\">"; + if (typeof console != "undefined") + console.warn(t3); + t2.getAttribute($name); + t2.removeAttribute($name); + } + } + if (!!t1.$isTemplateElement) + this.sanitizeTree$1(node.content); + break; + case 8: + case 11: + case 3: + case 4: + break; + default: + J.remove$0$ax(node); + } + } + }, + _ValidatingTreeSanitizer_sanitizeTree_walk: { + "^": "Closure:22;this_0", + call$1: function(node) { + var child, nextChild; + this.this_0.sanitizeNode$1(node); + child = node.lastChild; + for (; child != null; child = nextChild) { + nextChild = child.previousSibling; + this.call$1(child); + } + } + } +}], +["dart.dom.svg", "dart:svg", , P, { + "^": "", + ScriptElement: { + "^": "SvgElement;", + $isScriptElement: true, + "%": "SVGScriptElement" + }, + StyleElement0: { + "^": "SvgElement;disabled}", + "%": "SVGStyleElement" + }, + SvgElement: { + "^": "Element;", + set$innerHtml: function(receiver, value) { + receiver.textContent = null; + receiver.appendChild(this.createFragment$3$treeSanitizer$validator(receiver, value, null, null)); + }, + createFragment$3$treeSanitizer$validator: function(receiver, svg, treeSanitizer, validator) { + var t1, html, fragment, svgFragment, root; + t1 = H.setRuntimeTypeInfo([], [W.NodeValidator]); + validator = new W.NodeValidatorBuilder(t1); + t1.push(W._Html5NodeValidator$(null)); + t1.push(W._TemplatingNodeValidator$()); + t1.push(new W._SvgNodeValidator()); + treeSanitizer = new W._ValidatingTreeSanitizer(validator); + html = "" + svg + ""; + fragment = J.createFragment$2$treeSanitizer$x(document.body, html, treeSanitizer); + svgFragment = document.createDocumentFragment(); + fragment.toString; + t1 = new W._ChildNodeListLazy(fragment); + root = t1.get$single(t1); + for (; t1 = root.firstChild, t1 != null;) + svgFragment.appendChild(t1); + return svgFragment; + }, + get$onBlur: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_blur._eventType, false), [null]); + }, + get$onChange: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_change._eventType, false), [null]); + }, + get$onClick: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_click._eventType, false), [null]); + }, + get$onInput: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_input._eventType, false), [null]); + }, + $isSvgElement: true, + "%": "SVGAElement|SVGAltGlyphDefElement|SVGAltGlyphElement|SVGAltGlyphItemElement|SVGAnimateElement|SVGAnimateMotionElement|SVGAnimateTransformElement|SVGAnimationElement|SVGCircleElement|SVGClipPathElement|SVGComponentTransferFunctionElement|SVGCursorElement|SVGDefsElement|SVGDescElement|SVGDiscardElement|SVGEllipseElement|SVGFEBlendElement|SVGFEColorMatrixElement|SVGFEComponentTransferElement|SVGFECompositeElement|SVGFEConvolveMatrixElement|SVGFEDiffuseLightingElement|SVGFEDisplacementMapElement|SVGFEDistantLightElement|SVGFEDropShadowElement|SVGFEFloodElement|SVGFEFuncAElement|SVGFEFuncBElement|SVGFEFuncGElement|SVGFEFuncRElement|SVGFEGaussianBlurElement|SVGFEImageElement|SVGFEMergeElement|SVGFEMergeNodeElement|SVGFEMorphologyElement|SVGFEOffsetElement|SVGFEPointLightElement|SVGFESpecularLightingElement|SVGFESpotLightElement|SVGFETileElement|SVGFETurbulenceElement|SVGFilterElement|SVGFontElement|SVGFontFaceElement|SVGFontFaceFormatElement|SVGFontFaceNameElement|SVGFontFaceSrcElement|SVGFontFaceUriElement|SVGForeignObjectElement|SVGGElement|SVGGeometryElement|SVGGlyphElement|SVGGlyphRefElement|SVGGradientElement|SVGGraphicsElement|SVGHKernElement|SVGImageElement|SVGLineElement|SVGLinearGradientElement|SVGMPathElement|SVGMarkerElement|SVGMaskElement|SVGMetadataElement|SVGMissingGlyphElement|SVGPathElement|SVGPatternElement|SVGPolygonElement|SVGPolylineElement|SVGRadialGradientElement|SVGRectElement|SVGSVGElement|SVGSetElement|SVGStopElement|SVGSwitchElement|SVGSymbolElement|SVGTSpanElement|SVGTextContentElement|SVGTextElement|SVGTextPathElement|SVGTextPositioningElement|SVGTitleElement|SVGUseElement|SVGVKernElement|SVGViewElement;SVGElement" + } +}], +["dart.isolate", "dart:isolate", , P, { + "^": "", + Capability: { + "^": "Object;", + $isCapability: true, + static: {Capability_Capability: function() { + return new H.CapabilityImpl((Math.random() * 0x100000000 >>> 0) + (Math.random() * 0x100000000 >>> 0) * 4294967296); + }} + } +}], +["dart.typed_data.implementation", "dart:_native_typed_data", , H, { + "^": "", + NativeTypedData: { + "^": "Interceptor;", + _invalidIndex$2: function(receiver, index, $length) { + var t1 = J.getInterceptor$n(index); + if (t1.$lt(index, 0) || t1.$ge(index, $length)) + throw H.wrapException(P.RangeError$range(index, 0, $length)); + else + throw H.wrapException(P.ArgumentError$("Invalid list index " + H.S(index))); + }, + "%": ";ArrayBufferView;NativeTypedArray|NativeTypedArray_ListMixin|NativeTypedArray_ListMixin_FixedLengthListMixin|NativeTypedArrayOfInt" + }, + NativeUint8List: { + "^": "NativeTypedArrayOfInt;", + get$length: function(receiver) { + return receiver.length; + }, + $index: function(receiver, index) { + var t1 = receiver.length; + if (index >>> 0 !== index || index >= t1) + this._invalidIndex$2(receiver, index, t1); + return receiver[index]; + }, + $indexSet: function(receiver, index, value) { + var t1 = receiver.length; + if (index >>> 0 !== index || index >= t1) + this._invalidIndex$2(receiver, index, t1); + receiver[index] = value; + }, + $isList: true, + $asList: function() { + return [J.JSInt]; + }, + $isEfficientLength: true, + "%": ";Uint8Array" + }, + NativeTypedArray: { + "^": "NativeTypedData;", + get$length: function(receiver) { + return receiver.length; + }, + $isJavaScriptIndexingBehavior: true + }, + NativeTypedArrayOfInt: { + "^": "NativeTypedArray_ListMixin_FixedLengthListMixin;", + $isList: true, + $asList: function() { + return [J.JSInt]; + }, + $isEfficientLength: true + }, + NativeTypedArray_ListMixin: { + "^": "NativeTypedArray+ListMixin;", + $isList: true, + $asList: function() { + return [J.JSInt]; + }, + $isEfficientLength: true + }, + NativeTypedArray_ListMixin_FixedLengthListMixin: { + "^": "NativeTypedArray_ListMixin+FixedLengthListMixin;" + } +}], +["dart2js._js_primitives", "dart:_js_primitives", , H, { + "^": "", + printString: function(string) { + if (typeof dartPrint == "function") { + dartPrint(string); + return; + } + if (typeof console == "object" && typeof console.log == "function") { + console.log(string); + return; + } + if (typeof window == "object") + return; + if (typeof print == "function") { + print(string); + return; + } + throw "Unable to print message: " + String(string); + } +}], +]); +Isolate.$finishClasses($$, $, null); +$$ = null; + +// Runtime type support +J.JSString.$isString = true; +J.JSString.$isObject = true; +J.JSInt.$isint = true; +J.JSInt.$isObject = true; +W.Node.$isNode = true; +W.Node.$isObject = true; +J.JSNumber.$isObject = true; +P.Duration.$isObject = true; +P.Object.$isObject = true; +W.NodeValidator.$isObject = true; +W.MouseEvent.$isEvent = true; +W.MouseEvent.$isObject = true; +W.Event.$isEvent = true; +W.Event.$isObject = true; +J.JSBool.$isbool = true; +J.JSBool.$isObject = true; +H.RawReceivePortImpl.$isObject = true; +H._IsolateEvent.$isObject = true; +H._IsolateContext.$isObject = true; +P.Symbol.$isSymbol = true; +P.Symbol.$isObject = true; +P.StackTrace.$isStackTrace = true; +P.StackTrace.$isObject = true; +J.JSDouble.$isdouble = true; +J.JSDouble.$isObject = true; +W.Element.$isElement = true; +W.Element.$isNode = true; +W.Element.$isObject = true; +W._Html5NodeValidator.$is_Html5NodeValidator = true; +W._Html5NodeValidator.$isObject = true; +P._EventSink.$is_EventSink = true; +P._EventSink.$isObject = true; +// getInterceptor methods +J.getInterceptor = function(receiver) { + if (typeof receiver == "number") { + if (Math.floor(receiver) == receiver) + return J.JSInt.prototype; + return J.JSDouble.prototype; + } + if (typeof receiver == "string") + return J.JSString.prototype; + if (receiver == null) + return J.JSNull.prototype; + if (typeof receiver == "boolean") + return J.JSBool.prototype; + if (receiver.constructor == Array) + return J.JSArray.prototype; + if (typeof receiver != "object") + return receiver; + if (receiver instanceof P.Object) + return receiver; + return J.getNativeInterceptor(receiver); +}; +J.getInterceptor$asx = function(receiver) { + if (typeof receiver == "string") + return J.JSString.prototype; + if (receiver == null) + return receiver; + if (receiver.constructor == Array) + return J.JSArray.prototype; + if (typeof receiver != "object") + return receiver; + if (receiver instanceof P.Object) + return receiver; + return J.getNativeInterceptor(receiver); +}; +J.getInterceptor$ax = function(receiver) { + if (receiver == null) + return receiver; + if (receiver.constructor == Array) + return J.JSArray.prototype; + if (typeof receiver != "object") + return receiver; + if (receiver instanceof P.Object) + return receiver; + return J.getNativeInterceptor(receiver); +}; +J.getInterceptor$n = function(receiver) { + if (typeof receiver == "number") + return J.JSNumber.prototype; + if (receiver == null) + return receiver; + if (!(receiver instanceof P.Object)) + return J.UnknownJavaScriptObject.prototype; + return receiver; +}; +J.getInterceptor$ns = function(receiver) { + if (typeof receiver == "number") + return J.JSNumber.prototype; + if (typeof receiver == "string") + return J.JSString.prototype; + if (receiver == null) + return receiver; + if (!(receiver instanceof P.Object)) + return J.UnknownJavaScriptObject.prototype; + return receiver; +}; +J.getInterceptor$s = function(receiver) { + if (typeof receiver == "string") + return J.JSString.prototype; + if (receiver == null) + return receiver; + if (!(receiver instanceof P.Object)) + return J.UnknownJavaScriptObject.prototype; + return receiver; +}; +J.getInterceptor$x = function(receiver) { + if (receiver == null) + return receiver; + if (typeof receiver != "object") + return receiver; + if (receiver instanceof P.Object) + return receiver; + return J.getNativeInterceptor(receiver); +}; +J.$add$ns = function(receiver, a0) { + if (typeof receiver == "number" && typeof a0 == "number") + return receiver + a0; + return J.getInterceptor$ns(receiver).$add(receiver, a0); +}; +J.$eq = function(receiver, a0) { + if (receiver == null) + return a0 == null; + if (typeof receiver != "object") + return a0 != null && receiver === a0; + return J.getInterceptor(receiver).$eq(receiver, a0); +}; +J.$ge$n = function(receiver, a0) { + if (typeof receiver == "number" && typeof a0 == "number") + return receiver >= a0; + return J.getInterceptor$n(receiver).$ge(receiver, a0); +}; +J.$index$asx = function(receiver, a0) { + if (receiver.constructor == Array || typeof receiver == "string" || H.isJsIndexable(receiver, receiver[init.dispatchPropertyName])) + if (a0 >>> 0 === a0 && a0 < receiver.length) + return receiver[a0]; + return J.getInterceptor$asx(receiver).$index(receiver, a0); +}; +J.$indexSet$ax = function(receiver, a0, a1) { + if ((receiver.constructor == Array || H.isJsIndexable(receiver, receiver[init.dispatchPropertyName])) && !receiver.immutable$list && a0 >>> 0 === a0 && a0 < receiver.length) + return receiver[a0] = a1; + return J.getInterceptor$ax(receiver).$indexSet(receiver, a0, a1); +}; +J.$mul$ns = function(receiver, a0) { + if (typeof receiver == "number" && typeof a0 == "number") + return receiver * a0; + return J.getInterceptor$ns(receiver).$mul(receiver, a0); +}; +J.$sub$n = function(receiver, a0) { + if (typeof receiver == "number" && typeof a0 == "number") + return receiver - a0; + return J.getInterceptor$n(receiver).$sub(receiver, a0); +}; +J.addEventListener$3$x = function(receiver, a0, a1, a2) { + return J.getInterceptor$x(receiver).addEventListener$3(receiver, a0, a1, a2); +}; +J.contains$1$asx = function(receiver, a0) { + return J.getInterceptor$asx(receiver).contains$1(receiver, a0); +}; +J.createFragment$2$treeSanitizer$x = function(receiver, a0, a1) { + return J.getInterceptor$x(receiver).createFragment$2$treeSanitizer(receiver, a0, a1); +}; +J.createFragment$3$treeSanitizer$validator$x = function(receiver, a0, a1, a2) { + return J.getInterceptor$x(receiver).createFragment$3$treeSanitizer$validator(receiver, a0, a1, a2); +}; +J.elementAt$1$ax = function(receiver, a0) { + return J.getInterceptor$ax(receiver).elementAt$1(receiver, a0); +}; +J.forEach$1$ax = function(receiver, a0) { + return J.getInterceptor$ax(receiver).forEach$1(receiver, a0); +}; +J.get$_key$x = function(receiver) { + return J.getInterceptor$x(receiver).get$_key(receiver); +}; +J.get$error$x = function(receiver) { + return J.getInterceptor$x(receiver).get$error(receiver); +}; +J.get$hashCode$ = function(receiver) { + return J.getInterceptor(receiver).get$hashCode(receiver); +}; +J.get$isEmpty$asx = function(receiver) { + return J.getInterceptor$asx(receiver).get$isEmpty(receiver); +}; +J.get$iterator$ax = function(receiver) { + return J.getInterceptor$ax(receiver).get$iterator(receiver); +}; +J.get$length$asx = function(receiver) { + return J.getInterceptor$asx(receiver).get$length(receiver); +}; +J.get$name$x = function(receiver) { + return J.getInterceptor$x(receiver).get$name(receiver); +}; +J.get$nodes$x = function(receiver) { + return J.getInterceptor$x(receiver).get$nodes(receiver); +}; +J.get$onBlur$x = function(receiver) { + return J.getInterceptor$x(receiver).get$onBlur(receiver); +}; +J.get$onChange$x = function(receiver) { + return J.getInterceptor$x(receiver).get$onChange(receiver); +}; +J.get$onClick$x = function(receiver) { + return J.getInterceptor$x(receiver).get$onClick(receiver); +}; +J.get$onInput$x = function(receiver) { + return J.getInterceptor$x(receiver).get$onInput(receiver); +}; +J.get$value$x = function(receiver) { + return J.getInterceptor$x(receiver).get$value(receiver); +}; +J.preventDefault$0$x = function(receiver) { + return J.getInterceptor$x(receiver).preventDefault$0(receiver); +}; +J.remove$0$ax = function(receiver) { + return J.getInterceptor$ax(receiver).remove$0(receiver); +}; +J.remove$1$ax = function(receiver, a0) { + return J.getInterceptor$ax(receiver).remove$1(receiver, a0); +}; +J.removeEventListener$3$x = function(receiver, a0, a1, a2) { + return J.getInterceptor$x(receiver).removeEventListener$3(receiver, a0, a1, a2); +}; +J.round$0$n = function(receiver) { + return J.getInterceptor$n(receiver).round$0(receiver); +}; +J.send$1$x = function(receiver, a0) { + return J.getInterceptor$x(receiver).send$1(receiver, a0); +}; +J.set$disabled$x = function(receiver, value) { + return J.getInterceptor$x(receiver).set$disabled(receiver, value); +}; +J.set$href$x = function(receiver, value) { + return J.getInterceptor$x(receiver).set$href(receiver, value); +}; +J.set$innerHtml$x = function(receiver, value) { + return J.getInterceptor$x(receiver).set$innerHtml(receiver, value); +}; +J.set$value$x = function(receiver, value) { + return J.getInterceptor$x(receiver).set$value(receiver, value); +}; +J.toList$0$ax = function(receiver) { + return J.getInterceptor$ax(receiver).toList$0(receiver); +}; +J.toLowerCase$0$s = function(receiver) { + return J.getInterceptor$s(receiver).toLowerCase$0(receiver); +}; +J.toString$0 = function(receiver) { + return J.getInterceptor(receiver).toString$0(receiver); +}; +J.toStringAsFixed$1$n = function(receiver, a0) { + return J.getInterceptor$n(receiver).toStringAsFixed$1(receiver, a0); +}; +J.trim$0$s = function(receiver) { + return J.getInterceptor$s(receiver).trim$0(receiver); +}; +C.C_DynamicRuntimeType = new H.DynamicRuntimeType(); +C.C_OutOfMemoryError = new P.OutOfMemoryError(); +C.C__RootZone = new P._RootZone(); +C.Duration_0 = new P.Duration(0); +C.EventStreamProvider_blur = new W.EventStreamProvider("blur"); +C.EventStreamProvider_change = new W.EventStreamProvider("change"); +C.EventStreamProvider_click = new W.EventStreamProvider("click"); +C.EventStreamProvider_input = new W.EventStreamProvider("input"); +C.JSArray_methods = J.JSArray.prototype; +C.JSInt_methods = J.JSInt.prototype; +C.JSNumber_methods = J.JSNumber.prototype; +C.JSString_methods = J.JSString.prototype; +C.JS_CONST_0 = function(hooks) { + if (typeof dartExperimentalFixupGetTag != "function") return hooks; + hooks.getTag = dartExperimentalFixupGetTag(hooks.getTag); +}; +C.JS_CONST_Fs4 = function(hooks) { return hooks; } +; +C.JS_CONST_IX5 = function getTagFallback(o) { + var constructor = o.constructor; + if (typeof constructor == "function") { + var name = constructor.name; + if (typeof name == "string" + && name !== "" + && name !== "Object" + && name !== "Function.prototype") { + return name; + } + } + var s = Object.prototype.toString.call(o); + return s.substring(8, s.length - 1); +}; +C.JS_CONST_QJm = function(getTagFallback) { + return function(hooks) { + if (typeof navigator != "object") return hooks; + var ua = navigator.userAgent; + if (ua.indexOf("DumpRenderTree") >= 0) return hooks; + if (ua.indexOf("Chrome") >= 0) { + function confirm(p) { + return typeof window == "object" && window[p] && window[p].name == p; + } + if (confirm("Window") && confirm("HTMLElement")) return hooks; + } + hooks.getTag = getTagFallback; + }; +}; +C.JS_CONST_U4w = function(hooks) { + var userAgent = typeof navigator == "object" ? navigator.userAgent : ""; + if (userAgent.indexOf("Firefox") == -1) return hooks; + var getTag = hooks.getTag; + var quickMap = { + "BeforeUnloadEvent": "Event", + "DataTransfer": "Clipboard", + "GeoGeolocation": "Geolocation", + "WorkerMessageEvent": "MessageEvent", + "XMLDocument": "!Document"}; + function getTagFirefox(o) { + var tag = getTag(o); + return quickMap[tag] || tag; + } + hooks.getTag = getTagFirefox; +}; +C.JS_CONST_aQP = function() { + function typeNameInChrome(o) { + var name = o.constructor.name; + if (name) return name; + var s = Object.prototype.toString.call(o); + return s.substring(8, s.length - 1); + } + function getUnknownTag(object, tag) { + if (/^HTML[A-Z].*Element$/.test(tag)) { + var name = Object.prototype.toString.call(object); + if (name == "[object Object]") return null; + return "HTMLElement"; + } + } + function getUnknownTagGenericBrowser(object, tag) { + if (object instanceof HTMLElement) return "HTMLElement"; + return getUnknownTag(object, tag); + } + function prototypeForTag(tag) { + if (typeof window == "undefined") return null; + if (typeof window[tag] == "undefined") return null; + var constructor = window[tag]; + if (typeof constructor != "function") return null; + return constructor.prototype; + } + function discriminator(tag) { return null; } + var isBrowser = typeof navigator == "object"; + return { + getTag: typeNameInChrome, + getUnknownTag: isBrowser ? getUnknownTagGenericBrowser : getUnknownTag, + prototypeForTag: prototypeForTag, + discriminator: discriminator }; +}; +C.JS_CONST_gkc = function(hooks) { + var userAgent = typeof navigator == "object" ? navigator.userAgent : ""; + if (userAgent.indexOf("Trident/") == -1) return hooks; + var getTag = hooks.getTag; + var quickMap = { + "BeforeUnloadEvent": "Event", + "DataTransfer": "Clipboard", + "HTMLDDElement": "HTMLElement", + "HTMLDTElement": "HTMLElement", + "HTMLPhraseElement": "HTMLElement", + "Position": "Geoposition" + }; + function getTagIE(o) { + var tag = getTag(o); + var newTag = quickMap[tag]; + if (newTag) return newTag; + if (tag == "Object") { + if (window.DataView && (o instanceof window.DataView)) return "DataView"; + } + return tag; + } + function prototypeForTagIE(tag) { + var constructor = window[tag]; + if (constructor == null) return null; + return constructor.prototype; + } + hooks.getTag = getTagIE; + hooks.prototypeForTag = prototypeForTagIE; +}; +C.JS_CONST_rr7 = function(hooks) { + var getTag = hooks.getTag; + var prototypeForTag = hooks.prototypeForTag; + function getTagFixed(o) { + var tag = getTag(o); + if (tag == "Document") { + if (!!o.xmlVersion) return "!Document"; + return "!HTMLDocument"; + } + return tag; + } + function prototypeForTagFixed(tag) { + if (tag == "Document") return null; + return prototypeForTag(tag); + } + hooks.getTag = getTagFixed; + hooks.prototypeForTag = prototypeForTagFixed; +}; +C.JsonCodec_null_null = new P.JsonCodec(null, null); +C.JsonDecoder_null = new P.JsonDecoder(null); +C.JsonEncoder_null = new P.JsonEncoder(null); +Isolate.makeConstantList = function(list) { + list.immutable$list = init; + list.fixed$length = init; + return list; +}; +C.List_1GN = H.setRuntimeTypeInfo(Isolate.makeConstantList(["*::class", "*::dir", "*::draggable", "*::hidden", "*::id", "*::inert", "*::itemprop", "*::itemref", "*::itemscope", "*::lang", "*::spellcheck", "*::title", "*::translate", "A::accesskey", "A::coords", "A::hreflang", "A::name", "A::shape", "A::tabindex", "A::target", "A::type", "AREA::accesskey", "AREA::alt", "AREA::coords", "AREA::nohref", "AREA::shape", "AREA::tabindex", "AREA::target", "AUDIO::controls", "AUDIO::loop", "AUDIO::mediagroup", "AUDIO::muted", "AUDIO::preload", "BDO::dir", "BODY::alink", "BODY::bgcolor", "BODY::link", "BODY::text", "BODY::vlink", "BR::clear", "BUTTON::accesskey", "BUTTON::disabled", "BUTTON::name", "BUTTON::tabindex", "BUTTON::type", "BUTTON::value", "CANVAS::height", "CANVAS::width", "CAPTION::align", "COL::align", "COL::char", "COL::charoff", "COL::span", "COL::valign", "COL::width", "COLGROUP::align", "COLGROUP::char", "COLGROUP::charoff", "COLGROUP::span", "COLGROUP::valign", "COLGROUP::width", "COMMAND::checked", "COMMAND::command", "COMMAND::disabled", "COMMAND::label", "COMMAND::radiogroup", "COMMAND::type", "DATA::value", "DEL::datetime", "DETAILS::open", "DIR::compact", "DIV::align", "DL::compact", "FIELDSET::disabled", "FONT::color", "FONT::face", "FONT::size", "FORM::accept", "FORM::autocomplete", "FORM::enctype", "FORM::method", "FORM::name", "FORM::novalidate", "FORM::target", "FRAME::name", "H1::align", "H2::align", "H3::align", "H4::align", "H5::align", "H6::align", "HR::align", "HR::noshade", "HR::size", "HR::width", "HTML::version", "IFRAME::align", "IFRAME::frameborder", "IFRAME::height", "IFRAME::marginheight", "IFRAME::marginwidth", "IFRAME::width", "IMG::align", "IMG::alt", "IMG::border", "IMG::height", "IMG::hspace", "IMG::ismap", "IMG::name", "IMG::usemap", "IMG::vspace", "IMG::width", "INPUT::accept", "INPUT::accesskey", "INPUT::align", "INPUT::alt", "INPUT::autocomplete", "INPUT::checked", "INPUT::disabled", "INPUT::inputmode", "INPUT::ismap", "INPUT::list", "INPUT::max", "INPUT::maxlength", "INPUT::min", "INPUT::multiple", "INPUT::name", "INPUT::placeholder", "INPUT::readonly", "INPUT::required", "INPUT::size", "INPUT::step", "INPUT::tabindex", "INPUT::type", "INPUT::usemap", "INPUT::value", "INS::datetime", "KEYGEN::disabled", "KEYGEN::keytype", "KEYGEN::name", "LABEL::accesskey", "LABEL::for", "LEGEND::accesskey", "LEGEND::align", "LI::type", "LI::value", "LINK::sizes", "MAP::name", "MENU::compact", "MENU::label", "MENU::type", "METER::high", "METER::low", "METER::max", "METER::min", "METER::value", "OBJECT::typemustmatch", "OL::compact", "OL::reversed", "OL::start", "OL::type", "OPTGROUP::disabled", "OPTGROUP::label", "OPTION::disabled", "OPTION::label", "OPTION::selected", "OPTION::value", "OUTPUT::for", "OUTPUT::name", "P::align", "PRE::width", "PROGRESS::max", "PROGRESS::min", "PROGRESS::value", "SELECT::autocomplete", "SELECT::disabled", "SELECT::multiple", "SELECT::name", "SELECT::required", "SELECT::size", "SELECT::tabindex", "SOURCE::type", "TABLE::align", "TABLE::bgcolor", "TABLE::border", "TABLE::cellpadding", "TABLE::cellspacing", "TABLE::frame", "TABLE::rules", "TABLE::summary", "TABLE::width", "TBODY::align", "TBODY::char", "TBODY::charoff", "TBODY::valign", "TD::abbr", "TD::align", "TD::axis", "TD::bgcolor", "TD::char", "TD::charoff", "TD::colspan", "TD::headers", "TD::height", "TD::nowrap", "TD::rowspan", "TD::scope", "TD::valign", "TD::width", "TEXTAREA::accesskey", "TEXTAREA::autocomplete", "TEXTAREA::cols", "TEXTAREA::disabled", "TEXTAREA::inputmode", "TEXTAREA::name", "TEXTAREA::placeholder", "TEXTAREA::readonly", "TEXTAREA::required", "TEXTAREA::rows", "TEXTAREA::tabindex", "TEXTAREA::wrap", "TFOOT::align", "TFOOT::char", "TFOOT::charoff", "TFOOT::valign", "TH::abbr", "TH::align", "TH::axis", "TH::bgcolor", "TH::char", "TH::charoff", "TH::colspan", "TH::headers", "TH::height", "TH::nowrap", "TH::rowspan", "TH::scope", "TH::valign", "TH::width", "THEAD::align", "THEAD::char", "THEAD::charoff", "THEAD::valign", "TR::align", "TR::bgcolor", "TR::char", "TR::charoff", "TR::valign", "TRACK::default", "TRACK::kind", "TRACK::label", "TRACK::srclang", "UL::compact", "UL::type", "VIDEO::controls", "VIDEO::height", "VIDEO::loop", "VIDEO::mediagroup", "VIDEO::muted", "VIDEO::preload", "VIDEO::width"]), [J.JSString]); +C.List_wSV = H.setRuntimeTypeInfo(Isolate.makeConstantList(["bind", "if", "ref", "repeat", "syntax"]), [J.JSString]); +C.List_yrN = H.setRuntimeTypeInfo(Isolate.makeConstantList(["A::href", "AREA::href", "BLOCKQUOTE::cite", "BODY::background", "COMMAND::icon", "DEL::cite", "FORM::action", "IMG::src", "INPUT::src", "INS::cite", "Q::cite", "VIDEO::poster"]), [J.JSString]); +C.NodeList_methods = W.NodeList.prototype; +C.PlainJavaScriptObject_methods = J.PlainJavaScriptObject.prototype; +C.UnknownJavaScriptObject_methods = J.UnknownJavaScriptObject.prototype; +C.Window_methods = W.Window.prototype; +$.libraries_to_load = {}; +$.Primitives_mirrorFunctionCacheName = "$cachedFunction"; +$.Primitives_mirrorInvokeCacheName = "$cachedInvocation"; +$.Closure_functionCounter = 0; +$.BoundClosure_selfFieldNameCache = null; +$.BoundClosure_receiverFieldNameCache = null; +$.RuntimeFunctionType_inAssert = false; +$.getTagFunction = null; +$.alternateTagFunction = null; +$.prototypeForTagFunction = null; +$.dispatchRecordsForInstanceTags = null; +$.interceptorsForUncacheableTags = null; +$.initNativeDispatchFlag = null; +$.owner = null; +$.balance = null; +$.error = null; +$.number = null; +$.amount = null; +$.btn_other = null; +$.btn_deposit = null; +$.btn_interest = null; +$.bac = null; +$.printToZone = null; +$._nextCallback = null; +$._lastCallback = null; +$.Zone__current = C.C__RootZone; +$.Expando__keyCount = 0; +$.Element__parseDocument = null; +$.Element__parseRange = null; +$.Element__defaultValidator = null; +$.Element__defaultSanitizer = null; +$.Device__isOpera = null; +$.Device__isWebKit = null; +Isolate.$lazy($, "globalThis", "globalThis", "get$globalThis", function() { + return function() { return this; }(); +}); +Isolate.$lazy($, "globalWindow", "globalWindow", "get$globalWindow", function() { + return $.get$globalThis().window; +}); +Isolate.$lazy($, "globalWorker", "globalWorker", "get$globalWorker", function() { + return $.get$globalThis().Worker; +}); +Isolate.$lazy($, "globalPostMessageDefined", "globalPostMessageDefined", "get$globalPostMessageDefined", function() { + return $.get$globalThis().postMessage !== void 0; +}); +Isolate.$lazy($, "thisScript", "IsolateNatives_thisScript", "get$IsolateNatives_thisScript", function() { + return H.IsolateNatives_computeThisScript(); +}); +Isolate.$lazy($, "workerIds", "IsolateNatives_workerIds", "get$IsolateNatives_workerIds", function() { + return new P.Expando(null); +}); +Isolate.$lazy($, "noSuchMethodPattern", "TypeErrorDecoder_noSuchMethodPattern", "get$TypeErrorDecoder_noSuchMethodPattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokeCallErrorOn({ toString: function() { return "$receiver$"; } })); +}); +Isolate.$lazy($, "notClosurePattern", "TypeErrorDecoder_notClosurePattern", "get$TypeErrorDecoder_notClosurePattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokeCallErrorOn({ $method$: null, toString: function() { return "$receiver$"; } })); +}); +Isolate.$lazy($, "nullCallPattern", "TypeErrorDecoder_nullCallPattern", "get$TypeErrorDecoder_nullCallPattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokeCallErrorOn(null)); +}); +Isolate.$lazy($, "nullLiteralCallPattern", "TypeErrorDecoder_nullLiteralCallPattern", "get$TypeErrorDecoder_nullLiteralCallPattern", function() { + return H.TypeErrorDecoder_extractPattern(function() { + var $argumentsExpr$ = '$arguments$' + try { + null.$method$($argumentsExpr$); + } catch (e) { + return e.message; + } +}()); +}); +Isolate.$lazy($, "undefinedCallPattern", "TypeErrorDecoder_undefinedCallPattern", "get$TypeErrorDecoder_undefinedCallPattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokeCallErrorOn(void 0)); +}); +Isolate.$lazy($, "undefinedLiteralCallPattern", "TypeErrorDecoder_undefinedLiteralCallPattern", "get$TypeErrorDecoder_undefinedLiteralCallPattern", function() { + return H.TypeErrorDecoder_extractPattern(function() { + var $argumentsExpr$ = '$arguments$' + try { + (void 0).$method$($argumentsExpr$); + } catch (e) { + return e.message; + } +}()); +}); +Isolate.$lazy($, "nullPropertyPattern", "TypeErrorDecoder_nullPropertyPattern", "get$TypeErrorDecoder_nullPropertyPattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokePropertyErrorOn(null)); +}); +Isolate.$lazy($, "nullLiteralPropertyPattern", "TypeErrorDecoder_nullLiteralPropertyPattern", "get$TypeErrorDecoder_nullLiteralPropertyPattern", function() { + return H.TypeErrorDecoder_extractPattern(function() { + try { + null.$method$; + } catch (e) { + return e.message; + } +}()); +}); +Isolate.$lazy($, "undefinedPropertyPattern", "TypeErrorDecoder_undefinedPropertyPattern", "get$TypeErrorDecoder_undefinedPropertyPattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokePropertyErrorOn(void 0)); +}); +Isolate.$lazy($, "undefinedLiteralPropertyPattern", "TypeErrorDecoder_undefinedLiteralPropertyPattern", "get$TypeErrorDecoder_undefinedLiteralPropertyPattern", function() { + return H.TypeErrorDecoder_extractPattern(function() { + try { + (void 0).$method$; + } catch (e) { + return e.message; + } +}()); +}); +Isolate.$lazy($, "_toStringList", "IterableMixinWorkaround__toStringList", "get$IterableMixinWorkaround__toStringList", function() { + return []; +}); +Isolate.$lazy($, "_toStringVisiting", "_toStringVisiting", "get$_toStringVisiting", function() { + return P.HashSet_HashSet$identity(null); +}); +Isolate.$lazy($, "_toStringList", "Maps__toStringList", "get$Maps__toStringList", function() { + return []; +}); +Isolate.$lazy($, "_allowedElements", "_Html5NodeValidator__allowedElements", "get$_Html5NodeValidator__allowedElements", function() { + var t1 = P.LinkedHashSet_LinkedHashSet(null, null, null, null); + t1.addAll$1(0, ["A", "ABBR", "ACRONYM", "ADDRESS", "AREA", "ARTICLE", "ASIDE", "AUDIO", "B", "BDI", "BDO", "BIG", "BLOCKQUOTE", "BR", "BUTTON", "CANVAS", "CAPTION", "CENTER", "CITE", "CODE", "COL", "COLGROUP", "COMMAND", "DATA", "DATALIST", "DD", "DEL", "DETAILS", "DFN", "DIR", "DIV", "DL", "DT", "EM", "FIELDSET", "FIGCAPTION", "FIGURE", "FONT", "FOOTER", "FORM", "H1", "H2", "H3", "H4", "H5", "H6", "HEADER", "HGROUP", "HR", "I", "IFRAME", "IMG", "INPUT", "INS", "KBD", "LABEL", "LEGEND", "LI", "MAP", "MARK", "MENU", "METER", "NAV", "NOBR", "OL", "OPTGROUP", "OPTION", "OUTPUT", "P", "PRE", "PROGRESS", "Q", "S", "SAMP", "SECTION", "SELECT", "SMALL", "SOURCE", "SPAN", "STRIKE", "STRONG", "SUB", "SUMMARY", "SUP", "TABLE", "TBODY", "TD", "TEXTAREA", "TFOOT", "TH", "THEAD", "TIME", "TR", "TRACK", "TT", "U", "UL", "VAR", "VIDEO", "WBR"]); + return t1; +}); +Isolate.$lazy($, "_attributeValidators", "_Html5NodeValidator__attributeValidators", "get$_Html5NodeValidator__attributeValidators", function() { + return H.fillLiteralMap([], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null)); +}); +// Native classes + +init.functionAliases = {}; +; +init.metadata = [{func: "dynamic__String", args: [J.JSString]}, +{func: "void_", void: true}, +{func: "dynamic__Event", args: [W.Event]}, +{func: "bool__dynamic_dynamic", ret: J.JSBool, args: [null, null]}, +{func: "int__dynamic", ret: J.JSInt, args: [null]}, +{func: "Object__dynamic", ret: P.Object, args: [null]}, +{func: "bool__Object_Object", ret: J.JSBool, args: [P.Object, P.Object]}, +{func: "int__Object", ret: J.JSInt, args: [P.Object]}, +{func: "bool__Element_String_String__Html5NodeValidator", ret: J.JSBool, args: [W.Element, J.JSString, J.JSString, W._Html5NodeValidator]}, +{func: "args0"}, +{func: "args2", args: [null, null]}, +{func: "args1", args: [null]}, +{func: "dynamic__dynamic_String", args: [null, J.JSString]}, +{func: "void__dynamic__StackTrace", void: true, args: [null], opt: [P.StackTrace]}, +, +{func: "dynamic__dynamic__dynamic", args: [null], opt: [null]}, +{func: "bool_", ret: J.JSBool}, +{func: "dynamic__dynamic_StackTrace", args: [null, P.StackTrace]}, +{func: "dynamic__Symbol_dynamic", args: [P.Symbol, null]}, +{func: "int__String", ret: J.JSInt, args: [J.JSString]}, +{func: "double__String", ret: J.JSDouble, args: [J.JSString]}, +{func: "String__int", ret: J.JSString, args: [J.JSInt]}, +{func: "void__Node", void: true, args: [W.Node]}, +]; +$ = null; +Isolate = Isolate.$finishIsolateConstructor(Isolate); +$ = new Isolate(); +function convertToFastObject(properties) { + function MyClass() {}; + MyClass.prototype = properties; + new MyClass(); + return properties; +} +A = convertToFastObject(A); +B = convertToFastObject(B); +C = convertToFastObject(C); +D = convertToFastObject(D); +E = convertToFastObject(E); +F = convertToFastObject(F); +G = convertToFastObject(G); +H = convertToFastObject(H); +J = convertToFastObject(J); +K = convertToFastObject(K); +L = convertToFastObject(L); +M = convertToFastObject(M); +N = convertToFastObject(N); +O = convertToFastObject(O); +P = convertToFastObject(P); +Q = convertToFastObject(Q); +R = convertToFastObject(R); +S = convertToFastObject(S); +T = convertToFastObject(T); +U = convertToFastObject(U); +V = convertToFastObject(V); +W = convertToFastObject(W); +X = convertToFastObject(X); +Y = convertToFastObject(Y); +Z = convertToFastObject(Z); +!function() { + function intern(s) { + var o = {}; + o[s] = 1; + return Object.keys(convertToFastObject(o))[0]; + } + init.getIsolateTag = function(name) { + return intern("___dart_" + name + init.isolateTag); + }; + var tableProperty = "___dart_isolate_tags_"; + var usedProperties = Object[tableProperty] || (Object[tableProperty] = Object.create(null)); + var rootProperty = "_ZxYxX"; + for (var i = 0;; i++) { + var property = intern(rootProperty + "_" + i + "_"); + if (!(property in usedProperties)) { + usedProperties[property] = 1; + init.isolateTag = property; + break; + } + } +}(); +init.dispatchPropertyName = init.getIsolateTag("dispatch_record"); +// BEGIN invoke [main]. +;(function (callback) { + if (typeof document === "undefined") { + callback(null); + return; + } + if (document.currentScript) { + callback(document.currentScript); + return; + } + + var scripts = document.scripts; + function onLoad(event) { + for (var i = 0; i < scripts.length; ++i) { + scripts[i].removeEventListener("load", onLoad, false); + } + callback(event.target); + } + for (var i = 0; i < scripts.length; ++i) { + scripts[i].addEventListener("load", onLoad, false); + } +})(function(currentScript) { + init.currentScript = currentScript; + + if (typeof dartMainRunner === "function") { + dartMainRunner((function(a){H.startRootIsolate(M.main$closure(),a)}), []); + } else { + (function(a){H.startRootIsolate(M.main$closure(),a)})([]); + } +}); +// END invoke [main]. +function init() { + Isolate.$isolateProperties = {}; + function generateAccessor(fieldDescriptor, accessors, cls) { + var fieldInformation = fieldDescriptor.split("-"); + var field = fieldInformation[0]; + var len = field.length; + var code = field.charCodeAt(len - 1); + var reflectable; + if (fieldInformation.length > 1) + reflectable = true; + else + reflectable = false; + code = code >= 60 && code <= 64 ? code - 59 : code >= 123 && code <= 126 ? code - 117 : code >= 37 && code <= 43 ? code - 27 : 0; + if (code) { + var getterCode = code & 3; + var setterCode = code >> 2; + var accessorName = field = field.substring(0, len - 1); + var divider = field.indexOf(":"); + if (divider > 0) { + accessorName = field.substring(0, divider); + field = field.substring(divider + 1); + } + if (getterCode) { + var args = getterCode & 2 ? "receiver" : ""; + var receiver = getterCode & 1 ? "this" : "receiver"; + var body = "return " + receiver + "." + field; + var property = cls + ".prototype.get$" + accessorName + "="; + var fn = "function(" + args + "){" + body + "}"; + if (reflectable) + accessors.push(property + "$reflectable(" + fn + ");\n"); + else + accessors.push(property + fn + ";\n"); + } + if (setterCode) { + var args = setterCode & 2 ? "receiver, value" : "value"; + var receiver = setterCode & 1 ? "this" : "receiver"; + var body = receiver + "." + field + " = value"; + var property = cls + ".prototype.set$" + accessorName + "="; + var fn = "function(" + args + "){" + body + "}"; + if (reflectable) + accessors.push(property + "$reflectable(" + fn + ");\n"); + else + accessors.push(property + fn + ";\n"); + } + } + return field; + } + Isolate.$isolateProperties.$generateAccessor = generateAccessor; + function defineClass(name, cls, fields) { + var accessors = []; + var str = "function " + cls + "("; + var body = ""; + for (var i = 0; i < fields.length; i++) { + if (i != 0) + str += ", "; + var field = generateAccessor(fields[i], accessors, cls); + var parameter = "parameter_" + field; + str += parameter; + body += "this." + field + " = " + parameter + ";\n"; + } + str += ") {\n" + body + "}\n"; + str += cls + ".builtin$cls=\"" + name + "\";\n"; + str += "$desc=$collectedClasses." + cls + ";\n"; + str += "if($desc instanceof Array) $desc = $desc[1];\n"; + str += cls + ".prototype = $desc;\n"; + if (typeof defineClass.name != "string") { + str += cls + ".name=\"" + cls + "\";\n"; + } + str += accessors.join(""); + return str; + } + var inheritFrom = function() { + function tmp() { + } + var hasOwnProperty = Object.prototype.hasOwnProperty; + return function(constructor, superConstructor) { + tmp.prototype = superConstructor.prototype; + var object = new tmp(); + var properties = constructor.prototype; + for (var member in properties) + if (hasOwnProperty.call(properties, member)) + object[member] = properties[member]; + object.constructor = constructor; + constructor.prototype = object; + return object; + }; + }(); + Isolate.$finishClasses = function(collectedClasses, isolateProperties, existingIsolateProperties) { + var pendingClasses = {}; + if (!init.allClasses) + init.allClasses = {}; + var allClasses = init.allClasses; + var hasOwnProperty = Object.prototype.hasOwnProperty; + if (typeof dart_precompiled == "function") { + var constructors = dart_precompiled(collectedClasses); + } else { + var combinedConstructorFunction = "function $reflectable(fn){fn.$reflectable=1;return fn};\n" + "var $desc;\n"; + var constructorsList = []; + } + for (var cls in collectedClasses) { + if (hasOwnProperty.call(collectedClasses, cls)) { + var desc = collectedClasses[cls]; + if (desc instanceof Array) + desc = desc[1]; + var classData = desc["^"], supr, name = cls, fields = classData; + if (typeof classData == "string") { + var split = classData.split("/"); + if (split.length == 2) { + name = split[0]; + fields = split[1]; + } + } + var s = fields.split(";"); + fields = s[1] == "" ? [] : s[1].split(","); + supr = s[0]; + split = supr.split(":"); + if (split.length == 2) { + supr = split[0]; + var functionSignature = split[1]; + if (functionSignature) + desc.$signature = function(s) { + return function() { + return init.metadata[s]; + }; + }(functionSignature); + } + if (supr && supr.indexOf("+") > 0) { + s = supr.split("+"); + supr = s[0]; + var mixin = collectedClasses[s[1]]; + if (mixin instanceof Array) + mixin = mixin[1]; + for (var d in mixin) { + if (hasOwnProperty.call(mixin, d) && !hasOwnProperty.call(desc, d)) + desc[d] = mixin[d]; + } + } + if (typeof dart_precompiled != "function") { + combinedConstructorFunction += defineClass(name, cls, fields); + constructorsList.push(cls); + } + if (supr) + pendingClasses[cls] = supr; + } + } + if (typeof dart_precompiled != "function") { + combinedConstructorFunction += "return [\n " + constructorsList.join(",\n ") + "\n]"; + var constructors = new Function("$collectedClasses", combinedConstructorFunction)(collectedClasses); + combinedConstructorFunction = null; + } + for (var i = 0; i < constructors.length; i++) { + var constructor = constructors[i]; + var cls = constructor.name; + var desc = collectedClasses[cls]; + var globalObject = isolateProperties; + if (desc instanceof Array) { + globalObject = desc[0] || isolateProperties; + desc = desc[1]; + } + allClasses[cls] = constructor; + globalObject[cls] = constructor; + } + constructors = null; + var finishedClasses = {}; + init.interceptorsByTag = Object.create(null); + init.leafTags = {}; + function finishClass(cls) { + var hasOwnProperty = Object.prototype.hasOwnProperty; + if (hasOwnProperty.call(finishedClasses, cls)) + return; + finishedClasses[cls] = true; + var superclass = pendingClasses[cls]; + if (!superclass || typeof superclass != "string") + return; + finishClass(superclass); + var constructor = allClasses[cls]; + var superConstructor = allClasses[superclass]; + if (!superConstructor) + superConstructor = existingIsolateProperties[superclass]; + var prototype = inheritFrom(constructor, superConstructor); + if (hasOwnProperty.call(prototype, "%")) { + var nativeSpec = prototype["%"].split(";"); + if (nativeSpec[0]) { + var tags = nativeSpec[0].split("|"); + for (var i = 0; i < tags.length; i++) { + init.interceptorsByTag[tags[i]] = constructor; + init.leafTags[tags[i]] = true; + } + } + if (nativeSpec[1]) { + tags = nativeSpec[1].split("|"); + if (nativeSpec[2]) { + var subclasses = nativeSpec[2].split("|"); + for (var i = 0; i < subclasses.length; i++) { + var subclass = allClasses[subclasses[i]]; + subclass.$nativeSuperclassTag = tags[0]; + } + } + for (i = 0; i < tags.length; i++) { + init.interceptorsByTag[tags[i]] = constructor; + init.leafTags[tags[i]] = false; + } + } + } + } + for (var cls in pendingClasses) + finishClass(cls); + }; + Isolate.$lazy = function(prototype, staticName, fieldName, getterName, lazyValue) { + var sentinelUndefined = {}; + var sentinelInProgress = {}; + prototype[fieldName] = sentinelUndefined; + prototype[getterName] = function() { + var result = $[fieldName]; + try { + if (result === sentinelUndefined) { + $[fieldName] = sentinelInProgress; + try { + result = $[fieldName] = lazyValue(); + } finally { + if (result === sentinelUndefined) { + if ($[fieldName] === sentinelInProgress) { + $[fieldName] = null; + } + } + } + } else { + if (result === sentinelInProgress) + H.throwCyclicInit(staticName); + } + return result; + } finally { + $[getterName] = function() { + return this[fieldName]; + }; + } + }; + }; + Isolate.$finishIsolateConstructor = function(oldIsolate) { + var isolateProperties = oldIsolate.$isolateProperties; + function Isolate() { + var hasOwnProperty = Object.prototype.hasOwnProperty; + for (var staticName in isolateProperties) + if (hasOwnProperty.call(isolateProperties, staticName)) + this[staticName] = isolateProperties[staticName]; + function ForceEfficientMap() { + } + ForceEfficientMap.prototype = this; + new ForceEfficientMap(); + } + Isolate.prototype = oldIsolate.prototype; + Isolate.prototype.constructor = Isolate; + Isolate.$isolateProperties = isolateProperties; + Isolate.$finishClasses = oldIsolate.$finishClasses; + Isolate.makeConstantList = oldIsolate.makeConstantList; + return Isolate; + }; +} +})() + +//# sourceMappingURL=bank_terminal_s5.dart.js.map +//@ sourceMappingURL=bank_terminal_s5.dart.js.map diff --git a/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart.js.map b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart.js.map new file mode 100644 index 0000000..8ac99bc --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart.js.map @@ -0,0 +1,8 @@ +{ + "version": 3, + "file": "bank_terminal_s5.dart.js", + "sourceRoot": "", + "sources": ["packages/$sdk/lib/_internal/lib/interceptors.dart","packages/$sdk/lib/_internal/lib/js_array.dart","packages/$sdk/lib/internal/iterable.dart","packages/$sdk/lib/_internal/lib/js_number.dart","packages/$sdk/lib/_internal/lib/js_string.dart","packages/$sdk/lib/_internal/lib/js_helper.dart","packages/$sdk/lib/_internal/lib/isolate_helper.dart","packages/$sdk/lib/collection/queue.dart","packages/$sdk/lib/_internal/lib/collection_patch.dart","packages/$sdk/lib/async/timer.dart","packages/$sdk/lib/_internal/lib/native_helper.dart","packages/$sdk/lib/_internal/lib/js_rti.dart","packages/$sdk/lib/_internal/lib/core_patch.dart","packages/$sdk/lib/_internal/lib/regexp_helper.dart","../packages/bank_terminal_s5/model/bank_account.dart","../packages/bank_terminal_s5/model/person.dart","packages/$sdk/lib/core/date_time.dart","bank_terminal_s5.dart","packages/$sdk/lib/html/dart2js/html_dart2js.dart","packages/$sdk/lib/internal/lists.dart","packages/$sdk/lib/internal/symbol.dart","packages/$sdk/lib/_internal/lib/js_names.dart","packages/$sdk/lib/async/async_error.dart","packages/$sdk/lib/async/schedule_microtask.dart","packages/$sdk/lib/_internal/lib/async_patch.dart","packages/$sdk/lib/async/stream_pipe.dart","packages/$sdk/lib/async/zone.dart","packages/$sdk/lib/core/duration.dart","packages/$sdk/lib/async/future_impl.dart","packages/$sdk/lib/async/stream.dart","packages/$sdk/lib/collection/hash_map.dart","packages/$sdk/lib/collection/iterable.dart","packages/$sdk/lib/collection/maps.dart","packages/$sdk/lib/collection/hash_set.dart","packages/$sdk/lib/collection/list.dart","packages/$sdk/lib/_internal/lib/convert_patch.dart","packages/$sdk/lib/convert/json.dart","packages/$sdk/lib/core/string.dart","packages/$sdk/lib/core/errors.dart","packages/$sdk/lib/core/exceptions.dart","packages/$sdk/lib/core/list.dart","packages/$sdk/lib/core/print.dart","packages/$sdk/lib/_internal/lib/internal_patch.dart","packages/$sdk/lib/core/expando.dart","packages/$sdk/lib/core/null.dart","packages/$sdk/lib/core/object.dart","packages/$sdk/lib/core/string_buffer.dart","packages/$sdk/lib/collection/linked_hash_set.dart","packages/$sdk/lib/svg/dart2js/svg_dart2js.dart","packages/$sdk/lib/_internal/lib/isolate_patch.dart","packages/$sdk/lib/typed_data/dart2js/native_typed_data_dart2js.dart","packages/$sdk/lib/_internal/lib/js_primitives.dart"], + "names": ["getInterceptor","makeDispatchRecord","getNativeInterceptor","initNativeDispatch","lookupAndCacheInterceptor","bool","int","Primitives","String","IterableMixinWorkaround","E","length","List","Iterator","iterable","factory","num","roundToDouble","double","other","result","_skipLeadingWhitespace","_skipTrailingWhitespace","static","string","_isWhitespace","_callInIsolate","isolate","_globalState","leaveJsAsync","weakPorts","_addRegistration","entry","rootContext","computeThisScriptFromTrace","_deserializeMessage","msg","_add","events","workerIds","_serializeMessage","fillLiteralMap","_log","print","context","mirrorFunctionCacheName","mirrorInvokeCacheName","replyTo","runStartFunction","supportsWorkers","_visited","_Manager","isWorker","fromCommandLine","topEventLoop","isolates","managers","mainManager","pauseCapability","pauseTokens","isPaused","_updateGlobalState","_length","delayedEvents","_head","_table","_tail","_grow","_modificationCount","doneHandlers","terminateCapability","responsePort","dynamic","isolateStatics","code","RawReceivePortImpl","ports","id","_shutdown","port","_current","_IsolateEvent","dequeue","event","next","runIteration","_runHelper","fn","_startIsolate","topLevel","_isolateId","_receivePort","message","addPause","removePause","addDoneListener","removeDoneListener","setErrorsFatal","handlePing","_workerId","_receivePortId","_isClosed","_handler","visitSendPort","visitCapability","x","SendPort","list","Capability","operator","tagged","traverse","isPrimitive","visitPrimitive","_dispatch","visitList","visitMap","visitObject","copy","Map","map","_nextFreeRefId","_serializeList","deserialize","_deserialized","_deserializeHelper","_deserializeList","_deserializeMap","deserializeSendPort","deserializeCapability","deserializeObject","dartList","keys","values","TimerImpl","_handle","_inEventLoop","convertDartClosureToJS","callback","_id","value","match","handleError","source","name","joinArguments","getRuntimeTypeInfo","objectTypeName","array","a","_fromCharCodeApply","stringFromCodePoints","receiver","iae","ioore","wrapException","toStringWrapper","JS","throwExpression","unwrapException","saveStackTrace","nsme","notClosure","nullCall","nullLiteralCall","undefCall","undefLiteralCall","nullProperty","undefProperty","undefLiteralProperty","object","invokeClosure","JS_CALL_IN_ISOLATE","info","functionCounter","forwardCallTo","functions","forwardInterceptedCallTo","cspForwardCall","computeFieldNamed","selfFieldNameCache","BoundClosure","receiverFieldNameCache","cspForwardInterceptedCall","closureFromTearOff","Closure","RuntimeFunctionType","RuntimeType","Object","getRuntimeTypeArguments","substitute","getRuntimeTypeArgument","getTypeArgumentByIndex","type","_contents","runtimeTypeToString","invokeOn","isSubtype","computeSignature","isFunctionSubtype","areSubtypes","names","areAssignable","areAssignableMaps","getTagFunction","alternateTagFunction","makeLeafDispatchRecord","patchInteriorProto","makeDefaultDispatchRecord","initNativeDispatchFlag","initNativeDispatchContinue","dispatchRecordsForInstanceTags","interceptorsForUncacheableTags","initHooks","tags","prototypeForTagFunction","applyHooksTransformer","matchTypeError","_pattern","_arguments","_argumentsExpr","_expr","_method","_receiver","_message","JsNoSuchMethodError","_trace","_exception","closure","_self","_target","RuntimeError","_extractFunctionTypeObjectFrom","toRti","returnType","parameterTypes","listToRti","optionalParameterTypes","namedParameters","extractKeys","Match","_nativeRegExp","_match","_MatchImplementation","set","makeNative","_number","owner","_balance","acc","_pin_code","date_created","date_modified","JSON","BankAccount","json","DateTime","_name","_gender","per","address","_email","date_birth","_date_birth","Person","millisecondsSinceEpoch","document","balance","number","btn_other","amount","btn_deposit","btn_interest","error","_tryResume","_wrapZone","disable_transactions","readData","_getItem","window","bac","clearData","nonNegative","changeBalance","_setItem","e","interest","f","_toStringList","otherList","Lists","dst","src","symbol","action","elementAt","_iterable","_index","_f","_iterator","T","_source","Function","errorHandler","zone","_nextCallback","_lastCallback","_asyncRunCallbackLoop","_createTimer","_runUserCode","onSuccess","userCode","onError","subscription","future","_cancelAndErrorClosure","Zone","_rootCreateTimer","Timer","_duration","_rootRun","_scheduleAsyncCallback","_state","Future","_registerErrorHandler","_addListener","_resultOrListeners","_AsyncError","_zone","_rootScheduleMicrotask","listener","_Future","current","_chainCoreFuture","_chainForeignFuture","_removeListeners","_setValue","_propagateToListeners","_setError","internalFuture","asyncError","_rootHandleUncaughtError","_propagateMultipleListeners","_onValueCallback","_whenCompleteActionCallback","otherZone","handleValueCallback","handleWhenCompleteCallback","target","_rootRunUnary","_errorTestCallback","_onErrorCallback","errorCallback","_rootRunBinary","completeResult","_cancelAndValue","_cancelAndError","run","handleUncaughtError","runUnary","ZoneCallback","registerCallback","ZoneUnaryCallback","registerUnaryCallback","_toStringVisiting","_iterablePartsToStrings","it","parts","ultimateString","penultimateString","m","Iterable","V","_strings","_nums","_rest","_computeHashCode","_findBucketIndex","_newHashTable","_addHashTableEntry","_setTableEntry","_keys","_computeKeys","key","_map","_offset","cell","_newLinkedCell","_removeHashTableEntry","_unlinkCell","_first","_modifications","LinkedHashMapCell","_last","last","previous","Maps","_cell","_computeElements","bucket","_elements","element","objectHashCode","_set","add","LinkedHashSetCell","iterator","_iterableToString","queue","ListQueue","_queue","_position","_end","_convertJsonToDart","revive","walk","_parseJson","cause","JsonUnsupportedObjectError","JsonCyclicError","_reviver","decoder","_JsonStringifier","_toEncodableFunction","encoder","JsonEncoder","JsonDecoder","s","_sink","_seen","stringifyJsonValue","checkCycle","_toEncodable","escape","stringifyValue","_removeSeen","stringifier","_symbol_dev","printString","sb","_symbolToString","isUtc","_fourDigits","lazyAsJsDate","_twoDigits","_threeDigits","re","parseIntOrZero","parseDoubleOrZero","Duration","twoDigits","inMinutes","inSeconds","sixDigits","inMicroseconds","StackTrace","ArgumentError","RangeError","UnsupportedError","UnimplementedError","StateError","modifiedObject","Error","ConcurrentModificationError","variableName","CyclicInitializationError","_getKey","_keyCount","objects","write","StringBuffer","fragment","ElementStream","_eventType","_localName","DocumentFragment","_validators","_defaultValidator","_defaultSanitizer","_parseDocument","_parseRange","_createElement","base","tagName","contextElement","treeSanitizer","text","append","createFragment","parentNode","nodeValue","Node","_key","forEach","table","section","row","content","Location","_isDartLocation","_location_wrapper","Element","_this","_element","attributes","_matches","node","StreamSubscription","_unlisten","_onData","_pauseCount","_useCapture","_allowedElements","_attributeValidators","validator","_Html5NodeValidator","_hiddenAnchor","_loc","v","allowedElements","allowedUriAttributes","uriPolicy","allowedAttributes","_templateAttrs","_TemplatingNodeValidator","attributeName","allowsElement","_array","_ptr","attrs","sanitizeTree","template","sanitizeNode","child","root","svgFragment","_invalidIndex","computeThisScript","extractPattern","provokeCallErrorOn","provokePropertyErrorOn","Set"], + "mappings": "A;A,yC;A,sB;A,0E;A,a;A,E;A,8B;A,6E;A,6E;A,2C;A;A,6B;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;;A;A;;A,iB;A;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;A;A,oB,wB,A,E,C,E,C;A,E,G,C,C,E,C;A,E,Q,C,C,C;A,E,E,G,C,C,a;A,E,C;A,C,A;A,kB,sB,A,E,C,E,C;A,E,G,C,C,E,C;A,E,c,C,CAuDAA;AAOE;GACF,A,C;A,E,kB,C,CAUAC;AAiCE;GAEF,A,C;A,E,oB,C,CAWAC;;;;;QAKMC;;;QAKO;;;AAEW;;AACD;;;AAGjB;;aASM,iBAAA;;kBAIQC;;;;AAQd;;AAEA;;AAIJ;GACF,A,C;A,E,W,C,C,C;A,E,E,G,C,C,S,C;A,E,E,G,C,CAqHEC;AAAwB;KAAyB,A,C;A,E,E,Y,C,CAEjDC;AAAiB,YAAGC;KAA+B,A,C;A,E,E,U,C,CAEnDC;AAAkB,YAAGD;KAA+B,A,C;A,E,E,G,C,C,8I;A,E,C,C;A,E,M,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,U,C,CAoBpDC;AAAkB;KAAmC,A,C;A,E,E,Y,C,CAIrDF;AAAiB;KAA2C,A,C;A,E,E,O,C,C,I;A,E,C,C;A,E,M,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,G,C,CAe5DD;AAAwB;KAAyB,A,C;A,E,E,U,C,CAGjDG;AAAkB;KAAS,A,C;A,E,E,Y,C,CAE3BF;AAAiB;KAAI,A;A,E,C,C;A,E,gB,C,C,C;A,E,E,G,C,C,c,C;A,E,E,Y,C,CA0CrBA;AAAiB;KAAI,A;A,E,C,C;A,E,qB,C,C,C;A,E,E,G,C,C,mB;A,E,C,C;A,E,uB,C,C,C;A,E,E,G,C,C,mB;A,E,C,C;A,E,O,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,Q,C,CCnPrBD;;;0BA3CU;AA6CR,kBAAkB,IAAE;YACN,MAAR;;AAEF;;AAEJ,AACA;KACF,A,C;A,E,E,S,C,CA8BA;AACE,YAAOI;KACT,A,C;A,E,E,W,C,CAkDAC;;;AACE,YAAO;KACT,A,C;A,E,E,U,C,CAiGAL;;AACE,kBAAkB,IAAEM;YACN,MAAR;AAAkB;AACxB,AACA;KACF,A,C;A,E,E,W,C,CAEAN;AAAiB,YAAGM;KAAW,A,C;A,E,E,U,C,CAI/BH;AAAkB,YAAGC;KAAwD,A,C;A,E,E,iB,C,CAE7EG;;;AAEI;;;;AAEA;;KAEJ,A,C;A,E,E,Q,C,C,Q,C,S,C,C,C;A,E,E,E,M,C,I,C,iB,C,S,C,C,I,C,C;A,E,E,C,C;A,E,E,Y,C,CAIAC;AAAyB,0CCjCaC;KDiCe,A,C;A,E,E,Y,C,CAErDR;AAAiB,YAAGC;KAA+B,A,C;A,E,E,U,C,CAEnDD;AAAe;KAAoC,A,C;A,E,E,U,C,CAEnD;UAEgB;aAAW,iBAAA;;0BAxQjB;;KA2QV,A,C;A,E,E,M,C,CAEAI;;;UAEY,SAAGC,mBAAgB;aAAW,iBAAA;AACxC;KACF,A,C;A,E,E,S,C,CAEA;;0BAzRU;;;UA4RE,SAAGA,mBAAgB;aAAW,iBAAA;;KAE1C,A,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,I,C;A,E,E,kB,C,C,I,C;A,E,E,M,C,C,C,qB,C,CAvVAI;;8EAGkC;eACxB,iBAAA;;;AAER;OACF,A,C;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,kB,C;A,E,E,Y,C,CEuBAV;AAAkB;KAAmC,A,C;A,E,E,W,C,CAErDW;AAGE;KACF,A,C;A,E,E,O,C,CASAV;;UACW,2BAAsB;AAC7B;;aA0B8B;AAvB9B;;WAGI,iBAAA;KACR,A,C;A,E,E,O,C,CAKAA;AAAY,YAAGW,cAAAA;KAAuB,A,C;A,E,E,e,C,CAMtCC;UACW;AACP;;AAEA;KAEJ,A,C;A,E,E,iB,C,CAmBAV;;UAG2C;aACjC,iBAAA;;;aAzEsC,AAAR;;;;AA4ET;AAC7B;KACF,A,C;A,E,E,U,C,CAqCAA;;AAEI;;AAEA;KAEJ,A,C;A,E,E,Y,C,CAEAF;AAAiB;KAAoC,A,C;A,E,E,I,C,CAIrDU;;;AAEE;KACF,A,C;A,E,E,I,C,CAEAA;AAEE;KACF,A,C;A,E,E,I,C,CAOAA;;;AAEE;KACF,A,C;A,E,E,W,C,CA0BAA;AACE,kEAEM;KACR,A,C;A,E,E,mB,C,CAiCAA;;UAC8B;;;aAMwB;;;AANpD;KAOF,A,C;A,E,E,G,C,CAkCAX;;aAC2B,iBAAA;AACzB;KACF,A,C;A,E,E,G,C,CAOAA;;;AAEE;KACF,A,C;A,E,E,G,C,CAEAA;;aAC2B,iBAAA;AACzB;KACF,A,C;A,E,E,M,C,C,I,C;A,E,E,M,C,C,C,G,C,C,yC,C;A,E,C,C;A,E,K,C,C,C;A,E,E,G,C,C,e,C;A,E,E,M,C,C,I,C;A,E,E,M,C,C,I;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,kB,C;A,E,E,M,C,C,I;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,qB,C;A,E,E,Y,C,CCvSAC;UAEY;aAAW,iBAAA;UACX,SAAGK;aAAc,iBAAA;AAC3B;KACF,A,C;A,E,E,I,C,CAqBAH;;;AAEE;KACF,A,C;A,E,E,Y,C,CAyCAH;;UAEyB,QAAE;aACjB,iBAAA,6BAA+B;iBAKhB,QADHc;UAEL,WAAER;AAAQ;AACvB;KAGJ,A,C;A,E,E,Y,C,C,Q,C,S,C,C,O,C,C,C;A,E,E,E,M,C,I,C,Y,C,S,C,C,O,C,C,C,C,C;A,E,E,C,C;A,E,E,W,C,CAEAH;;mBAEmCG;;0BCmiC3B;UDjiCS;aAAY,iBAAA;;;UACZ;aAAkB,iBAAA;UACpB,WAAEA;aAAc,iBAAA;AAC7B;KACF,A,C;A,E,E,W,C,C,Q,C,S,C,C,U,C,C,C;A,E,E,E,M,C,I,C,W,C,S,C,C,U,C,C,I,C,C;A,E,E,C,C;A,E,E,a,C,CAEAH;AACE;KACF,A,C;A,E,E,M,C,CAsGAA;;;iBAMMY;;AAAoB;UACRA;qBAGDC;;AACoB;;;WAMO;kBAA3BD,wCAEFE;;AAEqC;AAClD;KACF,A,C;A,E,E,I,C,CA0DAd;;UACQ;AAAU;yBACE;AAAkB;UAC1B;;AAMV;aACY;mBAAqB;;;;;;AAIjC,AACA;KACF,A,C;A,E,E,W,C,CAoEAH;AAAiB,YAAGM;KAAW,A,C;A,E,E,U,C,CAW/BH;AAAkB;KAAO,A,C;A,E,E,Y,C,CAQzBF;;AAIE,gBAAoBK,kCAAF;eACE,YAAQ;eACR,YAAQ,QAAuB,AAAA,CAAR;;;AAE3C,aACkB,YAAQ,QAAuB,AAAA,CAAR;;AAEzC,YAAkB,aAAQ,QAAuB,AAAA,CAAR;KAC3C,A,C;A,E,E,U,C,CAIAL;AAAe;KAA+B,A,C;A,E,E,M,C,CAE9CE;;;UAEY,SAAGG,mBAAgB;aAAW,iBAAA;AACxC;KACF,A,C;A,E,E,S,C,C,I,C;A,E,E,M,C,C,C,sB,C,CA/RAY;YAGe;;;;;;;;;;AAUP;;AAEA;;;;;;;;;;;;;;;;;;;;;;AAuBF;;AAEA;;OAEN,A,C,C,+B,C,CAIAA;;AAGE,kBAAeC,eAAF;cApKH;8BAAiB;;cAsKZ,mBACA,mBACT,CAACC;;;;AAIP,AACA;OACF,A,C,C,gC,C,CAIAF;;AAGE,kBArLaZ,eAqLA;mBAC4B;cAtL/B;8BAAiB;;cAuLZ,mBACA,mBACT,CAACc;;;AAIP,AACA;OACF,A,C;A,E,C;A,C,A;A,oB,wB,A,E,C,E,C;A,E,G,C,C,E,C;A,E,c,C,CErLFC;iBACeC;IACbC,AAAAA;AACA;GACF,A,C;A,E,Y,C,CAmBAC;aACED;IAAAA,yBAA6C,AAA7CA;GAEF,A,C;A,E,gB,C,CAiBA;;;;;;;;;;;;;;;;;QAWMA;AAAuB;;SA8KZA;IAAAA,mBAA0B;SAGE;SAGhB;;uEAQQ,2BACI,sCAMT;IAwI5BE;IACAC;IA1UFH;IAMAA;;SAEII;;MACFC;;WACSD;;QACTC;;QAEAA;;IAEFL,AAAAA;GACF,A,C;A,E,gC,C,CAyeEL;;QAEoB;AAChB;;AAEmB,YAagBW;;AAZX;QAEtBN;AAAuB,YAAOM;AAClC;GACF,A,C;A,E,yC,C,CAUAX;;;;;;aAU6B,iBAAA;;;QAaf;AAAS;;QAOT;AAAS;SAEf,iBAAA;GACR,A,C;A,E,oC,C,CAaAA;;UACYY;;YACFC;;QAEJR,oCAAgCQ;uBACVA;4CAEhBR;eAEKQ;kBACGD,sBAAoBC;qBACjBA;sBACCA;kBACJD,sBAAoBC;;aA5ZzBR;QAAAA,mBAA0B;aAGE;aAGhB;;uEAQQ,2BACI,sCAMT;QAwI5BE;QACAC;QCkIwBM,ADrFxBC,AAmNIV;QAUAA;QACAA,AAAAA;;;aAGaQ;cAAqBA;aACrBA;aAAaA;aACbA;aAAmBA;aACnBA;;;;;;mBAgNFR;QAAAA,mBAA0B;QAEzCW;QACAX,AAAAA;2BACuCY,oBDg4BlCC,iECz3BUD,4CAEJA,mFDu3BwB;;;YC/kCtB,AAFOJ;UAGdA,WAAAA,wBAAiBA;QAEnBR,AAAAA;;;QAGAA,AAAAA,sCAA6BW;;QAE7BX,AAAAA;;;QAGAc,sBAAKN;;;YAGDR;eACFA;eACIY,oBD+jCLC,mDAA8B;UChkC7Bb;;;UAGAe,QAAMP;;;aAIFA,iBAAAA;;GAEZ,A,C;A,E,mB,C,CAGAb;;QACMK;WACFA;WACIY,oBDijCDC,iDAA8B;MCljCjCb;;;;;;QAGA;;aAGQ,iBAAA;;;GAGZ,A,C;A,E,4B,C,CAkHAL;;cA9wBmCK;SAoxBJgB;IDjU7BC,uCAAwB;IACxBC,qCAAsB;SCmURF;SAgNqBhB,AAAAA;SA/MrBgB;IAFdG,qEAGcH;;;MAeZA;MCtUsBP,ADrFxBC,AA4ZEV;;MAGAoB;GAEJ,A,C;A,E,iB,C,CAmOFR;;QA35ByBS;;MA+6BKC;AAlB1B,YAAO;;;MA8CeA;AA5CtB,YAAO;;GAEX,A,C;A,E,mB,C,CAGAf;QAp6ByBc;AAs6BrB,YAAO;;AAGP;GAEJ,A,C;A,E,6B,C,CAkME1B;AACE;GACF,A,C;A,E,yB,C,CA6FAA;AACE;GACF,A,C;A,E,wB,C,C,C;A,E,E,G,C,C,yB,C;A,E,E,M,C,CAryCmB;MAAKS;KAAa,A;A,E,C,C;A,E,yB,C,C,C;A,E,E,G,C,C,yB,C;A,E,E,M,C,CAElB;MAAKA;KAAmB,A;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,0K,C;A,E,E,U,C,CA8G3CmB;;;;;MAeEC;;aAFoC,cAIgB;;;MADpDH;MAEAI,6BAAsC;;;;MAhBtCC;MACAC,gBAAW;MACXC,gBAAW;UACPJ;;QACFK;;;;;KAGJ,A;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,2J,C;A,E,E,U,C,CA8EA;UACsB,CAAA,AAAhBC;AAAqC;UACrCC,AAAAA,qCAA2B,CAACC;QAC9BA;MAEFC;KACF,A,C;A,E,E,a,C,CAEA;;UACM,CAACD;AAAU;WACfD;MAAAA;UE4kCkBG;AF1kChB,kBAAMC,yBLoBUpD,WAEG;;kBKtBboD;mBACkBA;eA8J1BzB,AA7JIV;eCiPKoC;eAAcC;eAAAA;eAAH,CAAA,AAAL,SAAsB;UAArCD;;kBAAuBC;UACvBA;qBACaC;YAAOC;UACpBC,wBAAkB,AAAlBA;;ADnPE,QACAR;;MAEFC;KACF,A,C;A,E,E,iB,C,CAEA;eACMQ;;;QACFA;;UAIEA;AAAqC;MACzCA,AAAAA;KACF,A,C;A,E,E,oB,C,CAEA;eACMA;;AAAsB;MAC1BA;KACF,A,C;A,E,E,gB,C,CAEA;UAC0B,CAAA,AAApBC;AAAyC;MAC7C;KACF,A,C;A,E,E,Y,C,CAEA;UACe;QCkNWjC,ADrFxBC,AA5HEV;;QAMA2C;KAEJ,A,C;A,E,E,M,C,CAKAC;;YACY5C;MACVA;UAauB6C;;;iBATZC;;QAET9C;YACQ;cAMa6C;;AAJvB;KACF,A,C;A,E,E,Q,C,CAgCAE;AAAsC,YAAGC,AAAAA;KAAa,A,C;A,E,E,kB,C,CAEtD;eACMA;UAAAA;aACI,iBAAA;MAERA;KACF,A,C;A,E,E,oB,C,CAkBA;UACsC,AAAnB,AEsGDd,AFtGZc,iCEu9BYd,AFv9BGhC,0CAAwB8B;QACzChC,AAAAA,uCAAsBiD;;QAEtBC;KAEJ,A,C;A,E,E,W,C,CAEA;MACElD,AAAAA,sCAA6BiD;eAGzBR;UAAa;AACf,yCJ3HkCvD,qBI2HlC;UACEiE,WJ1HWC;AI2Hb,KAEJ,A;A,E,C,C;A,E,kC,C,C,C;A,E,E,G,C,C,0B,C;A,E,E,M,C,CAnG4C;MACtCT;KACF,A;A,E,C,C;A,E,U,C,C,C;A,E,E,G,C,C,mC,C;A,E,E,S,C,CAiIJU;;WACM3C;WC5Dc0B;iBAASE;AD4DP;MCwFpBE,wBAAkB,AAAlBA;WACWH;;;cAAAA;eAAAA;MACXA;MACAD,WAAoB,CAAA,AAAL,SAAsB;AD1FrC;KACF,A,C;A,E,E,c,C,CAmBA3D;;eACgB6E;;YAjBe,AAAzBtD,wCACGA,AAAAA,2CAAkCA,AAAAA,oCAClCA,6CE+CWkC,AF9CXlC,AAAAA;4BAQC;;YA9OJwB,wBEoRcU,AFnRXP,yCACAD,AAAAA;eACLG;eAAwBjB,oBD2gDrBC,uCAA8B;UC3gDjCgB;;;AAqPA;;MAEF0B;AACA;KACF,A,C;A,E,E,Y,C,CAMA;UACmB;QAMfC;;AAGA,eAAOC;;AAAiB,KAE5B,A,C;A,E,E,K,C,CAKA;;UACM,AAACzD;QACH0D;;;UAGEA;;eADF;;;eAGE1D;eAAqCY,oBDovCpCC,0EAA8B;UCpvC/Bb;;;;KAIN,A;A,E,C,C;A,E,0B,C,C,C;A,E,E,G,C,C,kB,C;A,E,E,M,C,CAzBI;UACM,CAACyD;AAAgB;MG7azB;KH+aE,A;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,2B,C;A,E,E,S,C,CAiCJ;eACM1D;UAAAA;QACFA,AAAAA;AACA;;MAEFA,UAAa4D;KACf,A;A,E,C,C;A,E,gB,C,C,C;A,E,E,G,C,C,S;A,E,C,C;A,E,4C,C,C,C;A,E,E,G,C,C,8E,C;A,E,E,M,C,CA0HiD;MACzCC;KAEF,A;A,E,C,C;A,E,6C,C,C,C;A,E,E,G,C,C,oD,C;A,E,E,M,C,CAwLJ;;UACM;QACFC;;;;aACSA;;UACTA;;eACSA;;YACTA;;YAEAA;;;KAEJ,A;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,S,C;A,E,E,W,C,C,I,C;A,E,E,a,C,C,I;A,E,C,C;A,E,iB,C,C,C;A,E,E,G,C,C,uC,C;A,E,E,M,C,CA8EF;;;WAEwCC;gBAAtB9D,AAAAA;;AACK;WACjB+D;UAAAA;AAAwB;wBAQwB,AAA5B/D,2CACc,AAA/BA,AAAAA;;;cAGCY;;;;;UAEJb;;gBAplBIiE;;YAEJC,mBAASD,kBAAYA;;;YAGrBE,sBAAYF;;;YAGZG,0BAAgBH;;;YAGhBI,6BAAmBJ;;;YAGnBK,yBAAeL,kBAAYA;;;YAG3BM,qBAAWN,kBAAYA;;;YAGvBjD;;AAkkBF;;WAEFf;;MCvawBS,ADrFxBC;KAogBF,A,C;A,E,E,G,C,CAEAjC;;;AAA4B,+DACV,MAAbsF,mBAAgBxE;KAAmB,A,C;A,E,E,Y,C,CAExCb;AAAiB,YAAGqF,AAAAA;KAAgB,A,C;A,E,E,oB,C,C,I,C;A,E,E,W,C,C,I,C;A,E,E,a,C,C,I;A,E,C,C;A,E,8B,C,C,C;A,E,E,G,C,C,0C,C;A,E,E,M,C,CAbS;;WACpCA;UAAD,CAACA;;;qBAEKxD;;QAERwD;;KAEJ,A;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,mD,C;A,E,E,M,C,CAkBF;;sBACwBnD,oBDkyBjBC,uEAA8B;UC7xB/Bb;QAGFA,AAAAA;;;kBAG2BA,AAAAA,oCAAsBuE;YACrC;;;KAIhB,A,C;A,E,E,G,C,CAEA9F;;;AACE,6DACe,MAAV8F,gBAAahF,oBACF,MAAXuE,iBAAcvE,qBACC,MAAfiF,qBAAkBjF;KACzB,A,C;A,E,E,Y,C,CAEAb;;WAEU6F;;;WAAoBT;;;WAAmBU;;cAAAA;AAA/C,YAA6C,EAAA,AAApB,AAAP,WAAqB;KACzC,A,C;A,E,E,kB,C,C,I,C;A,E,E,W,C,C,I,C;A,E,E,a,C,C,I;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,iC,C;A,E,E,U,C,C,Q,C,I,C,C,C;A,E,E,E,M,C,I,C,Q,C,M,C,I,C,C;A,E,E,C,C;A,E,E,uB,C,CAkCA;UACMC;AAAW;MACfC;KACF,A,C;A,E,E,M,C,C,C,G,C,C,gC,C;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,qC,C;A,E,E,e,C,CAmEAC;;AAC8B,4BAaR3E,mCAChBmD,cAAiBA,AAAAA;;AAbK,4BAiBNA,aAAgBA,cAAiBA;;KAfvD,A,C;A,E,E,iB,C,CAEAyB;;AAEI,8BAAsBC;;KAG1B,A;A,E,C,C;A,E,S,C,C,C;A,E,E,G,C,C,kB,C;A,E,E,e,C,CAiBAF;;AAC8B,uCAaCxB,gBAAmBA;;AAZtB,qCAiBtBA,aAAiCA,kBAAjBA;;KAftB,A,C;A,E,E,iB,C,CAEAyB;;AAEI,oCAAoCC;;KAGxC,A;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,6B,C;A,E,E,qB,C,CAcAC;;;kBACkBC;kBACAA;sBACIA;UAGN,iBAAG/E;kBACDA,AAAAA;;AACO;sBACHD;;AACO;AACzB;;AAEA;KAEJ,A,C;A,E,E,uB,C,CAEAiF;AACE,kCAAoCD;KACtC,A;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,e,C;A,E,E,M,C,CAOAE;AACE;KACF,A,C;A,E,E,S,C,CAGA;MACEC,AAAAA;;KAEF,A,C;A,E,E,O,C,CAGA;MAEEA;KACF,A,C;A,E,E,S,C,CAGA;;AACE,qBAAyBA,AAAAA,2BAAiB;aACrBA;;gBAAAA;QAAAA;;AACrB,MACAA;KACF,A;A,E,C,C;A,E,2B,C,C,C;A,E,E,G,C,C,S,C;A,E,E,M,C,CAoCAD;AAAuB;KAAO,A,C;A,E,E,S,C,CAC9B;KAAyC,A,C;A,E,E,O,C,CAEzC;KAAe,A,C;A,E,E,S,C,CACf;KAAiB,A;A,E,C,C;A,E,iB,C,C,C;A,E,E,G,C,C,S,C;A,E,E,U,C,CAWjBE;;UACMC;AAAgB,cAAOC;MAC3B/D,AAAAA;;;iBAGWgE;;QAEThE,AAAAA;;AAEF;KACF,A,C;A,E,E,W,C,CAEAgE;;;AAGsB,cAAOD;;;AACZ,cAAOE;;AACR,cAAOC;;AACF,cAAOb;;AACL,cAAOC;AAG5B,YAAOa;KACT,A,C;A,E,E,a,C,CAQAA;;KAGA,A;A,E,C,C;A,E,O,C,C,C;A,E,E,G,C,C,oB,C;A,E,E,gB,C,CAWAJ;AAAkB;KAAI,A,C;A,E,E,W,C,CAEtBrG;;aACcsC,AAAAA;UACH;AAAS;;YAERyD;;;MAIVzD,AAAAA;AACA,kBAAkB;QAChBoE,UAAUJ,iBAAUP;AACtB,AACA;KACF,A,C;A,E,E,U,C,CAEAY;;;aACarE,AAAAA;;UACF;AAAS;aAGX;;MACPA,AAAAA;MACAsE;AAGA;KACF,A,C;A,E,E,e,C,CAEAjB;AAA0B,+BAAS;KAAwB,A,C;A,E,E,iB,C,CAE3DC;AAA8B,+BAAS;KAAwB,A;A,E,C,C;A,E,wB,C,C,C;A,E,E,G,C,C,yB,C;A,E,E,M,C,CARjD;;MACVc,kCAAKJ,qBAAkBA;KACzB,A;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,oB,C;A,E,E,gB,C,CAaFD;AAAkB;KAAI,A,C;A,E,E,W,C,CAEtBE;;eACejE,AAAAA;UACF;AAAS;WAEXuE;MAAAA,sBAAc;MACvBvE,AAAAA;AAGA,0BAFcwE;KAGhB,A,C;A,E,E,U,C,CAEAN;;eACelE,AAAAA;UACF;AAAS;WAEXuE;MAAAA,sBAAc;MACvBvE,AAAAA;;AAIA,yBAHWwE,sBAAeF,cAAAA,oBACbE,sBAAeF,cAAAA;KAG9B,A,C;A,E,E,gB,C,CAEAE;;;YACYf;;MAGG;AACb,kBAAkB;aACJO,iBAAUP;;;QAAtBvF;;AACF,AACA;KACF,A,C;A,E,E,e,C,CAEAmF;AAA0B,+BAAS;KAAwB,A,C;A,E,E,iB,C,CAE3DC;AAA8B,+BAAS;KAAwB,A;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,S,C;A,E,E,a,C,CAa/DmB;UACMX;AAAgB;MAEpBY,qBAAgB;AAChB,YAAOC;KACT,A,C;A,E,E,oB,C,CAEAA;;;AACsB;;cAEZpB;;eAWCA;AAVK,gBAWDmB,AAAAA;;AAVE,gBAAOE;;AACR,gBAAOC;;AACF,gBAAOC;;AACL,gBAAOC;;AACjB,gBAAOC;;KAEpB,A,C;A,E,E,kB,C,CASAtH;;;WACW6F;iBAEOA;MAChBmB,AAAAA;;YACUO;;cAAAA;;AACV,aAAkB;QAChBA,0BAAcN,0BAAmBM;AACnC,AACA;KACF,A,C;A,E,E,iB,C,CAEAZ;;eACe;;WACJd;MACTmB,AAAAA;aACYnB;eACEA;;YACJ2B;;cAAAA;;;AAEV,aAAkB;QAGhBhH,oBAFUyG,0BAAmBO,qBACjBP,0BAAmBQ;AAEjC,AACA;KACF,A,C;A,E,E,mB,C,CAMAH;;KAGA,A;A,E,C,C;A,E,S,C,C,C;A,E,E,G,C,C,mC,C;A,E,E,W,C,CAQAI;;;sDAE2C1G;;;;QASvC2G;aAQA3G;aACIA;QC97BkBS,ADrFxBC;QAohCEkG;;;YAuDgD;eAz+CpD5G;UAAAA,yBAA6C,AAA7CA;UA67CI2G,6BAEaE;;eAIP,iBAAA;;KAEV,A,C;A,E,E,M,C,C,C,U,C,CAxCAH;;;;OAwCA,A,C;A,E,C,C;A,E,0B,C,C,C;A,E,E,G,C,C,6B,C;A,E,E,M,C,CApCI;MACEC;MACAG;KACF,A;A,E,C,C;A,E,2B,C,C,C;A,E,E,G,C,C,6B,C;A,E,E,M,C,CAiBA;MACEH;MACA1G;MACA6G;KACF,A;A,E,C,C;A,E,c,C,C,C;A,E,E,G,C,C,a,C;A,E,E,Y,C,CAgEJpI;iBAIaqI;;;aACQ,AAAN,kDAAc;aACG,AAAhB,CAAN,AAAA,gBAAc,AAAA;aAEJ,AAAL,CADR,CAAA,OAAQ;aAGQ,AAAR,CADR,CAAA,OAAQ;AAGb,YADK,EAAA,OAAQ;KAEf,A,C;A,E,E,G,C,CAEAtI;;;;;AAC8B;;aAETsI;aAAKxH;AAAtB;;AAEF;KACF,A,C;A,E,E,iB,C,C,I,C;A,E,E,a,C,C,I;A,E,C;A,C,A;A,e,mB,A,E,C,E,C;A,E,G,C,C,E,C;A,E,a,C,CDjgDFd;;QACa;;UAEE;AAAS;;AAEtB;GACF,A,C;A,E,C,C,CAEAG;;;AACuB;;UAET;AAER;;AAGF;;AAEA;;AAEA;UAEQoI;;WACgB,iBAAA;AAC1B;GACF,A,C;A,E,yB,C,CAkcErH;;;;;;AAME;GACF,A,C;A,E,gC,C,C,CAIAA;SACQ,iBAAA;GACR,A,C,C,Q,C,C,0C,C,C,C,C,C,C,C,C,C,C,C;A,E,mB,C,CAEAA;;;;;;QAec;;;;UACY,AAAhBsH;AAEF;;;UAEsB,AAApBA;AAEF;AAEF,YAAOC;;;AAyCQ,YAAOA;AAC1B;GACF,A,C;A,E,sB,C,CAEAvH;;;;;QASM;AAIF,YAAOuH;;;gBAIOC;;AAEZ;AAEF,YAAOD;;AAET;GACF,A,C;A,E,yB,C,CAYAvH;;2BACwCvB;;;;;;QAWxBgJ;cACLA;AAET,mBAnBoBC,gBAmBIC;GAC1B,A,C;A,E,yB,C,CAEA3H;AAEE,6BADc4H;GAEhB,A,C;A,E,6B,C,CAwDA5H;;UAGY6H;AACV,cAEU,gCAFQ;;;;aAMK;aAAY;;;;;AAInC,AACA;GACF,A,C;A,E,+B,C,CAEA7H;;;;AAEE,6CH3boCT,6BG2bpC;UHzbekE;;aG0bQ,iBAAA;UACf;QACJqE;eACW;QACXA,OAAa,SAA0B,AAAP,oCAAX;QACrBA,OAAa,SAAK;;aAEZ,iBAAA;;AAEV,AACA,UAAOC;GACT,A,C;A,E,8B,C,CAEA/H;;AACE,4CH1coCT,4BG0cpC;UHxcekE;;aGycQ,iBAAA;UACf;aAAW,iBAAA;UACX;AAAU,cAAOuE;;AACzB,AACA,UAAOD;GACT,A,C;A,E,kC,C,CA6CA/H;;;;;;;;;;;;;;cAWsB;;wBAUV,6BACA;;;QAGA,oBAAc;;;;;;AAAO;;AAC/B;GACF,A,C;A,E,uB,C,CAaAA;;+BAGOiI;AAEL;GACF,A,C;A,E,sB,C,CA+DAjI;;;AAIE;GACF,A,C;A,E,sB,C,CAEAA;;;;GAKA,A,C;A,E,G,C,CA6LFkI;SACQ,iBAAA;GACR,A,C;A,E,K,C,CASAC;;MACwBF;;MACHC;SACb,iBAAA;GACR,A,C;A,E,a,C,CA6CAE;;;;;;;;;;;AAoBE;GACF,A,C;A,E,e,C,CAGAC;AAGE,UAAOC;GACT,A,C;A,E,e,C,CAQAC;;;;;;;;;;;;GAEA,A,C;A,E,e,C,CAgZAC;;;;AAgBkB;;AAC6B;;AAG3C,YAAOC;aACE;AACT;;;;oBAgByB;WACa,AAAP;;;AAO3B,kBAAOA,WACH;;;;AAGJ,kBAAOA;;;;;;;;;;;;;;cAiCEC;UAA8B;AACzC,cAAOD,WAAe;;gBACJE;YAAoC;;AAMtD,gBAAOF,WAAe;;kBACJG;;oBACAC;;sBACAC;;wBACAC;;0BACAC;;4BACAH;;8BACAI;;gCACAC;6BAA8C;;;;;;;;;;;;;;;;;AAChE,kBAAOT;;;;;AAMT,YAAOA;;;yCKxrDyC;AL8rD9C;AAMF,YAAOA;;;;AAQL;AAOJ;GACF,A,C;A,E,c,C,CAwBA1J;;AAEI,YAAOoK;;AAEP,YAAOnK;GAEX,A,C;A,E,c,C,CAUAkC;;;AAKE,oBAAa;eAC4B;eACE;MACzCrB;;AACF,AACA;GACF,A,C;A,E,a,C,CAEAuJ;;QAOwB;AACpB,YAAOC;aACoB;AAC3B,YAAOA;aACoB;AAC3B,YAAOA;aACoB;AAC3B,YAAOA;aACoB;AAC3B,YAAOA;;WAED,iBAAA;GAGV,A,C;A,E,sB,C,CAMAnC;;;AACuB;;;AAEa;gLCnvDC7G;;ADswDnC;GACF,A,C;A,E,mB,C,CA+CEL;;;;;;mBAmBqBsJ,AAFG;;;;;;;;;;MA0CTC,4BAAe;;;;;;SAUxB;;;mBAKWC;;;;;;;;;;;;;;;;AA8Bf,cAAoBC,yBAAF;aACLA;;UAEM;+BAEMD;;;;AAEzB;AAIA;GACF,A,C;A,E,sB,C,CAEAxJ;;;;AAOI;;AAQA;;AAQA;;AAQA;;AAQA;;AAQA;;AAQA;;GAQJ,A,C;A,E,qB,C,CAIAA;;;AACqB,YAAO0J;;;;;wDAUQ;AAChC,YAAOC,iCAHU;;;;aAgOIC;QAArBC;;;;MApNSN,4BAAe;AALxB;;;;;;WAyNqBK;MAArBC;;;;IAvMON,4BAAe;AALxB;GAOF,A,C;A,E,iC,C,CAEAvJ;;;;;;aAUU,iBAAA;;AAEN;;AAQA;;AAQA;;AAQA;;AAQA;;AAQA;;AAQA;;GAUJ,A,C;A,E,gC,C,CAEAA;;gBACqB8J;;;WAmIQF;MAAzBG;;;;;;;wBAvHgC;AAChC,YAAOC,4CAHU;;;;MAYRT,4BAAe;AALxB;;kEAYQ;;;IAMDA,4BAAe;AALxB;GAOF,A,C;A,E,kB,C,CAMFU;;;AAME,UAAOC;GAOT,A,C;A,E,e,C,CA8gBA;SACQ,iBAAA;GAER,A,C;A,E,iB,C,CA6KAC;AAGE;GAKF,A,C;A,E,qB,C,CA4BAC;AAAoC;GAA6B,A,C;A,E,kB,C,CM71FjEC;QAGa;;AACX;GACF,A,C;A,E,kB,C,CAMA1C;;AACsB;AACpB;GACF,A,C;A,E,uB,C,CAKA2C;AAGE,UAAOC,qDAAyB5C;GAClC,A,C;A,E,sB,C,CAOA6C;qBACkBF;AAChB;GACF,A,C;A,E,sB,C,CAGAG;cACY9C;AACV;GACF,A,C;A,E,mB,C,CAmCA1I;;AAEI;;AAGA,mCAjBQyI;;AAoBR;;AAGE,YAAOgD;;AAMT;GAEJ,A,C;A,E,a,C,CAOAzL;;;AAEqB;aAIG;AACtB,sEAAmC;;;;QCmEjC0L,mBAA6CA;;UD5DhC;;YAGAC;;MCyDbD,mBAA6CA;;ADxD/C,AACA;GACF,A,C;A,E,U,C,CAsBAJ;;;;qBA+YuCM;;;;qBAAAA;;AA/XrC;GACF,A,C;A,E,W,C,CA6FA/L;;;AAE8B;;AAO5B,gBAAkB;UACZ,CAACgM;AACH;AAEJ,AACA;GACF,A,C;A,E,gB,C,CAMAC;AAEE,UAAOF,gCADaP;GAEtB,A,C;A,E,S,C,CAyEAxL;;;AAEyB;;AAEK;;UAiOM;;AA5NE;;;AAEK;;;AAGvC,YAAOkM;;;AAKP;;;;;YAOSJ;;UA2MuB;AAtMD;yCACUA;;;QAOtC,+BAA0C;AAC7C;;;AAGF,UA1JOK,eAAYV;GA2JrB,A,C;A,E,a,C,CASAzL;;;AAE8B;;AAEb;;AAEA;;;;UAQD;AAAW;eAEX;AAAY;AAG1B,gBAAkB;;;UACZ,EA1BCgM,uBAAmBA;AA2BtB;;AAEJ,AACA;GACF,A,C;A,E,iB,C,CAEAhM;;;AACiB;;AACA;;;;AAOf,cAAoBoM,qBAAF;cACLA;;AAET;;;UAIE,EAjDCJ,6BAAmBA;AAiDS;;AACnC,AACA;GACF,A,C;A,E,iB,C,CAEAhM;;QAgIoC;AA9Hc;;UA8Hd;AA1H9B;eA0H8B;;;UArH5B,EAjECgM,yCAAmBA;AAiEqB;;;;;;;;;;QAsB5B;AAEjB;QAE0C,AAAzB,0CACA;AAEjB;;UAII,CAACK;AAAwD;UACzD,CAACA;AAEH;;AAMF,oBAAW;;;YACL,EA5GDL,uBAAmBA;AA8GpB;;AAEJ,AAKA,iCAAY;;;YACN,EAtHDA,uBAAmBA;AAwHpB;;AAEJ,AAIA,qBAAY;;;YACN,EA/HDA,uBAAmBA;AAiIpB;;AAEJ;AAOF,UAAOM;GACT,A,C;A,E,Q,C,CAYAf;AAGE;GACF,A,C;A,E,uB,C,CD3jBApL;;AAOE;GACF,A,C;A,E,uB,C,CAEAF;AAAoC,UAAGC;GAAiC,A,C;A,E,c,C,CAKxE;;GAOA,A,C;A,E,yB,C,CA2EAH;;UAEewM;;QAKF;;AAAS;;;QAEJ;AAAS;;;YAMjBC;UACE;;YAGK;;AAAS;;;YAEJ;AAAS;;;;;AAc3B;;;;eAQSC;;;AAET;;;;AAKA;;;WAIuBA;;AAAvB;;;AAIA,YAAOC;;WAKD,iBAAA;;WAMiBD;;AAAvB;;AAEA,YAAOC;GAEX,A,C;A,E,kB,C,CAYAA;;;aAEe9M;;AAEb;GACF,A,C;A,E,sB,C,CAGA6M;AAGE,UAAO7M;GACT,A,C;A,E,yB,C,CAEA+M;;;AAII,YAPK/M;;AASL,YAAOA;GAEX,A,C;A,E,kB,C,CAiBA;;AACsC;IACpCgN;IACAC;GACF,A,C;A,E,0B,C,CAEA;;IAEEC;IACAC;IAEAC;;;;;AAUE,kBAAkB,IAAEC;cACRA;gBACEC;YACF;mBAEKP;cACF;;;;AAIf;AAKF,gBAAkB,IAAEM;;;;YAIsB;YACP;YACJ;YACI;YACK;;;AAExC,GACF,A,C;A,E,S,C,CAsCA;;;YAoBUE,sCAJAA,wCAFAA,wCADAA,wCADAA,wCADAA,wCAHAA;;;;;;AA0BJ,oBAAkB;;;;;AAKlB;;;;IAQJZ;IACAC;IAEAU;GAEF,A,C;A,E,qB,C,CAEAC;AAEE;GACF,A,C;A,E,c,C,C,C;A,E,E,G,C,C,6I,C;A,E,E,M,C,C,C,G,C,C,0J,C,C,6B,C,CLnCEzM;;;;AAEoB;;;;;;AAclB,uDAR0C,yFAKgB;OAM5D,A,C;A,E,C,C;A,E,gB,C,C,C;A,E,E,G,C,C,mE,C;A,E,E,gB,C,CAo6BA0M;;yBAE+BC;;AACV;;WAEfC;UAAW;;WAGXC;UAAe;;WAGfC;UAAM;;WAGNC;UAAQ;;WAGRC;UAAU;;AAId;KACF,A,C;A,E,E,M,C,C,C,G,C,C,iZ,C,C,+B,C,CAsBAxM;;;;;;;;;;;AA4CE;OAMF,A,C,C,mC,C,CAMAA;AAmDE;;;;;;;;OACF,A,C,C,uC,C,CAkCAA;AASE;;;;;;;OACF,A,C;A,E,C,C;A,E,S,C,C,C;A,E,E,G,C,C,wB,C;A,E,E,U,C,CAsCAf;eACMsN;;AAAiB,mCAAoBE;AACzC;KACF,A,C;A,E,E,Q,C,C,I;A,E,C,C;A,E,mB,C,C,C;A,E,E,G,C,C,kC,C;A,E,E,U,C,CAaAxN;;WACMsN;;AAAiB,2CAA4BE;WAC7CD;;AACF,4EAAoDC;AAEtD,iGACOA;KACT,A,C;A,E,E,Q,C,C,I,C;A,E,E,M,C,C,C,oB,C,CAZAC;;;;;;OAGuE,A,C;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,gB,C;A,E,E,U,C,CAiBvEzN;eAAqBwN;AAAH,YAAGA;KAA+C,A;A,E,C,C;A,E,8B,C,C,C;A,E,E,G,C,C,iB,C;A,E,E,M,C,CAepEhE;;;;AAOE;KACF,A;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,0B,C;A,E,E,U,C,CAuIAxJ;;WACM0N;UAAO;AAAS;WAGoBC;;;MAGjCD;AAAP;KACF,A;A,E,C,C;A,E,qB,C,C,C;A,E,E,G,C,C,qB,C;A,E,E,M,C,CAwCqC;AAAG,YAAGE;KAAQ,A;A,E,C,C;A,E,sB,C,C,C;A,E,E,G,C,C,4B,C;A,E,E,M,C,CAEd;AAAG,YAAGA;KAAY,A;A,E,C,C;A,E,sB,C,C,C;A,E,E,G,C,C,mC,C;A,E,E,M,C,CAElB;AAAG,YAAGA;KAAkB,A;A,E,C,C;A,E,sB,C,C,C;A,E,E,G,C,C,0C,C;A,E,E,M,C,CAExB;AAAG,YAAGA;KAAwB,A;A,E,C,C;A,E,sB,C,C,C;A,E,E,G,C,C,sD,C;A,E,E,M,C,CAE9B;AAAG,YAAGA;KAA8B,A;A,E,C,C;A,E,O,C,C,C;A,E,E,G,C,C,S,C;A,E,E,U,C,CAgazE5N;AAAkB;KAAY,A;A,E,C,C;A,E,c,C,C,C;A,E,E,G,C,C,U;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,sE,C;A,E,E,G,C,CA0C9BH;;;;AAC8B;;AACA;AAC5B,YACIgO,gBAAOlN,eACPmN,6BAASnN,6BACT4M,mBAAW5M;KACjB,A,C;A,E,E,Y,C,CAEAb;;WAEMyN;;2BAGiBxN,4BAA0B8N;;2BACA,yBAG1BN,sBAIAxN;WAEKA,4BAA0B+N;;;AAApD,YAAwB,EAAA;KAC1B,A,C;A,E,E,e,C,C,I,C;A,E,E,M,C,C,C,G,C,C,qE,C,C,mB,C,CAGA/M;AAAoC,cAAG6M;OAAa,A,C,C,uB,C,CAKpD7M;AAAwC,cAAG6M;OAAiB,A,C,C,0B,C,CAM5D7M;;;eAEyB4J;UAArBC;;AAEF;OACF,A,C,C,8B,C,CAYA7J;;;;;;AAIE,kBAAoBkL,qBAAF;kBACLA;;AAET;;AAEJ,OACF,A,C;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,e,C;A,E,E,U,C,CA8bAjM;AAAkB,oCAAmBoF;KAAQ,A,C;A,E,E,M,C,C,C,a,C,CAD7C2I;;OAA0B,A,C;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,S;A,E,C,C;A,E,mB,C,C,C;A,E,E,G,C,C,8E,C;A,E,E,S,C,CA4B1BlO;+BAC2BmO;AACzB,kDAEMjC,wCAAsCkC;KAC9C,A,C;A,E,E,gC,C,CAwCAD;wBACoBxO;AAClB;KAGF,A,C;A,E,E,O,C,CAEAyO;;;WAzDmBC;;;;;qBAgEVA;WAGHC;UAAe,cAAW,AJlgFZhO;sBIogFbiO;WAGDC;UAAuB,cAAW,AJvgFpBlO;qBIygFbiO;WAGDE;UAAgB;;eAEPC;AACX,kBAAoB3G,oBAAF;kBACLA;4BACDyB;;AAEZ;;AAKF;KACF,A,C;A,E,E,U,C,CAWArJ;;WAGMmO;UAAe;AACjB,kBAAoBA,oDAAF;iBACGA;;;;;AAIrB;;;;WAEEE;UAAuB,cAAW,AJ/iFpBlO;iBIkjFT,cAFgB;AAGvB,kBAAoBkO,sCAAF;iBACGA;;;;;AAIrB;;aAESC;YAAgB;mBAGlB,cAFgB;iBAGZC;AACX,oBAAoB3G,wCAAF;oBACLA;;;0BAEDyB;;AAGZ;;;AAKF,YADO,yBAAU6E;KAEnB,A,C;A,E,E,M,C,C,C,G,C,C,8B,C,C,6B,C,CAhDAnN;;;;AAGE,kBAAoBoF,oBAAF;sBACYA,AAAAA;AAC9B,AACA;OACF,A,C;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,c,C;A,E,E,U,C,CA2EAnG;AAAkB;KAAY,A,C;A,E,E,O,C,CAE9BiO;AAAQ;KAAO,A,C;A,E,E,qB,C,C,I;A,E,C,C;A,E,iB,C,C,C;A,E,E,G,C,C,qB,C;A,E,E,M,C,CK7gFE;AAAI;KAAsC,A;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,4B,C;A,E,E,M,C,CAEvD;AAAgB;KAAqD,A;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,6B,C;A,E,E,M,C,CAErE;AAAa;KAAsC,A;A,E,C,C;A,E,c,C,C,C;A,E,E,G,C,C,gE,C;A,E,E,Y,C,CGrVvDO;;;;UAGsBC;;AAEL;AACf,YAAO;KACT,A,C;A,E,E,M,C,C,C,yB,C,CAhCA1N;;;;;;;AAiBiD;;aAIzC,iBAAA;OAER,A,C;A,E,C,C;A,E,oB,C,C,C;A,E,E,G,C,C,uB,C;A,E,E,M,C,CA0EAf;eAD2B0O;;cAAAA;AACG,YADHA;KACkB,A,C;A,E,E,sB,C,CAV7CC;KAGA,A,C;A,E,E,M,C,C,C,qB,C,CAHAA;;;;OAGA,A,C;A,E,C;A,C,A;A,kB,+C,A,E,C,E,C;A,E,G,C,C,E,C;A,E,W,C,C,C;A,E,E,G,C,C,oE,C;A,E,E,U,C,CC1IAC;;2BACuBxG;AAAe;WDoB5ByG;;;;QCjBiBC;KAC3B,A,C;A,E,E,U,C,CA8CA9O;AAAkB,wCAAuB+O,oCApDpBD,sCAQCE;KA6CK,A,C;A,E,E,Q,C,CAE3BhP;gBACY;MACViP,2BAzDmBH;MA0DnBG,0BAAeF,AAAAA;MACfE,4BAnDoBD;MAoDpBC,6BA/CkBC;MAgDlBD,kCAAuBE,AAAAA;MACvBF,kCAAuBG,aAAAA;AAEvB,YADWC;KAEb,A,C;A,E,E,sB,C,CArCAC;;;MACE,gBAAcC;;2BACmBA;MAAjC;WACeA;UArBL;QAAMP;WAsBAO;;UAjBN,iBAAc;QAAWL;MAkBnC,qBAAqBM,iBAAeD;KACtC,A,C;A,E,E,M,C,C,C,G,C,C,sB,C;A,E,C,C;A,E,M,C,C,C;A,E,E,G,C,C,gE,C;A,E,E,U,C,CCAAvP;AAAkB,8BAtCCyP,wCAUEC;KA4BwB,A,C;A,E,E,Q,C,CAE7C3I;gBAC4B;MAC1B4I,yBA1CiBF;MA2CjBE,4BAAiBC;MACjBD,0BAvCkBE;MAwClBF,2BAnCmBD;MAoCnBC,8BAAmBG,aA/BOC;AAgC1B;KACF,A,C;A,E,E,iB,C,CAnBAC;;;WACcT;UA5BF,cAAW,AAACnH;QAAeqH;MA6BrC,eAAeF;WACFA;UAzBH,cAAW,AAACnH;QAAeyH;WA0BvBN;;UArBJ,mBAAgB;QAAQG;WAsBhBF,iBAAeD;;;UCiTH,AAAvBU;QDjUkBF;KAiB1B,A;A,E,C;A,C,A;A,K,yB,A,E,C,E,C;A,E,G,C,C,E,C;A,E,I,C,C,CElCH;IAOEhB,UCu9lCyCmB;IDt9lCzCC,YCs9lCyCD;IDr9lCzCE,WCq9lCyCF;IDp9lCzCG,cCo9lCyCH;IDn9lCzCI,WCm9lCyCJ;IDl9lCzCK,gBCk9lCyCL;IDj9lCzCM,iBCi9lCyCN;IDh9lCzCO,UCg9lCyCP;aD58lCzCE;IC4p/BEM,uDA/EI,YAAc,eA8EcC,mCA9EW;SD5k/B7CL;IC2p/BEI,uDA/EI,YAAc,eA8EcC,sCA9EW;SD3k/B7CL;IC0p/BEI,uDA/EI,YAAc,eA8EcC,sCA9EW;SD1k/B7CN;ICyp/BEK,uDA/EI,YAAc,eA8EcC,oCA9EW;SDzk/B7CJ;ICwp/BEG,uDA/EI,YAAc,eA8EcC,wCA9EW;SDxk/B7CH;ICup/BEE,uDA/EI,YAAc,eA8EcC,mCA9EW;ID5l/B7CC;GACF,A,C,C,Q,C,C,c,C,C,C,C,C,C,C,C,C,C,C;A,E,Q,C,C,CAsBAC;;+BAE2BT;QCyvvBOU,ADxvvB3BC;MACHN;MACAL;AACA;;IAEFK;SHI6DpB,+BIivvB3ByB,ADnvvBhBC;mDHL8BvB,iBAAeD;;IGM/DyB;IAEAjC,uCFxCmBU,AEwCKuB;IACxBb,qCAA0Ba,sBH7BJhC;IG+BtB4B;GACF,A,C,C,Q,C,C,kB,C,C,C,C,C,C,C,C,C,C,C;A,E,S,C,C,CAEAK;IAGEb;IACArB;IACAoB;IACAC;IACAQ;AAPiB;GAAU,A,C,C,Q,C,C,mB,C,C,C,C,C,C,C,C,C,C,C;A,E,oB,C,CAU7BA;IACEN;IACAC;IACAC;GACF,A,C;A,E,W,C,C,CAEAU;;;;cL4CWnR,yBKzCcuQ;;6BADvB;QAGES;QACAT;;;;;GAEJ,A,C,C,Q,C,C,qB,C,C,C,C,C,C,C,C,C,C,C;A,E,a,C,C,CAEAa;;mBLkCWpR,yBKhC0BuQ;SAElB;;;WHjCP,UA/BYtB;UAEV;QAAMA;;;MA8BhBI;;WAIQ,UApCYJ;UAEV;QAAMA;;;MAmChBI;;IIyuvB4CgC,AD5svB9CL,iDH1EqBjC,gBG0E8BkC;IAEnDb,qCAA0Ba,sBHpEJhC;IGsEtBqC;IACAA;GACF,A,C,C,Q,C,C,uB,C,C,C,C,C,C,C,C,C,C,C;A,E,Q,C,C,CAEAC;;;SH1EwBtC;;SAyCD;;;SAAX,YAAqB;QAvCnB;MAAMA;II4wvB4BoC,ADlsvB9CL,iDHpFqBjC,gBGoF8BkC;IACnDb,qCAA0Ba,sBH7EJhC;IG8EtBqC;IACAA;GACF,A,C,C,Q,C,C,kB,C,C,C,C,C,C,C,C,C,C;A,C;C;A,mB,kB,A,E,C,E,C;A,E,G,C,C,E,C;A,E,+B,C,Cf8oBEtQ;;AACE,2CA/boCT,2BA+bpC;MACEiR,SA9ba/M;AA+bf,GACF,A,C;A,E,2B,C,CAEAzD;;AACE,2CArcoCT,2BAqcpC;UACMiR,SApcS/M;AAocH;AACZ,AACA;GACF,A,C;A,E,wC,C,CA+KAzD;;AAEE,oEAAkB,IAAEyQ;UACJA;AACZ;AAEJ,aAEsB;;MAEpBA;MACA5Q;MACAA;MACAA;;;;;MAGA4Q;;AAEF,UU3qByB9F;GV4qB3B,A,C;A,E,oC,C,CAmFA3K;;QAdY,aAAa,QAAEoF;wBACjB,6BAA+BA;QAE/B,eAAe,MAAEA;wBACjB,+BAAiCA;cAaxB;;AACA;QAEH;;QAYU,AAAT,sBAAWsL;WAClB,iBAAA;IAERC;GACF,A,C;A,E,U,C,CiBhiCA3Q;;QAEe;AACX,eAA8B,AAAR,0BAAkC,AAAR,uCACzC;;;QACL4Q,oCAASC;;AACX;AAEA,gBAAkD,+DAAX;;;QACrCD,oCAASC;;AACX,GAEJ,A,C;A,E,c,C,CCuGA7Q;AAAqC,UAAG8Q;GAAY,A,C;A,E,Y,C,C,C;A,E,E,G,C,C,e,C;A,E,E,Y,C,ClBzFpDxR;AAAyB,sCAkRaC;KAlRe,A,C;A,E,E,S,C,CAErD;;gBACe;AACb,kBAAkB;QAChBwR,cAAOC;YACI,YAAG;eACN,iBAAA;;AAEV,KACF,A,C;A,E,E,W,C,CAEAlS;AAAiB,YAAGM;KAAW,A,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,0C,C;A,E,E,W,C,CAwQ/BD;AAAc,YAAGsE;KAAQ,A,C;A,E,E,U,C,CAEzB3E;;WACemS;;gBAAAA;UACD,AAAR1O;aACI,iBAAA;WAEJ2O;UAAO;QACTzN;AACA;;MAEFA,gBAAWwN;MACXC,cAAM,AAANA;AACA;KACF,A;A,E,C,C;A,E,c,C,C,C;A,E,E,G,C,C,2B,C;A,E,E,Y,C,CAkBA5R;eAAqD2R;sCAAAA,qBAAoBE;;AAAhD;KAAmD,A,C;A,E,E,U,C,CAG5EpS;eAAkBkS;AAAH,YAAGA;KAAgB,A,C;A,E,E,W,C,CAClCnS;eAAoBmS;AAAH,YAAGA;KAAiB,A,C;A,E,E,e,C,C,Q,C,E,C,C,E,C,C,C;A,E,E,E,M,C,C,E,C,C;A,E,E,C,C;A,E,E,M,C,C,C,6B,C,CAbrCzR;;AAEI;AAEF;OACF,A,C;A,E,C,C;A,E,6B,C,C,C;A,E,E,G,C,C,6B,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,c,C,C,C;A,E,E,G,C,C,gC,C;A,E,E,I,C,C,Q,C,I,C,C,C;A,E,E,E,M,C,I,C,E,C,M,C,I,C,C;A,E,E,C,C;A,E,E,U,C,CA8BAV;eACMsS;UAAAA;QACF3N,gBAAW0N,UAAGC;AACd;;MAEF3N;AACA;KACF,A,C;A,E,E,W,C,CAEA4N;AAAc,YAAG5N;KAAQ,A;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,yB,C;A,E,E,I,C,C,Q,C,I,C,C,C;A,E,E,E,M,C,I,C,E,C,M,C,I,C,C;A,E,E,C,C;A,E,E,U,C,CAezB1E;AAAe,YAAGuS,kBAAAA;KAAc,A,C;A,E,E,W,C,CAChCD;AAAuB,YAAGF,WAAGG,iBAAAA;KAAyB,A,C;A,E,E,e,C,C,Q,C,E,C,C,E,C,C,C;A,E,E,E,M,C,C,E,C,C;A,E,E,C,C;A,E,E,e,C,C,Q,C,E,C,C,E,C,C,C;A,E,E,E,M,C,C,E,C,C;A,E,E,C,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,2B,C;A,E,E,Y,C,CAYtDhS;mCAAiD2R,kBAAAA,iBAAoBE;;AAA5C;KAA+C,A;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,uB,C;A,E,E,I,C,C,Q,C,I,C,C,C;A,E,E,E,M,C,I,C,E,C,M,C,I,C,C;A,E,E,C,C;A,E,E,U,C,CASxErS;AACE,oBAAOsS,gBAAAA;YACDD,UAAGC;AACL;AAEJ,AACA;KACF,A,C;A,E,E,W,C,CAEAjS;AAAc,YAAGiS,AAAAA;KAAiB,A;A,E,C,C;A,E,oB,C,C,C;A,E,E,G,C,C,S;A,E,C;A,C,A;A,mB,kB,A,E,C,E,C;A,E,G,C,C,E,C;A,E,W,C,CmB9VpC/R;;;;;;;;;AASE;GACF,A;A,C,A;A,e,c,A,E,C,E,C;A,E,G,C,C,E,C;A,E,qB,C,CCnEAkS;;SACMC;;MACKC;AAAP;;MAEOA;AAAP;;GAEJ,A,C;A,E,qB,C,CCJA;;AAIE,WAAa;MACXhR;cACwBA;MAAhBiR;;AACV,IAEAC;GACF,A,C;A,E,iB,C,C,CAEA;;;MAEIC;;MADF;MCAEC;MDIAH,kBAAgBA;;;;GAGpB,A,C,C,Q,C,C,2B,C,C,C,C,C,C,C,C,C,C,C;A,E,sB,C,CAEA;;;;;MAIoBC;MAAhBD;MCbAG;;;MDgBgBF;MAAhBA;;GAEJ,A,C;A,E,Y,C,CEzCAG;;;MAIIC,iBAAUC;;WADZ;;;MAGEC;;;GAEJ,A,C;A,E,e,C,CAIA;IAIqBC;IAIjBC;GAEJ,A,C;A,E,sB,C,CAGAC;AAAwE;GAEzB,A,C;A,E,e,C,CAI/C;IACqBF;IAIjBC;GAEJ,A,C;A,E,W,C,ChBAE3S;;;MAIW6S;AAAP,YiBi0BAC;;AjB/zBF,UiB+zBEA,4CjB9zBYD;GAChB,A,C;A,E,Y,C,CevCIE;uBGkLgC,4BAAVC;AH/K1B,UAAO,cADU;GAEnB,A,C;A,E,W,C,CE6XExS;;IAIEyD;AACA;GACF,A,C;A,E,wB,C,CAySF;IA+JsBgP;GAlJtB,A,C;A,E,Q,C,CAEAxP;;;AAC6B,YAAOuN;UAEvB6B;;WAEF7B;AAAP;;MApTA6B;;GAwTJ,A,C;A,E,a,C,CAEApP;;;AAC6B,YAAOuN;UAEvB6B;;WAEF7B;AAAP;;MA/TA6B;;GAmUJ,A,C;A,E,c,C,CAEApP;;;AAE6B,YAAOuN;UAEvB6B;;WAEF7B;AAAP;;MA3UA6B;;GA+UJ,A,C;A,E,sB,C,CAiBA;IAIEK,yBAHc,yBACRjB;GAGR,A,C;A,E,gB,C,CAEAc;AAKE,UAAOV,0BAHO,yBACDJ;GAGf,A,C;A,E,W,C,C,C;A,E,E,G,C,C,2B,C;A,E,E,Q,C,C,I;A,E,C,C;A,E,O,C,C,C;A,E,E,G,C,C,yI,C;A,E,E,e,C,CEzrBE3S;AAAqB,YAAU,AAAP6T;KAAgB,A,C;A,E,E,a,C,CACxC7T;AAAmB,YAAG6T;KAAgB,A,C;A,E,E,a,C,CACtC7T;AAAmB,YAAG6T;KAAgB,A,C;A,E,E,c,C,CAEtC9E;;QAGI8E;;QAGAA;KAEJ,A,C;A,E,E,c,C,CA0FAC;;;MArByBP;8EACAQ;MAuBvBC;AACA;KACF,A,C;A,E,E,iB,C,CAqBAzB;AAEE,YAAO0B;KACT,A,C;A,E,E,U,C,CAEAC;AAEE,YAAOD;KACT,A,C;A,E,E,W,C,CAEA;MAEEJ;MACAI;KACF,A,C;A,E,E,W,C,CAEA;MAEEJ;MACAI;KACF,A,C;A,E,E,c,C,CAEA;;UAtJ+B,AAAPJ;aA0JpBM;QAAAA;QFkoBFC;;QE9nBEC,yBAAyBJ;QACzBA;;KAEJ,A,C;A,E,E,kB,C,CAEAK;;gBAIoBL;MAClBA;AAEA,wBAAe;eACEM;QACfA;;AAGF,AACA;KACF,A,C;A,E,E,W,C,CAyCA;;;;;UASMC;;UAEAC;;oBAGkBC;QACpBC;QACAC;;KAEJ,A,C;A,E,E,gB,C,C,CAeA;sBAOsBF;MACpBG;MACAD;KACF,A,C,C,Q,C,K,C,C,C;A,E,E,E,M,C,I,C,gB,C,K,C,C,I,C,C;A,E,E,C,C,C,kB,C,C,Q,C,C,Q,C,C,oB,C,C,C,C,C,C,C,C,E,C,C,E,C,C;A,E,E,U,C,C,I,C;A,E,E,S,C,C,I,C;A,E,E,M,C,C,C,G,C,C,8F,C,C,Q,C,CA1MAN;;OAGiE,A,C,C,2B,C,CAwHjEpT;QA/KI2S;QAqLFnL;OAYF,A,C,C,wB,C,CAIAxH;QArMI2S;YAP2B,AAAPA;UAoNpBe;;UAEAE;OAEJ,A,C,C,mC,C,CAqHA5T;;AAGE;uBAEcmT;UACZA;UACAO;cACiB;;;;;;AAAQ,OAC7B,A,C,C,6B,C,CAUA1T;;;;AACE;;cACM,CAACwH;AAAoB;qBACTA;;;yBAEWA;iBACzBA;iBACIqM;iBAAkBA;YADtBrM;YFgaFsM;AE9ZE;;;AAEqB;cAEI,AAAvBX;YAGFY;AACA;;;wBAMkBvM,8BAAmBA;;;;;iBAvXpBmL;6BAsD4BqB;mBA8Ud,aA1UVC;;;;;;;mBA2UTd;;mBACK3L;cAAAA;cF0XiB0M;mBE1XlB;;;;;2BAEW1M;mBACzBA;mBACIqM;mBAAkBA;cADtBrM;cF8XJsM;AE5XI;;;gBAIE;cF7ERrQ;;;;kBEsK4B,CAxePkP,gCAsD4BqB;wCAmbtBG;;cAGrB5M;gBAE+B,CA9ehBoL,gCA0DIsB;cAqbnBG;gBAGU;cFrKhB/B;;AEuK8B;;;oBAKtB;;;;;;oBAxfqB,AAAPM;kBAOpBA;;;;;kBA8fQW;;gBAGFC;AAEF;;;;yBAIUJ;;YAlYhBR;YACAI;;yBAoYgBI;;iBAEOU;iBAAkBA;YAjYzClB;YACAI;;;;;;AAoYA,OACF,A,C;A,E,C,C;A,E,4B,C,C,C;A,E,E,G,C,C,6B,C;A,E,E,M,C,CA9X4B;MACtBW;KACF,A;A,E,C,C;A,E,mC,C,C,C;A,E,E,G,C,C,qB,C;A,E,E,M,C,CAgCU;;;kBA0DQF;MACpBC;MACAC;KAzDE,A;A,E,C,C;A,E,oC,C,C,C;A,E,E,G,C,C,qB,C;A,E,E,M,C,CAKS;MAEPW;KACF,A,C;A,E,E,M,C,C,Q,C,K,C,C,C;A,E,E,E,M,C,I,C,M,C,K,C,C,I,C,C;A,E,E,C;A,E,C,C;A,E,iD,C,C,C;A,E,E,G,C,C,kD,C;A,E,E,M,C,CA4MEvV;;;;;aApZiB6T,yBAsD4BqB;QAgWlBvC;4CFwXA6C;AEtXvB;;aAHF;;;;AAME;;;KAEJ,A;A,E,C,C;A,E,yC,C,C,C;A,E,E,G,C,C,yC,C;A,E,E,M,C,CAEA;;mBAC2B9M;;aAhaVmL,yBAuDgC4B;;UA4WtC;;;eAE6BV;UAApBpC;wBFyWO6C;;eE1WvB;;;eAImCT;;;;;;AAGjC;;;sBA3aWlB,yBAwDsB6B;kCAuXJ;;;;eAEzBC;;;;iBAEoCZ;iBACAA;YAFfpC;wCF+VjCiD;;iBE1V+Cb;YADdpC;wCFwVJ6C;;;eE9VvB;;;eAUmCT;;;;;;AAGjC;;;;;;;;;KAQN,A;A,E,C,C;A,E,wD,C,C,C;A,E,E,G,C,C,qD,C;A,E,E,M,C,CAEA;;;;;;;aAvciBlB,yBA0DIsB;QAgZAxC;8BFkUPgB;;aEnUZ;;;;eAG4BjL,cAAAA;;;;;;;;sCACDA;;;;;;;;QAOzB2L;;QAEAwB;;KAeJ,A;A,E,C,C;A,E,gE,C,C,C;A,E,E,G,C,C,8B,C;A,E,E,M,C,CAfwB;MAGlBjB;KACF,A;A,E,C,C;A,E,iE,C,C,C;A,E,E,G,C,C,8B,C;A,E,E,M,C,CAAY;;;;yBAKS;;QACjBiB;;MAEFjB;KACF,A,C;A,E,E,M,C,C,Q,C,K,C,C,C;A,E,E,E,M,C,I,C,M,C,K,C,C,I,C,C;A,E,E,C;A,E,C,C;A,E,mB,C,C,C;A,E,E,G,C,C,sB,C;A,E,E,U,C,C,Q,C,C,C,C;A,E,E,E,M,C,I,C,Q,C,M,C,C,C;A,E,E,C;A,E,C,C;A,E,M,C,C,C;A,E,E,G,C,C,S,C;A,E,E,S,C,CCVVd;;;eACmB;;0BAEF,gJAQFT;AAKb;KACF,A,C;A,E,E,U,C,CAsEAS;;;eACwB;;MAEtB,4HAEWT;AAKX;KACF,A,C;A,E,E,W,C,CAWAS;;;eACyB;;0BAER,kIAIJT;AAKX;KACF,A;A,E,C,C;A,E,sB,C,C,C;A,E,E,G,C,C,wC,C;A,E,E,M,C,CAtHM;MACEL,wGAGEM;KAEJ,A,C;A,E,E,U,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,gB,C,Q,C,C,C,C,C;A,E,E,E,E,M,C,C,I,C,C,Y,C,C,I,C,C,C,C,C,C,C;A,E,E,E,C,C,C,I,C,M,C,C,Q,C,C;A,E,E,C;A,E,C,C;A,E,uB,C,C,C;A,E,E,G,C,C,8B,C;A,E,E,M,C,CAJI;AAAG,YAAGrB;KAAc,A;A,E,C,C;A,E,wB,C,C,C;A,E,E,G,C,C,a,C;A,E,E,M,C,CACpB;KAAK,A;A,E,C,C;A,E,uB,C,C,C;A,E,E,G,C,C,oB,C;A,E,E,M,C,CAKD;MACNoB;KACF,A;A,E,C,C;A,E,qB,C,C,C;A,E,E,G,C,C,kB,C;A,E,E,M,C,CA6EF;;mBAAW;KAAI,A;A,E,C,C;A,E,sB,C,C,C;A,E,E,G,C,C,0B,C;A,E,E,M,C,CAEP;MACNA;KACF,A;A,E,C,C;A,E,sB,C,C,C;A,E,E,G,C,C,2B,C;A,E,E,M,C,CAkBA;MACEyC;KACF,A;A,E,C,C;A,E,uB,C,C,C;A,E,E,G,C,C,oB,C;A,E,E,M,C,CAEQ;MACNzC;KACF,A;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,S;A,E,C,C;A,E,U,C,C,C;A,E,E,G,C,C,S;A,E,C,C;A,E,uB,C,C,C;A,E,E,G,C,C,yC,C;A,E,E,M,C,CJxoBwB;AAAG,YAAGA;KAAuC,A;A,E,C,C;A,E,8B,C,C,C;A,E,E,G,C,C,oC,C;A,E,E,M,C,CAQxE;AAA+B,YAAG0C;KACQ,A;A,E,C,C;A,E,uB,C,C,C;A,E,E,G,C,C,4B,C;A,E,E,M,C,CAOf;AAAG,YAAG1C;KAAsB,A;A,E,C,C;A,E,S,C,C,C;A,E,E,G,C,C,S,C;A,E,E,Y,C,CCkhBxDlP;;;aAEW6R;AAAP;;aADF;;;AAGE,cAAOC;;;KAEX,A,C;A,E,E,iB,C,CAEA9R;;;aAEW+R;AAAP;;aADF;;;AAGE,cAAOD;;;KAEX,A,C;A,E,E,yB,C,CAUAE;uBAC4BC;;AAExB;;AAEA;KAEJ,A,C;A,E,E,c,C,C,Q,C,C,C,C,C;A,E,E,E,M,C,I,C,yB,C,C,C,C,I,C,C;A,E,E,C,C;A,E,E,8B,C,CAEAC;uBACiCC;;AAE7B;;AAEA;KAEJ,A;A,E,C,C;A,E,8B,C,C,C;A,E,E,G,C,C,+B,C;A,E,E,M,C,CAbW;AAAG,YAAG;KAA0B,A;A,E,C,C;A,E,+B,C,C,C;A,E,E,G,C,C,+B,C;A,E,E,M,C,CAEhC;AAAG,YAAG;KAAmB,A;A,E,C,C;A,E,mC,C,C,C;A,E,E,G,C,C,gC,C;A,E,E,M,C,CAOzB;AAAM,YAAG;KAAoC,A;A,E,C,C;A,E,oC,C,C,C;A,E,E,G,C,C,gC,C;A,E,E,M,C,CAE7C;AAAM,YAAG;KAA6B,A;A,E,C,C;A,E,gC,C,C,C;A,E,E,G,C,C,gC,C;A,E,E,M,C,CA+FxC;MACP1C;KASF,A;A,E,C,C;A,E,iC,C,C,C;A,E,E,G,C,C,gC,C;A,E,E,M,C,CATyB;;;MACrBtR;;;gBAE6CsO;UACnC;QACRtO;;KAGJ,A;A,E,C,C;A,E,S,C,C,C;A,E,E,G,C,C,Y,C;A,E,E,M,C,CA0IFkE;AAAwB;KAAO,A,C;A,E,E,qB,C,CAI/BrC;AAA0D,YACtD6Q;KAA6D,A,C;A,E,E,K,C,CAKjE7Q;AAAiB,YAAGwP;KAA6B,A,C;A,E,E,U,C,CAEjDxP;AAA8B,YAAGqR;KAAuC,A,C;A,E,E,kB,C,CAKxEW;AAAmC;KACW,A,C;A,E,E,uB,C,CAE9CE;AAAgD;KACG,A;A,E,C;A,C,A;A,oB,mB,A,E,C,E,C;A,E,G,C,C,E,C;A,E,c,C,C,CIj2BrDrW;AAA0B,UAAK;GAAI,A,C,C,Q,C,C,wB,C,C,C,C,C,C,C,C,C,C,C;A,E,gB,C,C,CAEnCC;AAAwB,UAAG+I;GAAU,A,C,C,Q,C,C,0B,C,C,C,C,C,C,C,C,C,C,C;A,E,e,C,CtBD7BtI;AAME;GAqBR,A,C;A,E,wB,C,CAu4BMA;AAA2B;GAAqB,A,C;A,E,iB,C,CuBrhBxDP;;QACMoW;AAAsC;IAC1CA;;;MAGEC;;MAEAD;;SAEM;IAAA;IAAA;AAAR,UnB5I2B1K;GmB6I7B,A,C;A,E,uB,C,CAGA;;SAiBgBpL;;;AAGd;YAAc,gBAAwB;;UAChC,CAACgW;AAAe;iBACHA;MACjBC;iBACsB,AAAZ3R;;;AAEZ,QASI,CAAC0R;UACO;AAA4B;;;uBACrBC;;;0BACGA;;oBAEND;;UAEV,CAACA;YACO;UACRC;AACA;;;;;4BAGkBA;mBACY,AAAtBC;;mBAECF;;AAIX,eAAOA;sBAEMA;;cAED;AAQR;oBAAc,gBACD;;;;yBACyB,AAA1BC,AAAAA;;;AAEZ,YACAA;AACA;;;AAEJ;;mBAIqD,AAA3B,AAAtBC,wBAAwBC;;;QAOtB,QAAe,AAAbF;;;;;AAQZ;YAAc,gBAA+B,AAAbA;;;;iBACM,AAA1BA,AAAAA;;;;;;AAKZ,QACY;MACVA;IAEFA;IACAA;GACF,A,C;A,E,2B,C,CvBhEQhW;AAME;GAqBR,A,C;A,E,2B,C,CA20BMA;AAME;GAqBR,A,C;A,E,gB,C,CwB5vCAQ;;;AACE,iDAAkB,IAAEyQ;UACJA;AAAwB;AACxC,aAEa;;MAEXA;MACA5Q;;MAEA8V;MASA9V;;;;;MAGA4Q;;AAGF,UpBwKyB9F;GoBvK3B,A,C;A,E,Q,C,C,C;A,E,E,G,C,C,uD,C;A,E,E,U,C,CxB3CA5L;AAAe,YAAGwD;KAAO,A,C;A,E,E,W,C,CACzBzD;AAAiB,YAAGyD;KAAY,A,C;A,E,E,Q,C,CAGhCqT;AACE;KACF,A,C;A,E,E,U,C,CAEAA;AACE,YAAO;KACT,A,C;A,E,E,M,C,CA2BAC;;qCAqK8B;kBAnKZC;;;;;;;AACd;;eAEWC;;;;;;;AACX;;eAEWC;;AACO;sBA+MTC;gBA7MGC;AACZ,cAAc,2BAA8C;;KAEhE,A,C;A,E,E,S,C,CAEA;;qCAqJ8B;kBAnJZJ;;oBAC4BK;UAArBL;;QACrBM;;eAEWL;;iBACsBI;UAAfJ;;QAClBK;;eAEWJ;;iBACsBG;UAAfH;;eACPC;;;UAGTI;UACA9T,2BAAO,AAAPA;UACA+T;;kBAEYJ;cACF;mBAC6B;;;YAGrC3T,2BAAO,AAAPA;YACA+T;;;;KAIR,A,C;A,E,E,S,C,CAqCA;;aACcC;AACZ,qBAAyB1P,oBAAe;;QAEtCkK,mBAAY;qBACoBuF;eACxB,iBAAA;;AAEV,KACF,A,C;A,E,E,c,C,CAEAjX;;WACMiX;UAAM;AAAS;qBACI/T;;gBAITuT;UACF;;;AAGV,+BAAkB;;;;AAIlB;;aAISC;UACF;;;AAGP,oBAAkB;;;;AAMlB;aAISC;UACF;;;AAGP,oBAAkB;;;AAIhB,uBAAkB;;;;AAIlB;AACF;MAGKM;AAAP;KACF,A,C;A,E,E,oB,C,CAEA;;QAEI/T,2BAAO,AAAPA;QACA+T;;MAEFD;KACF,A,C;A,E,E,kB,C,CAyBAtX;AAIE,YAAkCyX;KACpC,A,C;A,E,E,kB,C,CAwCAzX;;;AACsB;;AAEpB,kBAAkB;YACiB;AAAQ;AAC3C,AACA;KACF,A,C;A,E,E,M,C,C,I,C;A,E,E,M,C,C,I,C;A,E,E,M,C,C,C,uB,C,CA9BAiB;;;;;OAYA,A,C,C,sB,C,CAoBAA;;QAQEqW;;AAEA;OACF,A,C;A,E,C,C;A,E,uB,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,M,C,CA5QwC;AAAO,YAAG;KAAS,A;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,U,C,CA+U3DtX;AAAe,YAAG0X,AAAAA;KAAY,A,C;A,E,E,W,C,CAC9B3X;AAAiB,YAAG2X,AAAAA;KAAiB,A,C;A,E,E,Y,C,CAErCnX;eACmCmX;AAAjC,0CAAuCA;KACzC,A,C;A,E,E,S,C,CAMA;;WACcA;aAAAA;AACZ,yCAAwD;QACtDjG;qBACgCiG;eACxB,iBAAA;;AAEV,KACF,A,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,gD,C;A,E,E,W,C,CAWAtX;AAAc,YAAGsE;KAAQ,A,C;A,E,E,U,C,CAEzB3E;;aACawX;eACEI;WACmBD;mBAAAA;aACxB,iBAAA;eACU;QAChBhT;AACA;;QAEAA;QAIAiT;AACA;;KAEJ,A;A,E,C,C;A,E,c,C,C,C;A,E,E,G,C,C,6E,C;A,E,E,U,C,CAgEA3X;AAAe,YAAGwD;KAAO,A,C;A,E,E,W,C,CACzBzD;AAAiB,YAAGyD;KAAY,A,C;A,E,E,Q,C,CAGhCqT;AACE;KACF,A,C;A,E,E,U,C,CAEAA;AACE,YAAO;KACT,A,C;A,E,E,a,C,CAEA9W;;;eAOeiX;;AACO;AAElB,cAAY;;eAEDC;;AACO;AAElB,cAAqC,AAA9BE,8BA8MED;;KA5Mb,A,C;A,E,E,M,C,CAYAJ;;qCAiK8B;kBA/JZC;;AACO;;AAErB,qCAA+Ba;;eAEpBZ;;AACO;;AAElB,qCAA+BY;;eAEpBX;;AACO;sBAmLTC;gBAjLGC;YACF;AAAK;AAEf,cAAOS;;KAEX,A,C;A,E,E,S,C,CAEA;;qCA2I8B;kBAzIZb;;oBAC4BK;UAArBL;;QACrBM;;eAEWL;;iBACsBI;UAAfJ;;QAClBK;;eAEWJ;;iBACsBG;UAAfH;;eACPC;;;wBAGgBW;;kBAGbV;cACF;YAERS;;wBAEyBC;;;KAKjC,A,C;A,E,E,Q,C,CASAf;;qCAsG8B;AApG1B,cAAOgB,8BAAsBf;;AAE7B,cAAOe,8BAAsBd;;eAElBC;;AACO;sBA8HTC;gBA5HGC;YACF;AAAK;;QAIfY;AAGA,cAAOH;;KAEX,A,C;A,E,E,S,C,CAUA;;aAC2BI;sBACLC;AACpB,aAAY;QACVjG,cAAO4F,qBAAWA;YACA,kBAAGK;eACb,iBAAA;eAEDL;;AACT,KACF,A,C;A,E,E,oB,C,CAEA;;;qBAG+BC;;QAE3BD;KAEJ,A,C;A,E,E,uB,C,CAEAd;;;AACqB;;;AAED;MAClBiB;;AAEA,YAAOH;KACT,A,C;A,E,E,gB,C,CAUAM;;;UAEMF;QACOG;QAATH;;eAEyBG;QACzBP;QACQQ;QAARD;;MAEF3U,2BAAO,AAAPA;MAbAyU,sBAAsC,AAAL,AAAfA;AAelB;KACF,A,C;A,E,E,a,C,CAGA;;iBAC+BL;aACJA;;QAGvBI;;QAEAK;;QAIAF;;QAEArT;MAEFtB,2BAAO,AAAPA;MAlCAyU,sBAAsC,AAAL,AAAfA;KAoCpB,A,C;A,E,E,kB,C,CAaAjY;AAIE,YAAkCyX;KACpC,A,C;A,E,E,kB,C,CAoBAzX;;;AACsB;;AAEpB,kBAAkB;YAEF,MAAV4X;AAAkB;AACxB,AACA;KACF,A,C;A,E,E,U,C,CAeA1X;AAAkB,YAAGoY;KAAsB,A,C;A,E,E,M,C,C,I,C;A,E,E,M,C,C,I,C;A,E,E,M,C,C,C,4B,C,CAb3CrX;;;;AAUE;OACF,A,C;A,E,C,C;A,E,6B,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,M,C,CAzPwC;AAAO,YAAG;KAAS,A;A,E,C,C;A,E,iB,C,C,C;A,E,E,G,C,C,wC;A,E,C,C;A,E,wB,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,U,C,CAyU3DjB;AAAe,YAAG0X,AAAAA;KAAY,A,C;A,E,E,W,C,CAC9B3X;AAAiB,YAAG2X,AAAAA;KAAiB,A,C;A,E,E,Y,C,CAErCnX;;WACyCmX;8CAAMA;MA2B7Ca,WAAQb;AA3BR;KACF,A,C;A,E,E,S,C,CAMA;;WAC2BA;aAAAA;sBACLA;AACpB,aAAY;QACVjG,SAAEmG;YACgB,kBAAGF;eACb,iBAAA;eAEDE;;AACT,KACF,A,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,wB,C,C,C;A,E,E,G,C,C,uD,C;A,E,E,W,C,CAaAxX;AAAc,YAAGsE;KAAQ,A,C;A,E,E,U,C,CAEzB3E;eACwB2X;UAAH,AAAfO,wBAAkBP;aACd,iBAAA;;aACGa;;UACT7T;AACA;;UAEAA,4BAAW6T;UACXA,aAAQA;AACR;;;KAEJ,A;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,e,C;A,E,E,Y,C,CA+DAhY;AACE,yCAAoCiY;KACtC,A,C;A,E,E,U,C,CAEAxY;AAAe,YAAGwD;KAAO,A,C;A,E,E,W,C,CACzBzD;AAAiB,YAAGyD;KAAY,A,C;A,E,E,U,C,CAGhCzD;;wCAkLsC;kBAhLpBgX;AACd,yCAuMW;;eArMAC;AACX,sCAoMW;;eAlMAC;;AACO;AAElB,cAAwC,AAAjCE,8BA4MED;;KA1Mb,A,C;A,E,E,Q,C,CAEA9W;;0CAmKsC;;;;;AAjKlC,cAAO;aAEE6W;;AACO;oBAmMPC;cAjMCC;UACF;AAAK;AACf,YAAOsB;KACT,A,C;A,E,E,K,C,CAGA1Y;;aAUekX;;;;;QACOA;;;aACPC;;;;;YAMC,AADEC;AACI;;;MAGlB3T,2BAAO,AAAPA;MACAkV;AACA;KAEJ,A,C;A,E,E,Q,C,CAQA3Y;;aAMekX;;AACO;oBAmJTC;cAjJGC;UACF;AAAK;MAGf3T,2BAAO,AAAPA;MACAkV;;AAIA;KAEJ,A,C;A,E,E,kB,C,CA2BApY;;WACMoY;UAAU;AAAS;qBACAlV;;gBAITuT;UACF;;;AAGV,+BAAkB;;;;AAIlB;;aAISC;UACF;;;AAGP,oBAAkB;;;;AAMlB;aAISC;UACF;;;AAGP,oBAAkB;;;AAIhB,uBAAkB;;;;AAGlB;AACF;MAGKyB;AAAP;KACF,A,C;A,E,E,kB,C,CAiCA1Y;AAKE,YAAkC2Y;KACpC,A,C;A,E,E,kB,C,CAwBA3Y;;;AACsB;;AAEpB,kBAAkB;YACiB;AAAY;AAC/C,AACA;KACF,A,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,gB,C,C,C;A,E,E,G,C,C,6D,C;A,E,E,kB,C,CAmBAA;AAIE,YIhrCyC4Y;KJirC3C,A,C;A,E,E,kB,C,CAEA5Y;;;AACsB;;AAEpB,kBAAkB;;;AACsC;;AACxD,AACA;KACF,A;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,oD,C;A,E,E,W,C,CAyEAI;AAAc,YAAGsE;KAAQ,A,C;A,E,E,U,C,CAEzB3E;;iBACiB2Y;eACFf;WACuBkB;uBAAAA;aAC5B,iBAAA;eACU;QAChBnU;AACA;;QAEAA;QAIAiT;AACA;;KAEJ,A;A,E,C,C;A,E,c,C,C,C;A,E,E,G,C,C,mF,C;A,E,E,Y,C,CAuEApX;iDACyC0X;MAyXvCM,WAAQM;AAzXR;KACF,A,C;A,E,E,U,C,CAEA7Y;AAAe,YAAGwD;KAAO,A,C;A,E,E,W,C,CACzBzD;AAAiB,YAAGyD;KAAY,A,C;A,E,E,U,C,CAGhCzD;;wCA+MsC;kBA7MpBgX;;AACO;AAErB,cAAY;;eAEDC;;AACO;AAElB,cAAY;;eAEDC;;AACO;AAElB,cAAwC,AAAjCE,8BAiOED;;KA/Nb,A,C;A,E,E,Q,C,CAEA9W;;0CA4LsC;;;;;AA1LlC,cAAO;;eAEI6W;;AACO;sBAwNTC;gBAtNGC;YACF;AAAK;AACf,cAAOsB,AAAAA;;KAEX,A,C;A,E,E,S,C,CAEA;;aAC2BT;sBACLC;AACpB,aAAY;QACVjG,cAAO4F;YACW,kBAAGK;eACb,iBAAA;eAEDL;;AACT,KACF,A,C;A,E,E,K,C,CAaA7X;;yCAwJsC;kBAtJpBgX;;;;;UACOA;;;AACrB,cAAOM;;eAEIL;;;;;UACOA;;;AAClB,cAAOK;;eAEIJ;;;;;UACOA;;;eACPC;;;wBAGgBW;;cAIf,AADEV;AACI;sBACSU;;AAG3B;;KAEJ,A,C;A,E,E,Q,C,CAEA;;AACE,gBAAA,4BAAA;QACEiB,cN5pCapU;AM6pCf,KACF,A,C;A,E,E,Q,C,CAEA3E;;wCAuHsC;AArHlC,cAAO+X,8BAAsBf;;AAE7B,cAAOe,8BAAsBd;;eAElBC;;AACO;sBAiJTC;gBA/IGC;YACF;AAAK;QAIfY;AACA;;KAEJ,A,C;A,E,E,oB,C,CA2CAhY;UAEW;AAAS;uBACa8X;AAC/B;KACF,A,C;A,E,E,uB,C,CAEA9X;;;AACqB;;;AAED;MAClBgY;;AAEA;KACF,A,C;A,E,E,gB,C,CAUAgB;;;UAEMf;QACOG;QAATH;;eAEyBG;QACzBP;QACQQ;QAARD;;MAEF3U,2BAAO,AAAPA;MAbAyU,sBAAsC,AAAL,AAAfA;AAelB;KACF,A,C;A,E,E,a,C,CAGA;;iBAC+BL;aACJA;;QAGvBI;;QAEAK;;QAIAF;;QAEArT;MAEFtB,2BAAO,AAAPA;MAlCAyU,sBAAsC,AAAL,AAAfA;KAoCpB,A,C;A,E,E,kB,C,CAcAjY;AAKE,YAAkC2Y;KACpC,A,C;A,E,E,kB,C,CAoBA3Y;;;AACsB;;AAEpB,kBAAkB;YAEE,MAAd4X;AAA0B;AAChC,AACA;KACF,A,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,iB,C,C,C;A,E,E,G,C,C,gD;A,E,C,C;A,E,qB,C,C,C;A,E,E,G,C,C,uD,C;A,E,E,W,C,CAyHAxX;AAAc,YAAGsE;KAAQ,A,C;A,E,E,U,C,CAEzB3E;eACwB8Y;UAAH,AAAfZ,wBAAkBY;aACd,iBAAA;;aACGN;;UACT7T;AACA;;UAEAA,4BAAW6T;UACXA,aAAQA;AACR;;;KAEJ,A;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,e,C;A,E,E,U,C,CyBjsDArY;AAAkB,YAAGC;KAAwD,A,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,S,C;A,E,E,S,C,CF8J7E;;AACE,gBAAA,yBAAA;QAAwBsR,SAAxB;AAAkC,KACpC,A,C;A,E,E,iB,C,CAqDAnR;AAAwC,YACpC;KAA0C,A,C;A,E,E,Q,C,C,Q,C,S,C,C,C;A,E,E,E,M,C,I,C,iB,C,S,C,C,I,C,C;A,E,E,C,C;A,E,E,U,C,CAI9CN;;WAGgBgZ;AACd,sBAAOxC;;AAEP,AACA;KACF,A,C;A,E,E,W,C,CAEAzW;AAAiB,YAAG,EAACiZ,AAAAA;KAAmB,A,C;A,E,E,U,C,CAwCxC5Y;;WACgB4Y;UACV,CAACxC;aAAqB,iBAAA;eACfA;UACPA;aAAqB,iBAAA;AACzB;KACF,A,C;A,E,E,W,C,CAwCApW;;UAC6B;aAAW,iBAAA;AAEtC,gBAAA,4CAAA;kBAAA;;AACsB;;;AAEtB,WACM,iBAAA;KACR,A,C;A,E,E,U,C,CAkBAF;AAAkB,YAAG+Y;KAAuB,A;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,I,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,S,C,C,C;A,E,E,G,C,C,S,C;A,E,E,Y,C,CGnW5C1Y;AAAyB,0ChCoQaC;KgCpQe,A,C;A,E,E,W,C,CAErDJ;AAAuB,YAAG;KAAW,A,C;A,E,E,S,C,CAErC;;gBACe;AACb,kBAAkB;QAChB4R,cAAO;YACI,YAAG;eACN,iBAAA;;AAEV,KACF,A,C;A,E,E,W,C,CAEAjS;AAAiB,YAAGM;KAAW,A,C;A,E,E,O,C,CA0G/BwW;AAAwC;KAAmC,A,C;A,E,E,U,C,CAgV3E3W;;UACMoW;AACF;eAGW;;QAEXA;QACAxV;QACAA;QACAA;;QAECwV;;AAGH,YtBpPyB1K;KsBqP3B,A,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,I,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,wB,C,C,C;A,E,E,G,C,C,2B,C;A,E,E,M,C,CF5ac;;UACL;QACD9K;;;MAGFA;MACAA;MACAA;KACF,A;A,E,C,C;A,E,S,C,C,C;A,E,E,G,C,C,oD,C;A,E,E,Y,C,CzB0SJP;AAAyB,4CAiTZ2Y,YACcA,yBACTA;KAnTyC,A,C;A,E,E,S,C,CAE3D;;0BAC0BpV;AACxB,eAAaJ,YAAS,MAAGE,gBAAmB,CAAA,AAAL,QAAsB,AAAdD,AAAAA;aACtCA;;gBAAAA;QAAPqO,cAAOrO;YAqMqB,sBAAGG;4BACzB;;AApMR,KACF,A,C;A,E,E,W,C,CAEA/D;AAAiB,YAAG2D,gBAASE;KAAK,A,C;A,E,E,U,C,CAElC5D;AAAe,YAAmB,EAAA,AAAT,AAAN4D,aAAQF,aAAwB,AAAdC,AAAAA;KAAkB,A,C;A,E,E,U,C,CAmIvDzD;AAAkB,YAAGC;KAAwD,A,C;A,E,E,M,C,CAiE7E;;WACEwD;WAAOC;;;cAAPD;MAAAA;WACoB,CAAA,AAAL,SAAsB;MAArCC;UACIF;QAAgBG;MACpBC,0BAAkB,AAAlBA;KACF,A,C;A,E,E,O,C,CA2CA;;iBAC+C,AAAdH,AAAAA;;;WACnBA;WAAgBD;cAAF,AAAdC;MNtXZxD;WMwXiCuD;WAAOC;MNxXxCxD,wDMwX+B;MAC/BuD;MACAE,aAAQD,AAAAA;MACRA;KACF,A,C;A,E,E,W,C,CArSAwV;;;MAOExV;KACF,A,C;A,E,E,kB,C,C,I,C;A,E,E,M,C,C,C,G,C,C,6B,C;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,kF,C;A,E,E,W,C,CA0UAvD;AAAc,YAAGsE;KAAQ,A,C;A,E,E,U,C,CAEzB3E;;WACEqZ;UA9G8B,AA8GJtV,4BA9GOA;0BACzB;WA8GJuV;iBAAaC;QACf5U;AACA;;WAES0U;;;cAAAA;MAAX1U,4BAAW0U;MACXC,6BAA4B,CAAA,AAAL,SAA6B;AACpD;KACF,A;A,E,C;A,C,A;A,iB,gB,A,E,C,E,C;A,E,G,C,C,E,C;A,E,kB,C,C4BhqBFE;;AA+CE,UAAOC,qBAAaC;GACtB,A,C;A,E,U,C,CApEMC;;;;;;;;;WAIJ;;WAKQ,iBAAA;;;AAGR,UAAOH;GACT,A,C;A,E,mB,C,C,CCsSAjO;AAAmC,UAAGlB;GAAe,A,C,C,Q,C,C,6B,C,C,C,C,C,C,C,C,C,C,C;A,E,0B,C,C,C;A,E,E,G,C,C,a,C;A,E,E,M,C,CD7RpB;AAAa;KAAG9B,A;A,E,C,C;A,E,uB,C,C,C;A,E,E,G,C,C,qB,C;A,E,E,M,C,CAE/CmR;;;AAGI;;;AASA,wCAAkB,IAAEpT;oBAOQmT,aAAUC;AACtC,AACA;;;Y9B6rDGtX,qBAA8B;A8BvrDnC,sCAAkB,IAAE2F;cACLA;QACbZ,sBAAWsS,eAAYC;;AACzB;;QAQEvS,8BAAmBsS,uBAAoBC;AAEzC;KACF,A;A,E,C,C;A,E,K,C,C,C;A,E,E,G,C,C,S;A,E,C,C;A,E,S,C,C,C;A,E,E,G,C,C,S;A,E,C,C;A,E,0B,C,C,C;A,E,E,G,C,C,+B,C;A,E,E,U,C,CClEAvZ;UACY,AAANyZ;AACF;;AAEA;KAEJ,A,C;A,E,E,M,C,C,C,2B,C,CARAC;;OAAkE,A,C;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,oD,C;A,E,E,U,C,CAqBlE1Z;AAAkB;KAAmC,A,C;A,E,E,M,C,C,C,gB,C,CADrD2Z;;OAA6C,A,C;A,E,C,C;A,E,S,C,C,C;A,E,E,G,C,C,6B,C;A,E,E,gB,C,CA+E7C3V;AAEuB,YAkKUwV,sBAAkBI,AAlKrBC;KAE9B,A,C;A,E,E,Q,C,C,Q,C,M,C,C,C;A,E,E,E,M,C,I,C,gB,C,M,C,C,I,C,C;A,E,E,C,C;A,E,E,oB,C,CAaA7Z;AAE2B,YAgEvB8Z,qCAA8BC,AAhEAC;KAElC,A,C;A,E,E,Q,C,C,Q,C,K,C,C,C;A,E,E,E,M,C,I,C,oB,C,K,C,C,I,C,C;A,E,E,C,C;A,E,E,W,C,CAEAC;AAC4B;KAE5B,A,C;A,E,E,W,C,CAEAC;AACwB;KAExB,A;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,gC;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,oB;A,E,C,C;A,E,gB,C,C,C;A,E,E,G,C,C,iC,C;A,E,E,c,C,C,Q,C,I,C,C,C;A,E,E,E,M,C,I,C,Y,C,M,C,I,C,C;A,E,E,C,C;A,E,E,Q,C,CAiOA;;;gBAEqBC;;cAAAA;WAoCfC;;;AAnCJ,aAAkB;mBACDD;YACF;;YACA;cACL;iBAAsBA;YxBzHhCzO,eAA6CA;;mBwB0H9B;sBCvQO;ezBgGf3L;UA6CP2L,eAA6CA;;;0ByB7IvB;mBzBgGf3L;cA6CP2L,eAA6CA;;;0ByB7IvB;mBzBgGf3L;cA6CP2L,eAA6CA;;;0ByB7IvB;mBzBgGf3L;cA6CP2L,eAA6CA;;;0ByB7IvB;mBzBgGf3L;cA6CP2L,eAA6CA;;;0ByB7IvB;mBzBgGf3L;cA6CP2L,eAA6CA;;;0ByB7IvB;mBzBgGf3L;cA6CP2L,eAA6CA;0ByB7IvB;mBzBgGf3L;cA6CP2L,eAA6CA;0ByB7IvB;mBzBgGf3L;cA6CP2L,eAA6CA;mBwBgJM,AAAN;mBAhCf,UAAU,UAAS;0BC7P3B;mBzBgGf3L;cA6CP2L,eAA6CA;mBwBiJD;mBAjCd,UAAU,UAAS;0BC7P3B;mBzBgGf3L;cA6CP2L,eAA6CA;;;;cwBqJnC;iBAAsByO;YxBrJhCzO,eAA6CA;;mBwBsJ9B;sBCnSO;ezBgGf3L;UA6CP2L,eAA6CA;sByB7IvB;ezBgGf3L;UA6CP2L,eAA6CA;;;AwB0J7C;;QxB1JAA,eAA6CA;iBwB6J3B;aACJyO;QxB9JdzO,eAA6CA;;KwBgK/C,A,C;A,E,E,Y,C,CAEA;;AACE,gBAAoB2O,iBAAAA,kBAAF;aACMA;;eACd,iBAAA;;AAEV,MACAA;KACF,A,C;A,E,E,gB,C,CAEA;;UAIM,CAACC;QACHC;;uBAEmBC;cACb,CAACF;iBACG;iBAAA;;eAuEZD;;kBAAAA;UAAAA;;eA1EE;;eAOQ,iBAAA;;;;KAGZ,A,C;A,E,E,oB,C,CAQAxa;;;YAEQ,CAACqK;AAAiB;QACtBkQ,AAAAA,mBA1FKnU;AA2FL;;QAEAmU,AAAAA;AACA;;QAEAA,AAAAA;AACC;;QAEDA,AAAAA;AACA;;aAEAA;QAAAA;QACAK;QACAL;AACA;;;;UAEAG;eAEAH;UAAAA;cACa,AAATvR;YACF6R,sBAAe7R;AACf,wBAAkB,IAAEA;cxB5NxB6C,eAA6CA;cwB8NvCgP,sBAAe7R;;AACjB;UAEFuR;UACAO;AACA;;UAEAJ;eAEAH;UAAAA;AAEA,oBAAA,kBAAmB1D,wCAAnB;kBAAA;YxBzOFhL,eAA6CA;YwB4OzC+O;YxB5OJ/O,eAA6CA;YwB8OzCgP,sBAAehE;;AACjB,UACA0D;UACAO;AACA;;AAEA;;KAEJ,A,C;A,E,E,a,C,CAEA;eAGEN;;cAAAA;MAAAA;KACF,A,C;A,E,E,M,C,C,C,G,C,C,gW,C,C,0B,C,CA7JAtZ;;;iBAEwB;QAOtB6Z;AALA,cxBxFyBlP;OwByF3B,A,C;A,E,C;A,C,A;A,c,a,A,E,C,E,C;A,E,G,C,C,E,C;A,E,e,C,CxB7VF1L;AAAsC,UAAG6a;GAAkC,A,C;A,E,kB,C,C0B8DzE9Z;;;AAEI,YAAOmJ;;;M1B+KPwB;A0BlKA,gBAAoB1K,iCAAF;mBACDA;YACF;;iB1B0K4B0K;YAA7CA;;iBAA6CA;YAA7CA;;iBAA6CA;YAA7CA;;iBAA6CA;YAA7CA;gB0BhKqB;c1BgKrBA;;cAAAA;;;iB0BzJsC,gBAAkB,gBACM;wBDWxC;iBzBgGf3L;iBA6CsC2L;YAA7CA;;;eAA6CA;UAA7CA;;eAA6CA;UAA7CA;;sByB7IsB;ezBgGf3L;eA6CsC2L;UAA7CA;;;A0B/IE;M1B+IFA;A0B7IE;;AAEF,6BjCoiBc/C;GiCniBhB,A,C;A,E,mB,C,CCzGApI;AAAiC;GAAwC,A,C;A,E,S,C,C,C3BgOrEV;AACJ;GACF,A,C,C,Q,C,C,mB,C,C,C,C,C,C,C,C,C,C,C;A,E,gB,C,C,CA7NMC;AAAoC,UAAG4Y;GAAsB,A,C,C,Q,C,C,0B,C,C,C,C,C,C,C,C,C,C,C;A,E,gB,C,CAgL3DnY;;aACU;QACH;AACT,gBAAoBK,sBAAF;QAChBA;AACF,AAEF;GACF,A,C;A,E,c,C,C4BrHAL;;;AAEE,cAAA,0BAAA;MACE4F,UADF;AAEA;AACc;;AACd;GACF,A,C;A,E,K,C,CC5FF;;ICOE2U;GDAF,A,C;A,E,kC,C,C,C;A,E,E,G,C,C,kB,C;A,E,E,M,C,C7BoR8B;;UAChB;QACJC;MAEFA,gBAASC;KAIX,A;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,qC,C;A,E,E,G,C,CI6BJnb;;;UACM;AAAsB;AAC1B,YAAQoQ,iCAA0BtP,gCAC1Bsa,eAASta;KACnB,A,C;A,E,E,Y,C,CA4DAb;AAAiB,YAAGmQ;KAAsB,A,C;A,E,E,U,C,CA+D1CjQ;;WX+ZUgJ;UW9ZGkS,4BX+ZgCC,uDACHA;UW/Z7BC,2BXoa2BD,oDACHA;UWpaxBC,2BXya4BD,mDACHA;UWzazBC,2BX8a6BD,oDACHA;YW9axBC,2BXmb6BD,sDACHA;YWnb1BC,2BXwb6BD,sDACHA;WWxb3BE,6BX6bmCF,2DACHA;;AW5b1C;;AAEA;KAEJ,A,C;A,E,E,2C,C,CAjKA3L;UAImC;;KAInC,A,C;A,E,E,e,C,CJ7KMA;MAGJzP;KACF,A,C;A,E,E,W,C,C,I,C;A,E,E,M,C,C,C,G,C,C,kZ,C,C,c,C,CI4EAgB;;gBAwBgBua,qBHpNNzM;YGqNE;;eH1FeH;;kBAAAA;kBDvDlB3O,sBCuDkB2O;;kBAAAA;kBDvDlB3O,sBCuDkB2O;;kBAAAA;gBDvDlB3O,sBCuDkB2O;;kBAAAA;iBGwGZ6M,UHxGY7M;;kBAAAA;mBGyGV6M,UHzGU7M;;kBAAAA;mBG0GV6M,UH1GU7M;;kBAAAA;wBG4GL,YAA6B,UAA5B8M,gDH5GI9M;;;;;;;;kBAAAA;cGkHV,AHlHUA;;oBAAAA;iBAAAA;gBGoHR;qBAEU;;sBHtHFA;+BDvDlB3O,sBCuDkB2O;;sBAAAA;iCGwHI6M,UHxHJ7M;;sBDvDlB3O;iCIgLgB,4BAAM;;sBAAN;uBACV,iBAAQ;;;;;mCJhIdA;AIyIL,gBAAO,2DADsC;;eAIvC,iBAAA;OAEV,A,C,C,mC,C,CAcAyP;;;;OAQA,A,C,C,oB,C,CA+GAzO;;;eAEkB;YACP;AAAS;YACT;AAAQ;YACR;AAAO;AAChB;OACF,A,C,C,qB,C,CAEAA;YACQ;AAAQ;YACR;AAAO;AACb;OACF,A,C,C,mB,C,CAEAA;YACQ;AAAO;AACb;OACF,A,C;A,E,C,C;A,E,6B,C,C,C;A,E,E,G,C,C,a,C;A,E,E,M,C,CApMIjB;;AACuB;AACrB,YJpJGC;KIqJL,A;A,E,C,C;A,E,gC,C,C,C;A,E,E,G,C,C,a,C;A,E,E,M,C,CAEAW;;AACuB;AACrB,YJ7IGX;KI8IL,A;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,I,C,CW1JJ0b;AACE,YAAO,mBAAqC,AAAVlI,iBAAY5S;KAChD,A,C;A,E,E,I,C,CAMA8a;AACE,YAAO,mBAAqC,qBAAVlI,gBAAY5S;KAChD,A,C;A,E,E,I,C,CASA8a;AACE,YAAO,mBxBzCMhb,2BAAAA,gCwByCgC,AAAV8S;KACrC,A,C;A,E,E,G,C,CAmBA1T;AAAgC,YAAkB,qBAAf,gBAAiBc;KAAe,A,C;A,E,E,G,C,CAYnEd;AAAiC,YAAkB,qBAAf,gBAAkBc;KAAe,A,C;A,E,E,G,C,CAMrEd;AAAiC,YAAkB,qBAAf,gBAAkBc;KAAe,A,C;A,E,E,G,C,CA2CrEd;;;;AAC0B;AACxB,YAAO0T,oBAAa5S;KACtB,A,C;A,E,E,Y,C,CAEAb;AAAiB,YAAGyT;KAAkB,A,C;A,E,E,U,C,CAYtCvT;;;WAtB0BuT;UAoCL;AAGjB,yBADI,kBAA2B;wBAGRmI,UAAUC,4BA5DN;wBA6DJD,UAAUE,4BAtDN;mBAwDzBC,2CAAUC;AACd,kBAvE2B;KAwE7B,A,C;A,E,E,W,C,C,I,C;A,E,E,M,C,C,C,G,C,C,4c,C,C,S,C,CA/JA;8BAU8D,AADV,AADA,AADJ,AADF,AAAvB,qBACC,qBACE,qBACA,oBACK;OACD,A,C;A,E,C,C;A,E,2B,C,C,C;A,E,E,G,C,C,a,C;A,E,E,M,C,CA6H5B9b;UACQ;AAAW;UACX;AAAU;UACV;AAAS;UACT;AAAQ;UACR;AAAO;AACb;KACF,A;A,E,C,C;A,E,2B,C,C,C;A,E,E,G,C,C,a,C;A,E,E,M,C,CACAA;UACQ;AAAO;AACb;KACF,A;A,E,C,C;A,E,K,C,C,C;A,E,E,G,C,C,S,C;A,E,E,c,C,Cf7GI+b;AAA0B;KAAqC,A,C;A,E,E,Q,C,C,I;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,Q,C;A,E,E,U,C,C0BgCrE/b;AAAkB;KAAmB,A;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,e,C;A,E,E,U,C,CAYrCA;eACMoF;UAAQ;AACV;AAEF;KACF,A,C;A,E,E,M,C,C,C,c,C,CAPA4W;;OAA6B,A,C;A,E,C,C;A,E,U,C,C,C;A,E,E,G,C,C,uB,C;A,E,E,U,C,CAgC7Bhc;AAAkB,kCAAiBoF;KAAQ,A,C;A,E,E,M,C,C,C,W,C,CAb3C6W;;OAAwC,A,C,C,gB,C,CAGxCA;;OAAmD,A,C,C,gB,C,CAOnDA;;OACqD,A,C;A,E,C,C;A,E,gB,C,C,C;A,E,E,G,C,C,e,C;A,E,E,U,C,CAqFrDjc;AAAkB,yCAA4BoF;KAAQ,A,C;A,E,E,M,C,C,C,iB,C,CADtD8W;;OAA8B,A,C;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,e,C;A,E,E,U,C,CAkB9Blc;eAAsB;AAAJ,YAAiB;KAEU,A,C;A,E,E,Q,C,C,I,C;A,E,E,M,C,C,C,mB,C,CAH7Cmc;;OAAyC,A,C;A,E,C,C;A,E,U,C,C,C;A,E,E,G,C,C,e,C;A,E,E,U,C,CAgBzCnc;AAAkB,6BAAgBoF;KAAQ,A,C;A,E,E,M,C,C,C,W,C,CAD1CgX;;OAAwB,A,C;A,E,C,C;A,E,2B,C,C,C;A,E,E,G,C,C,sB,C;A,E,E,U,C,CAkBxBpc;eACMqc;;AACF;AAEF,gEACUC;KACZ,A,C;A,E,E,M,C,C,C,4B,C,CARAC;;OAAkD,A,C;A,E,C,C;A,E,gB,C,C,C;A,E,E,G,C,C,S,C;A,E,E,U,C,CAclDvc;AAAkB;KAAkB,A,C;A,E,E,c,C,CAEpC+b;AAA0B;KAAO,A,C;A,E,E,Q,C,C,I;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,S,C;A,E,E,U,C,CAMjC/b;AAAkB;KAAmB,A,C;A,E,E,c,C,CAErC+b;AAA0B;KAAO,A,C;A,E,E,Q,C,C,I;A,E,C,C;A,E,yB,C,C,C;A,E,E,G,C,C,oB,C;A,E,E,U,C,CAajC/b;AAAkB,2CAEewc;KAAwC,A,C;A,E,E,M,C,C,C,0B,C,CAHzEC;;OAA8C,A,C;A,E,C,C;A,E,wB,C,C,C;A,E,E,G,C,C,gB,C;A,E,E,U,C,CC5U9Czc;eACMoF;;AAAiB;AACrB;KACF,A;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,gB,C;A,E,E,U,C,CAmBApF;AAAkB,uCAAsBoF;KAAQ,A,C;A,E,E,kB,C,C,I,C;A,E,E,M,C,C,C,gB,C,CAFhD;;OAA0C,A,C;A,E,C,C;A,E,O,C,C,C;A,E,E,G,C,C,a,C;A,E,E,U,C,CItB1CpF;AAAkB,8BAAawI;KAAK,A,C;A,E,E,M,C,C/ByC9B4J;mBACSrS;AACb,qCAAiCA,iCAA+B2c;KAClE,A,C;A,E,E,S,C,CAEM;mBACS3c;;;QAGXA;;MAEFA,iCAA+B2c;KACjC,A,C;A,E,E,S,C,CAEA1c;;YACeD;;;QAEY4c,sBAAS;;QAChC5c;;AAEF;KACF,A,C;A,E,E,M,C,C,C,G,C,C,6E,C;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,S;A,E,C,C;A,E,I,C,C,C;A,E,E,G,C,C,S,C;A,E,E,U,C,CgCvEAC;AAAkB;KAAS,A;A,E,C,C;A,E,M,C,C,C;A,E,E,G,C,C,G,C;A,E,E,G,C,CCuC3BH;AAAwB;KAAyB,A,C;A,E,E,Y,C,CjC7B3CC;AAAiB,YAAGC;KAA+B,A,C;A,E,E,U,C,CAGnDC;AAAkB,YAAGD;KAA+B,A;A,E,C,C;A,E,U,C,C,C;A,E,E,G,C,C,S;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,U,C,CAmOpDD;AAAe,YAAG4L,AAAAA;KAAgB,A,C;A,E,E,W,C,CkC1OxC7L;AAAiB,YlC0OO6L,AAAAA;KkC1OO,A,C;A,E,E,O,C,ClC4OzB;;MAEJA,iBAA6CA;KAC/C,A,C;A,E,E,U,C,CkCjOA;;iBACsBkR;UAChB,CAAC9D;AAAqB;U1CkVR3Y;A0ChVhB;gBACQ2Y;;UlC2NVpN,iBAA6CA;iBkC1NlCoN;AAAoB;QAE7B+D,aAAM/D;AACN,eAAOA;UlCuNTpN,iBAA6CA;gBkCrNnCoN;;UlCqNVpN,iBAA6CA;;AkCpN3C;KAEJ,A,C;A,E,E,U,C,ClC6NM1L;AAAkB,YAAG0L;KAAS,A,C;A,E,E,c,C,CAvB9BoR;MAEFpR;KAIJ,A,C;A,E,E,M,C,C,C,a,C,CANMoR;;;;OAMN,A,C;A,E,C,C;A,E,M,C,C,C;A,E,E,G,C,C,S;A,E,C;A,C,A;A,kB,a,A,E,C,E,C;A,E,G,C,C,E,C;A,E,oB,C,CMy0RAvc;;eAEiB2P,6CAAAA;IAGR6M;;SAAAA;AAAP,UAAOA;GACT,A,C;A,E,sB,C,CAsujBAhc;;;AAII;;MADF;AAGE;;;GAEJ,A,C;A,E,S,C,CA2nQF4P;;;AAEiC;AAE/B,UAAOyC;GACT,A,C;A,E,W,C,C,C;A,E,E,G,C,C,U,C;A,E,E,G,C,C,muB;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,6C,C;A,E,E,U,C,CA3rlCEpT;;KAAwB,A,C;A,E,E,G,C,C,mB;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,6C,C;A,E,E,U,C,CA6UxBA;;KAAwB,A,C;A,E,E,G,C,C,iB;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,G,C,C,iB;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,c,C;A,E,E,U,C,CAmVxBgd;AAAgC,0EAin9BQC;KAjn9BqB,A,C;A,E,E,c,C,C,I,C;A,E,E,G,C,C,iB;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,oC,C;A,E,E,G,C,C,mB;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,c,C;A,E,E,G,C,C,+D;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,c,C;A,E,E,U,C,CA65N7Djd;;KAAwB,A,C;A,E,E,G,C,C,c;A,E,C,C;A,E,O,C,C,C;A,E,E,G,C,C,O,C;A,E,E,c,C,CAy9CxB+G;AAAmC;KAAiC,A,C;A,E,E,U,C,CA+PpE/G;AAAkB,YApBMkd;KAoBM,A,C;A,E,E,wC,C,CAmY9BC;;;;;;;UAw6vBEC,QA/GI;UA+GJA,QAVI;UAz5vBEC;;;;;;;UAKFC;;;UAEAA;;;;;aASepN,AAAAA;QAAjBqN;QACAC,wBAAcD;eAx2ETE;QA42ELC,mBAAYxN;QACZqN,AA26HoB/Z;;;;yBAv6HH+Z;;yBAj3EZE,iBAm3EyCE;QAC9CJ,AAAAA;;;QAIAC;mBACWA;;QAEXI;mBAEWL;AACX,oBAAOK,2BAA0B;UAC/Bb;AACF;WAEoBQ;UAAH;QACjBK;MAGFC;MAEA3N;AAEA;KACF,A,C;A,E,E,8B,C,C,Q,C,S,C,C,I,C,C,a,C,C,C;A,E,E,E,M,C,I,C,wC,C,S,C,C,I,C,C,a,C,C,I,C,C;A,E,E,C,C;A,E,E,a,C,CAQA;MACE;KACF,A,C;A,E,E,sC,C,CAuBA;MAEE4N;MACAC,qBAAOC;KAET,A,C;A,E,E,c,C,C,Q,C,S,C,C,I,C,C,C;A,E,E,E,M,C,I,C,sC,C,S,C,C,I,C,C,I,C,C,I,C,C;A,E,E,C,C;A,E,E,U,C,CA0jCAhB;AAAgC,0EAg+oBQC;KAh+oBqB,A,C;A,E,E,Y,C,CAK7DD;AAAkC,0EA29oBMC;KA39oByB,A,C;A,E,E,W,C,CAKjED;AAAsC,0EAs9oBEC;KAt9oB4B,A,C;A,E,E,W,C,CAoKpED;AAAiC,0EAkzoBOC;KAlzoBuB,A,C;A,E,E,U,C,C,I,C;A,E,E,G,C,C,U;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,G,C,C,kB;A,E,C,C;A,E,U,C,C,C;A,E,E,G,C,C,c,C;A,E,E,G,C,C,Y;A,E,C,C;A,E,K,C,C,C;A,E,E,G,C,C,c,C;A,E,E,gB,C,CAmoB/D;;KAA4B,A,C;A,E,E,G,C,C,yvB;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,c,C;A,E,E,kB,C,CA6N5B;;KAAoF,A,C;A,E,E,qB,C,CAQpF;;KAAuF,A,C;A,E,E,G,C,C,c;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,6B,C;A,E,E,G,C,C,qB;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,2B,C;A,E,E,G,C,C,iB;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,G,C,C,mB;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,oC,C;A,E,E,U,C,C,I,C;A,E,E,G,C,C,kB;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,6B,C;A,E,E,G,C,C,mB;A,E,C,C;A,E,S,C,C,C;A,E,E,G,C,C,oB,C;A,E,E,G,C,C,e;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,6B,C;A,E,E,G,C,C,iB;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,uC,C;A,E,E,U,C,CAy4IvFjd;;KAAwB,A,C;A,E,E,W,C,C,I,C;A,E,E,G,C,C,U;A,E,C,C;A,E,U,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,G,C,C,gB;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,oB,C;A,E,E,G,C,C,oD;A,E,C,C;A,E,W,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,G,C,C,iB;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,oB,C;A,E,E,G,C,C,kB;A,E,C,C;A,E,U,C,C,C;A,E,E,G,C,C,W,C;A,E,E,M,C,CA80DxB;;KAAiD,A,C;A,E,E,M,C,C,Q,C,S,C,C,I,C,C,C;A,E,E,E,M,C,S,C,I,C,I,C,C;A,E,E,C,C;A,E,E,G,C,C,Y;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,c,C;A,E,E,G,C,C,oB;A,E,C,C;A,E,U,C,C,C;A,E,E,G,C,C,U,C;A,E,E,G,C,C,8F;A,E,C,C;A,E,I,C,C,C;A,E,E,G,C,C,c,C;A,E,E,S,C,CA4+BjDI;AACE;KACF,A,C;A,E,E,Q,C,CAgBA;eAGM;UAAgB;QAElB6d;KAEJ,A,C;A,E,E,U,C,CA4CAje;eAAqBke;AAAH;KAAmD,A,C;A,E,E,G,C,C,0F;A,E,C,C;A,E,Q,C,C,C;A,E,E,G,C,C,2C,C;A,E,E,U,C,CA8brEpe;AAAe;KAA8B,A,C;A,E,E,M,C,CAE7Cqe;eAEoBhe;;aACV,iBAAA;AACR;KACF,A,C;A,E,E,S,C,CACA;WACQ,iBAAA;KACR,A,C;A,E,E,W,C,CAiCAge;;;AAA0B,YAAG;KAAW,A,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,I,C,C;A,E,E,C,C;A,E,E,kB,C,C,I,C;A,E,E,6B,C,C,I,C;A,E,E,G,C,C,wB;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,G,C,C,mB;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,uB,C;A,E,E,G,C,C,qB;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,8B,C;A,E,E,G,C,C,mB;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,0B,C;A,E,E,G,C,C,mB;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,0B,C;A,E,E,G,C,C,kB;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,oB,C;A,E,E,G,C,C,qB;A,E,C,C;A,E,K,C,C,C;A,E,E,G,C,C,c,C;A,E,E,U,C,CAolDxCne;;KAAwB,A,C;A,E,E,G,C,C,O;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,4C,C;A,E,E,G,C,C,mB;A,E,C,C;A,E,sB,C,C,C;A,E,E,G,C,C,c,C;A,E,E,G,C,C,wB;A,E,C,C;A,E,O,C,C,C;A,E,E,G,C,C,c,C;A,E,E,M,C,CAg5ExBA;AAA+B,YAAG8Q;KAAa,A,C;A,E,E,S,C,CAE/C;MAA8CM;KAAsB,A,C;A,E,E,S,C,CAepE;;AACE;cACcgN;;AACK;QAEjB7M,cAtB8BT;;AAuBhC,KACF,A,C;A,E,E,Q,C,CAEA6F;;MAEE0H;AACA;KACF,A,C;A,E,E,U,C,CAEA1H;;MAEE0H;AACA;KACF,A,C;A,E,E,U,C,CAEAve;AAAe,YAAGwD;KAAO,A,C;A,E,E,W,C,CAEzBzD;AAAiB,YAAGue;KAAe,A,C;A,E,E,M,C,C,I,C;A,E,E,M,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,Q,C,C,C,C,Q,C,C;A,E,E,C,C;A,E,E,G,C,C,S;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,uB,C;A,E,E,G,C,C,kB;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,c,C;A,E,E,wC,C,CAqZnCjB;;;AAGI;cAKU;iBArohBgBjN;MAwohB5B6M;MAAAA,+CAAsBuB;AAEtB;KACF,A,C;A,E,E,G,C,C,kB;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,c,C;A,E,E,wC,C,CA4FAnB;;;AAGI;iBA1uhB0BjN;WA+uhBd,6CArwhBPuN;MAqwhBO;;gBAAA;MAEJc;;YAAAA;MACVxB;MAAsByB;MAAtBzB;AACA;KACF,A,C;A,E,E,G,C,C,qB;A,E,C,C;A,E,mB,C,C,C;A,E,E,G,C,C,c,C;A,E,E,wC,C,CAwDAI;;;AAGI;iBA/yhB0BjN;WAozhBd,6CA10hBPuN;MA00hBO;;gBAAA;MAEdV;MAAsBwB;MAAtBxB;AACA;KACF,A,C;A,E,E,G,C,C,yB;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,c,C;A,E,E,sC,C,CAkEA;;MAEEe;iBACeE;MAGfS,AAAAA;KACF,A,C;A,E,E,c,C,C,Q,C,S,C,C,I,C,C,C;A,E,E,E,M,C,I,C,sC,C,S,C,C,I,C,C,I,C,C,I,C,C;A,E,E,C,C;A,E,E,kB,C,C,I,C;A,E,E,G,C,C,qB;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,oC,C;A,E,E,G,C,C,qB;A,E,C,C;A,E,O,C,C,C;A,E,E,G,C,C,Q,C;A,E,E,G,C,C,qF;A,E,C,C;A,E,M,C,C,C;A,E,E,G,C,C,c,C;A,E,E,Y,C,CAkzEAC;;UAIMC;AAAyB;kBACjBC;QACVA;AAEF,YAAOA;KACT,A,C;A,E,E,U,C,CAovCA5e;;KAAwB,A,C;A,E,E,G,C,C,kB;A,E,C,C;A,E,K,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,G,C,C,M;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,4C,C;A,E,E,U,C,CA88DxBF;AAAe;KAA8B,A,C;A,E,E,M,C,CAE7Cqe;eAEoBhe;;aACV,iBAAA;AACR;KACF,A,C;A,E,E,S,C,CACA;WACQ,iBAAA;KACR,A,C;A,E,E,W,C,CAiCAge;;;AAA0B,YAAG;KAAW,A,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,I,C,C;A,E,E,C,C;A,E,E,kB,C,C,I,C;A,E,E,6B,C,C,I,C;A,E,E,G,C,C,8B;A,E,C,C;A,E,4B,C,C,C;A,E,E,G,C,C,a,C;A,E,E,M,C,CAz7pBV;AAAI;KAAQU,A;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,gB,C;A,E,E,U,C,CAwiU1CV;;WAgIkBW;UAAAA,AAAAA;;aA9HE,iBAAA;UACZ;aAAW,iBAAA;AACjB;KACF,A,C;A,E,E,Q,C,CAMA;;WAGmBrN;WAAiBqN;UAA5B;AAEF,mBAgHYA,AAAAA,6BAhH8B;UACxCA,eAAarN;AACf,AAEF;KAKJ,A,C;A,E,E,S,C,CAwEA;;WACEqN;WAqC4BA;;cAAAA;MArC5BA,uBAqC4BA;KApC9B,A,C;A,E,E,Y,C,CAEAze;AAA4B,YAAGye,iCAAAA,AAAAA;KAAyB,A,C;A,E,E,U,C,CA2BxDhf;AAAe,YAAGgf,AAAAA,AAAAA;KAAuB,A,C;A,E,E,M,C,CAOzCX;eAA8BW,AAAAA;;cAAAA;AAAH,YAAGA;KAAuB,A,C;A,E,E,W,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,I,C,C;A,E,E,C,C;A,E,E,O,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,I,C,C;A,E,E,C;A,E,C,C;A,E,qB,C,C,C;A,E,E,G,C,C,wB,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,I,C,C;A,E,E,C,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,wC,C,C,C;A,E,E,G,C,C,2C,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,I,C,C;A,E,E,C,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,oB,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,M,C,CA6jJ3C;AAAO,YAAGlX;KAAU,A;A,E,C,C;A,E,sB,C,C,C;A,E,E,G,C,C,qB,C;A,E,E,M,C,CAMpB;AAAO,YAAGC;KAAY,A;A,E,C,C;A,E,sB,C,C,C;A,E,E,G,C,C,wB,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,I,C,C;A,E,E,C,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,yC,C,C,C;A,E,E,G,C,C,4C,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,I,C,C;A,E,E,C,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,S,C;A,E,E,S,C,CA4uNhC;;AACE,gBAAgBD,iDhB3v8BoBtH,qBgB2v8BpC;chBzv8BekE;QgB2v8Bb+M,cADY;;AAEd,KACF,A,C;A,E,E,Q,C,CAEAoF;;mBAEmBoI,AAAAA;;AAEjB,iBAAsBC,0BAAqB;;gBAF1BD;aAGFC;YAATC;UACFrX,UAASoX;;AAEb,AACA;KACF,A,C;A,E,E,U,C,CAEArI;;mBAEmBoI,AAAAA;;AAEjB,iBAAsBC,0BAAqB;;gBAF1BD;aAGFC;YAATC;UACFpX,YAAWmX;;AAEf,AACA;KACF,A,C;A,E,E,W,C,CAKAnf;AACE,YAAOM;KACT,A,C;A,E,E,M,C,C,I,C;A,E,E,M,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,Q,C,C,C,C,Q,C,C;A,E,E,C;A,E,C,C;A,E,oB,C,C,C;A,E,E,G,C,C,wB,C;A,E,E,M,C,CAwBAH;AACE,YAAO+e,AAAAA;KACT,A,C;A,E,E,S,C,CAEA;MACEA,AAAAA;KACF,A,C;A,E,E,U,C,CAWAjf;AACE,YAAO8H,AAAAA;KACT,A,C;A,E,E,U,C,CAEA/H;AAAyB,YAAGqf;KAA0B,A;A,E,C,C;A,E,mB,C,C,C;A,E,E,G,C,C,mB;A,E,C,C;A,E,Y,C,C,C;A,E,E,G,C,C,S,C;A,E,E,qC,C,CAm+BtDC;iDAMM,cAAc,iBA8EcxO,qBA9EW;;MA+E3CD;AAhFA;KAEF,A;A,E,C,C;A,E,uB,C,C,C;A,E,E,G,C,C,6C;A,E,C,C;A,E,wB,C,C,C;A,E,E,G,C,C,uE,C;A,E,E,Q,C,CAiFAiD;UAUsB7F;AATL;MAEfsR;MAEAtR;MACAuR;AACA;KACF,A,C;A,E,E,Y,C,CAuCA;eACMA;UAAQ,cAAW,AATJC;QAUjBxR,uBAAAA,cAAyBmP,qBAAqBsC;KAElD,A,C;A,E,E,W,C,CAEA;eACMF;UAAQ;QACVvR,0BAAAA,cAA4BmP,qBAAqBsC;KAErD,A;A,E,C,C;A,E,mB,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,e,C,CA+jBA1f;AACE,YAAO2f,4DAA0B/G;KACnC,A,C;A,E,E,iB,C,CAEA5Y;;gBACgB4Y;;kBACEgH;;oBAEFA;;AAGZ;AAEF,YAAOC;KACT,A,C;A,E,E,+B,C,CA5BAC;;;UAGMF;AACF,gEAAA;UACEA,gBhBn//BWjb;AgBo//Bb,AAEA,+DAAA;UACEib,gBhBv//BWjb;AgBw//Bb;KAEJ,A,C;A,E,E,M,C,C,C,G,C,C,0J,C,C,oB,C,CAZAmb;;YA1txBSlC;2CAsh3Ba1M;;;;OAhzFtB,A,C,C,+C,C,C,CAkBAhQ;AAEE;OACF,A,C,C,Q,C,C,yD,C,C,C,C,C,C,C,C,C,C,C,C,0C,C,C,CAEAA;;aAESqB;aA0xFPwd;;QAAAA;aAEQA;aAA0BC;;aAAAA;;eAC9BD;eAAsBC;;iBACtBD;iBAA0BC;;;;;;;eACzBD,8BACDA,0BACAA;;;AAjyFJ;OACF,A,C,C,Q,C,C,oD,C,C,C,C,C,C,C,C,C,C,C;A,E,C,C;A,E,kB,C,C,C;A,E,E,G,C,C,S,C;A,E,E,Y,C,CASAvf;AAIE,mDAo7DcuI;KAn7DhB,A,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,I,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,oB,C,C,C;A,E,E,G,C,C,oB,C;A,E,E,e,C,CA++CA/I;AACE,YjBjijC6BI,+BiBiijCtBmd;KACT,A,C;A,E,E,iB,C,CAEAvd;AACE,YjBrijC6BI,+BiBqijCtBmd;KAET,A;A,E,C,C;A,E,0C,C,C,C;A,E,E,G,C,C,sB,C;A,E,E,M,C,CANyB;AAAI,YAAG0C;KAAuB,A;A,E,C,C;A,E,4C,C,C,C;A,E,E,G,C,C,8C,C;A,E,E,M,C,CAKjD;AAAI,YAAGA;KAA+C,A;A,E,C,C;A,E,oB,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,e,C,CAkG5DjgB;AACE,YAAOkgB,AAAAA,oCAAyBtH;KAClC,A,C;A,E,E,iB,C,CAEA5Y;;gBACgB4Y;WACVuH;UAAAA;AACF,cAAOC,AAAAA;eACED;AACT,cAAOC,AAAAA;;aACEC;YAAAA;AACT;iBACSA;AACT;iBACSA;AACT;iBACSA;AACT;;AAEF;KACF,A;A,E,C,C;A,E,wB,C,C,C;A,E,E,G,C,C,sG,C;A,E,E,iB,C,CA2DArgB;;AAEI;;AAIA;UAz5GKkf;AA65GL,cAAOoB,AAAAA;AAET;KACF,A,C;A,E,E,M,C,C,C,G,C,C,0C,C,C,yB,C,CAtBAC;;;a6Bh7jCS;QAAA;aAAA;QAAA;;a7Bw2jCoC;a6Bx2jCpC;QAAA;;O7Bu7jCT,A,C;A,E,C,C;A,E,gC,C,C,C;A,E,E,G,C,C,a,C;A,E,E,M,C,CAF+C;AAAO;KAAmB,A;A,E,C,C;A,E,iB,C,C,C;A,E,E,G,C,C,S,C;A,E,E,e,C,CAsBzEvgB;;;AAEI;;AAGA;AAEF;KACF,A,C;A,E,E,iB,C,CAEAA;oCAC+BwgB;AAC3B;AAEF,YAAOC;KACT,A;A,E,C,C;A,E,qB,C,C,C;A,E,E,G,C,C,sD,C;A,E,E,U,C,CA8OAzgB;;qBAC+B,AAAVsZ;WACA7V;UAAF;QACfkB,sBAAW+b,aAAAA;QACXpH;AACA;;MAEF3U;MACA2U;AACA;KACF,A,C;A,E,E,W,C,CAEA/G;AAAc,YAAG5N;KAAQ,A;A,E,C,C;A,E,gB,C,C,C;A,E,E,G,C,C,a,C;A,E,E,Y,C,CAilBzBxE;AAAoB,YAAQwgB;KAAiB,A,C;A,E,E,Q,C,CA0B7CxgB;AAAgB,YAAQwgB;KAAa,A,C;A,E,E,Y,C,CAMrCxgB;AAAoB,YAAQwgB;KAAiB,A,C;A,E,E,U,C,CAiB7CxgB;AAAkB,YAAgCwgB;KAAK,A,C;A,E,E,W,C,C,I;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,S;A,E,C,C;A,E,oB,C,C,C;A,E,E,G,C,C,2B;A,E,C,C;A,E,wB,C,C,C;A,E,E,G,C,C,kB,C;A,E,E,c,C,CAgPvD;MAYEjH;KACF,A,C;A,E,E,c,C,CAEA;;cACU2F;;;kBAGQzG;cACR,CAACiH,AAAAA;;uDAEiCjH;;;YACpCA;;;eAvjJCsG;mBAAAA;cA4jJQ;gBACL,CAACW,AAAAA;;gEAEKjH;;;cACRA;;;iBAOOgI,2BAAAA;AACX,mBAA0B,AAvjJvB7Y,AAAAA,kCAujJ8B;;oBADtB6Y;oBAEE7Y;gBACP,CAAC8X,AAAAA,uCAAmClX,0BA1kJvCuW;;2DA6kJStG,0CA7kJTsG;;;cAQQA;cACfA;;;AAukJI;YAIE2B,oBAAaC;;;;;;;;UASfzB;;KAEN,A;A,E,C,C;A,E,0C,C,C,C;A,E,E,G,C,C,mB,C;A,E,E,M,C,CA9DE;;MACE0B;cAEY1B;AACZ,aAAa;oBAEK2B;QAChBtH;;AAEF,KACF,A;A,E,C;A,C,A;A,iB,Y,A,E,C,E,C;A,E,G,C,C,E,C;A,E,a,C,C,C;A,E,E,G,C,C,a,C;A,E,E,gB,C,C,I,C;A,E,E,G,C,C,kB;A,E,C,C;A,E,a,C,C,C;A,E,E,G,C,C,sB,C;A,E,E,G,C,C,iB;A,E,C,C;A,E,U,C,C,C;A,E,E,G,C,C,U,C;A,E,E,a,C,C8B957BF;M9BgwJEuE;MACAC,qBAAOC;K8B/vJT,A,C;A,E,E,wC,C,CAEAb;;;;M9Bwk5BEC,QA/GI;MA+GJA,QAVI;MAUJA;;;iB8B3j5BelN,mCAAAA;oB9Bu1EaA;M8Bl1EjB6M;;aAAAA;AACX,kBAAO+D,iBAAgB;QACrBC;AACF,AACA;KACF,A,C;A,E,E,U,C,CAuVA/D;AAAgC,0E9B26zBQC;K8B36zBqB,A,C;A,E,E,Y,C,CAe7DD;AAAkC,0E9B45zBMC;K8B55zByB,A,C;A,E,E,W,C,CAKjED;AAAsC,0E9Bu5zBEC;K8Bv5zB4B,A,C;A,E,E,W,C,CA2EpED;AAAiC,0E9B40zBOC;K8B50zBuB,A,C;A,E,E,a,C,C,I,C;A,E,E,G,C,C,ioD;A,E,C;A,C,A;A,iB,gB,A,E,C,E,C;A,E,G,C,C,E,C;A,E,U,C,C,C;A,E,E,G,C,C,S,C;A,E,E,a,C,C,I,C;A,E,E,M,C,C,C,qB,C,CCxoLzD1c;AAAqB,oC5CwjGb,sCAAS;O4CxjGoB,A,C;A,E,C;A,C,A;A,mC,2B,A,E,C,E,C;A,E,G,C,C,E,C;A,E,e,C,C,C;A,E,E,G,C,C,c,C;A,E,E,e,C,CCQ3C;;UACY,oBAAa;aACf,iBAAA;;aAEA,iBAAA;KAEV,A,C;A,E,E,G,C,C,oI;A,E,C,C;A,E,e,C,C,C;A,E,E,G,C,C,wB,C;A,E,E,U,C,CAk2BAT;AAAe;KAAmC,A,C;A,E,E,M,C,CAElDA;eACqBK;mCAj2BO;QACxB6gB;AAi2BF;KACF,A,C;A,E,E,S,C,CAEA;eACqB7gB;mCAt2BO;QACxB6gB;;KAu2BJ,A,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,K,C,C;A,E,E,C,C;A,E,E,kB,C,C,I,C;A,E,E,G,C,C,a;A,E,C,C;A,E,gB,C,C,C;A,E,E,G,C,C,kB,C;A,E,E,U,C,CAzdAlhB;AAAe;KAAmC,A,C;A,E,E,6B,C,C,I;A,E,C,C;A,E,qB,C,C,C;A,E,E,G,C,C,kD,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,K,C,C;A,E,E,C,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,0B,C,C,C;A,E,E,G,C,C,6B,C;A,E,E,O,C,C,I,C;A,E,E,O,C,C,Q,C,C,C,C;A,E,E,E,M,C,C,C,C,K,C,C;A,E,E,C,C;A,E,E,kB,C,C,I;A,E,C,C;A,E,+C,C,C,C;A,E,E,G,C,C,kD;A,E,C;A,C,A;A,2B,uB,A,E,C,E,C;A,E,G,C,C,E,C;A,E,W,C,CC7cpD;;;AAII;;;;AAOA;;;AAKA;;;AAMA;;;GAOJ,A;A,C,A;A;A;A;;A,uB;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A,yB;A,C,C,c,C,C,C,Q,C,Q,C,C,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C,C,C;A,E,E,E,C,C,I,C,K,C,Q,C,C,E,C,Q,C;A,E,E,E,M,C,C,C,K,C,S,C;A,E,E,M,C,C,C,Q,C,S,C;A,E,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C;A,E,E,M,C,C,C,Q,C,S,C;A,E,E,C,C,Q,C,E,C,I,C;A,E,E,M,C,C,C,M,C,S,C;A,E,E,C,C,M,C,Q,C,E,C,S,C;A,E,E,M,C,C,C,M,C,S,C;A,E,E,C,C,Q,C,W,C,E,C,K,C;A,E,E,M,C,C,C,O,C,S,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C;A,E,E,M,C,Q,C;A,E,E,C,C,Q,C,U,C,C,C,M,C;A,E,E,M,C,Q,C;A,E,M,C,C,C,oB,C,Q,C,C;A,C;A,C,C,kB,C,C,C,Q,C,Q,C,C,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C;A,E,E,M,C,C,C,Q,C,S,C;A,E,E,C,C,Q,C,E,C,I,C;A,E,E,M,C,Q,C;A,E,E,C,C,Q,C,W,C,E,C,K,C;A,E,E,M,C,C,C,O,C,S,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C;A,E,E,M,C,Q,C;A,E,E,C,C,Q,C,U,C,C,C,M,C;A,E,E,M,C,Q,C;A,E,M,C,C,C,oB,C,Q,C,C;A,C;A,C,C,iB,C,C,C,Q,C,Q,C,C,C;A,E,E,C,C,Q,C,E,C,I,C;A,E,E,M,C,Q,C;A,E,E,C,C,Q,C,W,C,E,C,K,C;A,E,E,M,C,C,C,O,C,S,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C;A,E,E,M,C,Q,C;A,E,E,C,C,Q,C,U,C,C,C,M,C;A,E,E,M,C,Q,C;A,E,M,C,C,C,oB,C,Q,C,C;A,C;A,C,C,gB,C,C,C,Q,C,Q,C,C,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C;A,E,E,M,C,C,C,Q,C,S,C;A,E,E,C,C,Q,C,E,C,I,C;A,E,E,M,C,Q,C;A,E,E,C,C,C,C,Q,C,U,C,C,C,M,C,C;A,E,E,M,C,C,C,uB,C,S,C;A,E,M,C,Q,C;A,C;A,C,C,iB,C,C,C,Q,C,Q,C,C,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C;A,E,E,M,C,C,C,Q,C,S,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C;A,E,E,M,C,C,C,Q,C,S,C;A,E,E,C,C,Q,C,E,C,I,C;A,E,E,M,C,Q,C;A,E,E,C,C,C,C,Q,C,U,C,C,C,M,C,C;A,E,E,M,C,C,C,uB,C,S,C;A,E,M,C,Q,C;A,C;A,C,C,gB,C,C,C,Q,C,Q,C,C,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C;A,E,E,M,C,C,C,Q,C,S,C;A,E,E,C,C,Q,C,E,C,I,C;A,E,E,M,C,Q,C;A,E,E,C,C,C,C,Q,C,U,C,C,C,M,C,C;A,E,E,M,C,C,C,uB,C,S,C;A,E,M,C,Q,C;A,C;A,C,C,gB,C,C,C,Q,C,Q,C,C,C;A,E,E,C,C,Q,C,E,C,I,C;A,E,E,M,C,Q,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C;A,E,E,M,C,Q,C;A,E,E,C,C,Q,C,U,C,C,C,M,C;A,E,E,M,C,Q,C;A,E,M,C,C,C,oB,C,Q,C,C;A,C;A,C,C,O,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C,E,C,M,C,E,C,E,C,Q,C;A,E,E,M,C,Q,C,C,C,E,C;A,E,M,C,C,C,iB,C,Q,C,C,I,C,Q,C,C,E,C,C;A,C;A,C,C,G,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,E,C,C,Q,C,E,C,I,C;A,E,E,M,C,E,C,E,C,I,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C;A,E,E,M,C,E,C,E,C,I,C,E,C,Q,C,G,C,E,C;A,E,M,C,C,C,c,C,Q,C,C,G,C,Q,C,C,E,C,C;A,C;A,C,C,K,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C,E,C,M,C,E,C,E,C,Q,C;A,E,E,M,C,Q,C,E,C,E,C;A,E,M,C,C,C,gB,C,Q,C,C,G,C,Q,C,C,E,C,C;A,C;A,C,C,U,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,E,C,C,Q,C,W,C,E,C,K,C,E,C,M,C,Q,C,E,C,Q,C,E,C,e,C,Q,C,C,Q,C,yB,C,C,C;A,E,E,E,C,C,E,C,G,C,C,C,G,C,E,C,E,C,E,C,C,C,Q,C,M,C;A,E,E,E,M,C,Q,C,E,C,C;A,E,M,C,C,C,kB,C,Q,C,C,M,C,Q,C,C,E,C,C;A,C;A,C,C,Y,C,C,C,Q,C,Q,C,C,E,C,C,E,C,C,C;A,E,E,C,C,C,Q,C,W,C,E,C,K,C,E,C,e,C,Q,C,C,Q,C,yB,C,C,C,C,E,C,C,Q,C,c,C,E,C,E,C,G,C,C,C,G,C,E,C,E,C,E,C,C,C,Q,C,M,C;A,E,E,M,C,Q,C,E,C,C,C,C,E,C;A,E,M,C,C,C,iB,C,Q,C,C,S,C,Q,C,C,E,C,C,E,C,C;A,C;A,C,C,O,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C,E,C,M,C,E,C,E,C,Q,C;A,E,E,M,C,Q,C,C,C,E,C;A,E,M,C,C,C,iB,C,Q,C,C,I,C,Q,C,C,E,C,C;A,C;A,C,C,M,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,E,C,C,M,C,Q,C,E,C,Q,C,E,C,M,C,E,C,E,C,Q,C;A,E,E,M,C,Q,C,C,C,E,C;A,E,M,C,C,C,gB,C,Q,C,C,I,C,Q,C,C,E,C,C;A,C;A,C,C,oB,C,C,C,Q,C,Q,C,C,E,C,C,E,C,C,E,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,kB,C,Q,C,C,E,C,C,E,C,C,E,C,C;A,C;A,C,C,c,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,M,C,C,C,kB,C,Q,C,C,U,C,Q,C,C,E,C,C;A,C;A,C,C,gC,C,C,C,Q,C,Q,C,C,E,C,C,E,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,8B,C,Q,C,C,E,C,C,E,C,C;A,C;A,C,C,0C,C,C,C,Q,C,Q,C,C,E,C,C,E,C,C,E,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,wC,C,Q,C,C,E,C,C,E,C,C,E,C,C;A,C;A,C,C,c,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,M,C,C,C,iB,C,Q,C,C,W,C,Q,C,C,E,C,C;A,C;A,C,C,Y,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,M,C,C,C,iB,C,Q,C,C,S,C,Q,C,C,E,C,C;A,C;A,C,C,U,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,Q,C,Q,C,C;A,C;A,C,C,W,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,S,C,Q,C,C;A,C;A,C,C,a,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,c,C,Q,C,C,Y,C,Q,C,C;A,C;A,C,C,e,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,kB,C,Q,C,C,W,C,Q,C,C;A,C;A,C,C,e,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,iB,C,Q,C,C,Y,C,Q,C,C;A,C;A,C,C,c,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,kB,C,Q,C,C,U,C,Q,C,C;A,C;A,C,C,U,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,Q,C,Q,C,C;A,C;A,C,C,W,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,S,C,Q,C,C;A,C;A,C,C,Y,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,U,C,Q,C,C;A,C;A,C,C,c,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,Y,C,Q,C,C;A,C;A,C,C,a,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,W,C,Q,C,C;A,C;A,C,C,a,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,W,C,Q,C,C;A,C;A,C,C,W,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,S,C,Q,C,C;A,C;A,C,C,kB,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,gB,C,Q,C,C;A,C;A,C,C,W,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,iB,C,Q,C,C,Q,C,Q,C,C;A,C;A,C,C,W,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,M,C,C,C,iB,C,Q,C,C,Q,C,Q,C,C,E,C,C;A,C;A,C,C,uB,C,C,C,Q,C,Q,C,C,E,C,C,E,C,C,E,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,qB,C,Q,C,C,E,C,C,E,C,C,E,C,C;A,C;A,C,C,S,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,O,C,Q,C,C;A,C;A,C,C,Q,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,M,C,Q,C,C,E,C,C;A,C;A,C,C,c,C,C,C,Q,C,Q,C,C,K,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,Y,C,Q,C,C,K,C,C;A,C;A,C,C,U,C,C,C,Q,C,Q,C,C,K,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,Q,C,Q,C,C,K,C,C;A,C;A,C,C,e,C,C,C,Q,C,Q,C,C,K,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,a,C,Q,C,C,K,C,C;A,C;A,C,C,W,C,C,C,Q,C,Q,C,C,K,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,S,C,Q,C,C,K,C,C;A,C;A,C,C,W,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,iB,C,Q,C,C,Q,C,Q,C,C;A,C;A,C,C,e,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,a,C,Q,C,C;A,C;A,C,C,U,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,c,C,Q,C,C,U,C,Q,C,C;A,C;A,C,C,mB,C,C,C,Q,C,Q,C,C,E,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,iB,C,Q,C,C,E,C,C;A,C;A,C,C,Q,C,C,C,Q,C,Q,C,C,C;A,E,M,C,C,C,gB,C,Q,C,C,M,C,Q,C,C;A,C;A,C,C,oB,C,C,C,I,oB,C,C;A,C,C,kB,C,C,C,I,kB,C,C;A,C,C,W,C,C,C,I,W,C,C;A,C,C,U,C,C,C,I,U,C,C,C;A,C,C,wB,C,C,C,I,qB,C,M,C;A,C,C,0B,C,C,C,I,qB,C,Q,C;A,C,C,yB,C,C,C,I,qB,C,O,C;A,C,C,yB,C,C,C,I,qB,C,O,C;A,C,C,e,C,C,C,S,C,S;A,C,C,a,C,C,C,O,C,S;A,C,C,gB,C,C,C,U,C,S;A,C,C,gB,C,C,C,U,C,S;A,C,C,U,C,C,C;;;C;A,C,C,Y,C,C,C;A;A,C,C,Y,C,C,C;;;;;;;;;;;;;C;A,C,C,Y,C,C,C;;;;;;;;;;;;;C;A,C,C,Y,C,C,C;;;;;;;;;;;;;;;C;A,C,C,Y,C,C,C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;C;A,C,C,Y,C,C,C;;;;;;;;;;;;;;;;;;;;;;;;;;;;C;A,C,C,Y,C,C,C;;;;;;;;;;;;;;;;;C;A,C,C,mB,C,C,C,I,W,C,I,C,C,I,C;A,C,C,gB,C,C,C,I,a,C,I,C;A,C,C,gB,C,C,C,I,a,C,I,C;A,O;;;;;A,C,C,Q,C,C,C,C,C,kB,C,O,C,gB,C,C,U,C,C,Q,C,C,c,C,C,W,C,C,O,C,C,U,C,C,a,C,C,Y,C,C,c,C,C,S,C,C,e,C,C,U,C,C,c,C,C,c,C,C,W,C,C,a,C,C,S,C,C,U,C,C,a,C,C,W,C,C,S,C,C,iB,C,C,W,C,C,c,C,C,c,C,C,a,C,C,gB,C,C,c,C,C,iB,C,C,a,C,C,mB,C,C,c,C,C,gB,C,C,U,C,C,a,C,C,e,C,C,Y,C,C,Y,C,C,a,C,C,W,C,C,mB,C,C,kB,C,C,c,C,C,kB,C,C,c,C,C,e,C,C,gB,C,C,e,C,C,gB,C,C,Y,C,C,W,C,C,c,C,C,W,C,C,a,C,C,Y,C,C,iB,C,C,gB,C,C,mB,C,C,gB,C,C,kB,C,C,iB,C,C,kB,C,C,kB,C,C,mB,C,C,gB,C,C,qB,C,C,e,C,C,a,C,C,e,C,C,e,C,C,c,C,C,Y,C,C,a,C,C,oB,C,C,a,C,C,Y,C,C,Y,C,C,c,C,C,oB,C,C,e,C,C,c,C,C,Y,C,C,kB,C,C,c,C,C,a,C,C,W,C,C,W,C,C,W,C,C,W,C,C,W,C,C,W,C,C,W,C,C,a,C,C,U,C,C,W,C,C,e,C,C,e,C,C,qB,C,C,gB,C,C,sB,C,C,qB,C,C,e,C,C,Y,C,C,U,C,C,a,C,C,a,C,C,a,C,C,Y,C,C,W,C,C,a,C,C,a,C,C,Y,C,C,e,C,C,kB,C,C,c,C,C,Y,C,C,qB,C,C,gB,C,C,iB,C,C,kB,C,C,c,C,C,a,C,C,Y,C,C,kB,C,C,Y,C,C,iB,C,C,a,C,C,oB,C,C,iB,C,C,iB,C,C,a,C,C,a,C,C,iB,C,C,a,C,C,e,C,C,c,C,C,e,C,C,kB,C,C,iB,C,C,c,C,C,kB,C,C,Y,C,C,mB,C,C,e,C,C,U,C,C,W,C,C,a,C,C,W,C,C,e,C,C,a,C,C,Y,C,C,a,C,C,Y,C,C,Y,C,C,Y,C,C,c,C,C,uB,C,C,a,C,C,c,C,C,W,C,C,U,C,C,oB,C,C,iB,C,C,kB,C,C,e,C,C,kB,C,C,e,C,C,a,C,C,c,C,C,U,C,C,Y,C,C,e,C,C,e,C,C,iB,C,C,sB,C,C,kB,C,C,kB,C,C,c,C,C,kB,C,C,c,C,C,kB,C,C,c,C,C,c,C,C,gB,C,C,e,C,C,oB,C,C,oB,C,C,c,C,C,c,C,C,gB,C,C,c,C,C,c,C,C,a,C,C,gB,C,C,e,C,C,U,C,C,W,C,C,U,C,C,a,C,C,U,C,C,a,C,C,a,C,C,a,C,C,Y,C,C,Y,C,C,a,C,C,W,C,C,Y,C,C,W,C,C,qB,C,C,wB,C,C,gB,C,C,oB,C,C,qB,C,C,gB,C,C,uB,C,C,oB,C,C,oB,C,C,gB,C,C,oB,C,C,gB,C,C,c,C,C,a,C,C,gB,C,C,e,C,C,U,C,C,W,C,C,U,C,C,a,C,C,U,C,C,a,C,C,a,C,C,a,C,C,Y,C,C,Y,C,C,a,C,C,W,C,C,Y,C,C,W,C,C,c,C,C,a,C,C,gB,C,C,e,C,C,W,C,C,a,C,C,U,C,C,a,C,C,Y,C,C,gB,C,C,a,C,C,c,C,C,gB,C,C,a,C,C,U,C,C,iB,C,C,e,C,C,a,C,C,mB,C,C,c,C,C,gB,C,C,c,C,C,C,C,Y,C;A,C,C,Q,C,C,C,C,C,kB,C,O,C,gB,C,C,M,C,C,I,C,C,K,C,C,Q,C,C,Q,C,C,C,C,Y,C;A,C,C,Q,C,C,C,C,C,kB,C,O,C,gB,C,C,S,C,C,Y,C,C,kB,C,C,kB,C,C,e,C,C,W,C,C,c,C,C,U,C,C,Y,C,C,W,C,C,S,C,C,e,C,C,C,C,Y,C;A,C,C,gB,C,C,C,U,C,S;A,C,C,6B,C,C,C,uB,C,S;A,C,C,+B,C,C,C,yB,C,S;A,C,C,c,C,C,C,Q,C,S;A,uB;A,C,C,kC,C,C,C,iB;A,C,C,gC,C,C,C,mB;A,C,C,uB,C,C,C,C;A,C,C,+B,C,C,C,I;A,C,C,mC,C,C,C,I;A,C,C,4B,C,C,C,K;A,C,C,c,C,C,C,I;A,C,C,oB,C,C,C,I;A,C,C,uB,C,C,C,I;A,C,C,8B,C,C,C,I;A,C,C,8B,C,C,C,I;A,C,C,sB,C,C,C,I;A,C,C,K,C,C,C,I;A,C,C,O,C,C,C,I;A,C,C,K,C,C,C,I;A,C,C,M,C,C,C,I;A,C,C,M,C,C,C,I;A,C,C,S,C,C,C,I;A,C,C,W,C,C,C,I;A,C,C,Y,C,C,C,I;A,C,C,G,C,C,C,I;A,C,C,W,C,C,C,I;A,C,C,a,C,C,C,I;A,C,C,a,C,C,C,I;A,C,C,a,C,C,C,C,C,W;A,C,C,iB,C,C,C,C;A,C,C,sB,C,C,C,I;A,C,C,mB,C,C,C,I;A,C,C,yB,C,C,C,I;A,C,C,yB,C,C,C,I;A,C,C,e,C,C,C,I;A,C,C,gB,C,C,C,I;A,O,C,K,C,C,C,C,Y,C,C,Y,C,C,gB,C,C,Q,C,C,C7CqgBA;;CAA+C,A,C;A,O,C,K,C,C,C,C,c,C,C,c,C,C,kB,C,C,Q,C,C,CAC/C;;CAAiD,A,C;A,O,C,K,C,C,C,C,c,C,C,c,C,C,kB,C,C,Q,C,C,CACjD;;CAAiD,A,C;A,O,C,K,C,C,C,C,0B,C,C,0B,C,C,8B,C,C,Q,C,C,CACjDD;;CACoD,A,C;A,O,C,K,C,C,C,C,Y,C,C,2B,C,C,+B,C,C,Q,C,C,CAUlDkB;QAA2BkgB;CAAmB,A,C;A,O,C,K,C,C,C,C,W,C,C,0B,C,C,8B,C,C,Q,C,C,CAG9ClgB;;CAAwD,A,C;A,O,C,K,C,C,C,C,qB,C,C,sC,C,C,0C,C,C,Q,C,C,CDirBxDA;QACImgB,mCAAeC;CAA4C,A,C;A,O,C,K,C,C,C,C,mB,C,C,oC,C,C,wC,C,C,Q,C,C,CAI/DpgB;QACImgB,mCAAeC;CAA0D,A,C;A,O,C,K,C,C,C,C,iB,C,C,kC,C,C,sC,C,C,Q,C,C,CAI7EpgB;QACImgB,mCAAeC;CAAmC,A,C;A,O,C,K,C,C,C,C,wB,C,C,yC,C,C,6C,C,C,Q,C,C,CAItDpgB;QACImgB;;;;;;;;CAAwC,A,C;A,O,C,K,C,C,C,C,sB,C,C,uC,C,C,2C,C,C,Q,C,C,CAI5CngB;QACImgB,mCAAeC;CAAqC,A,C;A,O,C,K,C,C,C,C,6B,C,C,8C,C,C,kD,C,C,Q,C,C,CAIxDpgB;QACImgB;;;;;;;;CAA6C,A,C;A,O,C,K,C,C,C,C,qB,C,C,sC,C,C,0C,C,C,Q,C,C,CAIjDngB;QACImgB,mCAAeE;CAAuC,A,C;A,O,C,K,C,C,C,C,4B,C,C,6C,C,C,iD,C,C,Q,C,C,CAI1DrgB;QACImgB;;;;;;;CAA4C,A,C;A,O,C,K,C,C,C,C,0B,C,C,2C,C,C,+C,C,C,Q,C,C,CAIhDngB;QACImgB,mCAAeE;CAAyC,A,C;A,O,C,K,C,C,C,C,iC,C,C,kD,C,C,sD,C,C,Q,C,C,CAI5DrgB;QACImgB;;;;;;;CAAiD,A,C;A,O,C,K,C,C,C,C,e,C,C,uC,C,C,2C,C,C,Q,C,C,CH9jBrDngB;;CAAsC,A,C;A,O,C,K,C,C,C,C,mB,C,C,mB,C,C,uB,C,C,Q,C,C,CgCjuBxCsgB;QAAwB;CAAsB,A,C;A,O,C,K,C,C,C,C,e,C,C,oB,C,C,wB,C,C,Q,C,C,CFwD5CtgB;;CAAsC,A,C;A,O,C,K,C,C,C,C,kB,C,C,sC,C,C,0C,C,C,Q,C,C,Cd81/BtCA;W6Bz1/BS;EAAA;;C7B87/BP,A,C;A,O,C,K,C,C,C,C,sB,C,C,0C,C,C,8C,C,C,Q,C,C,CA2RFA;Qb1h9BOkB,sBAA8B;Ca0h9BuB,A,C;A,iB;A;A,I,C,e,C,C,C,C,C,C;A;iB,6C;A,2B;A,yC;A,kE;A,kD;A,sD;A,wE;A,qD;A,0I;A,e;A,mC;A,6B;A,2D;A,kF;A;A,8D;A,8B;A,iE;A,yD;A,uD;A,6D;A,uD;A,gD;A;A;A;A;A;;;;A;;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A;A,C,Q,C,C,C,C;A,E,Q,C,M,C,C,C,C,C;A,E,E,I,C,C,C,C,C,C,C;A,E,E,C,C,C,C,C,C,C,C,C;A,E,E,M,C,M,C,I,C,mB,C,C,C,C,C,C,C,C;A,E,C;A,E,I,C,a,C,C,C,Q,C,I,C,C,C;A,E,E,M,C,M,C,U,C,C,C,I,C,C,C,I,C,U,C,C;A,E,C,C;A,E,I,a,C,C,C,uB,C;A,E,I,c,C,C,C,M,C,a,C,C,E,C,C,M,C,a,C,C,C,C,M,C,M,C,I,C,C,C;A,E,I,Y,C,C,C,Q,C;A,E,G,C,C,I,C,C,C,C,C,C,C,C,C,E,C,C,C;A,E,E,I,Q,C,C,C,M,C,Y,C,C,C,G,C,C,C,C,C,C,C,G,C,C;A,E,E,E,C,C,C,C,Q,C,E,C,c,C,C,C,C;A,E,E,E,c,C,Q,C,C,C,C,C,C;A,E,E,E,I,C,U,C,C,C,Q,C;A,E,E,E,K,C;A,E,E,C;A,E,C;A,C,C,C;A,I,C,oB,C,C,C,I,C,a,C,iB,C;A,uB;A;;;;;;;;;;;;;;;;;;;;;;;;;;;;;A,qB;A;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;A" +} diff --git a/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart.precompiled.js b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart.precompiled.js new file mode 100644 index 0000000..18e3a05 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.dart.precompiled.js @@ -0,0 +1,12868 @@ +// Generated by dart2js, the Dart to JavaScript compiler. +// The code supports the following hooks: +// dartPrint(message): +// if this function is defined it is called instead of the Dart [print] +// method. +// +// dartMainRunner(main, args): +// if this function is defined, the Dart [main] method will not be invoked +// directly. Instead, a closure that will invoke [main], and its arguments +// [args] is passed to [dartMainRunner]. +(function($) { +function dart(){ this.x = 0 }var A = new dart; +delete A.x; +var B = new dart; +delete B.x; +var C = new dart; +delete C.x; +var D = new dart; +delete D.x; +var E = new dart; +delete E.x; +var F = new dart; +delete F.x; +var G = new dart; +delete G.x; +var H = new dart; +delete H.x; +var J = new dart; +delete J.x; +var K = new dart; +delete K.x; +var L = new dart; +delete L.x; +var M = new dart; +delete M.x; +var N = new dart; +delete N.x; +var O = new dart; +delete O.x; +var P = new dart; +delete P.x; +var Q = new dart; +delete Q.x; +var R = new dart; +delete R.x; +var S = new dart; +delete S.x; +var T = new dart; +delete T.x; +var U = new dart; +delete U.x; +var V = new dart; +delete V.x; +var W = new dart; +delete W.x; +var X = new dart; +delete X.x; +var Y = new dart; +delete Y.x; +var Z = new dart; +delete Z.x; +function Isolate() {} +init(); + +$ = Isolate.$isolateProperties; +var $$ = {}; + +// Native classes +(function (reflectionData) { + "use strict"; + function map(x){x={x:x};delete x.x;return x} + function processStatics(descriptor) { + for (var property in descriptor) { + if (!hasOwnProperty.call(descriptor, property)) continue; + if (property === "^") continue; + var element = descriptor[property]; + var firstChar = property.substring(0, 1); + var previousProperty; + if (firstChar === "+") { + mangledGlobalNames[previousProperty] = property.substring(1); + var flag = descriptor[property]; + if (flag > 0) descriptor[previousProperty].$reflectable = flag; + if (element && element.length) init.typeInformation[previousProperty] = element; + } else if (firstChar === "@") { + property = property.substring(1); + $[property]["@"] = element; + } else if (firstChar === "*") { + globalObject[previousProperty].$defaultValues = element; + var optionalMethods = descriptor.$methodsWithOptionalArguments; + if (!optionalMethods) { + descriptor.$methodsWithOptionalArguments = optionalMethods = {} + } + optionalMethods[property] = previousProperty; + } else if (typeof element === "function") { + globalObject[previousProperty = property] = element; + functions.push(property); + init.globalFunctions[property] = element; + } else if (element.constructor === Array) { + addStubs(globalObject, element, property, true, descriptor, functions); + } else { + previousProperty = property; + var newDesc = {}; + var previousProp; + for (var prop in element) { + if (!hasOwnProperty.call(element, prop)) continue; + firstChar = prop.substring(0, 1); + if (prop === "static") { + processStatics(init.statics[property] = element[prop]); + } else if (firstChar === "+") { + mangledNames[previousProp] = prop.substring(1); + var flag = element[prop]; + if (flag > 0) element[previousProp].$reflectable = flag; + } else if (firstChar === "@" && prop !== "@") { + newDesc[prop.substring(1)]["@"] = element[prop]; + } else if (firstChar === "*") { + newDesc[previousProp].$defaultValues = element[prop]; + var optionalMethods = newDesc.$methodsWithOptionalArguments; + if (!optionalMethods) { + newDesc.$methodsWithOptionalArguments = optionalMethods={} + } + optionalMethods[prop] = previousProp; + } else { + var elem = element[prop]; + if (prop !== "^" && elem != null && elem.constructor === Array && prop !== "<>") { + addStubs(newDesc, elem, prop, false, element, []); + } else { + newDesc[previousProp = prop] = elem; + } + } + } + $$[property] = [globalObject, newDesc]; + classes.push(property); + } + } + } + function addStubs(descriptor, array, name, isStatic, originalDescriptor, functions) { + var f, funcs = [originalDescriptor[name] = descriptor[name] = f = array[0]]; + f.$stubName = name; + functions.push(name); + for (var index = 0; index < array.length; index += 2) { + f = array[index + 1]; + if (typeof f != "function") break; + f.$stubName = array[index + 2]; + funcs.push(f); + if (f.$stubName) { + originalDescriptor[f.$stubName] = descriptor[f.$stubName] = f; + functions.push(f.$stubName); + } + } + for (var i = 0; i < funcs.length; index++, i++) { + funcs[i].$callName = array[index + 1]; + } + var getterStubName = array[++index]; + array = array.slice(++index); + var requiredParameterInfo = array[0]; + var requiredParameterCount = requiredParameterInfo >> 1; + var isAccessor = (requiredParameterInfo & 1) === 1; + var isSetter = requiredParameterInfo === 3; + var isGetter = requiredParameterInfo === 1; + var optionalParameterInfo = array[1]; + var optionalParameterCount = optionalParameterInfo >> 1; + var optionalParametersAreNamed = (optionalParameterInfo & 1) === 1; + var isIntercepted = requiredParameterCount + optionalParameterCount != funcs[0].length; + var functionTypeIndex = array[2]; + var unmangledNameIndex = 2 * optionalParameterCount + requiredParameterCount + 3; + var isReflectable = array.length > unmangledNameIndex; + + if (getterStubName) { + f = tearOff(funcs, array, isStatic, name, isIntercepted); + f.getterStub = true; + if (isStatic) init.globalFunctions[name] = f; + originalDescriptor[getterStubName] = descriptor[getterStubName] = f; + funcs.push(f); + if (getterStubName) functions.push(getterStubName); + f.$stubName = getterStubName; + f.$callName = null; + if (isIntercepted) init.interceptedNames[getterStubName] = true; + } + if (isReflectable) { + for (var i = 0; i < funcs.length; i++) { + funcs[i].$reflectable = 1; + funcs[i].$reflectionInfo = array; + } + var mangledNames = isStatic ? init.mangledGlobalNames : init.mangledNames; + var unmangledName = array[unmangledNameIndex]; + var reflectionName = unmangledName; + if (getterStubName) mangledNames[getterStubName] = reflectionName; + if (isSetter) { + reflectionName += "="; + } else if (!isGetter) { + reflectionName += ":" + requiredParameterCount + ":" + optionalParameterCount; + } + mangledNames[name] = reflectionName; + funcs[0].$reflectionName = reflectionName; + funcs[0].$metadataIndex = unmangledNameIndex + 1; + if (optionalParameterCount) descriptor[unmangledName + "*"] = funcs[0]; + } + } + function tearOffGetterNoCsp(funcs, reflectionInfo, name, isIntercepted) { + return isIntercepted + ? new Function("funcs", "reflectionInfo", "name", "H", "c", + "return function tearOff_" + name + (functionCounter++)+ "(x) {" + + "if (c === null) c = H.closureFromTearOff(" + + "this, funcs, reflectionInfo, false, [x], name);" + + "return new c(this, funcs[0], x, name);" + + "}")(funcs, reflectionInfo, name, H, null) + : new Function("funcs", "reflectionInfo", "name", "H", "c", + "return function tearOff_" + name + (functionCounter++)+ "() {" + + "if (c === null) c = H.closureFromTearOff(" + + "this, funcs, reflectionInfo, false, [], name);" + + "return new c(this, funcs[0], null, name);" + + "}")(funcs, reflectionInfo, name, H, null) + } + function tearOffGetterCsp(funcs, reflectionInfo, name, isIntercepted) { + var cache = null; + return isIntercepted + ? function(x) { + if (cache === null) cache = H.closureFromTearOff(this, funcs, reflectionInfo, false, [x], name); + return new cache(this, funcs[0], x, name) + } + : function() { + if (cache === null) cache = H.closureFromTearOff(this, funcs, reflectionInfo, false, [], name); + return new cache(this, funcs[0], null, name) + } + } + function tearOff(funcs, reflectionInfo, isStatic, name, isIntercepted) { + var cache; + return isStatic + ? function() { + if (cache === void 0) cache = H.closureFromTearOff(this, funcs, reflectionInfo, true, [], name).prototype; + return cache; + } + : tearOffGetter(funcs, reflectionInfo, name, isIntercepted); + } + var functionCounter = 0; + var tearOffGetter = (typeof dart_precompiled == "function") + ? tearOffGetterCsp : tearOffGetterNoCsp; + if (!init.libraries) init.libraries = []; + if (!init.mangledNames) init.mangledNames = map(); + if (!init.mangledGlobalNames) init.mangledGlobalNames = map(); + if (!init.statics) init.statics = map(); + if (!init.typeInformation) init.typeInformation = map(); + if (!init.globalFunctions) init.globalFunctions = map(); + if (!init.interceptedNames) init.interceptedNames = map(); + var libraries = init.libraries; + var mangledNames = init.mangledNames; + var mangledGlobalNames = init.mangledGlobalNames; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var length = reflectionData.length; + for (var i = 0; i < length; i++) { + var data = reflectionData[i]; + var name = data[0]; + var uri = data[1]; + var metadata = data[2]; + var globalObject = data[3]; + var descriptor = data[4]; + var isRoot = !!data[5]; + var fields = descriptor && descriptor["^"]; + var classes = []; + var functions = []; + processStatics(descriptor); + libraries.push([name, uri, classes, functions, metadata, fields, isRoot, + globalObject]); + } +}) +([ +["_foreign_helper", "dart:_foreign_helper", , H, { + "^": "", + JS_CONST: { + "^": "Object;code" + } +}], +["_interceptors", "dart:_interceptors", , J, { + "^": "", + getInterceptor: function(object) { + return void 0; + }, + makeDispatchRecord: function(interceptor, proto, extension, indexability) { + return {i: interceptor, p: proto, e: extension, x: indexability}; + }, + getNativeInterceptor: function(object) { + var record, proto, objectProto, interceptor; + record = object[init.dispatchPropertyName]; + if (record == null) + if ($.initNativeDispatchFlag == null) { + H.initNativeDispatch(); + record = object[init.dispatchPropertyName]; + } + if (record != null) { + proto = record.p; + if (false === proto) + return record.i; + if (true === proto) + return object; + objectProto = Object.getPrototypeOf(object); + if (proto === objectProto) + return record.i; + if (record.e === objectProto) + throw H.wrapException(P.UnimplementedError$("Return interceptor for " + H.S(proto(object, record)))); + } + interceptor = H.lookupAndCacheInterceptor(object); + if (interceptor == null) { + proto = Object.getPrototypeOf(object); + if (proto == null || proto === Object.prototype) + return C.PlainJavaScriptObject_methods; + else + return C.UnknownJavaScriptObject_methods; + } + return interceptor; + }, + Interceptor: { + "^": "Object;", + $eq: function(receiver, other) { + return receiver === other; + }, + get$hashCode: function(receiver) { + return H.Primitives_objectHashCode(receiver); + }, + toString$0: function(receiver) { + return H.Primitives_objectToString(receiver); + }, + "%": "DOMError|DOMImplementation|FileError|MediaError|MediaKeyError|Navigator|NavigatorUserMediaError|PositionError|SQLError|SVGAnimatedNumberList" + }, + JSBool: { + "^": "bool/Interceptor;", + toString$0: function(receiver) { + return String(receiver); + }, + get$hashCode: function(receiver) { + return receiver ? 519018 : 218159; + }, + $isbool: true + }, + JSNull: { + "^": "Null/Interceptor;", + $eq: function(receiver, other) { + return null == other; + }, + toString$0: function(receiver) { + return "null"; + }, + get$hashCode: function(receiver) { + return 0; + } + }, + JavaScriptObject: { + "^": "Interceptor;", + get$hashCode: function(_) { + return 0; + } + }, + PlainJavaScriptObject: { + "^": "JavaScriptObject;" + }, + UnknownJavaScriptObject: { + "^": "JavaScriptObject;" + }, + JSArray: { + "^": "List/Interceptor;", + remove$1: function(receiver, element) { + var i; + if (!!receiver.fixed$length) + H.throwExpression(P.UnsupportedError$("remove")); + for (i = 0; i < receiver.length; ++i) + if (J.$eq(receiver[i], element)) { + receiver.splice(i, 1); + return true; + } + return false; + }, + forEach$1: function(receiver, f) { + return H.IterableMixinWorkaround_forEach(receiver, f); + }, + elementAt$1: function(receiver, index) { + if (index < 0 || index >= receiver.length) + return H.ioore(receiver, index); + return receiver[index]; + }, + contains$1: function(receiver, other) { + var i; + for (i = 0; i < receiver.length; ++i) + if (J.$eq(receiver[i], other)) + return true; + return false; + }, + get$isEmpty: function(receiver) { + return receiver.length === 0; + }, + toString$0: function(receiver) { + return H.IterableMixinWorkaround_toStringIterable(receiver, "[", "]"); + }, + toList$1$growable: function(receiver, growable) { + var t1; + if (growable) + return H.setRuntimeTypeInfo(receiver.slice(), [H.getTypeArgumentByIndex(receiver, 0)]); + else { + t1 = H.setRuntimeTypeInfo(receiver.slice(), [H.getTypeArgumentByIndex(receiver, 0)]); + t1.fixed$length = init; + return t1; + } + }, + toList$0: function($receiver) { + return this.toList$1$growable($receiver, true); + }, + get$iterator: function(receiver) { + return new H.ListIterator(receiver, receiver.length, 0, null); + }, + get$hashCode: function(receiver) { + return H.Primitives_objectHashCode(receiver); + }, + get$length: function(receiver) { + return receiver.length; + }, + set$length: function(receiver, newLength) { + if (newLength < 0) + throw H.wrapException(P.RangeError$value(newLength)); + if (!!receiver.fixed$length) + H.throwExpression(P.UnsupportedError$("set length")); + receiver.length = newLength; + }, + $index: function(receiver, index) { + if (typeof index !== "number" || Math.floor(index) !== index) + throw H.wrapException(new P.ArgumentError(index)); + if (index >= receiver.length || index < 0) + throw H.wrapException(P.RangeError$value(index)); + return receiver[index]; + }, + $indexSet: function(receiver, index, value) { + if (!!receiver.immutable$list) + H.throwExpression(P.UnsupportedError$("indexed set")); + if (typeof index !== "number" || Math.floor(index) !== index) + throw H.wrapException(new P.ArgumentError(index)); + if (index >= receiver.length || index < 0) + throw H.wrapException(P.RangeError$value(index)); + receiver[index] = value; + }, + $isList: true, + $isList: true, + $asList: null, + $isEfficientLength: true, + static: {JSArray_JSArray$fixed: function($length, $E) { + var t1; + if (typeof $length !== "number" || Math.floor($length) !== $length || $length < 0) + throw H.wrapException(P.ArgumentError$("Length must be a non-negative integer: " + H.S($length))); + t1 = H.setRuntimeTypeInfo(new Array($length), [$E]); + t1.fixed$length = init; + return t1; + }} + }, + JSNumber: { + "^": "num/Interceptor;", + get$isFinite: function(receiver) { + return isFinite(receiver); + }, + remainder$1: function(receiver, b) { + return receiver % b; + }, + toInt$0: function(receiver) { + var t1; + if (receiver >= -2147483648 && receiver <= 2147483647) + return receiver | 0; + if (isFinite(receiver)) { + t1 = receiver < 0 ? Math.ceil(receiver) : Math.floor(receiver); + return t1 + 0; + } + throw H.wrapException(P.UnsupportedError$('' + receiver)); + }, + round$0: function(receiver) { + return this.toInt$0(this.roundToDouble$0(receiver)); + }, + roundToDouble$0: function(receiver) { + if (receiver < 0) + return -Math.round(-receiver); + else + return Math.round(receiver); + }, + toStringAsFixed$1: function(receiver, fractionDigits) { + var result, t1; + if (fractionDigits > 20) + throw H.wrapException(P.RangeError$(fractionDigits)); + result = receiver.toFixed(fractionDigits); + if (receiver === 0) + t1 = 1 / receiver < 0; + else + t1 = false; + if (t1) + return "-" + result; + return result; + }, + toString$0: function(receiver) { + if (receiver === 0 && 1 / receiver < 0) + return "-0.0"; + else + return "" + receiver; + }, + get$hashCode: function(receiver) { + return receiver & 0x1FFFFFFF; + }, + $add: function(receiver, other) { + if (typeof other !== "number") + throw H.wrapException(new P.ArgumentError(other)); + return receiver + other; + }, + $sub: function(receiver, other) { + return receiver - other; + }, + $mul: function(receiver, other) { + if (typeof other !== "number") + throw H.wrapException(new P.ArgumentError(other)); + return receiver * other; + }, + _tdivFast$1: function(receiver, other) { + return (receiver | 0) === receiver ? receiver / other | 0 : this.toInt$0(receiver / other); + }, + _shrOtherPositive$1: function(receiver, other) { + var t1; + if (receiver > 0) + t1 = other > 31 ? 0 : receiver >>> other; + else { + t1 = other > 31 ? 31 : other; + t1 = receiver >> t1 >>> 0; + } + return t1; + }, + $lt: function(receiver, other) { + if (typeof other !== "number") + throw H.wrapException(P.ArgumentError$(other)); + return receiver < other; + }, + $le: function(receiver, other) { + if (typeof other !== "number") + throw H.wrapException(new P.ArgumentError(other)); + return receiver <= other; + }, + $ge: function(receiver, other) { + if (typeof other !== "number") + throw H.wrapException(P.ArgumentError$(other)); + return receiver >= other; + }, + $isnum: true, + static: {"^": "JSNumber__MIN_INT32,JSNumber__MAX_INT32"} + }, + JSInt: { + "^": "int/JSNumber;", + $isnum: true, + $isint: true + }, + JSDouble: { + "^": "double/JSNumber;", + $isnum: true + }, + JSString: { + "^": "String/Interceptor;", + codeUnitAt$1: function(receiver, index) { + if (index < 0) + throw H.wrapException(P.RangeError$value(index)); + if (index >= receiver.length) + throw H.wrapException(P.RangeError$value(index)); + return receiver.charCodeAt(index); + }, + $add: function(receiver, other) { + if (typeof other !== "string") + throw H.wrapException(new P.ArgumentError(other)); + return receiver + other; + }, + startsWith$2: function(receiver, pattern, index) { + var endIndex; + if (index > receiver.length) + throw H.wrapException(P.RangeError$range(index, 0, receiver.length)); + endIndex = index + pattern.length; + if (endIndex > receiver.length) + return false; + return pattern === receiver.substring(index, endIndex); + }, + startsWith$1: function($receiver, pattern) { + return this.startsWith$2($receiver, pattern, 0); + }, + substring$2: function(receiver, startIndex, endIndex) { + if (endIndex == null) + endIndex = receiver.length; + if (typeof endIndex !== "number" || Math.floor(endIndex) !== endIndex) + H.throwExpression(P.ArgumentError$(endIndex)); + if (startIndex < 0) + throw H.wrapException(P.RangeError$value(startIndex)); + if (typeof endIndex !== "number") + return H.iae(endIndex); + if (startIndex > endIndex) + throw H.wrapException(P.RangeError$value(startIndex)); + if (endIndex > receiver.length) + throw H.wrapException(P.RangeError$value(endIndex)); + return receiver.substring(startIndex, endIndex); + }, + substring$1: function($receiver, startIndex) { + return this.substring$2($receiver, startIndex, null); + }, + toLowerCase$0: function(receiver) { + return receiver.toLowerCase(); + }, + trim$0: function(receiver) { + var result, endIndex, startIndex, t1, endIndex0; + result = receiver.trim(); + endIndex = result.length; + if (endIndex === 0) + return result; + if (this.codeUnitAt$1(result, 0) === 133) { + startIndex = J.JSString__skipLeadingWhitespace(result, 1); + if (startIndex === endIndex) + return ""; + } else + startIndex = 0; + t1 = endIndex - 1; + endIndex0 = this.codeUnitAt$1(result, t1) === 133 ? J.JSString__skipTrailingWhitespace(result, t1) : endIndex; + if (startIndex === 0 && endIndex0 === endIndex) + return result; + return result.substring(startIndex, endIndex0); + }, + $mul: function(receiver, times) { + var s, result; + if (0 >= times) + return ""; + if (times === 1 || receiver.length === 0) + return receiver; + if (times !== times >>> 0) + throw H.wrapException(C.C_OutOfMemoryError); + for (s = receiver, result = ""; true;) { + if ((times & 1) === 1) + result = s + result; + times = times >>> 1; + if (times === 0) + break; + s += s; + } + return result; + }, + get$isEmpty: function(receiver) { + return receiver.length === 0; + }, + toString$0: function(receiver) { + return receiver; + }, + get$hashCode: function(receiver) { + var t1, hash, i; + for (t1 = receiver.length, hash = 0, i = 0; i < t1; ++i) { + hash = 536870911 & hash + receiver.charCodeAt(i); + hash = 536870911 & hash + ((524287 & hash) << 10 >>> 0); + hash ^= hash >> 6; + } + hash = 536870911 & hash + ((67108863 & hash) << 3 >>> 0); + hash ^= hash >> 11; + return 536870911 & hash + ((16383 & hash) << 15 >>> 0); + }, + get$length: function(receiver) { + return receiver.length; + }, + $index: function(receiver, index) { + if (typeof index !== "number" || Math.floor(index) !== index) + throw H.wrapException(new P.ArgumentError(index)); + if (index >= receiver.length || index < 0) + throw H.wrapException(P.RangeError$value(index)); + return receiver[index]; + }, + $isString: true, + static: {JSString__isWhitespace: function(codeUnit) { + if (codeUnit < 256) + switch (codeUnit) { + case 9: + case 10: + case 11: + case 12: + case 13: + case 32: + case 133: + case 160: + return true; + default: + return false; + } + switch (codeUnit) { + case 5760: + case 6158: + case 8192: + case 8193: + case 8194: + case 8195: + case 8196: + case 8197: + case 8198: + case 8199: + case 8200: + case 8201: + case 8202: + case 8232: + case 8233: + case 8239: + case 8287: + case 12288: + case 65279: + return true; + default: + return false; + } + }, JSString__skipLeadingWhitespace: function(string, index) { + var t1, codeUnit; + for (t1 = string.length; index < t1;) { + if (index >= t1) + H.throwExpression(P.RangeError$value(index)); + codeUnit = string.charCodeAt(index); + if (codeUnit !== 32 && codeUnit !== 13 && !J.JSString__isWhitespace(codeUnit)) + break; + ++index; + } + return index; + }, JSString__skipTrailingWhitespace: function(string, index) { + var t1, index0, codeUnit; + for (t1 = string.length; index > 0; index = index0) { + index0 = index - 1; + if (index0 >= t1) + H.throwExpression(P.RangeError$value(index0)); + codeUnit = string.charCodeAt(index0); + if (codeUnit !== 32 && codeUnit !== 13 && !J.JSString__isWhitespace(codeUnit)) + break; + } + return index; + }} + } +}], +["_isolate_helper", "dart:_isolate_helper", , H, { + "^": "", + _callInIsolate: function(isolate, $function) { + var result = isolate.eval$1($function); + init.globalState.topEventLoop.run$0(); + return result; + }, + leaveJsAsync: function() { + var t1 = init.globalState.topEventLoop; + t1._activeJsAsyncCount = t1._activeJsAsyncCount - 1; + }, + startRootIsolate: function(entry, args) { + var t1, t2, t3, t4, t5, rootContext; + t1 = {}; + t1.args_0 = args; + args = args; + t1.args_0 = args; + if (args == null) { + args = []; + t1.args_0 = args; + t2 = args; + } else + t2 = args; + if (!J.getInterceptor(t2).$isList) + throw H.wrapException(new P.ArgumentError("Arguments to main must be a List: " + H.S(t2))); + t2 = new H._Manager(0, 0, 1, null, null, null, null, null, null, null, null, null, entry); + t2._Manager$1(entry); + init.globalState = t2; + if (init.globalState.isWorker === true) + return; + t2 = init.globalState; + t3 = t2.nextIsolateId; + t2.nextIsolateId = t3 + 1; + t2 = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSInt, H.RawReceivePortImpl); + t4 = P.LinkedHashSet_LinkedHashSet(null, null, null, J.JSInt); + t5 = new H.RawReceivePortImpl(0, null, false); + rootContext = new H._IsolateContext(t3, t2, t4, new Isolate(), t5, P.Capability_Capability(), P.Capability_Capability(), false, [], P.LinkedHashSet_LinkedHashSet(null, null, null, null), null, false); + t4.add$1(0, 0); + rootContext._addRegistration$2(0, t5); + init.globalState.rootContext = rootContext; + init.globalState.currentContext = rootContext; + t2 = H.getDynamicRuntimeType(); + t3 = H.buildFunctionType(t2, [t2])._isTest$1(entry); + if (t3) + rootContext.eval$1(new H.startRootIsolate_closure(t1, entry)); + else { + t2 = H.buildFunctionType(t2, [t2, t2])._isTest$1(entry); + if (t2) + rootContext.eval$1(new H.startRootIsolate_closure0(t1, entry)); + else + rootContext.eval$1(entry); + } + init.globalState.topEventLoop.run$0(); + }, + IsolateNatives_computeThisScript: function() { + var currentScript = init.currentScript; + if (currentScript != null) + return String(currentScript.src); + if (typeof version == "function" && typeof os == "object" && "system" in os) + return H.IsolateNatives_computeThisScriptFromTrace(); + if (typeof version == "function" && typeof system == "function") + return thisFilename(); + if (init.globalState.isWorker === true) + return H.IsolateNatives_computeThisScriptFromTrace(); + return; + }, + IsolateNatives_computeThisScriptFromTrace: function() { + var stack, matches; + stack = new Error().stack; + if (stack == null) { + stack = (function() {try { throw new Error() } catch(e) { return e.stack }})(); + if (stack == null) + throw H.wrapException(P.UnsupportedError$("No stack trace")); + } + matches = stack.match(new RegExp("^ *at [^(]*\\((.*):[0-9]*:[0-9]*\\)$", "m")); + if (matches != null) + return matches[1]; + matches = stack.match(new RegExp("^[^@]*@(.*):[0-9]*$", "m")); + if (matches != null) + return matches[1]; + throw H.wrapException(P.UnsupportedError$("Cannot extract URI from \"" + H.S(stack) + "\"")); + }, + IsolateNatives__processWorkerMessage: function(sender, e) { + var msg, t1, functionName, entryPoint, args, message, isSpawnUri, startPaused, replyTo, t2, t3, t4, context, uri, t5, t6, worker, t7, workerId; + msg = H._deserializeMessage(e.data); + t1 = J.getInterceptor$asx(msg); + switch (t1.$index(msg, "command")) { + case "start": + init.globalState.currentManagerId = t1.$index(msg, "id"); + functionName = t1.$index(msg, "functionName"); + entryPoint = functionName == null ? init.globalState.entry : init.globalFunctions[functionName](); + args = t1.$index(msg, "args"); + message = H._deserializeMessage(t1.$index(msg, "msg")); + isSpawnUri = t1.$index(msg, "isSpawnUri"); + startPaused = t1.$index(msg, "startPaused"); + replyTo = H._deserializeMessage(t1.$index(msg, "replyTo")); + t1 = init.globalState; + t2 = t1.nextIsolateId; + t1.nextIsolateId = t2 + 1; + t1 = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSInt, H.RawReceivePortImpl); + t3 = P.LinkedHashSet_LinkedHashSet(null, null, null, J.JSInt); + t4 = new H.RawReceivePortImpl(0, null, false); + context = new H._IsolateContext(t2, t1, t3, new Isolate(), t4, P.Capability_Capability(), P.Capability_Capability(), false, [], P.LinkedHashSet_LinkedHashSet(null, null, null, null), null, false); + t3.add$1(0, 0); + context._addRegistration$2(0, t4); + init.globalState.topEventLoop.events._add$1(new H._IsolateEvent(context, new H.IsolateNatives__processWorkerMessage_closure(entryPoint, args, message, isSpawnUri, startPaused, replyTo), "worker-start")); + init.globalState.currentContext = context; + init.globalState.topEventLoop.run$0(); + break; + case "spawn-worker": + t2 = t1.$index(msg, "functionName"); + uri = t1.$index(msg, "uri"); + t3 = t1.$index(msg, "args"); + t4 = t1.$index(msg, "msg"); + t5 = t1.$index(msg, "isSpawnUri"); + t6 = t1.$index(msg, "startPaused"); + t1 = t1.$index(msg, "replyPort"); + if (uri == null) + uri = $.get$IsolateNatives_thisScript(); + worker = new Worker(uri); + worker.onmessage = function(e) { H.IsolateNatives__processWorkerMessage(worker, e); }; + t7 = init.globalState; + workerId = t7.nextManagerId; + t7.nextManagerId = workerId + 1; + $.get$IsolateNatives_workerIds().$indexSet(0, worker, workerId); + init.globalState.managers.$indexSet(0, workerId, worker); + worker.postMessage(H._serializeMessage(H.fillLiteralMap(["command", "start", "id", workerId, "replyTo", H._serializeMessage(t1), "args", t3, "msg", H._serializeMessage(t4), "isSpawnUri", t5, "startPaused", t6, "functionName", t2], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null)))); + break; + case "message": + if (t1.$index(msg, "port") != null) + J.send$1$x(t1.$index(msg, "port"), t1.$index(msg, "msg")); + init.globalState.topEventLoop.run$0(); + break; + case "close": + init.globalState.managers.remove$1(0, $.get$IsolateNatives_workerIds().$index(0, sender)); + sender.terminate(); + init.globalState.topEventLoop.run$0(); + break; + case "log": + H.IsolateNatives__log(t1.$index(msg, "msg")); + break; + case "print": + if (init.globalState.isWorker === true) { + t1 = init.globalState.mainManager; + t2 = H._serializeMessage(H.fillLiteralMap(["command", "print", "msg", msg], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null))); + t1.toString; + self.postMessage(t2); + } else + P.print(t1.$index(msg, "msg")); + break; + case "error": + throw H.wrapException(t1.$index(msg, "msg")); + } + }, + IsolateNatives__log: function(msg) { + var trace, t1, t2, exception; + if (init.globalState.isWorker === true) { + t1 = init.globalState.mainManager; + t2 = H._serializeMessage(H.fillLiteralMap(["command", "log", "msg", msg], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null))); + t1.toString; + self.postMessage(t2); + } else + try { + $.get$globalThis().console.log(msg); + } catch (exception) { + H.unwrapException(exception); + trace = new H._StackTrace(exception, null); + throw H.wrapException(P.Exception_Exception(trace)); + } + + }, + IsolateNatives__startIsolate: function(topLevel, args, message, isSpawnUri, startPaused, replyTo) { + var context, t1, t2, t3; + context = init.globalState.currentContext; + t1 = context.id; + $.Primitives_mirrorFunctionCacheName = $.Primitives_mirrorFunctionCacheName + ("_" + t1); + $.Primitives_mirrorInvokeCacheName = $.Primitives_mirrorInvokeCacheName + ("_" + t1); + t1 = context.controlPort; + t2 = init.globalState.currentContext.id; + t3 = context.pauseCapability; + J.send$1$x(replyTo, ["spawned", new H._NativeJsSendPort(t1, t2), t3, context.terminateCapability]); + t2 = new H.IsolateNatives__startIsolate_runStartFunction(topLevel, args, message, isSpawnUri); + if (startPaused === true) { + context.addPause$2(t3, t3); + init.globalState.topEventLoop.events._add$1(new H._IsolateEvent(context, t2, "start isolate")); + } else + t2.call$0(); + }, + _serializeMessage: function(message) { + var t1; + if (init.globalState.supportsWorkers === true) { + t1 = new H._JsSerializer(0, new H._MessageTraverserVisitedMap()); + t1._visited = new H._JsVisitedMap(null); + return t1.traverse$1(message); + } else { + t1 = new H._JsCopier(new H._MessageTraverserVisitedMap()); + t1._visited = new H._JsVisitedMap(null); + return t1.traverse$1(message); + } + }, + _deserializeMessage: function(message) { + if (init.globalState.supportsWorkers === true) + return new H._JsDeserializer(null).deserialize$1(message); + else + return message; + }, + _MessageTraverser_isPrimitive: function(x) { + return x == null || typeof x === "string" || typeof x === "number" || typeof x === "boolean"; + }, + _Deserializer_isPrimitive: function(x) { + return x == null || typeof x === "string" || typeof x === "number" || typeof x === "boolean"; + }, + startRootIsolate_closure: { + "^": "Closure:9;box_0,entry_1", + call$0: function() { + this.entry_1.call$1(this.box_0.args_0); + } + }, + startRootIsolate_closure0: { + "^": "Closure:9;box_0,entry_2", + call$0: function() { + this.entry_2.call$2(this.box_0.args_0, null); + } + }, + _Manager: { + "^": "Object;nextIsolateId,currentManagerId,nextManagerId,currentContext,rootContext,topEventLoop,fromCommandLine,isWorker,supportsWorkers,isolates,mainManager,managers,entry", + _Manager$1: function(entry) { + var t1, t2, t3, $function; + t1 = $.get$globalWindow() == null; + t2 = $.get$globalWorker(); + t3 = t1 && $.get$globalPostMessageDefined() === true; + this.isWorker = t3; + if (!t3) + t2 = t2 != null && $.get$IsolateNatives_thisScript() != null; + else + t2 = true; + this.supportsWorkers = t2; + this.fromCommandLine = t1 && !t3; + t2 = H._IsolateEvent; + t3 = H.setRuntimeTypeInfo(new P.ListQueue(null, 0, 0, 0), [t2]); + t3.ListQueue$1(null, t2); + this.topEventLoop = new H._EventLoop(t3, 0); + this.isolates = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSInt, H._IsolateContext); + this.managers = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSInt, null); + if (this.isWorker === true) { + t1 = new H._MainManagerStub(); + this.mainManager = t1; + $function = function (e) { H.IsolateNatives__processWorkerMessage(t1, e); }; + $.get$globalThis().onmessage = $function; + $.get$globalThis().dartPrint = function (object) {}; + } + } + }, + _IsolateContext: { + "^": "Object;id,ports,weakPorts,isolateStatics<,controlPort<,pauseCapability,terminateCapability,isPaused,delayedEvents,pauseTokens,doneHandlers,errorsAreFatal", + addPause$2: function(authentification, resume) { + if (!this.pauseCapability.$eq(0, authentification)) + return; + if (this.pauseTokens.add$1(0, resume) && !this.isPaused) + this.isPaused = true; + this._updateGlobalState$0(); + }, + removePause$1: function(resume) { + var t1, t2, $event, t3, t4, t5; + if (!this.isPaused) + return; + t1 = this.pauseTokens; + t1.remove$1(0, resume); + if (t1._collection$_length === 0) { + for (t1 = this.delayedEvents; t2 = t1.length, t2 !== 0;) { + if (0 >= t2) + return H.ioore(t1, 0); + $event = t1.pop(); + t2 = init.globalState.topEventLoop.events; + t3 = t2._head; + t4 = t2._table; + t5 = t4.length; + t3 = (t3 - 1 & t5 - 1) >>> 0; + t2._head = t3; + if (t3 < 0 || t3 >= t5) + return H.ioore(t4, t3); + t4[t3] = $event; + if (t3 === t2._tail) + t2._grow$0(); + t2._modificationCount = t2._modificationCount + 1; + } + this.isPaused = false; + } + this._updateGlobalState$0(); + }, + addDoneListener$1: function(responsePort) { + var t1 = this.doneHandlers; + if (t1 == null) { + t1 = []; + this.doneHandlers = t1; + } + if (J.contains$1$asx(t1, responsePort)) + return; + this.doneHandlers.push(responsePort); + }, + removeDoneListener$1: function(responsePort) { + var t1 = this.doneHandlers; + if (t1 == null) + return; + J.remove$1$ax(t1, responsePort); + }, + setErrorsFatal$2: function(authentification, errorsAreFatal) { + if (!this.terminateCapability.$eq(0, authentification)) + return; + this.errorsAreFatal = errorsAreFatal; + }, + handlePing$2: function(responsePort, pingType) { + if (J.$eq(pingType, 2)) + init.globalState.topEventLoop.events._add$1(new H._IsolateEvent(this, new H._IsolateContext_handlePing_closure(responsePort), "ping")); + else + J.send$1$x(responsePort, null); + }, + eval$1: function(code) { + var old, result; + old = init.globalState.currentContext; + init.globalState.currentContext = this; + $ = this.isolateStatics; + result = null; + try { + result = code.call$0(); + } finally { + init.globalState.currentContext = old; + if (old != null) + $ = old.get$isolateStatics(); + } + return result; + }, + lookup$1: function(portId) { + return this.ports.$index(0, portId); + }, + _addRegistration$2: function(portId, port) { + var t1 = this.ports; + if (t1.containsKey$1(0, portId)) + throw H.wrapException(P.Exception_Exception("Registry: ports must be registered only once.")); + t1.$indexSet(0, portId, port); + }, + _updateGlobalState$0: function() { + if (this.ports._collection$_length - this.weakPorts._collection$_length > 0 || this.isPaused) + init.globalState.isolates.$indexSet(0, this.id, this); + else + this._shutdown$0(); + }, + _shutdown$0: function() { + init.globalState.isolates.remove$1(0, this.id); + var t1 = this.doneHandlers; + if (t1 != null) + for (t1 = new H.ListIterator(t1, t1.length, 0, null); t1.moveNext$0();) + J.send$1$x(t1._current, null); + } + }, + _IsolateContext_handlePing_closure: { + "^": "Closure:9;responsePort_0", + call$0: function() { + J.send$1$x(this.responsePort_0, null); + } + }, + _EventLoop: { + "^": "Object;events,_activeJsAsyncCount", + dequeue$0: function() { + var t1, t2, t3, t4, result; + t1 = this.events; + t2 = t1._head; + if (t2 === t1._tail) + return; + t1._modificationCount = t1._modificationCount + 1; + t3 = t1._table; + t4 = t3.length; + if (t2 >= t4) + return H.ioore(t3, t2); + result = t3[t2]; + t3[t2] = null; + t1._head = (t2 + 1 & t4 - 1) >>> 0; + return result; + }, + runIteration$0: function() { + var $event, t1, t2; + $event = this.dequeue$0(); + if ($event == null) { + if (init.globalState.rootContext != null && init.globalState.isolates.containsKey$1(0, init.globalState.rootContext.id) && init.globalState.fromCommandLine === true && init.globalState.rootContext.ports._collection$_length === 0) + H.throwExpression(P.Exception_Exception("Program exited with open ReceivePorts.")); + t1 = init.globalState; + if (t1.isWorker === true && t1.isolates._collection$_length === 0 && t1.topEventLoop._activeJsAsyncCount === 0) { + t1 = t1.mainManager; + t2 = H._serializeMessage(H.fillLiteralMap(["command", "close"], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null))); + t1.toString; + self.postMessage(t2); + } + return false; + } + $event.process$0(); + return true; + }, + _runHelper$0: function() { + if ($.get$globalWindow() != null) + new H._EventLoop__runHelper_next(this).call$0(); + else + for (; this.runIteration$0();) + ; + }, + run$0: function() { + var e, trace, exception, t1, t2; + if (init.globalState.isWorker !== true) + this._runHelper$0(); + else + try { + this._runHelper$0(); + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + trace = new H._StackTrace(exception, null); + t1 = init.globalState.mainManager; + t2 = H._serializeMessage(H.fillLiteralMap(["command", "error", "msg", H.S(e) + "\n" + H.S(trace)], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null))); + t1.toString; + self.postMessage(t2); + } + + } + }, + _EventLoop__runHelper_next: { + "^": "Closure:1;this_0", + call$0: function() { + if (!this.this_0.runIteration$0()) + return; + P.Timer_Timer(C.Duration_0, this); + } + }, + _IsolateEvent: { + "^": "Object;isolate,fn,message", + process$0: function() { + var t1 = this.isolate; + if (t1.isPaused) { + t1.delayedEvents.push(this); + return; + } + t1.eval$1(this.fn); + } + }, + _MainManagerStub: { + "^": "Object;" + }, + IsolateNatives__processWorkerMessage_closure: { + "^": "Closure:9;entryPoint_0,args_1,message_2,isSpawnUri_3,startPaused_4,replyTo_5", + call$0: function() { + H.IsolateNatives__startIsolate(this.entryPoint_0, this.args_1, this.message_2, this.isSpawnUri_3, this.startPaused_4, this.replyTo_5); + } + }, + IsolateNatives__startIsolate_runStartFunction: { + "^": "Closure:1;topLevel_0,args_1,message_2,isSpawnUri_3", + call$0: function() { + var t1, t2, t3; + if (this.isSpawnUri_3 !== true) + this.topLevel_0.call$1(this.message_2); + else { + t1 = this.topLevel_0; + t2 = H.getDynamicRuntimeType(); + t3 = H.buildFunctionType(t2, [t2, t2])._isTest$1(t1); + if (t3) + t1.call$2(this.args_1, this.message_2); + else { + t2 = H.buildFunctionType(t2, [t2])._isTest$1(t1); + if (t2) + t1.call$1(this.args_1); + else + t1.call$0(); + } + } + } + }, + _BaseSendPort: { + "^": "Object;", + $isSendPort: true, + $isCapability: true + }, + _NativeJsSendPort: { + "^": "_BaseSendPort;_receivePort,_isolateId", + send$1: function(_, message) { + var t1, t2, isolate, t3, shouldSerialize, msg; + t1 = {}; + t2 = this._isolateId; + isolate = init.globalState.isolates.$index(0, t2); + if (isolate == null) + return; + t3 = this._receivePort; + if (t3.get$_isClosed()) + return; + shouldSerialize = init.globalState.currentContext != null && init.globalState.currentContext.id !== t2; + t1.msg_0 = message; + if (shouldSerialize) { + msg = H._serializeMessage(message); + t1.msg_0 = msg; + t2 = msg; + } else + t2 = message; + if (isolate.get$controlPort() === t3) { + t1 = J.getInterceptor$asx(t2); + switch (t1.$index(t2, 0)) { + case "pause": + isolate.addPause$2(t1.$index(t2, 1), t1.$index(t2, 2)); + break; + case "resume": + isolate.removePause$1(t1.$index(t2, 1)); + break; + case "add-ondone": + isolate.addDoneListener$1(t1.$index(t2, 1)); + break; + case "remove-ondone": + isolate.removeDoneListener$1(t1.$index(t2, 1)); + break; + case "set-errors-fatal": + isolate.setErrorsFatal$2(t1.$index(t2, 1), t1.$index(t2, 2)); + break; + case "ping": + isolate.handlePing$2(t1.$index(t2, 1), t1.$index(t2, 2)); + break; + default: + P.print("UNKNOWN MESSAGE: " + H.S(t2)); + } + return; + } + t2 = init.globalState.topEventLoop; + t3 = "receive " + H.S(message); + t2.events._add$1(new H._IsolateEvent(isolate, new H._NativeJsSendPort_send_closure(t1, this, shouldSerialize), t3)); + }, + $eq: function(_, other) { + if (other == null) + return false; + return !!J.getInterceptor(other).$is_NativeJsSendPort && J.$eq(this._receivePort, other._receivePort); + }, + get$hashCode: function(_) { + return this._receivePort.get$_id(); + }, + $is_NativeJsSendPort: true, + $isSendPort: true, + $isCapability: true + }, + _NativeJsSendPort_send_closure: { + "^": "Closure:9;box_0,this_1,shouldSerialize_2", + call$0: function() { + var t1, t2; + t1 = this.this_1._receivePort; + if (!t1.get$_isClosed()) { + if (this.shouldSerialize_2) { + t2 = this.box_0; + t2.msg_0 = H._deserializeMessage(t2.msg_0); + } + t1.__isolate_helper$_add$1(this.box_0.msg_0); + } + } + }, + _WorkerSendPort: { + "^": "_BaseSendPort;_workerId,_receivePortId,_isolateId", + send$1: function(_, message) { + var workerMessage, manager; + workerMessage = H._serializeMessage(H.fillLiteralMap(["command", "message", "port", this, "msg", message], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null))); + if (init.globalState.isWorker === true) { + init.globalState.mainManager.toString; + self.postMessage(workerMessage); + } else { + manager = init.globalState.managers.$index(0, this._workerId); + if (manager != null) + manager.postMessage(workerMessage); + } + }, + $eq: function(_, other) { + if (other == null) + return false; + return !!J.getInterceptor(other).$is_WorkerSendPort && J.$eq(this._workerId, other._workerId) && J.$eq(this._isolateId, other._isolateId) && J.$eq(this._receivePortId, other._receivePortId); + }, + get$hashCode: function(_) { + var t1, t2, t3; + t1 = this._workerId; + if (typeof t1 !== "number") + return t1.$shl(); + t2 = this._isolateId; + if (typeof t2 !== "number") + return t2.$shl(); + t3 = this._receivePortId; + if (typeof t3 !== "number") + return H.iae(t3); + return (t1 << 16 ^ t2 << 8 ^ t3) >>> 0; + }, + $is_WorkerSendPort: true, + $isSendPort: true, + $isCapability: true + }, + RawReceivePortImpl: { + "^": "Object;_id<,_handler,_isClosed<", + _handler$1: function(arg0) { + return this._handler.call$1(arg0); + }, + __isolate_helper$_add$1: function(dataEvent) { + if (this._isClosed) + return; + this._handler$1(dataEvent); + }, + static: {"^": "RawReceivePortImpl__nextFreeId"} + }, + _JsSerializer: { + "^": "_Serializer;_nextFreeRefId,_visited", + visitSendPort$1: function(x) { + if (!!x.$is_NativeJsSendPort) + return ["sendport", init.globalState.currentManagerId, x._isolateId, x._receivePort.get$_id()]; + if (!!x.$is_WorkerSendPort) + return ["sendport", x._workerId, x._isolateId, x._receivePortId]; + throw H.wrapException("Illegal underlying port " + H.S(x)); + }, + visitCapability$1: function(x) { + if (!!x.$isCapabilityImpl) + return ["capability", x._id]; + throw H.wrapException("Capability not serializable: " + H.S(x)); + } + }, + _JsCopier: { + "^": "_Copier;_visited", + visitSendPort$1: function(x) { + if (!!x.$is_NativeJsSendPort) + return new H._NativeJsSendPort(x._receivePort, x._isolateId); + if (!!x.$is_WorkerSendPort) + return new H._WorkerSendPort(x._workerId, x._receivePortId, x._isolateId); + throw H.wrapException("Illegal underlying port " + H.S(x)); + }, + visitCapability$1: function(x) { + if (!!x.$isCapabilityImpl) + return new H.CapabilityImpl(x._id); + throw H.wrapException("Capability not serializable: " + H.S(x)); + } + }, + _JsDeserializer: { + "^": "_Deserializer;_deserialized", + deserializeSendPort$1: function(list) { + var t1, managerId, isolateId, receivePortId, isolate, receivePort; + t1 = J.getInterceptor$asx(list); + managerId = t1.$index(list, 1); + isolateId = t1.$index(list, 2); + receivePortId = t1.$index(list, 3); + if (J.$eq(managerId, init.globalState.currentManagerId)) { + isolate = init.globalState.isolates.$index(0, isolateId); + if (isolate == null) + return; + receivePort = isolate.lookup$1(receivePortId); + if (receivePort == null) + return; + return new H._NativeJsSendPort(receivePort, isolateId); + } else + return new H._WorkerSendPort(managerId, receivePortId, isolateId); + }, + deserializeCapability$1: function(list) { + return new H.CapabilityImpl(J.$index$asx(list, 1)); + } + }, + _JsVisitedMap: { + "^": "Object;tagged", + $index: function(_, object) { + return object.__MessageTraverser__attached_info__; + }, + $indexSet: function(_, object, info) { + this.tagged.push(object); + object.__MessageTraverser__attached_info__ = info; + }, + reset$0: function(_) { + this.tagged = []; + }, + cleanup$0: function() { + var $length, i, t1; + for ($length = this.tagged.length, i = 0; i < $length; ++i) { + t1 = this.tagged; + if (i >= t1.length) + return H.ioore(t1, i); + t1[i].__MessageTraverser__attached_info__ = null; + } + this.tagged = null; + } + }, + _MessageTraverserVisitedMap: { + "^": "Object;", + $index: function(_, object) { + return; + }, + $indexSet: function(_, object, info) { + }, + reset$0: function(_) { + }, + cleanup$0: function() { + } + }, + _MessageTraverser: { + "^": "Object;", + traverse$1: function(x) { + var result; + if (H._MessageTraverser_isPrimitive(x)) + return this.visitPrimitive$1(x); + this._visited.reset$0(0); + result = null; + try { + result = this._dispatch$1(x); + } finally { + this._visited.cleanup$0(); + } + return result; + }, + _dispatch$1: function(x) { + var t1; + if (x == null || typeof x === "string" || typeof x === "number" || typeof x === "boolean") + return this.visitPrimitive$1(x); + t1 = J.getInterceptor(x); + if (!!t1.$isList) + return this.visitList$1(x); + if (!!t1.$isMap) + return this.visitMap$1(x); + if (!!t1.$isSendPort) + return this.visitSendPort$1(x); + if (!!t1.$isCapability) + return this.visitCapability$1(x); + return this.visitObject$1(x); + }, + visitObject$1: function(x) { + throw H.wrapException("Message serialization: Illegal value " + H.S(x) + " passed"); + } + }, + _Copier: { + "^": "_MessageTraverser;", + visitPrimitive$1: function(x) { + return x; + }, + visitList$1: function(list) { + var copy, t1, len, i; + copy = this._visited.$index(0, list); + if (copy != null) + return copy; + t1 = J.getInterceptor$asx(list); + len = t1.get$length(list); + copy = Array(len); + copy.fixed$length = init; + this._visited.$indexSet(0, list, copy); + for (i = 0; i < len; ++i) + copy[i] = this._dispatch$1(t1.$index(list, i)); + return copy; + }, + visitMap$1: function(map) { + var t1, copy; + t1 = {}; + copy = this._visited.$index(0, map); + t1.copy_0 = copy; + if (copy != null) + return copy; + copy = P.LinkedHashMap_LinkedHashMap(null, null, null, null, null); + t1.copy_0 = copy; + this._visited.$indexSet(0, map, copy); + J.forEach$1$ax(map, new H._Copier_visitMap_closure(t1, this)); + return t1.copy_0; + }, + visitSendPort$1: function(x) { + return H.throwExpression(P.UnimplementedError$(null)); + }, + visitCapability$1: function(x) { + return H.throwExpression(P.UnimplementedError$(null)); + } + }, + _Copier_visitMap_closure: { + "^": "Closure:10;box_0,this_1", + call$2: function(key, val) { + var t1 = this.this_1; + J.$indexSet$ax(this.box_0.copy_0, t1._dispatch$1(key), t1._dispatch$1(val)); + } + }, + _Serializer: { + "^": "_MessageTraverser;", + visitPrimitive$1: function(x) { + return x; + }, + visitList$1: function(list) { + var copyId, id; + copyId = this._visited.$index(0, list); + if (copyId != null) + return ["ref", copyId]; + id = this._nextFreeRefId; + this._nextFreeRefId = id + 1; + this._visited.$indexSet(0, list, id); + return ["list", id, this._serializeList$1(list)]; + }, + visitMap$1: function(map) { + var copyId, id, t1; + copyId = this._visited.$index(0, map); + if (copyId != null) + return ["ref", copyId]; + id = this._nextFreeRefId; + this._nextFreeRefId = id + 1; + this._visited.$indexSet(0, map, id); + t1 = J.getInterceptor$x(map); + return ["map", id, this._serializeList$1(J.toList$0$ax(t1.get$keys(map))), this._serializeList$1(J.toList$0$ax(t1.get$values(map)))]; + }, + _serializeList$1: function(list) { + var t1, len, result, i, t2; + t1 = J.getInterceptor$asx(list); + len = t1.get$length(list); + result = []; + C.JSArray_methods.set$length(result, len); + for (i = 0; i < len; ++i) { + t2 = this._dispatch$1(t1.$index(list, i)); + if (i >= result.length) + return H.ioore(result, i); + result[i] = t2; + } + return result; + }, + visitSendPort$1: function(x) { + return H.throwExpression(P.UnimplementedError$(null)); + }, + visitCapability$1: function(x) { + return H.throwExpression(P.UnimplementedError$(null)); + } + }, + _Deserializer: { + "^": "Object;", + deserialize$1: function(x) { + if (H._Deserializer_isPrimitive(x)) + return x; + this._deserialized = P.HashMap_HashMap(null, null, null, null, null); + return this._deserializeHelper$1(x); + }, + _deserializeHelper$1: function(x) { + var t1, id; + if (x == null || typeof x === "string" || typeof x === "number" || typeof x === "boolean") + return x; + t1 = J.getInterceptor$asx(x); + switch (t1.$index(x, 0)) { + case "ref": + id = t1.$index(x, 1); + return this._deserialized.$index(0, id); + case "list": + return this._deserializeList$1(x); + case "map": + return this._deserializeMap$1(x); + case "sendport": + return this.deserializeSendPort$1(x); + case "capability": + return this.deserializeCapability$1(x); + default: + return this.deserializeObject$1(x); + } + }, + _deserializeList$1: function(x) { + var t1, id, dartList, len, i; + t1 = J.getInterceptor$asx(x); + id = t1.$index(x, 1); + dartList = t1.$index(x, 2); + this._deserialized.$indexSet(0, id, dartList); + t1 = J.getInterceptor$asx(dartList); + len = t1.get$length(dartList); + if (typeof len !== "number") + return H.iae(len); + i = 0; + for (; i < len; ++i) + t1.$indexSet(dartList, i, this._deserializeHelper$1(t1.$index(dartList, i))); + return dartList; + }, + _deserializeMap$1: function(x) { + var result, t1, id, keys, values, len, t2, i; + result = P.LinkedHashMap_LinkedHashMap(null, null, null, null, null); + t1 = J.getInterceptor$asx(x); + id = t1.$index(x, 1); + this._deserialized.$indexSet(0, id, result); + keys = t1.$index(x, 2); + values = t1.$index(x, 3); + t1 = J.getInterceptor$asx(keys); + len = t1.get$length(keys); + if (typeof len !== "number") + return H.iae(len); + t2 = J.getInterceptor$asx(values); + i = 0; + for (; i < len; ++i) + result.$indexSet(0, this._deserializeHelper$1(t1.$index(keys, i)), this._deserializeHelper$1(t2.$index(values, i))); + return result; + }, + deserializeObject$1: function(x) { + throw H.wrapException("Unexpected serialized object"); + } + }, + TimerImpl: { + "^": "Object;_once,_inEventLoop,_handle", + TimerImpl$2: function(milliseconds, callback) { + var t1, t2; + if (milliseconds === 0) + t1 = $.get$globalThis().setTimeout == null || init.globalState.isWorker === true; + else + t1 = false; + if (t1) { + this._handle = 1; + t1 = init.globalState.topEventLoop; + t2 = init.globalState.currentContext; + t1.events._add$1(new H._IsolateEvent(t2, new H.TimerImpl_internalCallback(this, callback), "timer")); + this._inEventLoop = true; + } else { + t1 = $.get$globalThis(); + if (t1.setTimeout != null) { + t2 = init.globalState.topEventLoop; + t2._activeJsAsyncCount = t2._activeJsAsyncCount + 1; + this._handle = t1.setTimeout(H.convertDartClosureToJS(new H.TimerImpl_internalCallback0(this, callback), 0), milliseconds); + } else + throw H.wrapException(P.UnsupportedError$("Timer greater than 0.")); + } + }, + static: {TimerImpl$: function(milliseconds, callback) { + var t1 = new H.TimerImpl(true, false, null); + t1.TimerImpl$2(milliseconds, callback); + return t1; + }} + }, + TimerImpl_internalCallback: { + "^": "Closure:1;this_0,callback_1", + call$0: function() { + this.this_0._handle = null; + this.callback_1.call$0(); + } + }, + TimerImpl_internalCallback0: { + "^": "Closure:1;this_2,callback_3", + call$0: function() { + this.this_2._handle = null; + H.leaveJsAsync(); + this.callback_3.call$0(); + } + }, + CapabilityImpl: { + "^": "Object;_id<", + get$hashCode: function(_) { + var hash = this._id; + if (typeof hash !== "number") + return hash.$shr(); + hash = C.JSNumber_methods._shrOtherPositive$1(hash, 0) ^ C.JSNumber_methods._tdivFast$1(hash, 4294967296); + hash = (~hash >>> 0) + (hash << 15 >>> 0) & 4294967295; + hash = ((hash ^ hash >>> 12) >>> 0) * 5 & 4294967295; + hash = ((hash ^ hash >>> 4) >>> 0) * 2057 & 4294967295; + return (hash ^ hash >>> 16) >>> 0; + }, + $eq: function(_, other) { + var t1, t2; + if (other == null) + return false; + if (other === this) + return true; + if (!!J.getInterceptor(other).$isCapabilityImpl) { + t1 = this._id; + t2 = other._id; + return t1 == null ? t2 == null : t1 === t2; + } + return false; + }, + $isCapabilityImpl: true, + $isCapability: true + } +}], +["_js_helper", "dart:_js_helper", , H, { + "^": "", + isJsIndexable: function(object, record) { + var result; + if (record != null) { + result = record.x; + if (result != null) + return result; + } + return !!J.getInterceptor(object).$isJavaScriptIndexingBehavior; + }, + S: function(value) { + var res; + if (typeof value === "string") + return value; + if (typeof value === "number") { + if (value !== 0) + return "" + value; + } else if (true === value) + return "true"; + else if (false === value) + return "false"; + else if (value == null) + return "null"; + res = J.toString$0(value); + if (typeof res !== "string") + throw H.wrapException(P.ArgumentError$(value)); + return res; + }, + Primitives_objectHashCode: function(object) { + var hash = object.$identityHash; + if (hash == null) { + hash = Math.random() * 0x3fffffff | 0; + object.$identityHash = hash; + } + return hash; + }, + Primitives__throwFormatException: [function(string) { + throw H.wrapException(P.FormatException$(string)); + }, "call$1", "Primitives__throwFormatException$closure", 2, 0, 0], + Primitives_parseInt: function(source, radix, handleError) { + var match, t1; + handleError = H.Primitives__throwFormatException$closure(); + if (typeof source !== "string") + H.throwExpression(new P.ArgumentError(source)); + match = /^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i.exec(source); + if (match != null) { + t1 = match.length; + if (2 >= t1) + return H.ioore(match, 2); + if (match[2] != null) + return parseInt(source, 16); + if (3 >= t1) + return H.ioore(match, 3); + if (match[3] != null) + return parseInt(source, 10); + return handleError.call$1(source); + } + if (match == null) + return handleError.call$1(source); + return parseInt(source, 10); + }, + Primitives_parseDouble: function(source, handleError) { + var result, trimmed; + if (typeof source !== "string") + H.throwExpression(new P.ArgumentError(source)); + handleError = H.Primitives__throwFormatException$closure(); + if (!/^\s*[+-]?(?:Infinity|NaN|(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(source)) + return handleError.call$1(source); + result = parseFloat(source); + if (isNaN(result)) { + trimmed = J.trim$0$s(source); + if (trimmed === "NaN" || trimmed === "+NaN" || trimmed === "-NaN") + return result; + return handleError.call$1(source); + } + return result; + }, + Primitives_objectTypeName: function(object) { + var $name, decompiled; + $name = C.JS_CONST_IX5(J.getInterceptor(object)); + if ($name === "Object") { + decompiled = String(object.constructor).match(/^\s*function\s*(\S*)\s*\(/)[1]; + if (typeof decompiled === "string") + $name = decompiled; + } + if (J.getInterceptor$s($name).codeUnitAt$1($name, 0) === 36) + $name = C.JSString_methods.substring$1($name, 1); + return $name + H.joinArguments(H.getRuntimeTypeInfo(object), 0, null); + }, + Primitives_objectToString: function(object) { + return "Instance of '" + H.Primitives_objectTypeName(object) + "'"; + }, + Primitives__fromCharCodeApply: function(array) { + var end, t1, result, i, subarray, t2; + end = array.length; + for (t1 = end <= 500, result = "", i = 0; i < end; i += 500) { + if (t1) + subarray = array; + else { + t2 = i + 500; + t2 = t2 < end ? t2 : end; + subarray = array.slice(i, t2); + } + result += String.fromCharCode.apply(null, subarray); + } + return result; + }, + Primitives_stringFromCodePoints: function(codePoints) { + var a, t1, i; + a = []; + a.$builtinTypeInfo = [J.JSInt]; + for (t1 = new H.ListIterator(codePoints, codePoints.length, 0, null); t1.moveNext$0();) { + i = t1._current; + if (typeof i !== "number" || Math.floor(i) !== i) + throw H.wrapException(P.ArgumentError$(i)); + if (i <= 65535) + a.push(i); + else if (i <= 1114111) { + a.push(55296 + (C.JSInt_methods._shrOtherPositive$1(i - 65536, 10) & 1023)); + a.push(56320 + (i & 1023)); + } else + throw H.wrapException(P.ArgumentError$(i)); + } + return H.Primitives__fromCharCodeApply(a); + }, + Primitives_stringFromCharCodes: function(charCodes) { + var t1, i; + for (t1 = new H.ListIterator(charCodes, charCodes.length, 0, null); t1.moveNext$0();) { + i = t1._current; + if (typeof i !== "number" || Math.floor(i) !== i) + throw H.wrapException(P.ArgumentError$(i)); + if (i < 0) + throw H.wrapException(P.ArgumentError$(i)); + if (i > 65535) + return H.Primitives_stringFromCodePoints(charCodes); + } + return H.Primitives__fromCharCodeApply(charCodes); + }, + Primitives_valueFromDecomposedDate: function(years, month, day, hours, minutes, seconds, milliseconds, isUtc) { + var jsMonth, value, t1, date; + if (typeof years !== "number" || Math.floor(years) !== years) + H.throwExpression(new P.ArgumentError(years)); + if (typeof month !== "number" || Math.floor(month) !== month) + H.throwExpression(new P.ArgumentError(month)); + if (typeof day !== "number" || Math.floor(day) !== day) + H.throwExpression(new P.ArgumentError(day)); + if (typeof hours !== "number" || Math.floor(hours) !== hours) + H.throwExpression(new P.ArgumentError(hours)); + if (typeof minutes !== "number" || Math.floor(minutes) !== minutes) + H.throwExpression(new P.ArgumentError(minutes)); + if (typeof seconds !== "number" || Math.floor(seconds) !== seconds) + H.throwExpression(new P.ArgumentError(seconds)); + jsMonth = J.$sub$n(month, 1); + value = isUtc ? Date.UTC(years, jsMonth, day, hours, minutes, seconds, milliseconds) : new Date(years, jsMonth, day, hours, minutes, seconds, milliseconds).valueOf(); + if (isNaN(value) || value < -8640000000000000 || value > 8640000000000000) + throw H.wrapException(new P.ArgumentError(null)); + t1 = J.getInterceptor$n(years); + if (t1.$le(years, 0) || t1.$lt(years, 100)) { + date = new Date(value); + if (isUtc) + date.setUTCFullYear(years); + else + date.setFullYear(years); + return date.valueOf(); + } + return value; + }, + Primitives_lazyAsJsDate: function(receiver) { + if (receiver.date === void 0) + receiver.date = new Date(receiver.millisecondsSinceEpoch); + return receiver.date; + }, + Primitives_getProperty: function(object, key) { + if (object == null || typeof object === "boolean" || typeof object === "number" || typeof object === "string") + throw H.wrapException(new P.ArgumentError(object)); + return object[key]; + }, + Primitives_setProperty: function(object, key, value) { + if (object == null || typeof object === "boolean" || typeof object === "number" || typeof object === "string") + throw H.wrapException(new P.ArgumentError(object)); + object[key] = value; + }, + iae: function(argument) { + throw H.wrapException(P.ArgumentError$(argument)); + }, + ioore: function(receiver, index) { + if (receiver == null) + J.get$length$asx(receiver); + if (typeof index !== "number" || Math.floor(index) !== index) + H.iae(index); + throw H.wrapException(P.RangeError$value(index)); + }, + wrapException: function(ex) { + var wrapper; + if (ex == null) + ex = new P.NullThrownError(); + wrapper = new Error(); + wrapper.dartException = ex; + if ("defineProperty" in Object) { + Object.defineProperty(wrapper, "message", { get: H.toStringWrapper }); + wrapper.name = ""; + } else + wrapper.toString = H.toStringWrapper; + return wrapper; + }, + toStringWrapper: function() { + return J.toString$0(this.dartException); + }, + throwExpression: function(ex) { + var wrapper; + if (ex == null) + ex = new P.NullThrownError(); + wrapper = new Error(); + wrapper.dartException = ex; + if ("defineProperty" in Object) { + Object.defineProperty(wrapper, "message", { get: H.toStringWrapper }); + wrapper.name = ""; + } else + wrapper.toString = H.toStringWrapper; + throw wrapper; + }, + unwrapException: function(ex) { + var t1, message, number, ieErrorCode, t2, t3, t4, nullLiteralCall, t5, t6, t7, t8, t9, match; + t1 = new H.unwrapException_saveStackTrace(ex); + if (ex == null) + return; + if (typeof ex !== "object") + return ex; + if ("dartException" in ex) + return t1.call$1(ex.dartException); + else if (!("message" in ex)) + return ex; + message = ex.message; + if ("number" in ex && typeof ex.number == "number") { + number = ex.number; + ieErrorCode = number & 65535; + if ((C.JSInt_methods._shrOtherPositive$1(number, 16) & 8191) === 10) + switch (ieErrorCode) { + case 438: + return t1.call$1(H.JsNoSuchMethodError$(H.S(message) + " (Error " + ieErrorCode + ")", null)); + case 445: + case 5007: + t2 = H.S(message) + " (Error " + ieErrorCode + ")"; + return t1.call$1(new H.NullError(t2, null)); + } + } + if (ex instanceof TypeError) { + t2 = $.get$TypeErrorDecoder_noSuchMethodPattern(); + t3 = $.get$TypeErrorDecoder_notClosurePattern(); + t4 = $.get$TypeErrorDecoder_nullCallPattern(); + nullLiteralCall = $.get$TypeErrorDecoder_nullLiteralCallPattern(); + t5 = $.get$TypeErrorDecoder_undefinedCallPattern(); + t6 = $.get$TypeErrorDecoder_undefinedLiteralCallPattern(); + t7 = $.get$TypeErrorDecoder_nullPropertyPattern(); + $.get$TypeErrorDecoder_nullLiteralPropertyPattern(); + t8 = $.get$TypeErrorDecoder_undefinedPropertyPattern(); + t9 = $.get$TypeErrorDecoder_undefinedLiteralPropertyPattern(); + match = t2.matchTypeError$1(message); + if (match != null) + return t1.call$1(H.JsNoSuchMethodError$(message, match)); + else { + match = t3.matchTypeError$1(message); + if (match != null) { + match.method = "call"; + return t1.call$1(H.JsNoSuchMethodError$(message, match)); + } else { + match = t4.matchTypeError$1(message); + if (match == null) { + match = nullLiteralCall.matchTypeError$1(message); + if (match == null) { + match = t5.matchTypeError$1(message); + if (match == null) { + match = t6.matchTypeError$1(message); + if (match == null) { + match = t7.matchTypeError$1(message); + if (match == null) { + match = nullLiteralCall.matchTypeError$1(message); + if (match == null) { + match = t8.matchTypeError$1(message); + if (match == null) { + match = t9.matchTypeError$1(message); + t2 = match != null; + } else + t2 = true; + } else + t2 = true; + } else + t2 = true; + } else + t2 = true; + } else + t2 = true; + } else + t2 = true; + } else + t2 = true; + if (t2) { + t2 = match == null ? null : match.method; + return t1.call$1(new H.NullError(message, t2)); + } + } + } + t2 = typeof message === "string" ? message : ""; + return t1.call$1(new H.UnknownJsTypeError(t2)); + } + if (ex instanceof RangeError) { + if (typeof message === "string" && message.indexOf("call stack") !== -1) + return new P.StackOverflowError(); + return t1.call$1(new P.ArgumentError(null)); + } + if (typeof InternalError == "function" && ex instanceof InternalError) + if (typeof message === "string" && message === "too much recursion") + return new P.StackOverflowError(); + return ex; + }, + objectHashCode: function(object) { + if (object == null || typeof object != 'object') + return J.get$hashCode$(object); + else + return H.Primitives_objectHashCode(object); + }, + fillLiteralMap: function(keyValuePairs, result) { + var $length, index, index0, index1; + $length = keyValuePairs.length; + for (index = 0; index < $length; index = index1) { + index0 = index + 1; + index1 = index0 + 1; + result.$indexSet(0, keyValuePairs[index], keyValuePairs[index0]); + } + return result; + }, + invokeClosure: function(closure, isolate, numberOfArguments, arg1, arg2, arg3, arg4) { + var t1 = J.getInterceptor(numberOfArguments); + if (t1.$eq(numberOfArguments, 0)) + return H._callInIsolate(isolate, new H.invokeClosure_closure(closure)); + else if (t1.$eq(numberOfArguments, 1)) + return H._callInIsolate(isolate, new H.invokeClosure_closure0(closure, arg1)); + else if (t1.$eq(numberOfArguments, 2)) + return H._callInIsolate(isolate, new H.invokeClosure_closure1(closure, arg1, arg2)); + else if (t1.$eq(numberOfArguments, 3)) + return H._callInIsolate(isolate, new H.invokeClosure_closure2(closure, arg1, arg2, arg3)); + else if (t1.$eq(numberOfArguments, 4)) + return H._callInIsolate(isolate, new H.invokeClosure_closure3(closure, arg1, arg2, arg3, arg4)); + else + throw H.wrapException(P.Exception_Exception("Unsupported number of arguments for wrapped closure")); + }, + convertDartClosureToJS: function(closure, arity) { + var $function; + if (closure == null) + return; + $function = closure.$identity; + if (!!$function) + return $function; + $function = (function(closure, arity, context, invoke) { return function(a1, a2, a3, a4) { return invoke(closure, context, arity, a1, a2, a3, a4); };})(closure,arity,init.globalState.currentContext,H.invokeClosure); + closure.$identity = $function; + return $function; + }, + Closure_fromTearOff: function(receiver, functions, reflectionInfo, isStatic, jsArguments, propertyName) { + var $function, callName, functionType, $prototype, $constructor, t1, isIntercepted, trampoline, signatureFunction, getReceiver, i, stub, stubCallName, t2; + $function = functions[0]; + $function.$stubName; + callName = $function.$callName; + $function.$reflectionInfo = reflectionInfo; + functionType = H.ReflectionInfo_ReflectionInfo($function).functionType; + $prototype = isStatic ? Object.create(new H.TearOffClosure().constructor.prototype) : Object.create(new H.BoundClosure(null, null, null, null).constructor.prototype); + $prototype.$initialize = $prototype.constructor; + if (isStatic) + $constructor = function(){this.$initialize()}; + else if (typeof dart_precompiled == "function") { + t1 = function(a,b,c,d) {this.$initialize(a,b,c,d)}; + $constructor = t1; + } else { + t1 = $.Closure_functionCounter; + $.Closure_functionCounter = J.$add$ns(t1, 1); + t1 = new Function("a", "b", "c", "d", "this.$initialize(a,b,c,d);" + t1); + $constructor = t1; + } + $prototype.constructor = $constructor; + $constructor.prototype = $prototype; + t1 = !isStatic; + if (t1) { + isIntercepted = jsArguments.length == 1 && true; + trampoline = H.Closure_forwardCallTo(receiver, $function, isIntercepted); + trampoline.$reflectionInfo = reflectionInfo; + } else { + $prototype.$name = propertyName; + trampoline = $function; + isIntercepted = false; + } + if (typeof functionType == "number") + signatureFunction = (function(s){return function(){return init.metadata[s]}})(functionType); + else if (t1 && typeof functionType == "function") { + getReceiver = isIntercepted ? H.BoundClosure_receiverOf : H.BoundClosure_selfOf; + signatureFunction = function(f,r){return function(){return f.apply({$receiver:r(this)},arguments)}}(functionType,getReceiver); + } else + throw H.wrapException("Error in reflectionInfo."); + $prototype.$signature = signatureFunction; + $prototype[callName] = trampoline; + for (t1 = functions.length, i = 1; i < t1; ++i) { + stub = functions[i]; + stubCallName = stub.$callName; + if (stubCallName != null) { + t2 = isStatic ? stub : H.Closure_forwardCallTo(receiver, stub, isIntercepted); + $prototype[stubCallName] = t2; + } + } + $prototype["call*"] = trampoline; + return $constructor; + }, + Closure_cspForwardCall: function(arity, isSuperCall, stubName, $function) { + var getSelf = H.BoundClosure_selfOf; + switch (isSuperCall ? -1 : arity) { + case 0: + return function(n,S){return function(){return S(this)[n]()}}(stubName,getSelf); + case 1: + return function(n,S){return function(a){return S(this)[n](a)}}(stubName,getSelf); + case 2: + return function(n,S){return function(a,b){return S(this)[n](a,b)}}(stubName,getSelf); + case 3: + return function(n,S){return function(a,b,c){return S(this)[n](a,b,c)}}(stubName,getSelf); + case 4: + return function(n,S){return function(a,b,c,d){return S(this)[n](a,b,c,d)}}(stubName,getSelf); + case 5: + return function(n,S){return function(a,b,c,d,e){return S(this)[n](a,b,c,d,e)}}(stubName,getSelf); + default: + return function(f,s){return function(){return f.apply(s(this),arguments)}}($function,getSelf); + } + }, + Closure_forwardCallTo: function(receiver, $function, isIntercepted) { + var stubName, arity, lookedUpFunction, t1, t2, $arguments; + if (isIntercepted) + return H.Closure_forwardInterceptedCallTo(receiver, $function); + stubName = $function.$stubName; + arity = $function.length; + lookedUpFunction = receiver[stubName]; + t1 = $function == null ? lookedUpFunction == null : $function === lookedUpFunction; + if (typeof dart_precompiled == "function" || !t1 || arity >= 27) + return H.Closure_cspForwardCall(arity, !t1, stubName, $function); + if (arity === 0) { + t1 = $.BoundClosure_selfFieldNameCache; + if (t1 == null) { + t1 = H.BoundClosure_computeFieldNamed("self"); + $.BoundClosure_selfFieldNameCache = t1; + } + t1 = "return function(){return this." + H.S(t1) + "." + H.S(stubName) + "();"; + t2 = $.Closure_functionCounter; + $.Closure_functionCounter = J.$add$ns(t2, 1); + return new Function(t1 + H.S(t2) + "}")(); + } + $arguments = "abcdefghijklmnopqrstuvwxyz".split("").splice(0, arity).join(","); + t1 = "return function(" + $arguments + "){return this."; + t2 = $.BoundClosure_selfFieldNameCache; + if (t2 == null) { + t2 = H.BoundClosure_computeFieldNamed("self"); + $.BoundClosure_selfFieldNameCache = t2; + } + t2 = t1 + H.S(t2) + "." + H.S(stubName) + "(" + $arguments + ");"; + t1 = $.Closure_functionCounter; + $.Closure_functionCounter = J.$add$ns(t1, 1); + return new Function(t2 + H.S(t1) + "}")(); + }, + Closure_cspForwardInterceptedCall: function(arity, isSuperCall, $name, $function) { + var getSelf, getReceiver; + getSelf = H.BoundClosure_selfOf; + getReceiver = H.BoundClosure_receiverOf; + switch (isSuperCall ? -1 : arity) { + case 0: + throw H.wrapException(H.RuntimeError$("Intercepted function with no arguments.")); + case 1: + return function(n,s,r){return function(){return s(this)[n](r(this))}}($name,getSelf,getReceiver); + case 2: + return function(n,s,r){return function(a){return s(this)[n](r(this),a)}}($name,getSelf,getReceiver); + case 3: + return function(n,s,r){return function(a,b){return s(this)[n](r(this),a,b)}}($name,getSelf,getReceiver); + case 4: + return function(n,s,r){return function(a,b,c){return s(this)[n](r(this),a,b,c)}}($name,getSelf,getReceiver); + case 5: + return function(n,s,r){return function(a,b,c,d){return s(this)[n](r(this),a,b,c,d)}}($name,getSelf,getReceiver); + case 6: + return function(n,s,r){return function(a,b,c,d,e){return s(this)[n](r(this),a,b,c,d,e)}}($name,getSelf,getReceiver); + default: + return function(f,s,r,a){return function(){a=[r(this)];Array.prototype.push.apply(a,arguments);return f.apply(s(this),a)}}($function,getSelf,getReceiver); + } + }, + Closure_forwardInterceptedCallTo: function(receiver, $function) { + var selfField, t1, stubName, arity, isCsp, lookedUpFunction, t2, $arguments; + selfField = H.BoundClosure_selfFieldName(); + t1 = $.BoundClosure_receiverFieldNameCache; + if (t1 == null) { + t1 = H.BoundClosure_computeFieldNamed("receiver"); + $.BoundClosure_receiverFieldNameCache = t1; + } + stubName = $function.$stubName; + arity = $function.length; + isCsp = typeof dart_precompiled == "function"; + lookedUpFunction = receiver[stubName]; + t2 = $function == null ? lookedUpFunction == null : $function === lookedUpFunction; + if (isCsp || !t2 || arity >= 28) + return H.Closure_cspForwardInterceptedCall(arity, !t2, stubName, $function); + if (arity === 1) { + t1 = "return function(){return this." + H.S(selfField) + "." + H.S(stubName) + "(this." + H.S(t1) + ");"; + t2 = $.Closure_functionCounter; + $.Closure_functionCounter = J.$add$ns(t2, 1); + return new Function(t1 + H.S(t2) + "}")(); + } + $arguments = "abcdefghijklmnopqrstuvwxyz".split("").splice(0, arity - 1).join(","); + t1 = "return function(" + $arguments + "){return this." + H.S(selfField) + "." + H.S(stubName) + "(this." + H.S(t1) + ", " + $arguments + ");"; + t2 = $.Closure_functionCounter; + $.Closure_functionCounter = J.$add$ns(t2, 1); + return new Function(t1 + H.S(t2) + "}")(); + }, + closureFromTearOff: function(receiver, functions, reflectionInfo, isStatic, jsArguments, $name) { + functions.fixed$length = init; + reflectionInfo.fixed$length = init; + return H.Closure_fromTearOff(receiver, functions, reflectionInfo, !!isStatic, jsArguments, $name); + }, + throwCyclicInit: function(staticName) { + throw H.wrapException(P.CyclicInitializationError$("Cyclic initialization for static " + H.S(staticName))); + }, + buildFunctionType: function(returnType, parameterTypes, optionalParameterTypes) { + return new H.RuntimeFunctionType(returnType, parameterTypes, optionalParameterTypes, null); + }, + getDynamicRuntimeType: function() { + return C.C_DynamicRuntimeType; + }, + setRuntimeTypeInfo: function(target, typeInfo) { + if (target != null) + target.$builtinTypeInfo = typeInfo; + return target; + }, + getRuntimeTypeInfo: function(target) { + if (target == null) + return; + return target.$builtinTypeInfo; + }, + getRuntimeTypeArguments: function(target, substitutionName) { + return H.substitute(target["$as" + H.S(substitutionName)], H.getRuntimeTypeInfo(target)); + }, + getRuntimeTypeArgument: function(target, substitutionName, index) { + var $arguments = H.getRuntimeTypeArguments(target, substitutionName); + return $arguments == null ? null : $arguments[index]; + }, + getTypeArgumentByIndex: function(target, index) { + var rti = H.getRuntimeTypeInfo(target); + return rti == null ? null : rti[index]; + }, + runtimeTypeToString: function(type, onTypeVariable) { + if (type == null) + return "dynamic"; + else if (typeof type === "object" && type !== null && type.constructor === Array) + return type[0].builtin$cls + H.joinArguments(type, 1, onTypeVariable); + else if (typeof type == "function") + return type.builtin$cls; + else if (typeof type === "number" && Math.floor(type) === type) + return C.JSInt_methods.toString$0(type); + else + return; + }, + joinArguments: function(types, startIndex, onTypeVariable) { + var buffer, index, firstArgument, allDynamic, argument, str; + if (types == null) + return ""; + buffer = P.StringBuffer$(""); + for (index = startIndex, firstArgument = true, allDynamic = true; index < types.length; ++index) { + if (firstArgument) + firstArgument = false; + else + buffer._contents = buffer._contents + ", "; + argument = types[index]; + if (argument != null) + allDynamic = false; + str = H.runtimeTypeToString(argument, onTypeVariable); + str = typeof str === "string" ? str : H.S(str); + buffer._contents = buffer._contents + str; + } + return allDynamic ? "" : "<" + H.S(buffer) + ">"; + }, + substitute: function(substitution, $arguments) { + if (typeof substitution === "object" && substitution !== null && substitution.constructor === Array) + $arguments = substitution; + else if (typeof substitution == "function") { + substitution = H.invokeOn(substitution, null, $arguments); + if (typeof substitution === "object" && substitution !== null && substitution.constructor === Array) + $arguments = substitution; + else if (typeof substitution == "function") + $arguments = H.invokeOn(substitution, null, $arguments); + } + return $arguments; + }, + areSubtypes: function(s, t) { + var len, i; + if (s == null || t == null) + return true; + len = s.length; + for (i = 0; i < len; ++i) + if (!H.isSubtype(s[i], t[i])) + return false; + return true; + }, + computeSignature: function(signature, context, contextName) { + return H.invokeOn(signature, context, H.getRuntimeTypeArguments(context, contextName)); + }, + isSubtype: function(s, t) { + var targetSignatureFunction, t1, typeOfS, t2, typeOfT, $name, substitution; + if (s === t) + return true; + if (s == null || t == null) + return true; + if ("func" in t) { + if (!("func" in s)) { + if ("$is_" + H.S(t.func) in s) + return true; + targetSignatureFunction = s.$signature; + if (targetSignatureFunction == null) + return false; + s = targetSignatureFunction.apply(s, null); + } + return H.isFunctionSubtype(s, t); + } + if (t.builtin$cls === "Function" && "func" in s) + return true; + t1 = typeof s === "object" && s !== null && s.constructor === Array; + typeOfS = t1 ? s[0] : s; + t2 = typeof t === "object" && t !== null && t.constructor === Array; + typeOfT = t2 ? t[0] : t; + $name = H.runtimeTypeToString(typeOfT, null); + if (typeOfT !== typeOfS) { + if (!("$is" + H.S($name) in typeOfS)) + return false; + substitution = typeOfS["$as" + H.S(H.runtimeTypeToString(typeOfT, null))]; + } else + substitution = null; + if (!t1 && substitution == null || !t2) + return true; + t1 = t1 ? s.slice(1) : null; + t2 = t2 ? t.slice(1) : null; + return H.areSubtypes(H.substitute(substitution, t1), t2); + }, + areAssignable: function(s, t, allowShorter) { + var sLength, tLength, i, t1, t2; + if (t == null && s == null) + return true; + if (t == null) + return allowShorter; + if (s == null) + return false; + sLength = s.length; + tLength = t.length; + if (allowShorter) { + if (sLength < tLength) + return false; + } else if (sLength !== tLength) + return false; + for (i = 0; i < tLength; ++i) { + t1 = s[i]; + t2 = t[i]; + if (!(H.isSubtype(t1, t2) || H.isSubtype(t2, t1))) + return false; + } + return true; + }, + areAssignableMaps: function(s, t) { + var t1, names, i, $name, tType, sType; + if (t == null) + return true; + if (s == null) + return false; + t1 = Object.getOwnPropertyNames(t); + t1.fixed$length = init; + names = t1; + for (t1 = names.length, i = 0; i < t1; ++i) { + $name = names[i]; + if (!Object.hasOwnProperty.call(s, $name)) + return false; + tType = t[$name]; + sType = s[$name]; + if (!(H.isSubtype(tType, sType) || H.isSubtype(sType, tType))) + return false; + } + return true; + }, + isFunctionSubtype: function(s, t) { + var sReturnType, tReturnType, sParameterTypes, tParameterTypes, sOptionalParameterTypes, tOptionalParameterTypes, sParametersLen, tParametersLen, sOptionalParametersLen, tOptionalParametersLen, pos, t1, t2, tPos, sPos; + if (!("func" in s)) + return false; + if ("void" in s) { + if (!("void" in t) && "ret" in t) + return false; + } else if (!("void" in t)) { + sReturnType = s.ret; + tReturnType = t.ret; + if (!(H.isSubtype(sReturnType, tReturnType) || H.isSubtype(tReturnType, sReturnType))) + return false; + } + sParameterTypes = s.args; + tParameterTypes = t.args; + sOptionalParameterTypes = s.opt; + tOptionalParameterTypes = t.opt; + sParametersLen = sParameterTypes != null ? sParameterTypes.length : 0; + tParametersLen = tParameterTypes != null ? tParameterTypes.length : 0; + sOptionalParametersLen = sOptionalParameterTypes != null ? sOptionalParameterTypes.length : 0; + tOptionalParametersLen = tOptionalParameterTypes != null ? tOptionalParameterTypes.length : 0; + if (sParametersLen > tParametersLen) + return false; + if (sParametersLen + sOptionalParametersLen < tParametersLen + tOptionalParametersLen) + return false; + if (sParametersLen === tParametersLen) { + if (!H.areAssignable(sParameterTypes, tParameterTypes, false)) + return false; + if (!H.areAssignable(sOptionalParameterTypes, tOptionalParameterTypes, true)) + return false; + } else { + for (pos = 0; pos < sParametersLen; ++pos) { + t1 = sParameterTypes[pos]; + t2 = tParameterTypes[pos]; + if (!(H.isSubtype(t1, t2) || H.isSubtype(t2, t1))) + return false; + } + for (tPos = pos, sPos = 0; tPos < tParametersLen; ++sPos, ++tPos) { + t1 = sOptionalParameterTypes[sPos]; + t2 = tParameterTypes[tPos]; + if (!(H.isSubtype(t1, t2) || H.isSubtype(t2, t1))) + return false; + } + for (tPos = 0; tPos < tOptionalParametersLen; ++sPos, ++tPos) { + t1 = sOptionalParameterTypes[sPos]; + t2 = tOptionalParameterTypes[tPos]; + if (!(H.isSubtype(t1, t2) || H.isSubtype(t2, t1))) + return false; + } + } + return H.areAssignableMaps(s.named, t.named); + }, + invokeOn: function($function, receiver, $arguments) { + return $function.apply(receiver, $arguments); + }, + toStringForNativeObject: function(obj) { + var t1 = $.getTagFunction; + return "Instance of " + (t1 == null ? "" : t1.call$1(obj)); + }, + hashCodeForNativeObject: function(object) { + return H.Primitives_objectHashCode(object); + }, + defineProperty: function(obj, property, value) { + Object.defineProperty(obj, property, {value: value, enumerable: false, writable: true, configurable: true}); + }, + lookupAndCacheInterceptor: function(obj) { + var tag, record, interceptor, interceptorClass, mark, t1; + tag = $.getTagFunction.call$1(obj); + record = $.dispatchRecordsForInstanceTags[tag]; + if (record != null) { + Object.defineProperty(obj, init.dispatchPropertyName, {value: record, enumerable: false, writable: true, configurable: true}); + return record.i; + } + interceptor = $.interceptorsForUncacheableTags[tag]; + if (interceptor != null) + return interceptor; + interceptorClass = init.interceptorsByTag[tag]; + if (interceptorClass == null) { + tag = $.alternateTagFunction.call$2(obj, tag); + if (tag != null) { + record = $.dispatchRecordsForInstanceTags[tag]; + if (record != null) { + Object.defineProperty(obj, init.dispatchPropertyName, {value: record, enumerable: false, writable: true, configurable: true}); + return record.i; + } + interceptor = $.interceptorsForUncacheableTags[tag]; + if (interceptor != null) + return interceptor; + interceptorClass = init.interceptorsByTag[tag]; + } + } + if (interceptorClass == null) + return; + interceptor = interceptorClass.prototype; + mark = tag[0]; + if (mark === "!") { + record = H.makeLeafDispatchRecord(interceptor); + $.dispatchRecordsForInstanceTags[tag] = record; + Object.defineProperty(obj, init.dispatchPropertyName, {value: record, enumerable: false, writable: true, configurable: true}); + return record.i; + } + if (mark === "~") { + $.interceptorsForUncacheableTags[tag] = interceptor; + return interceptor; + } + if (mark === "-") { + t1 = H.makeLeafDispatchRecord(interceptor); + Object.defineProperty(Object.getPrototypeOf(obj), init.dispatchPropertyName, {value: t1, enumerable: false, writable: true, configurable: true}); + return t1.i; + } + if (mark === "+") + return H.patchInteriorProto(obj, interceptor); + if (mark === "*") + throw H.wrapException(P.UnimplementedError$(tag)); + if (init.leafTags[tag] === true) { + t1 = H.makeLeafDispatchRecord(interceptor); + Object.defineProperty(Object.getPrototypeOf(obj), init.dispatchPropertyName, {value: t1, enumerable: false, writable: true, configurable: true}); + return t1.i; + } else + return H.patchInteriorProto(obj, interceptor); + }, + patchInteriorProto: function(obj, interceptor) { + var proto, record; + proto = Object.getPrototypeOf(obj); + record = J.makeDispatchRecord(interceptor, proto, null, null); + Object.defineProperty(proto, init.dispatchPropertyName, {value: record, enumerable: false, writable: true, configurable: true}); + return interceptor; + }, + makeLeafDispatchRecord: function(interceptor) { + return J.makeDispatchRecord(interceptor, false, null, !!interceptor.$isJavaScriptIndexingBehavior); + }, + makeDefaultDispatchRecord: function(tag, interceptorClass, proto) { + var interceptor = interceptorClass.prototype; + if (init.leafTags[tag] === true) + return J.makeDispatchRecord(interceptor, false, null, !!interceptor.$isJavaScriptIndexingBehavior); + else + return J.makeDispatchRecord(interceptor, proto, null, null); + }, + initNativeDispatch: function() { + if (true === $.initNativeDispatchFlag) + return; + $.initNativeDispatchFlag = true; + H.initNativeDispatchContinue(); + }, + initNativeDispatchContinue: function() { + var map, tags, i, tag, proto, record, interceptorClass; + $.dispatchRecordsForInstanceTags = Object.create(null); + $.interceptorsForUncacheableTags = Object.create(null); + H.initHooks(); + map = init.interceptorsByTag; + tags = Object.getOwnPropertyNames(map); + if (typeof window != "undefined") { + window; + for (i = 0; i < tags.length; ++i) { + tag = tags[i]; + proto = $.prototypeForTagFunction.call$1(tag); + if (proto != null) { + record = H.makeDefaultDispatchRecord(tag, map[tag], proto); + if (record != null) + Object.defineProperty(proto, init.dispatchPropertyName, {value: record, enumerable: false, writable: true, configurable: true}); + } + } + } + for (i = 0; i < tags.length; ++i) { + tag = tags[i]; + if (/^[A-Za-z_]/.test(tag)) { + interceptorClass = map[tag]; + map["!" + tag] = interceptorClass; + map["~" + tag] = interceptorClass; + map["-" + tag] = interceptorClass; + map["+" + tag] = interceptorClass; + map["*" + tag] = interceptorClass; + } + } + }, + initHooks: function() { + var hooks, transformers, i, transformer, getTag, getUnknownTag, prototypeForTag; + hooks = C.JS_CONST_aQP(); + hooks = H.applyHooksTransformer(C.JS_CONST_0, H.applyHooksTransformer(C.JS_CONST_rr7, H.applyHooksTransformer(C.JS_CONST_Fs4, H.applyHooksTransformer(C.JS_CONST_Fs4, H.applyHooksTransformer(C.JS_CONST_gkc, H.applyHooksTransformer(C.JS_CONST_U4w, H.applyHooksTransformer(C.JS_CONST_QJm(C.JS_CONST_IX5), hooks))))))); + if (typeof dartNativeDispatchHooksTransformer != "undefined") { + transformers = dartNativeDispatchHooksTransformer; + if (typeof transformers == "function") + transformers = [transformers]; + if (transformers.constructor == Array) + for (i = 0; i < transformers.length; ++i) { + transformer = transformers[i]; + if (typeof transformer == "function") + hooks = transformer(hooks) || hooks; + } + } + getTag = hooks.getTag; + getUnknownTag = hooks.getUnknownTag; + prototypeForTag = hooks.prototypeForTag; + $.getTagFunction = new H.initHooks_closure(getTag); + $.alternateTagFunction = new H.initHooks_closure0(getUnknownTag); + $.prototypeForTagFunction = new H.initHooks_closure1(prototypeForTag); + }, + applyHooksTransformer: function(transformer, hooks) { + return transformer(hooks) || hooks; + }, + ReflectionInfo: { + "^": "Object;jsFunction,data,isAccessor,requiredParameterCount,optionalParameterCount,areOptionalParametersNamed,functionType,cachedSortedIndices", + static: {"^": "ReflectionInfo_REQUIRED_PARAMETERS_INFO,ReflectionInfo_OPTIONAL_PARAMETERS_INFO,ReflectionInfo_FUNCTION_TYPE_INDEX,ReflectionInfo_FIRST_DEFAULT_ARGUMENT", ReflectionInfo_ReflectionInfo: function(jsFunction) { + var data, requiredParametersInfo, requiredParameterCount, optionalParametersInfo; + data = jsFunction.$reflectionInfo; + if (data == null) + return; + data.fixed$length = init; + data = data; + requiredParametersInfo = data[0]; + requiredParameterCount = requiredParametersInfo >> 1; + optionalParametersInfo = data[1]; + return new H.ReflectionInfo(jsFunction, data, (requiredParametersInfo & 1) === 1, requiredParameterCount, optionalParametersInfo >> 1, (optionalParametersInfo & 1) === 1, data[2], null); + }} + }, + TypeErrorDecoder: { + "^": "Object;_pattern,_arguments,_argumentsExpr,_expr,_method,_receiver", + matchTypeError$1: function(message) { + var match, result, t1; + match = new RegExp(this._pattern).exec(message); + if (match == null) + return; + result = {}; + t1 = this._arguments; + if (t1 !== -1) + result.arguments = match[t1 + 1]; + t1 = this._argumentsExpr; + if (t1 !== -1) + result.argumentsExpr = match[t1 + 1]; + t1 = this._expr; + if (t1 !== -1) + result.expr = match[t1 + 1]; + t1 = this._method; + if (t1 !== -1) + result.method = match[t1 + 1]; + t1 = this._receiver; + if (t1 !== -1) + result.receiver = match[t1 + 1]; + return result; + }, + static: {"^": "TypeErrorDecoder_noSuchMethodPattern,TypeErrorDecoder_notClosurePattern,TypeErrorDecoder_nullCallPattern,TypeErrorDecoder_nullLiteralCallPattern,TypeErrorDecoder_undefinedCallPattern,TypeErrorDecoder_undefinedLiteralCallPattern,TypeErrorDecoder_nullPropertyPattern,TypeErrorDecoder_nullLiteralPropertyPattern,TypeErrorDecoder_undefinedPropertyPattern,TypeErrorDecoder_undefinedLiteralPropertyPattern", TypeErrorDecoder_extractPattern: function(message) { + var match, $arguments, argumentsExpr, expr, method, receiver; + message = message.replace(String({}), '$receiver$').replace(new RegExp("[[\\]{}()*+?.\\\\^$|]", 'g'), '\\$&'); + match = message.match(/\\\$[a-zA-Z]+\\\$/g); + if (match == null) + match = []; + $arguments = match.indexOf("\\$arguments\\$"); + argumentsExpr = match.indexOf("\\$argumentsExpr\\$"); + expr = match.indexOf("\\$expr\\$"); + method = match.indexOf("\\$method\\$"); + receiver = match.indexOf("\\$receiver\\$"); + return new H.TypeErrorDecoder(message.replace('\\$arguments\\$', '((?:x|[^x])*)').replace('\\$argumentsExpr\\$', '((?:x|[^x])*)').replace('\\$expr\\$', '((?:x|[^x])*)').replace('\\$method\\$', '((?:x|[^x])*)').replace('\\$receiver\\$', '((?:x|[^x])*)'), $arguments, argumentsExpr, expr, method, receiver); + }, TypeErrorDecoder_provokeCallErrorOn: function(expression) { + return function($expr$) { + var $argumentsExpr$ = '$arguments$' + try { + $expr$.$method$($argumentsExpr$); + } catch (e) { + return e.message; + } +}(expression); + }, TypeErrorDecoder_provokePropertyErrorOn: function(expression) { + return function($expr$) { + try { + $expr$.$method$; + } catch (e) { + return e.message; + } +}(expression); + }} + }, + NullError: { + "^": "Error;_message,_method", + toString$0: function(_) { + var t1 = this._method; + if (t1 == null) + return "NullError: " + H.S(this._message); + return "NullError: Cannot call \"" + H.S(t1) + "\" on null"; + }, + $isError: true + }, + JsNoSuchMethodError: { + "^": "Error;_message,_method,_receiver", + toString$0: function(_) { + var t1, t2; + t1 = this._method; + if (t1 == null) + return "NoSuchMethodError: " + H.S(this._message); + t2 = this._receiver; + if (t2 == null) + return "NoSuchMethodError: Cannot call \"" + H.S(t1) + "\" (" + H.S(this._message) + ")"; + return "NoSuchMethodError: Cannot call \"" + H.S(t1) + "\" on \"" + H.S(t2) + "\" (" + H.S(this._message) + ")"; + }, + $isError: true, + static: {JsNoSuchMethodError$: function(_message, match) { + var t1, t2; + t1 = match == null; + t2 = t1 ? null : match.method; + t1 = t1 ? null : match.receiver; + return new H.JsNoSuchMethodError(_message, t2, t1); + }} + }, + UnknownJsTypeError: { + "^": "Error;_message", + toString$0: function(_) { + var t1 = this._message; + return C.JSString_methods.get$isEmpty(t1) ? "Error" : "Error: " + t1; + } + }, + unwrapException_saveStackTrace: { + "^": "Closure:11;ex_0", + call$1: function(error) { + if (!!J.getInterceptor(error).$isError) + if (error.$thrownJsError == null) + error.$thrownJsError = this.ex_0; + return error; + } + }, + _StackTrace: { + "^": "Object;_exception,_trace", + toString$0: function(_) { + var t1, trace; + t1 = this._trace; + if (t1 != null) + return t1; + t1 = this._exception; + trace = typeof t1 === "object" ? t1.stack : null; + t1 = trace == null ? "" : trace; + this._trace = t1; + return t1; + } + }, + invokeClosure_closure: { + "^": "Closure:9;closure_0", + call$0: function() { + return this.closure_0.call$0(); + } + }, + invokeClosure_closure0: { + "^": "Closure:9;closure_1,arg1_2", + call$0: function() { + return this.closure_1.call$1(this.arg1_2); + } + }, + invokeClosure_closure1: { + "^": "Closure:9;closure_3,arg1_4,arg2_5", + call$0: function() { + return this.closure_3.call$2(this.arg1_4, this.arg2_5); + } + }, + invokeClosure_closure2: { + "^": "Closure:9;closure_6,arg1_7,arg2_8,arg3_9", + call$0: function() { + return this.closure_6.call$3(this.arg1_7, this.arg2_8, this.arg3_9); + } + }, + invokeClosure_closure3: { + "^": "Closure:9;closure_10,arg1_11,arg2_12,arg3_13,arg4_14", + call$0: function() { + return this.closure_10.call$4(this.arg1_11, this.arg2_12, this.arg3_13, this.arg4_14); + } + }, + Closure: { + "^": "Object;", + toString$0: function(_) { + return "Closure"; + } + }, + TearOffClosure: { + "^": "Closure;" + }, + BoundClosure: { + "^": "TearOffClosure;_self,__js_helper$_target,_receiver,__js_helper$_name", + $eq: function(_, other) { + if (other == null) + return false; + if (this === other) + return true; + if (!J.getInterceptor(other).$isBoundClosure) + return false; + return this._self === other._self && this.__js_helper$_target === other.__js_helper$_target && this._receiver === other._receiver; + }, + get$hashCode: function(_) { + var t1, receiverHashCode; + t1 = this._receiver; + if (t1 == null) + receiverHashCode = H.Primitives_objectHashCode(this._self); + else + receiverHashCode = typeof t1 !== "object" ? J.get$hashCode$(t1) : H.Primitives_objectHashCode(t1); + t1 = H.Primitives_objectHashCode(this.__js_helper$_target); + if (typeof receiverHashCode !== "number") + return receiverHashCode.$xor(); + return (receiverHashCode ^ t1) >>> 0; + }, + $isBoundClosure: true, + static: {"^": "BoundClosure_selfFieldNameCache,BoundClosure_receiverFieldNameCache", BoundClosure_selfOf: function(closure) { + return closure._self; + }, BoundClosure_receiverOf: function(closure) { + return closure._receiver; + }, BoundClosure_selfFieldName: function() { + var t1 = $.BoundClosure_selfFieldNameCache; + if (t1 == null) { + t1 = H.BoundClosure_computeFieldNamed("self"); + $.BoundClosure_selfFieldNameCache = t1; + } + return t1; + }, BoundClosure_computeFieldNamed: function(fieldName) { + var template, t1, names, i, $name; + template = new H.BoundClosure("self", "target", "receiver", "name"); + t1 = Object.getOwnPropertyNames(template); + t1.fixed$length = init; + names = t1; + for (t1 = names.length, i = 0; i < t1; ++i) { + $name = names[i]; + if (template[$name] === fieldName) + return $name; + } + }} + }, + RuntimeError: { + "^": "Error;message", + toString$0: function(_) { + return "RuntimeError: " + H.S(this.message); + }, + static: {RuntimeError$: function(message) { + return new H.RuntimeError(message); + }} + }, + RuntimeType: { + "^": "Object;" + }, + RuntimeFunctionType: { + "^": "RuntimeType;returnType,parameterTypes,optionalParameterTypes,namedParameters", + _isTest$1: function(expression) { + var functionTypeObject = this._extractFunctionTypeObjectFrom$1(expression); + return functionTypeObject == null ? false : H.isFunctionSubtype(functionTypeObject, this.toRti$0()); + }, + _extractFunctionTypeObjectFrom$1: function(o) { + var interceptor = J.getInterceptor(o); + return "$signature" in interceptor ? interceptor.$signature() : null; + }, + toRti$0: function() { + var result, t1, t2, namedRti, keys, i, $name; + result = { "func": "dynafunc" }; + t1 = this.returnType; + t2 = J.getInterceptor(t1); + if (!!t2.$isVoidRuntimeType) + result.void = true; + else if (!t2.$isDynamicRuntimeType) + result.ret = t1.toRti$0(); + t1 = this.parameterTypes; + if (t1 != null && t1.length !== 0) + result.args = H.RuntimeFunctionType_listToRti(t1); + t1 = this.optionalParameterTypes; + if (t1 != null && t1.length !== 0) + result.opt = H.RuntimeFunctionType_listToRti(t1); + t1 = this.namedParameters; + if (t1 != null) { + namedRti = {}; + keys = H.extractKeys(t1); + for (t2 = keys.length, i = 0; i < t2; ++i) { + $name = keys[i]; + namedRti[$name] = t1[$name].toRti$0(); + } + result.named = namedRti; + } + return result; + }, + toString$0: function(_) { + var t1, t2, result, needsComma, i, type, keys, $name; + t1 = this.parameterTypes; + if (t1 != null) + for (t2 = t1.length, result = "(", needsComma = false, i = 0; i < t2; ++i, needsComma = true) { + type = t1[i]; + if (needsComma) + result += ", "; + result += H.S(type); + } + else { + result = "("; + needsComma = false; + } + t1 = this.optionalParameterTypes; + if (t1 != null && t1.length !== 0) { + result = (needsComma ? result + ", " : result) + "["; + for (t2 = t1.length, needsComma = false, i = 0; i < t2; ++i, needsComma = true) { + type = t1[i]; + if (needsComma) + result += ", "; + result += H.S(type); + } + result += "]"; + } else { + t1 = this.namedParameters; + if (t1 != null) { + result = (needsComma ? result + ", " : result) + "{"; + keys = H.extractKeys(t1); + for (t2 = keys.length, needsComma = false, i = 0; i < t2; ++i, needsComma = true) { + $name = keys[i]; + if (needsComma) + result += ", "; + result += H.S(t1[$name].toRti$0()) + " " + $name; + } + result += "}"; + } + } + return result + (") -> " + H.S(this.returnType)); + }, + static: {"^": "RuntimeFunctionType_inAssert", RuntimeFunctionType_listToRti: function(list) { + var result, t1, i; + list = list; + result = []; + for (t1 = list.length, i = 0; i < t1; ++i) + result.push(list[i].toRti$0()); + return result; + }} + }, + DynamicRuntimeType: { + "^": "RuntimeType;", + toString$0: function(_) { + return "dynamic"; + }, + toRti$0: function() { + return; + }, + $isDynamicRuntimeType: true + }, + initHooks_closure: { + "^": "Closure:11;getTag_0", + call$1: function(o) { + return this.getTag_0(o); + } + }, + initHooks_closure0: { + "^": "Closure:12;getUnknownTag_1", + call$2: function(o, tag) { + return this.getUnknownTag_1(o, tag); + } + }, + initHooks_closure1: { + "^": "Closure:0;prototypeForTag_2", + call$1: function(tag) { + return this.prototypeForTag_2(tag); + } + }, + JSSyntaxRegExp: { + "^": "Object;_nativeRegExp,_nativeGlobalRegExp,_nativeAnchoredRegExp", + firstMatch$1: function(str) { + var m; + if (typeof str !== "string") + H.throwExpression(new P.ArgumentError(str)); + m = this._nativeRegExp.exec(str); + if (m == null) + return; + return H._MatchImplementation$(this, m); + }, + static: {JSSyntaxRegExp_makeNative: function(pattern, multiLine, caseSensitive, global) { + var m, i, g, regexp, errorMessage; + m = multiLine ? "m" : ""; + i = caseSensitive ? "" : "i"; + g = global ? "g" : ""; + regexp = (function() {try {return new RegExp(pattern, m + i + g);} catch (e) {return e;}})(); + if (regexp instanceof RegExp) + return regexp; + errorMessage = String(regexp); + throw H.wrapException(P.FormatException$("Illegal RegExp pattern: " + pattern + ", " + errorMessage)); + }} + }, + _MatchImplementation: { + "^": "Object;pattern,_match", + $index: function(_, index) { + var t1 = this._match; + if (index >>> 0 !== index || index >= t1.length) + return H.ioore(t1, index); + return t1[index]; + }, + _MatchImplementation$2: function(pattern, _match) { + }, + static: {_MatchImplementation$: function(pattern, _match) { + var t1 = new H._MatchImplementation(pattern, _match); + t1._MatchImplementation$2(pattern, _match); + return t1; + }} + } +}], +["bank_terminal", "package:bank_terminal_s5/bank_terminal.dart", , D, { + "^": "", + BankAccount: { + "^": "Object;_number,owner,_balance,_pin_code,date_created,date_modified", + set$number: function(value) { + var t1; + if (value == null || J.get$isEmpty$asx(value) === true) + return; + t1 = H.JSSyntaxRegExp_makeNative("[0-9]{3}-[0-9]{7}-[0-9]{2}", false, true, false); + if (typeof value !== "string") + H.throwExpression(new P.ArgumentError(value)); + if (t1.test(value)) + this._number = value; + }, + toString$0: function(_) { + return "Bank account from " + H.S(this.owner) + " with number " + H.S(this._number) + " and balance " + H.S(this._balance); + }, + toJson$0: function() { + var acc = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSString, P.Object); + acc.$indexSet(0, "number", this._number); + acc.$indexSet(0, "owner", this.owner.toJson$0()); + acc.$indexSet(0, "balance", this._balance); + acc.$indexSet(0, "pin_code", this._pin_code); + acc.$indexSet(0, "creation_date", this.date_created.toString$0(0)); + acc.$indexSet(0, "modified_date", J.toString$0(this.date_modified)); + return C.JsonCodec_null_null.encode$1(acc); + }, + BankAccount$fromJson$1: function(json) { + var t1, t2, t3; + t1 = J.getInterceptor$asx(json); + this.set$number(t1.$index(json, "number")); + t2 = new D.Person(null, null, null, null, null); + t2.Person$fromJson$1(t1.$index(json, "owner")); + this.owner = t2; + t2 = t1.$index(json, "balance"); + if (J.$ge$n(t2, 0)) + this._balance = t2; + t2 = t1.$index(json, "pin_code"); + t3 = J.getInterceptor$n(t2); + if (t3.$ge(t2, 1) && t3.$le(t2, 999999)) + this._pin_code = t2; + this.date_modified = P.DateTime_parse(t1.$index(json, "modified_date")); + }, + static: {"^": "BankAccount_INTEREST"} + }, + Person: { + "^": "Object;_bank_terminal$_name,address,_email,_gender,_date_birth", + toString$0: function(_) { + return "Person: " + H.S(this._bank_terminal$_name) + ", " + H.S(this._gender); + }, + toJson$0: function() { + var per = P.LinkedHashMap_LinkedHashMap(null, null, null, J.JSString, P.Object); + per.$indexSet(0, "name", this._bank_terminal$_name); + per.$indexSet(0, "address", this.address); + per.$indexSet(0, "email", this._email); + per.$indexSet(0, "gender", this._gender); + per.$indexSet(0, "birthdate", J.toString$0(this._date_birth)); + return per; + }, + Person$fromJson$1: function(json) { + var t1, t2, t3; + t1 = J.getInterceptor$asx(json); + t2 = t1.$index(json, "name"); + if (t2 != null && J.get$isEmpty$asx(t2) !== true) + this._bank_terminal$_name = t2; + this.address = t1.$index(json, "address"); + t2 = t1.$index(json, "email"); + if (t2 != null && J.get$isEmpty$asx(t2) !== true) + this._email = t2; + t2 = t1.$index(json, "gender"); + t3 = J.getInterceptor(t2); + if (t3.$eq(t2, "M") || t3.$eq(t2, "F")) + this._gender = t2; + t1 = P.DateTime_parse(t1.$index(json, "birthdate")); + t2 = Date.now(); + new P.DateTime(t2, false).DateTime$_now$0(); + if (t1.millisecondsSinceEpoch < t2) + this._date_birth = t1; + } + } +}], +["", "bank_terminal_s5.dart", , M, { + "^": "", + main: [function() { + $.owner = document.querySelector("#owner"); + $.balance = document.querySelector("#balance"); + $.number = document.querySelector("#number"); + $.btn_other = document.querySelector("#btn_other"); + $.amount = document.querySelector("#amount"); + $.btn_deposit = document.querySelector("#btn_deposit"); + $.btn_interest = document.querySelector("#btn_interest"); + $.error = document.querySelector("#error"); + var t1 = J.get$onInput$x($.number); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.readData$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + t1 = J.get$onChange$x($.amount); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.nonNegative$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + t1 = J.get$onBlur$x($.amount); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.nonNegative$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + t1 = J.get$onClick$x($.btn_other); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.clearData$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + t1 = J.get$onClick$x($.btn_deposit); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.changeBalance$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + t1 = J.get$onClick$x($.btn_interest); + H.setRuntimeTypeInfo(new W._EventStreamSubscription(0, t1._target, t1._eventType, W._wrapZone(M.interest$closure()), t1._useCapture), [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); + M.disable_transactions(true); + }, "call$0", "main$closure", 0, 0, 1], + readData: [function(e) { + var key, t1, t2; + key = "Bankaccount:" + H.S(J.get$value$x($.number)); + if (window.localStorage.getItem(key) == null) { + J.set$innerHtml$x($.error, "Unknown bank account!"); + $.number.focus(); + return; + } + J.set$innerHtml$x($.error, ""); + t1 = C.JsonCodec_null_null.decode$1(window.localStorage.getItem(key)); + t2 = new D.BankAccount(null, null, null, null, P.DateTime_parse(J.$index$asx(t1, "creation_date")), null); + t2.BankAccount$fromJson$1(t1); + $.bac = t2; + J.set$innerHtml$x($.owner, "" + H.S(t2.owner._bank_terminal$_name) + ""); + J.set$innerHtml$x($.balance, "" + J.toStringAsFixed$1$n($.bac._balance, 2) + ""); + M.disable_transactions(false); + }, "call$1", "readData$closure", 2, 0, 2], + clearData: [function(e) { + J.set$value$x($.number, ""); + J.set$innerHtml$x($.owner, "----------"); + J.set$innerHtml$x($.balance, "0.0"); + $.number.focus(); + M.disable_transactions(true); + return; + }, "call$1", "clearData$closure", 2, 0, 2], + disable_transactions: function(off) { + J.set$disabled$x($.amount, off); + J.set$disabled$x($.btn_deposit, off); + J.set$disabled$x($.btn_interest, off); + }, + nonNegative: [function(e) { + var input, exception; + input = null; + try { + input = H.Primitives_parseDouble(J.get$value$x($.amount), null); + } catch (exception) { + if (!!J.getInterceptor(H.unwrapException(exception)).$isFormatException) { + window.alert("This is not a valid amount!"); + $.amount.focus(); + } else + throw exception; + } + + }, "call$1", "nonNegative$closure", 2, 0, 2], + changeBalance: [function(e) { + var money_amount, t1, t2; + money_amount = H.Primitives_parseDouble(J.get$value$x($.amount), null); + t1 = J.$ge$n(money_amount, 0); + t2 = $.bac; + if (t1) { + t1 = J.$add$ns(t2._balance, money_amount); + if (J.$ge$n(t1, 0)) + t2._balance = t1; + t1 = new P.DateTime(Date.now(), false); + t1.DateTime$_now$0(); + t2.date_modified = t1; + } else { + t1 = J.$add$ns(t2._balance, money_amount); + if (J.$ge$n(t1, 0)) + t2._balance = t1; + t1 = new P.DateTime(Date.now(), false); + t1.DateTime$_now$0(); + t2.date_modified = t1; + } + window.localStorage.setItem("Bankaccount:" + H.S($.bac._number), $.bac.toJson$0()); + J.set$innerHtml$x($.balance, "" + J.toStringAsFixed$1$n($.bac._balance, 2) + ""); + J.preventDefault$0$x(e); + e.stopPropagation(); + }, "call$1", "changeBalance$closure", 2, 0, 2], + interest: [function(e) { + var t1, t2, t3, t4; + t1 = $.bac; + t2 = t1._balance; + t3 = J.getInterceptor$ns(t2); + t4 = t3.$mul(t2, 5); + if (typeof t4 !== "number") + return t4.$div(); + t4 = t3.$add(t2, t4 / 100); + if (J.$ge$n(t4, 0)) + t1._balance = t4; + window.localStorage.setItem("Bankaccount:" + H.S($.bac._number), $.bac.toJson$0()); + J.set$innerHtml$x($.balance, "" + J.toStringAsFixed$1$n($.bac._balance, 2) + ""); + J.preventDefault$0$x(e); + e.stopPropagation(); + }, "call$1", "interest$closure", 2, 0, 2] +}, +1], +["dart._internal", "dart:_internal", , H, { + "^": "", + IterableMixinWorkaround_forEach: function(iterable, f) { + var t1; + for (t1 = new H.ListIterator(iterable, iterable.length, 0, null); t1.moveNext$0();) + f.call$1(t1._current); + }, + IterableMixinWorkaround_any: function(iterable, f) { + var t1; + for (t1 = new H.ListIterator(iterable, iterable.length, 0, null); t1.moveNext$0();) + if (f.call$1(t1._current) === true) + return true; + return false; + }, + IterableMixinWorkaround_toStringIterable: function(iterable, leftDelimiter, rightDelimiter) { + var result, i, t1; + for (i = 0; t1 = $.get$IterableMixinWorkaround__toStringList(), i < t1.length; ++i) + if (t1[i] === iterable) + return H.S(leftDelimiter) + "..." + H.S(rightDelimiter); + result = P.StringBuffer$(""); + try { + $.get$IterableMixinWorkaround__toStringList().push(iterable); + result.write$1(leftDelimiter); + result.writeAll$2(iterable, ", "); + result.write$1(rightDelimiter); + } finally { + t1 = $.get$IterableMixinWorkaround__toStringList(); + if (0 >= t1.length) + return H.ioore(t1, 0); + t1.pop(); + } + return result.get$_contents(); + }, + IterableMixinWorkaround_setRangeList: function(list, start, end, from, skipCount) { + var $length; + if (start < 0 || start > list.length) + H.throwExpression(P.RangeError$range(start, 0, list.length)); + if (end < start || end > list.length) + H.throwExpression(P.RangeError$range(end, start, list.length)); + $length = end - start; + if ($length === 0) + return; + if (skipCount < 0) + throw H.wrapException(new P.ArgumentError(skipCount)); + if (skipCount + $length > from.length) + throw H.wrapException(P.StateError$("Not enough elements")); + H.Lists_copy(from, skipCount, list, start, $length); + }, + Lists_copy: function(src, srcStart, dst, dstStart, count) { + var i, j, t1, t2; + if (srcStart < dstStart) + for (i = srcStart + count - 1, j = dstStart + count - 1, t1 = src.length; i >= srcStart; --i, --j) { + if (i < 0 || i >= t1) + return H.ioore(src, i); + C.JSArray_methods.$indexSet(dst, j, src[i]); + } + else + for (t1 = srcStart + count, t2 = src.length, j = dstStart, i = srcStart; i < t1; ++i, ++j) { + if (i < 0 || i >= t2) + return H.ioore(src, i); + C.JSArray_methods.$indexSet(dst, j, src[i]); + } + }, + Symbol_getName: function(symbol) { + return symbol.get$_name(); + }, + ListIterable: { + "^": "IterableBase;", + get$iterator: function(_) { + return new H.ListIterator(this, this.get$length(this), 0, null); + }, + forEach$1: function(_, action) { + var $length, i; + $length = this.get$length(this); + for (i = 0; i < $length; ++i) { + action.call$1(this.elementAt$1(0, i)); + if ($length !== this.get$length(this)) + throw H.wrapException(P.ConcurrentModificationError$(this)); + } + }, + get$isEmpty: function(_) { + return this.get$length(this) === 0; + }, + $isEfficientLength: true + }, + ListIterator: { + "^": "Object;_iterable,_length,_index,_current", + get$current: function() { + return this._current; + }, + moveNext$0: function() { + var t1, t2, $length, t3; + t1 = this._iterable; + t2 = J.getInterceptor$asx(t1); + $length = t2.get$length(t1); + if (this._length !== $length) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + t3 = this._index; + if (t3 >= $length) { + this._current = null; + return false; + } + this._current = t2.elementAt$1(t1, t3); + this._index = this._index + 1; + return true; + } + }, + MappedIterable: { + "^": "IterableBase;_iterable,_f", + get$iterator: function(_) { + var t1 = this._iterable; + t1 = new H.MappedIterator(null, t1.get$iterator(t1), this._f); + t1.$builtinTypeInfo = this.$builtinTypeInfo; + return t1; + }, + get$length: function(_) { + var t1 = this._iterable; + return t1.get$length(t1); + }, + get$isEmpty: function(_) { + var t1 = this._iterable; + return t1.get$isEmpty(t1); + }, + $asIterableBase: function($S, $T) { + return [$T]; + }, + static: {MappedIterable_MappedIterable: function(iterable, $function, $S, $T) { + if (!!iterable.$isEfficientLength) + return H.setRuntimeTypeInfo(new H.EfficientLengthMappedIterable(iterable, $function), [$S, $T]); + return H.setRuntimeTypeInfo(new H.MappedIterable(iterable, $function), [$S, $T]); + }} + }, + EfficientLengthMappedIterable: { + "^": "MappedIterable;_iterable,_f", + $isEfficientLength: true + }, + MappedIterator: { + "^": "Iterator;_current,_iterator,_f", + _f$1: function(arg0) { + return this._f.call$1(arg0); + }, + moveNext$0: function() { + var t1 = this._iterator; + if (t1.moveNext$0()) { + this._current = this._f$1(t1.get$current()); + return true; + } + this._current = null; + return false; + }, + get$current: function() { + return this._current; + } + }, + MappedListIterable: { + "^": "ListIterable;_source,_f", + _f$1: function(arg0) { + return this._f.call$1(arg0); + }, + get$length: function(_) { + return J.get$length$asx(this._source); + }, + elementAt$1: function(_, index) { + return this._f$1(J.elementAt$1$ax(this._source, index)); + }, + $asListIterable: function($S, $T) { + return [$T]; + }, + $asIterableBase: function($S, $T) { + return [$T]; + }, + $isEfficientLength: true + }, + WhereIterable: { + "^": "IterableBase;_iterable,_f", + get$iterator: function(_) { + var t1 = new H.WhereIterator(J.get$iterator$ax(this._iterable), this._f); + t1.$builtinTypeInfo = this.$builtinTypeInfo; + return t1; + } + }, + WhereIterator: { + "^": "Iterator;_iterator,_f", + _f$1: function(arg0) { + return this._f.call$1(arg0); + }, + moveNext$0: function() { + for (var t1 = this._iterator; t1.moveNext$0();) + if (this._f$1(t1.get$current()) === true) + return true; + return false; + }, + get$current: function() { + return this._iterator.get$current(); + } + }, + FixedLengthListMixin: { + "^": "Object;" + } +}], +["dart._js_names", "dart:_js_names", , H, { + "^": "", + extractKeys: function(victim) { + var t1 = H.setRuntimeTypeInfo((function(victim, hasOwnProperty) { + var result = []; + for (var key in victim) { + if (hasOwnProperty.call(victim, key)) result.push(key); + } + return result; +})(victim, Object.prototype.hasOwnProperty), [null]); + t1.fixed$length = init; + return t1; + } +}], +["dart.async", "dart:async", , P, { + "^": "", + _registerErrorHandler: function(errorHandler, zone) { + var t1 = H.getDynamicRuntimeType(); + t1 = H.buildFunctionType(t1, [t1, t1])._isTest$1(errorHandler); + if (t1) { + zone.toString; + return errorHandler; + } else { + zone.toString; + return errorHandler; + } + }, + _asyncRunCallbackLoop: function() { + var entry = $._nextCallback; + for (; entry != null;) { + entry.callback$0(); + entry = entry.next; + $._nextCallback = entry; + } + $._lastCallback = null; + }, + _asyncRunCallback: [function() { + var exception; + try { + P._asyncRunCallbackLoop(); + } catch (exception) { + H.unwrapException(exception); + P._createTimer(C.Duration_0, P._asyncRunCallback$closure()); + $._nextCallback = $._nextCallback.next; + throw exception; + } + + }, "call$0", "_asyncRunCallback$closure", 0, 0, 1], + _scheduleAsyncCallback: function(callback) { + var t1, t2; + t1 = $._lastCallback; + if (t1 == null) { + t1 = new P._AsyncCallbackEntry(callback, null); + $._lastCallback = t1; + $._nextCallback = t1; + P._createTimer(C.Duration_0, P._asyncRunCallback$closure()); + } else { + t2 = new P._AsyncCallbackEntry(callback, null); + t1.next = t2; + $._lastCallback = t2; + } + }, + _runUserCode: function(userCode, onSuccess, onError) { + var e, s, exception, t1; + try { + onSuccess.call$1(userCode.call$0()); + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + s = new H._StackTrace(exception, null); + onError.call$2(e, s); + } + + }, + _cancelAndError: function(subscription, future, error, stackTrace) { + subscription.cancel$0(); + future._completeError$2(error, stackTrace); + }, + _cancelAndErrorClosure: function(subscription, future) { + return new P._cancelAndErrorClosure_closure(subscription, future); + }, + _cancelAndValue: function(subscription, future, value) { + subscription.cancel$0(); + future._complete$1(value); + }, + Timer_Timer: function(duration, callback) { + var t1 = $.Zone__current; + if (t1 === C.C__RootZone) { + t1.toString; + return P._rootCreateTimer(t1, null, t1, duration, callback); + } + return P._rootCreateTimer(t1, null, t1, duration, t1.bindCallback$2$runGuarded(callback, true)); + }, + _createTimer: function(duration, callback) { + var milliseconds = C.JSInt_methods._tdivFast$1(duration._duration, 1000); + return H.TimerImpl$(milliseconds < 0 ? 0 : milliseconds, callback); + }, + Zone__enter: function(zone) { + var previous = $.Zone__current; + $.Zone__current = zone; + return previous; + }, + _rootHandleUncaughtError: function($self, $parent, zone, error, stackTrace) { + P._rootRun($self, null, $self, new P._rootHandleUncaughtError_closure(error, stackTrace)); + }, + _rootRun: function($self, $parent, zone, f) { + var old, t1; + if ($.Zone__current === zone) + return f.call$0(); + old = P.Zone__enter(zone); + try { + t1 = f.call$0(); + return t1; + } finally { + $.Zone__current = old; + } + }, + _rootRunUnary: function($self, $parent, zone, f, arg) { + var old, t1; + if ($.Zone__current === zone) + return f.call$1(arg); + old = P.Zone__enter(zone); + try { + t1 = f.call$1(arg); + return t1; + } finally { + $.Zone__current = old; + } + }, + _rootRunBinary: function($self, $parent, zone, f, arg1, arg2) { + var old, t1; + if ($.Zone__current === zone) + return f.call$2(arg1, arg2); + old = P.Zone__enter(zone); + try { + t1 = f.call$2(arg1, arg2); + return t1; + } finally { + $.Zone__current = old; + } + }, + _rootScheduleMicrotask: function($self, $parent, zone, f) { + P._scheduleAsyncCallback(C.C__RootZone !== zone ? zone.bindCallback$1(f) : f); + }, + _rootCreateTimer: function($self, $parent, zone, duration, callback) { + return P._createTimer(duration, C.C__RootZone !== zone ? zone.bindCallback$1(callback) : callback); + }, + _AsyncError: { + "^": "Object;error>,stackTrace<", + $isError: true + }, + _Future: { + "^": "Object;_state,_zone<,_resultOrListeners,_nextListener<,_onValueCallback,_errorTestCallback,_onErrorCallback,_whenCompleteActionCallback", + get$_isComplete: function() { + return this._state >= 4; + }, + get$_hasValue: function() { + return this._state === 4; + }, + get$_hasError: function() { + return this._state === 8; + }, + set$_isChained: function(value) { + if (value) + this._state = 2; + else + this._state = 0; + }, + then$2$onError: function(f, onError) { + var t1, result; + t1 = $.Zone__current; + t1.toString; + result = H.setRuntimeTypeInfo(new P._Future(0, t1, null, null, f, null, P._registerErrorHandler(onError, t1), null), [null]); + this._addListener$1(result); + return result; + }, + get$_async$_value: function() { + return this._resultOrListeners; + }, + get$_error: function() { + return this._resultOrListeners; + }, + _setValue$1: function(value) { + this._state = 4; + this._resultOrListeners = value; + }, + _setError$2: function(error, stackTrace) { + this._state = 8; + this._resultOrListeners = new P._AsyncError(error, stackTrace); + }, + _addListener$1: function(listener) { + var t1; + if (this._state >= 4) { + t1 = this._zone; + t1.toString; + P._rootScheduleMicrotask(t1, null, t1, new P._Future__addListener_closure(this, listener)); + } else { + listener._nextListener = this._resultOrListeners; + this._resultOrListeners = listener; + } + }, + _removeListeners$0: function() { + var current, prev, next; + current = this._resultOrListeners; + this._resultOrListeners = null; + for (prev = null; current != null; prev = current, current = next) { + next = current.get$_nextListener(); + current._nextListener = prev; + } + return prev; + }, + _complete$1: function(value) { + var t1, listeners; + t1 = J.getInterceptor(value); + if (!!t1.$isFuture) + if (!!t1.$is_Future) + P._Future__chainCoreFuture(value, this); + else + P._Future__chainForeignFuture(value, this); + else { + listeners = this._removeListeners$0(); + this._setValue$1(value); + P._Future__propagateToListeners(this, listeners); + } + }, + _completeError$2: [function(error, stackTrace) { + var listeners = this._removeListeners$0(); + this._setError$2(error, stackTrace); + P._Future__propagateToListeners(this, listeners); + }, function(error) { + return this._completeError$2(error, null); + }, "_completeError$1", "call$2", "call$1", "get$_completeError", 2, 2, 13, 14], + $is_Future: true, + $isFuture: true, + static: {"^": "_Future__INCOMPLETE,_Future__PENDING_COMPLETE,_Future__CHAINED,_Future__VALUE,_Future__ERROR", _Future$: function($T) { + return H.setRuntimeTypeInfo(new P._Future(0, $.Zone__current, null, null, null, null, null, null), [$T]); + }, _Future__chainForeignFuture: function(source, target) { + target._state = 2; + source.then$2$onError(new P._Future__chainForeignFuture_closure(target), new P._Future__chainForeignFuture_closure0(target)); + }, _Future__chainCoreFuture: function(source, target) { + target._state = 2; + if (source._state >= 4) + P._Future__propagateToListeners(source, target); + else + source._addListener$1(target); + }, _Future__propagateMultipleListeners: function(source, listeners) { + var listeners0; + do { + listeners0 = listeners.get$_nextListener(); + listeners._nextListener = null; + P._Future__propagateToListeners(source, listeners); + if (listeners0 != null) { + listeners = listeners0; + continue; + } else + break; + } while (true); + }, _Future__propagateToListeners: function(source, listeners) { + var t1, t2, t3, hasError, asyncError, t4, sourceValue, t5, zone, oldZone, chainSource, listeners0; + t1 = {}; + t1.source_4 = source; + for (t2 = source; true;) { + t3 = {}; + if (!t2.get$_isComplete()) + return; + hasError = t1.source_4.get$_hasError(); + if (hasError && listeners == null) { + t2 = t1.source_4; + asyncError = t2.get$_error(); + t2 = t2._zone; + t3 = J.get$error$x(asyncError); + t4 = asyncError.get$stackTrace(); + t2.toString; + P._rootHandleUncaughtError(t2, null, t2, t3, t4); + return; + } + if (listeners == null) + return; + if (listeners._nextListener != null) { + P._Future__propagateMultipleListeners(t1.source_4, listeners); + return; + } + t3.listenerHasValue_1 = true; + sourceValue = t1.source_4.get$_hasValue() ? t1.source_4.get$_async$_value() : null; + t3.listenerValueOrError_2 = sourceValue; + t3.isPropagationAborted_3 = false; + t2 = !hasError; + if (t2) { + t4 = listeners._state === 2; + if ((t4 ? null : listeners._onValueCallback) == null) { + t5 = (t4 ? null : listeners._whenCompleteActionCallback) != null; + t4 = t5; + } else + t4 = true; + } else + t4 = true; + if (t4) { + zone = listeners._zone; + if (hasError) { + t4 = t1.source_4.get$_zone(); + t4.toString; + zone.toString; + t4 = zone == null ? t4 != null : zone !== t4; + } else + t4 = false; + if (t4) { + t2 = t1.source_4; + asyncError = t2.get$_error(); + t2 = t2._zone; + t3 = J.get$error$x(asyncError); + t4 = asyncError.get$stackTrace(); + t2.toString; + P._rootHandleUncaughtError(t2, null, t2, t3, t4); + return; + } + oldZone = $.Zone__current; + if (oldZone == null ? zone != null : oldZone !== zone) + $.Zone__current = zone; + else + oldZone = null; + if (t2) { + if ((listeners._state === 2 ? null : listeners._onValueCallback) != null) + t3.listenerHasValue_1 = new P._Future__propagateToListeners_handleValueCallback(t3, listeners, sourceValue, zone).call$0(); + } else + new P._Future__propagateToListeners_handleError(t1, t3, listeners, zone).call$0(); + if ((listeners._state === 2 ? null : listeners._whenCompleteActionCallback) != null) + new P._Future__propagateToListeners_handleWhenCompleteCallback(t1, t3, hasError, listeners, zone).call$0(); + if (oldZone != null) + $.Zone__current = oldZone; + if (t3.isPropagationAborted_3) + return; + if (t3.listenerHasValue_1 === true) { + t2 = t3.listenerValueOrError_2; + t2 = (sourceValue == null ? t2 != null : sourceValue !== t2) && !!J.getInterceptor(t2).$isFuture; + } else + t2 = false; + if (t2) { + chainSource = t3.listenerValueOrError_2; + if (!!J.getInterceptor(chainSource).$is_Future) + if (chainSource._state >= 4) { + listeners._state = 2; + t1.source_4 = chainSource; + t2 = chainSource; + continue; + } else + P._Future__chainCoreFuture(chainSource, listeners); + else + P._Future__chainForeignFuture(chainSource, listeners); + return; + } + } + if (t3.listenerHasValue_1 === true) { + listeners0 = listeners._removeListeners$0(); + t2 = t3.listenerValueOrError_2; + listeners._state = 4; + listeners._resultOrListeners = t2; + } else { + listeners0 = listeners._removeListeners$0(); + asyncError = t3.listenerValueOrError_2; + t2 = J.get$error$x(asyncError); + t3 = asyncError.get$stackTrace(); + listeners._state = 8; + listeners._resultOrListeners = new P._AsyncError(t2, t3); + } + t1.source_4 = listeners; + t2 = listeners; + listeners = listeners0; + } + }} + }, + _Future__addListener_closure: { + "^": "Closure:9;this_0,listener_1", + call$0: function() { + P._Future__propagateToListeners(this.this_0, this.listener_1); + } + }, + _Future__chainForeignFuture_closure: { + "^": "Closure:11;target_0", + call$1: function(value) { + var t1, listeners; + t1 = this.target_0; + listeners = t1._removeListeners$0(); + t1._setValue$1(value); + P._Future__propagateToListeners(t1, listeners); + } + }, + _Future__chainForeignFuture_closure0: { + "^": "Closure:15;target_1", + call$2: function(error, stackTrace) { + this.target_1._completeError$2(error, stackTrace); + }, + call$1: function(error) { + return this.call$2(error, null); + } + }, + _Future__propagateToListeners_handleValueCallback: { + "^": "Closure:16;box_1,listener_3,sourceValue_4,zone_5", + call$0: function() { + var e, s, t1, t2, exception; + try { + t1 = this.zone_5; + t2 = this.listener_3; + t2 = t2._state === 2 ? null : t2._onValueCallback; + t1.toString; + this.box_1.listenerValueOrError_2 = P._rootRunUnary(t1, null, t1, t2, this.sourceValue_4); + return true; + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + s = new H._StackTrace(exception, null); + this.box_1.listenerValueOrError_2 = new P._AsyncError(e, s); + return false; + } + + } + }, + _Future__propagateToListeners_handleError: { + "^": "Closure:1;box_2,box_1,listener_6,zone_7", + call$0: function() { + var asyncError, test, matchesTest, e, s, errorCallback, e0, s0, t1, t2, t3, exception, listenerValueOrError, t4; + asyncError = this.box_2.source_4.get$_error(); + t1 = this.listener_6; + test = t1._state === 2 ? null : t1._errorTestCallback; + matchesTest = true; + if (test != null) + try { + t2 = this.zone_7; + t3 = J.get$error$x(asyncError); + t2.toString; + matchesTest = P._rootRunUnary(t2, null, t2, test, t3); + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + s = new H._StackTrace(exception, null); + t1 = J.get$error$x(asyncError); + t2 = e; + listenerValueOrError = (t1 == null ? t2 == null : t1 === t2) ? asyncError : new P._AsyncError(e, s); + t1 = this.box_1; + t1.listenerValueOrError_2 = listenerValueOrError; + t1.listenerHasValue_1 = false; + return; + } + + errorCallback = t1._state === 2 ? null : t1._onErrorCallback; + if (matchesTest === true && errorCallback != null) { + try { + t1 = errorCallback; + t2 = H.getDynamicRuntimeType(); + t2 = H.buildFunctionType(t2, [t2, t2])._isTest$1(t1); + t3 = this.zone_7; + t4 = this.box_1; + if (t2) { + t1 = J.get$error$x(asyncError); + t2 = asyncError.get$stackTrace(); + t3.toString; + t4.listenerValueOrError_2 = P._rootRunBinary(t3, null, t3, errorCallback, t1, t2); + } else { + t1 = J.get$error$x(asyncError); + t3.toString; + t4.listenerValueOrError_2 = P._rootRunUnary(t3, null, t3, errorCallback, t1); + } + } catch (exception) { + t1 = H.unwrapException(exception); + e0 = t1; + s0 = new H._StackTrace(exception, null); + t1 = J.get$error$x(asyncError); + t2 = e0; + listenerValueOrError = (t1 == null ? t2 == null : t1 === t2) ? asyncError : new P._AsyncError(e0, s0); + t1 = this.box_1; + t1.listenerValueOrError_2 = listenerValueOrError; + t1.listenerHasValue_1 = false; + return; + } + + this.box_1.listenerHasValue_1 = true; + } else { + t1 = this.box_1; + t1.listenerValueOrError_2 = asyncError; + t1.listenerHasValue_1 = false; + } + } + }, + _Future__propagateToListeners_handleWhenCompleteCallback: { + "^": "Closure:1;box_2,box_1,hasError_8,listener_9,zone_10", + call$0: function() { + var t1, e, s, t2, t3, exception; + t1 = {}; + t1.completeResult_0 = null; + try { + t2 = this.zone_10; + t3 = this.listener_9; + t3 = t3._state === 2 ? null : t3._whenCompleteActionCallback; + t2.toString; + t1.completeResult_0 = P._rootRun(t2, null, t2, t3); + } catch (exception) { + t2 = H.unwrapException(exception); + e = t2; + s = new H._StackTrace(exception, null); + if (this.hasError_8) { + t2 = J.get$error$x(this.box_2.source_4.get$_error()); + t3 = e; + t3 = t2 == null ? t3 == null : t2 === t3; + t2 = t3; + } else + t2 = false; + t3 = this.box_1; + if (t2) + t3.listenerValueOrError_2 = this.box_2.source_4.get$_error(); + else + t3.listenerValueOrError_2 = new P._AsyncError(e, s); + t3.listenerHasValue_1 = false; + } + + if (!!J.getInterceptor(t1.completeResult_0).$isFuture) { + t2 = this.listener_9; + t2.set$_isChained(true); + this.box_1.isPropagationAborted_3 = true; + t1.completeResult_0.then$2$onError(new P._Future__propagateToListeners_handleWhenCompleteCallback_closure(this.box_2, t2), new P._Future__propagateToListeners_handleWhenCompleteCallback_closure0(t1, t2)); + } + } + }, + _Future__propagateToListeners_handleWhenCompleteCallback_closure: { + "^": "Closure:11;box_2,listener_11", + call$1: function(ignored) { + P._Future__propagateToListeners(this.box_2.source_4, this.listener_11); + } + }, + _Future__propagateToListeners_handleWhenCompleteCallback_closure0: { + "^": "Closure:15;box_0,listener_12", + call$2: function(error, stackTrace) { + var t1, completeResult; + t1 = this.box_0; + if (!J.getInterceptor(t1.completeResult_0).$is_Future) { + completeResult = P._Future$(null); + t1.completeResult_0 = completeResult; + completeResult._setError$2(error, stackTrace); + } + P._Future__propagateToListeners(t1.completeResult_0, this.listener_12); + }, + call$1: function(error) { + return this.call$2(error, null); + } + }, + _AsyncCallbackEntry: { + "^": "Object;callback,next", + callback$0: function() { + return this.callback.call$0(); + } + }, + Stream: { + "^": "Object;", + forEach$1: function(_, action) { + var t1, future; + t1 = {}; + future = P._Future$(null); + t1.subscription_0 = null; + t1.subscription_0 = this.listen$4$cancelOnError$onDone$onError(new P.Stream_forEach_closure(t1, this, action, future), true, new P.Stream_forEach_closure0(future), future.get$_completeError()); + return future; + }, + get$length: function(_) { + var t1, future; + t1 = {}; + future = P._Future$(J.JSInt); + t1.count_0 = 0; + this.listen$4$cancelOnError$onDone$onError(new P.Stream_length_closure(t1), true, new P.Stream_length_closure0(t1, future), future.get$_completeError()); + return future; + }, + get$isEmpty: function(_) { + var t1, future; + t1 = {}; + future = P._Future$(J.JSBool); + t1.subscription_0 = null; + t1.subscription_0 = this.listen$4$cancelOnError$onDone$onError(new P.Stream_isEmpty_closure(t1, future), true, new P.Stream_isEmpty_closure0(future), future.get$_completeError()); + return future; + } + }, + Stream_forEach_closure: { + "^": "Closure;box_0,this_1,action_2,future_3", + call$1: function(element) { + P._runUserCode(new P.Stream_forEach__closure(this.action_2, element), new P.Stream_forEach__closure0(), P._cancelAndErrorClosure(this.box_0.subscription_0, this.future_3)); + }, + $signature: function() { + return H.computeSignature(function(T) { + return {func: "dynamic__T", args: [T]}; + }, this.this_1, "Stream"); + } + }, + Stream_forEach__closure: { + "^": "Closure:9;action_4,element_5", + call$0: function() { + return this.action_4.call$1(this.element_5); + } + }, + Stream_forEach__closure0: { + "^": "Closure:11;", + call$1: function(_) { + } + }, + Stream_forEach_closure0: { + "^": "Closure:9;future_6", + call$0: function() { + this.future_6._complete$1(null); + } + }, + Stream_length_closure: { + "^": "Closure:11;box_0", + call$1: function(_) { + var t1 = this.box_0; + t1.count_0 = t1.count_0 + 1; + } + }, + Stream_length_closure0: { + "^": "Closure:9;box_0,future_1", + call$0: function() { + this.future_1._complete$1(this.box_0.count_0); + } + }, + Stream_isEmpty_closure: { + "^": "Closure:11;box_0,future_1", + call$1: function(_) { + P._cancelAndValue(this.box_0.subscription_0, this.future_1, false); + } + }, + Stream_isEmpty_closure0: { + "^": "Closure:9;future_2", + call$0: function() { + this.future_2._complete$1(true); + } + }, + StreamSubscription: { + "^": "Object;" + }, + _EventSink: { + "^": "Object;" + }, + _cancelAndError_closure: { + "^": "Closure:9;future_0,error_1,stackTrace_2", + call$0: function() { + return this.future_0._completeError$2(this.error_1, this.stackTrace_2); + } + }, + _cancelAndErrorClosure_closure: { + "^": "Closure:17;subscription_0,future_1", + call$2: function(error, stackTrace) { + return P._cancelAndError(this.subscription_0, this.future_1, error, stackTrace); + } + }, + _cancelAndValue_closure: { + "^": "Closure:9;future_0,value_1", + call$0: function() { + return this.future_0._complete$1(this.value_1); + } + }, + _BaseZone: { + "^": "Object;", + runGuarded$1: function(f) { + var e, s, t1, exception; + try { + t1 = this.run$1(f); + return t1; + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + s = new H._StackTrace(exception, null); + return this.handleUncaughtError$2(e, s); + } + + }, + runUnaryGuarded$2: function(f, arg) { + var e, s, t1, exception; + try { + t1 = this.runUnary$2(f, arg); + return t1; + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + s = new H._StackTrace(exception, null); + return this.handleUncaughtError$2(e, s); + } + + }, + bindCallback$2$runGuarded: function(f, runGuarded) { + var registered = this.registerCallback$1(f); + if (runGuarded) + return new P._BaseZone_bindCallback_closure(this, registered); + else + return new P._BaseZone_bindCallback_closure0(this, registered); + }, + bindCallback$1: function(f) { + return this.bindCallback$2$runGuarded(f, true); + }, + bindUnaryCallback$2$runGuarded: function(f, runGuarded) { + var registered = this.registerUnaryCallback$1(f); + if (runGuarded) + return new P._BaseZone_bindUnaryCallback_closure(this, registered); + else + return new P._BaseZone_bindUnaryCallback_closure0(this, registered); + } + }, + _BaseZone_bindCallback_closure: { + "^": "Closure:9;this_0,registered_1", + call$0: function() { + return this.this_0.runGuarded$1(this.registered_1); + } + }, + _BaseZone_bindCallback_closure0: { + "^": "Closure:9;this_2,registered_3", + call$0: function() { + return this.this_2.run$1(this.registered_3); + } + }, + _BaseZone_bindUnaryCallback_closure: { + "^": "Closure:11;this_0,registered_1", + call$1: function(arg) { + return this.this_0.runUnaryGuarded$2(this.registered_1, arg); + } + }, + _BaseZone_bindUnaryCallback_closure0: { + "^": "Closure:11;this_2,registered_3", + call$1: function(arg) { + return this.this_2.runUnary$2(this.registered_3, arg); + } + }, + _rootHandleUncaughtError_closure: { + "^": "Closure:9;error_0,stackTrace_1", + call$0: function() { + P._scheduleAsyncCallback(new P._rootHandleUncaughtError__closure(this.error_0, this.stackTrace_1)); + } + }, + _rootHandleUncaughtError__closure: { + "^": "Closure:9;error_2,stackTrace_3", + call$0: function() { + var t1, trace; + t1 = this.error_2; + P.print("Uncaught Error: " + H.S(t1)); + trace = this.stackTrace_3; + if (trace == null && !!J.getInterceptor(t1).$isError) + trace = t1.get$stackTrace(); + if (trace != null) + P.print("Stack Trace: \n" + H.S(trace) + "\n"); + throw H.wrapException(t1); + } + }, + _RootZone: { + "^": "_BaseZone;", + $index: function(_, key) { + return; + }, + handleUncaughtError$2: function(error, stackTrace) { + return P._rootHandleUncaughtError(this, null, this, error, stackTrace); + }, + run$1: function(f) { + return P._rootRun(this, null, this, f); + }, + runUnary$2: function(f, arg) { + return P._rootRunUnary(this, null, this, f, arg); + }, + registerCallback$1: function(f) { + return f; + }, + registerUnaryCallback$1: function(f) { + return f; + } + } +}], +["dart.collection", "dart:collection", , P, { + "^": "", + _defaultEquals: [function(a, b) { + return J.$eq(a, b); + }, "call$2", "_defaultEquals$closure", 4, 0, 3], + _defaultHashCode: [function(a) { + return J.get$hashCode$(a); + }, "call$1", "_defaultHashCode$closure", 2, 0, 4], + HashMap_HashMap: function(equals, hashCode, isValidKey, $K, $V) { + return H.setRuntimeTypeInfo(new P._HashMap(0, null, null, null, null), [$K, $V]); + }, + HashSet_HashSet$identity: function($E) { + return H.setRuntimeTypeInfo(new P._IdentityHashSet(0, null, null, null, null), [$E]); + }, + _iterableToString: function(iterable) { + var parts, t1; + if ($.get$_toStringVisiting().contains$1(0, iterable)) + return "(...)"; + $.get$_toStringVisiting().add$1(0, iterable); + parts = []; + try { + P._iterablePartsToStrings(iterable, parts); + } finally { + $.get$_toStringVisiting().remove$1(0, iterable); + } + t1 = P.StringBuffer$("("); + t1.writeAll$2(parts, ", "); + t1.write$1(")"); + return t1._contents; + }, + _iterablePartsToStrings: function(iterable, parts) { + var it, $length, count, next, ultimateString, penultimateString, penultimate, ultimate, ultimate0, elision; + it = iterable.get$iterator(iterable); + $length = 0; + count = 0; + while (true) { + if (!($length < 80 || count < 3)) + break; + if (!it.moveNext$0()) + return; + next = H.S(it.get$current()); + parts.push(next); + $length += next.length + 2; + ++count; + } + if (!it.moveNext$0()) { + if (count <= 5) + return; + if (0 >= parts.length) + return H.ioore(parts, 0); + ultimateString = parts.pop(); + if (0 >= parts.length) + return H.ioore(parts, 0); + penultimateString = parts.pop(); + } else { + penultimate = it.get$current(); + ++count; + if (!it.moveNext$0()) { + if (count <= 4) { + parts.push(H.S(penultimate)); + return; + } + ultimateString = H.S(penultimate); + if (0 >= parts.length) + return H.ioore(parts, 0); + penultimateString = parts.pop(); + $length += ultimateString.length + 2; + } else { + ultimate = it.get$current(); + ++count; + for (; it.moveNext$0(); penultimate = ultimate, ultimate = ultimate0) { + ultimate0 = it.get$current(); + ++count; + if (count > 100) { + while (true) { + if (!($length > 75 && count > 3)) + break; + if (0 >= parts.length) + return H.ioore(parts, 0); + $length -= parts.pop().length + 2; + --count; + } + parts.push("..."); + return; + } + } + penultimateString = H.S(penultimate); + ultimateString = H.S(ultimate); + $length += ultimateString.length + penultimateString.length + 4; + } + } + if (count > parts.length + 2) { + $length += 5; + elision = "..."; + } else + elision = null; + while (true) { + if (!($length > 80 && parts.length > 3)) + break; + if (0 >= parts.length) + return H.ioore(parts, 0); + $length -= parts.pop().length + 2; + if (elision == null) { + $length += 5; + elision = "..."; + } + } + if (elision != null) + parts.push(elision); + parts.push(penultimateString); + parts.push(ultimateString); + }, + LinkedHashMap_LinkedHashMap: function(equals, hashCode, isValidKey, $K, $V) { + return H.setRuntimeTypeInfo(new P._LinkedHashMap(0, null, null, null, null, null, 0), [$K, $V]); + }, + LinkedHashSet_LinkedHashSet: function(equals, hashCode, isValidKey, $E) { + return H.setRuntimeTypeInfo(new P._LinkedHashSet(0, null, null, null, null, null, 0), [$E]); + }, + Maps_mapToString: function(m) { + var t1, result, i, t2; + t1 = {}; + for (i = 0; t2 = $.get$Maps__toStringList(), i < t2.length; ++i) + if (t2[i] === m) + return "{...}"; + result = P.StringBuffer$(""); + try { + $.get$Maps__toStringList().push(m); + result.write$1("{"); + t1.first_0 = true; + J.forEach$1$ax(m, new P.Maps_mapToString_closure(t1, result)); + result.write$1("}"); + } finally { + t1 = $.get$Maps__toStringList(); + if (0 >= t1.length) + return H.ioore(t1, 0); + t1.pop(); + } + return result.get$_contents(); + }, + _HashMap: { + "^": "Object;_collection$_length,_strings,_nums,_rest,_keys", + get$length: function(_) { + return this._collection$_length; + }, + get$isEmpty: function(_) { + return this._collection$_length === 0; + }, + get$keys: function(_) { + return H.setRuntimeTypeInfo(new P.HashMapKeyIterable(this), [H.getTypeArgumentByIndex(this, 0)]); + }, + get$values: function(_) { + return H.MappedIterable_MappedIterable(H.setRuntimeTypeInfo(new P.HashMapKeyIterable(this), [H.getTypeArgumentByIndex(this, 0)]), new P._HashMap_values_closure(this), H.getTypeArgumentByIndex(this, 0), H.getTypeArgumentByIndex(this, 1)); + }, + $index: function(_, key) { + var strings, t1, entry, nums, rest, bucket, index; + if (typeof key === "string" && key !== "__proto__") { + strings = this._strings; + if (strings == null) + t1 = null; + else { + entry = strings[key]; + t1 = entry === strings ? null : entry; + } + return t1; + } else if (typeof key === "number" && (key & 0x3ffffff) === key) { + nums = this._nums; + if (nums == null) + t1 = null; + else { + entry = nums[key]; + t1 = entry === nums ? null : entry; + } + return t1; + } else { + rest = this._rest; + if (rest == null) + return; + bucket = rest[this._computeHashCode$1(key)]; + index = this._findBucketIndex$2(bucket, key); + return index < 0 ? null : bucket[index + 1]; + } + }, + $indexSet: function(_, key, value) { + var strings, nums, rest, hash, bucket, index; + if (typeof key === "string" && key !== "__proto__") { + strings = this._strings; + if (strings == null) { + strings = P._HashMap__newHashTable(); + this._strings = strings; + } + this._addHashTableEntry$3(strings, key, value); + } else if (typeof key === "number" && (key & 0x3ffffff) === key) { + nums = this._nums; + if (nums == null) { + nums = P._HashMap__newHashTable(); + this._nums = nums; + } + this._addHashTableEntry$3(nums, key, value); + } else { + rest = this._rest; + if (rest == null) { + rest = P._HashMap__newHashTable(); + this._rest = rest; + } + hash = this._computeHashCode$1(key); + bucket = rest[hash]; + if (bucket == null) { + P._HashMap__setTableEntry(rest, hash, [key, value]); + this._collection$_length = this._collection$_length + 1; + this._keys = null; + } else { + index = this._findBucketIndex$2(bucket, key); + if (index >= 0) + bucket[index + 1] = value; + else { + bucket.push(key, value); + this._collection$_length = this._collection$_length + 1; + this._keys = null; + } + } + } + }, + forEach$1: function(_, action) { + var keys, $length, i, key; + keys = this._computeKeys$0(); + for ($length = keys.length, i = 0; i < $length; ++i) { + key = keys[i]; + action.call$2(key, this.$index(0, key)); + if (keys !== this._keys) + throw H.wrapException(P.ConcurrentModificationError$(this)); + } + }, + _computeKeys$0: function() { + var t1, result, strings, names, entries, index, i, nums, rest, bucket, $length, i0; + t1 = this._keys; + if (t1 != null) + return t1; + result = Array(this._collection$_length); + result.fixed$length = init; + strings = this._strings; + if (strings != null) { + names = Object.getOwnPropertyNames(strings); + entries = names.length; + for (index = 0, i = 0; i < entries; ++i) { + result[index] = names[i]; + ++index; + } + } else + index = 0; + nums = this._nums; + if (nums != null) { + names = Object.getOwnPropertyNames(nums); + entries = names.length; + for (i = 0; i < entries; ++i) { + result[index] = +names[i]; + ++index; + } + } + rest = this._rest; + if (rest != null) { + names = Object.getOwnPropertyNames(rest); + entries = names.length; + for (i = 0; i < entries; ++i) { + bucket = rest[names[i]]; + $length = bucket.length; + for (i0 = 0; i0 < $length; i0 += 2) { + result[index] = bucket[i0]; + ++index; + } + } + } + this._keys = result; + return result; + }, + _addHashTableEntry$3: function(table, key, value) { + if (table[key] == null) { + this._collection$_length = this._collection$_length + 1; + this._keys = null; + } + P._HashMap__setTableEntry(table, key, value); + }, + _computeHashCode$1: function(key) { + return J.get$hashCode$(key) & 0x3ffffff; + }, + _findBucketIndex$2: function(bucket, key) { + var $length, i; + if (bucket == null) + return -1; + $length = bucket.length; + for (i = 0; i < $length; i += 2) + if (J.$eq(bucket[i], key)) + return i; + return -1; + }, + $isMap: true, + $asMap: null, + static: {_HashMap__setTableEntry: function(table, key, value) { + if (value == null) + table[key] = table; + else + table[key] = value; + }, _HashMap__newHashTable: function() { + var table = Object.create(null); + P._HashMap__setTableEntry(table, "", table); + delete table[""]; + return table; + }} + }, + _HashMap_values_closure: { + "^": "Closure:11;this_0", + call$1: function(each) { + return this.this_0.$index(0, each); + } + }, + HashMapKeyIterable: { + "^": "IterableBase;_map", + get$length: function(_) { + return this._map._collection$_length; + }, + get$isEmpty: function(_) { + return this._map._collection$_length === 0; + }, + get$iterator: function(_) { + var t1 = this._map; + return new P.HashMapKeyIterator(t1, t1._computeKeys$0(), 0, null); + }, + forEach$1: function(_, f) { + var t1, keys, $length, i; + t1 = this._map; + keys = t1._computeKeys$0(); + for ($length = keys.length, i = 0; i < $length; ++i) { + f.call$1(keys[i]); + if (keys !== t1._keys) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + } + }, + $isEfficientLength: true + }, + HashMapKeyIterator: { + "^": "Object;_map,_keys,_offset,_collection$_current", + get$current: function() { + return this._collection$_current; + }, + moveNext$0: function() { + var keys, offset, t1; + keys = this._keys; + offset = this._offset; + t1 = this._map; + if (keys !== t1._keys) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + else if (offset >= keys.length) { + this._collection$_current = null; + return false; + } else { + this._collection$_current = keys[offset]; + this._offset = offset + 1; + return true; + } + } + }, + _LinkedHashMap: { + "^": "Object;_collection$_length,_strings,_nums,_rest,_first,_last,_modifications", + get$length: function(_) { + return this._collection$_length; + }, + get$isEmpty: function(_) { + return this._collection$_length === 0; + }, + get$keys: function(_) { + return H.setRuntimeTypeInfo(new P.LinkedHashMapKeyIterable(this), [H.getTypeArgumentByIndex(this, 0)]); + }, + get$values: function(_) { + return H.MappedIterable_MappedIterable(H.setRuntimeTypeInfo(new P.LinkedHashMapKeyIterable(this), [H.getTypeArgumentByIndex(this, 0)]), new P._LinkedHashMap_values_closure(this), H.getTypeArgumentByIndex(this, 0), H.getTypeArgumentByIndex(this, 1)); + }, + containsKey$1: function(_, key) { + var nums, rest; + if ((key & 0x3ffffff) === key) { + nums = this._nums; + if (nums == null) + return false; + return nums[key] != null; + } else { + rest = this._rest; + if (rest == null) + return false; + return this._findBucketIndex$2(rest[this._computeHashCode$1(key)], key) >= 0; + } + }, + $index: function(_, key) { + var strings, cell, nums, rest, bucket, index; + if (typeof key === "string" && key !== "__proto__") { + strings = this._strings; + if (strings == null) + return; + cell = strings[key]; + return cell == null ? null : cell.get$_value(); + } else if (typeof key === "number" && (key & 0x3ffffff) === key) { + nums = this._nums; + if (nums == null) + return; + cell = nums[key]; + return cell == null ? null : cell.get$_value(); + } else { + rest = this._rest; + if (rest == null) + return; + bucket = rest[this._computeHashCode$1(key)]; + index = this._findBucketIndex$2(bucket, key); + if (index < 0) + return; + return bucket[index].get$_value(); + } + }, + $indexSet: function(_, key, value) { + var strings, nums, rest, hash, bucket, index; + if (typeof key === "string" && key !== "__proto__") { + strings = this._strings; + if (strings == null) { + strings = P._LinkedHashMap__newHashTable(); + this._strings = strings; + } + this._addHashTableEntry$3(strings, key, value); + } else if (typeof key === "number" && (key & 0x3ffffff) === key) { + nums = this._nums; + if (nums == null) { + nums = P._LinkedHashMap__newHashTable(); + this._nums = nums; + } + this._addHashTableEntry$3(nums, key, value); + } else { + rest = this._rest; + if (rest == null) { + rest = P._LinkedHashMap__newHashTable(); + this._rest = rest; + } + hash = this._computeHashCode$1(key); + bucket = rest[hash]; + if (bucket == null) + rest[hash] = [this._newLinkedCell$2(key, value)]; + else { + index = this._findBucketIndex$2(bucket, key); + if (index >= 0) + bucket[index].set$_value(value); + else + bucket.push(this._newLinkedCell$2(key, value)); + } + } + }, + remove$1: function(_, key) { + var rest, bucket, index, cell; + if (typeof key === "string" && key !== "__proto__") + return this._removeHashTableEntry$2(this._strings, key); + else if (typeof key === "number" && (key & 0x3ffffff) === key) + return this._removeHashTableEntry$2(this._nums, key); + else { + rest = this._rest; + if (rest == null) + return; + bucket = rest[this._computeHashCode$1(key)]; + index = this._findBucketIndex$2(bucket, key); + if (index < 0) + return; + cell = bucket.splice(index, 1)[0]; + this._unlinkCell$1(cell); + return cell.get$_value(); + } + }, + forEach$1: function(_, action) { + var cell, modifications; + cell = this._first; + modifications = this._modifications; + for (; cell != null;) { + action.call$2(cell.get$_key(cell), cell._value); + if (modifications !== this._modifications) + throw H.wrapException(P.ConcurrentModificationError$(this)); + cell = cell._next; + } + }, + _addHashTableEntry$3: function(table, key, value) { + var cell = table[key]; + if (cell == null) + table[key] = this._newLinkedCell$2(key, value); + else + cell.set$_value(value); + }, + _removeHashTableEntry$2: function(table, key) { + var cell; + if (table == null) + return; + cell = table[key]; + if (cell == null) + return; + this._unlinkCell$1(cell); + delete table[key]; + return cell.get$_value(); + }, + _newLinkedCell$2: function(key, value) { + var cell, last; + cell = new P.LinkedHashMapCell(key, value, null, null); + if (this._first == null) { + this._last = cell; + this._first = cell; + } else { + last = this._last; + cell._previous = last; + last.set$_next(cell); + this._last = cell; + } + this._collection$_length = this._collection$_length + 1; + this._modifications = this._modifications + 1 & 67108863; + return cell; + }, + _unlinkCell$1: function(cell) { + var previous, next; + previous = cell.get$_previous(); + next = cell.get$_next(); + if (previous == null) + this._first = next; + else + previous.set$_next(next); + if (next == null) + this._last = previous; + else + next.set$_previous(previous); + this._collection$_length = this._collection$_length - 1; + this._modifications = this._modifications + 1 & 67108863; + }, + _computeHashCode$1: function(key) { + return J.get$hashCode$(key) & 0x3ffffff; + }, + _findBucketIndex$2: function(bucket, key) { + var $length, i; + if (bucket == null) + return -1; + $length = bucket.length; + for (i = 0; i < $length; ++i) + if (J.$eq(J.get$_key$x(bucket[i]), key)) + return i; + return -1; + }, + toString$0: function(_) { + return P.Maps_mapToString(this); + }, + $isMap: true, + $asMap: null, + static: {_LinkedHashMap__newHashTable: function() { + var table = Object.create(null); + table[""] = table; + delete table[""]; + return table; + }} + }, + _LinkedHashMap_values_closure: { + "^": "Closure:11;this_0", + call$1: function(each) { + return this.this_0.$index(0, each); + } + }, + LinkedHashMapCell: { + "^": "Object;_key>,_value@,_next@,_previous@" + }, + LinkedHashMapKeyIterable: { + "^": "IterableBase;_map", + get$length: function(_) { + return this._map._collection$_length; + }, + get$isEmpty: function(_) { + return this._map._collection$_length === 0; + }, + get$iterator: function(_) { + var t1, t2; + t1 = this._map; + t2 = new P.LinkedHashMapKeyIterator(t1, t1._modifications, null, null); + t2._cell = t1._first; + return t2; + }, + forEach$1: function(_, f) { + var t1, cell, modifications; + t1 = this._map; + cell = t1._first; + modifications = t1._modifications; + for (; cell != null;) { + f.call$1(cell.get$_key(cell)); + if (modifications !== t1._modifications) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + cell = cell._next; + } + }, + $isEfficientLength: true + }, + LinkedHashMapKeyIterator: { + "^": "Object;_map,_modifications,_cell,_collection$_current", + get$current: function() { + return this._collection$_current; + }, + moveNext$0: function() { + var t1 = this._map; + if (this._modifications !== t1._modifications) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + else { + t1 = this._cell; + if (t1 == null) { + this._collection$_current = null; + return false; + } else { + this._collection$_current = t1.get$_key(t1); + this._cell = t1._next; + return true; + } + } + } + }, + _HashSet: { + "^": "_HashSetBase;", + get$iterator: function(_) { + return new P.HashSetIterator(this, this._computeElements$0(), 0, null); + }, + get$length: function(_) { + return this._collection$_length; + }, + get$isEmpty: function(_) { + return this._collection$_length === 0; + }, + contains$1: function(_, object) { + var strings, nums, rest; + if (typeof object === "string" && object !== "__proto__") { + strings = this._strings; + return strings == null ? false : strings[object] != null; + } else if (typeof object === "number" && (object & 0x3ffffff) === object) { + nums = this._nums; + return nums == null ? false : nums[object] != null; + } else { + rest = this._rest; + if (rest == null) + return false; + return this._findBucketIndex$2(rest[this._computeHashCode$1(object)], object) >= 0; + } + }, + lookup$1: function(object) { + var t1, rest, bucket, index; + if (!(typeof object === "string" && object !== "__proto__")) + t1 = typeof object === "number" && (object & 0x3ffffff) === object; + else + t1 = true; + if (t1) + return this.contains$1(0, object) ? object : null; + rest = this._rest; + if (rest == null) + return; + bucket = rest[this._computeHashCode$1(object)]; + index = this._findBucketIndex$2(bucket, object); + if (index < 0) + return; + return J.$index$asx(bucket, index); + }, + add$1: function(_, element) { + var rest, table, hash, bucket; + rest = this._rest; + if (rest == null) { + table = Object.create(null); + table[""] = table; + delete table[""]; + this._rest = table; + rest = table; + } + hash = this._computeHashCode$1(element); + bucket = rest[hash]; + if (bucket == null) + rest[hash] = [element]; + else { + if (this._findBucketIndex$2(bucket, element) >= 0) + return false; + bucket.push(element); + } + this._collection$_length = this._collection$_length + 1; + this._elements = null; + return true; + }, + remove$1: function(_, object) { + var rest, bucket, index; + rest = this._rest; + if (rest == null) + return false; + bucket = rest[this._computeHashCode$1(object)]; + index = this._findBucketIndex$2(bucket, object); + if (index < 0) + return false; + this._collection$_length = this._collection$_length - 1; + this._elements = null; + bucket.splice(index, 1); + return true; + }, + _computeElements$0: function() { + var t1, result, strings, names, entries, index, i, nums, rest, bucket, $length, i0; + t1 = this._elements; + if (t1 != null) + return t1; + result = Array(this._collection$_length); + result.fixed$length = init; + strings = this._strings; + if (strings != null) { + names = Object.getOwnPropertyNames(strings); + entries = names.length; + for (index = 0, i = 0; i < entries; ++i) { + result[index] = names[i]; + ++index; + } + } else + index = 0; + nums = this._nums; + if (nums != null) { + names = Object.getOwnPropertyNames(nums); + entries = names.length; + for (i = 0; i < entries; ++i) { + result[index] = +names[i]; + ++index; + } + } + rest = this._rest; + if (rest != null) { + names = Object.getOwnPropertyNames(rest); + entries = names.length; + for (i = 0; i < entries; ++i) { + bucket = rest[names[i]]; + $length = bucket.length; + for (i0 = 0; i0 < $length; ++i0) { + result[index] = bucket[i0]; + ++index; + } + } + } + this._elements = result; + return result; + }, + _computeHashCode$1: function(element) { + return J.get$hashCode$(element) & 0x3ffffff; + }, + _findBucketIndex$2: function(bucket, element) { + var $length, i; + if (bucket == null) + return -1; + $length = bucket.length; + for (i = 0; i < $length; ++i) + if (J.$eq(bucket[i], element)) + return i; + return -1; + }, + $isEfficientLength: true + }, + _IdentityHashSet: { + "^": "_HashSet;_collection$_length,_strings,_nums,_rest,_elements", + _computeHashCode$1: function(key) { + return H.objectHashCode(key) & 0x3ffffff; + }, + _findBucketIndex$2: function(bucket, element) { + var $length, i, t1; + if (bucket == null) + return -1; + $length = bucket.length; + for (i = 0; i < $length; ++i) { + t1 = bucket[i]; + if (t1 == null ? element == null : t1 === element) + return i; + } + return -1; + } + }, + HashSetIterator: { + "^": "Object;_set,_elements,_offset,_collection$_current", + get$current: function() { + return this._collection$_current; + }, + moveNext$0: function() { + var elements, offset, t1; + elements = this._elements; + offset = this._offset; + t1 = this._set; + if (elements !== t1._elements) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + else if (offset >= elements.length) { + this._collection$_current = null; + return false; + } else { + this._collection$_current = elements[offset]; + this._offset = offset + 1; + return true; + } + } + }, + _LinkedHashSet: { + "^": "_HashSetBase;_collection$_length,_strings,_nums,_rest,_first,_last,_modifications", + get$iterator: function(_) { + var t1 = new P.LinkedHashSetIterator(this, this._modifications, null, null); + t1._cell = this._first; + return t1; + }, + get$length: function(_) { + return this._collection$_length; + }, + get$isEmpty: function(_) { + return this._collection$_length === 0; + }, + contains$1: function(_, object) { + var strings, nums, rest; + if (typeof object === "string" && object !== "__proto__") { + strings = this._strings; + if (strings == null) + return false; + return strings[object] != null; + } else if (typeof object === "number" && (object & 0x3ffffff) === object) { + nums = this._nums; + if (nums == null) + return false; + return nums[object] != null; + } else { + rest = this._rest; + if (rest == null) + return false; + return this._findBucketIndex$2(rest[this._computeHashCode$1(object)], object) >= 0; + } + }, + lookup$1: function(object) { + var t1, rest, bucket, index; + if (!(typeof object === "string" && object !== "__proto__")) + t1 = typeof object === "number" && (object & 0x3ffffff) === object; + else + t1 = true; + if (t1) + return this.contains$1(0, object) ? object : null; + else { + rest = this._rest; + if (rest == null) + return; + bucket = rest[this._computeHashCode$1(object)]; + index = this._findBucketIndex$2(bucket, object); + if (index < 0) + return; + return J.$index$asx(bucket, index).get$_collection$_element(); + } + }, + forEach$1: function(_, action) { + var cell, modifications; + cell = this._first; + modifications = this._modifications; + for (; cell != null;) { + action.call$1(cell.get$_collection$_element()); + if (modifications !== this._modifications) + throw H.wrapException(P.ConcurrentModificationError$(this)); + cell = cell._next; + } + }, + add$1: function(_, element) { + var strings, table, nums, rest, hash, bucket; + if (typeof element === "string" && element !== "__proto__") { + strings = this._strings; + if (strings == null) { + table = Object.create(null); + table[""] = table; + delete table[""]; + this._strings = table; + strings = table; + } + return this._addHashTableEntry$2(strings, element); + } else if (typeof element === "number" && (element & 0x3ffffff) === element) { + nums = this._nums; + if (nums == null) { + table = Object.create(null); + table[""] = table; + delete table[""]; + this._nums = table; + nums = table; + } + return this._addHashTableEntry$2(nums, element); + } else { + rest = this._rest; + if (rest == null) { + table = Object.create(null); + table[""] = table; + delete table[""]; + this._rest = table; + rest = table; + } + hash = this._computeHashCode$1(element); + bucket = rest[hash]; + if (bucket == null) + rest[hash] = [this._newLinkedCell$1(element)]; + else { + if (this._findBucketIndex$2(bucket, element) >= 0) + return false; + bucket.push(this._newLinkedCell$1(element)); + } + return true; + } + }, + addAll$1: function(_, objects) { + var t1; + for (t1 = J.get$iterator$ax(objects); t1.moveNext$0();) + this.add$1(0, t1._current); + }, + remove$1: function(_, object) { + var rest, bucket, index; + if (typeof object === "string" && object !== "__proto__") + return this._removeHashTableEntry$2(this._strings, object); + else if (typeof object === "number" && (object & 0x3ffffff) === object) + return this._removeHashTableEntry$2(this._nums, object); + else { + rest = this._rest; + if (rest == null) + return false; + bucket = rest[this._computeHashCode$1(object)]; + index = this._findBucketIndex$2(bucket, object); + if (index < 0) + return false; + this._unlinkCell$1(bucket.splice(index, 1)[0]); + return true; + } + }, + _addHashTableEntry$2: function(table, element) { + if (table[element] != null) + return false; + table[element] = this._newLinkedCell$1(element); + return true; + }, + _removeHashTableEntry$2: function(table, element) { + var cell; + if (table == null) + return false; + cell = table[element]; + if (cell == null) + return false; + this._unlinkCell$1(cell); + delete table[element]; + return true; + }, + _newLinkedCell$1: function(element) { + var cell, last; + cell = new P.LinkedHashSetCell(element, null, null); + if (this._first == null) { + this._last = cell; + this._first = cell; + } else { + last = this._last; + cell._previous = last; + last.set$_next(cell); + this._last = cell; + } + this._collection$_length = this._collection$_length + 1; + this._modifications = this._modifications + 1 & 67108863; + return cell; + }, + _unlinkCell$1: function(cell) { + var previous, next; + previous = cell.get$_previous(); + next = cell.get$_next(); + if (previous == null) + this._first = next; + else + previous.set$_next(next); + if (next == null) + this._last = previous; + else + next.set$_previous(previous); + this._collection$_length = this._collection$_length - 1; + this._modifications = this._modifications + 1 & 67108863; + }, + _computeHashCode$1: function(element) { + return J.get$hashCode$(element) & 0x3ffffff; + }, + _findBucketIndex$2: function(bucket, element) { + var $length, i; + if (bucket == null) + return -1; + $length = bucket.length; + for (i = 0; i < $length; ++i) + if (J.$eq(bucket[i].get$_collection$_element(), element)) + return i; + return -1; + }, + $isEfficientLength: true + }, + LinkedHashSetCell: { + "^": "Object;_collection$_element<,_next@,_previous@" + }, + LinkedHashSetIterator: { + "^": "Object;_set,_modifications,_cell,_collection$_current", + get$current: function() { + return this._collection$_current; + }, + moveNext$0: function() { + var t1 = this._set; + if (this._modifications !== t1._modifications) + throw H.wrapException(P.ConcurrentModificationError$(t1)); + else { + t1 = this._cell; + if (t1 == null) { + this._collection$_current = null; + return false; + } else { + this._collection$_current = t1.get$_collection$_element(); + this._cell = t1._next; + return true; + } + } + } + }, + _HashSetBase: { + "^": "IterableBase;", + toString$0: function(_) { + return H.IterableMixinWorkaround_toStringIterable(this, "{", "}"); + }, + $isEfficientLength: true + }, + IterableBase: { + "^": "Object;", + forEach$1: function(_, f) { + var t1; + for (t1 = this.get$iterator(this); t1.moveNext$0();) + f.call$1(t1.get$current()); + }, + toList$1$growable: function(_, growable) { + return P.List_List$from(this, growable, H.getRuntimeTypeArgument(this, "IterableBase", 0)); + }, + toList$0: function($receiver) { + return this.toList$1$growable($receiver, true); + }, + get$length: function(_) { + var it, count; + it = this.get$iterator(this); + for (count = 0; it.moveNext$0();) + ++count; + return count; + }, + get$isEmpty: function(_) { + return !this.get$iterator(this).moveNext$0(); + }, + get$single: function(_) { + var it, result; + it = this.get$iterator(this); + if (!it.moveNext$0()) + throw H.wrapException(P.StateError$("No elements")); + result = it.get$current(); + if (it.moveNext$0()) + throw H.wrapException(P.StateError$("More than one element")); + return result; + }, + elementAt$1: function(_, index) { + var t1, remaining, element; + if (index < 0) + throw H.wrapException(P.RangeError$value(index)); + for (t1 = this.get$iterator(this), remaining = index; t1.moveNext$0();) { + element = t1.get$current(); + if (remaining === 0) + return element; + --remaining; + } + throw H.wrapException(P.RangeError$value(index)); + }, + toString$0: function(_) { + return P._iterableToString(this); + } + }, + ListBase: { + "^": "Object+ListMixin;", + $isList: true, + $asList: null, + $isEfficientLength: true + }, + ListMixin: { + "^": "Object;", + get$iterator: function(receiver) { + return new H.ListIterator(receiver, this.get$length(receiver), 0, null); + }, + elementAt$1: function(receiver, index) { + return this.$index(receiver, index); + }, + forEach$1: function(receiver, action) { + var $length, i; + $length = this.get$length(receiver); + for (i = 0; i < $length; ++i) { + action.call$1(this.$index(receiver, i)); + if ($length !== this.get$length(receiver)) + throw H.wrapException(P.ConcurrentModificationError$(receiver)); + } + }, + get$isEmpty: function(receiver) { + return this.get$length(receiver) === 0; + }, + where$1: function(receiver, test) { + return H.setRuntimeTypeInfo(new H.WhereIterable(receiver, test), [H.getRuntimeTypeArgument(receiver, "ListMixin", 0)]); + }, + toString$0: function(receiver) { + var result; + if ($.get$_toStringVisiting().contains$1(0, receiver)) + return "[...]"; + result = P.StringBuffer$(""); + try { + $.get$_toStringVisiting().add$1(0, receiver); + result.write$1("["); + result.writeAll$2(receiver, ", "); + result.write$1("]"); + } finally { + $.get$_toStringVisiting().remove$1(0, receiver); + } + return result.get$_contents(); + }, + $isList: true, + $asList: null, + $isEfficientLength: true + }, + Maps_mapToString_closure: { + "^": "Closure:10;box_0,result_1", + call$2: function(k, v) { + var t1 = this.box_0; + if (!t1.first_0) + this.result_1.write$1(", "); + t1.first_0 = false; + t1 = this.result_1; + t1.write$1(k); + t1.write$1(": "); + t1.write$1(v); + } + }, + ListQueue: { + "^": "IterableBase;_table,_head,_tail,_modificationCount", + get$iterator: function(_) { + return new P._ListQueueIterator(this, this._tail, this._modificationCount, this._head, null); + }, + forEach$1: function(_, action) { + var modificationCount, i, t1; + modificationCount = this._modificationCount; + for (i = this._head; i !== this._tail; i = (i + 1 & this._table.length - 1) >>> 0) { + t1 = this._table; + if (i < 0 || i >= t1.length) + return H.ioore(t1, i); + action.call$1(t1[i]); + if (modificationCount !== this._modificationCount) + H.throwExpression(P.ConcurrentModificationError$(this)); + } + }, + get$isEmpty: function(_) { + return this._head === this._tail; + }, + get$length: function(_) { + return (this._tail - this._head & this._table.length - 1) >>> 0; + }, + toString$0: function(_) { + return H.IterableMixinWorkaround_toStringIterable(this, "{", "}"); + }, + _add$1: function(element) { + var t1, t2, t3; + t1 = this._table; + t2 = this._tail; + t3 = t1.length; + if (t2 < 0 || t2 >= t3) + return H.ioore(t1, t2); + t1[t2] = element; + t3 = (t2 + 1 & t3 - 1) >>> 0; + this._tail = t3; + if (this._head === t3) + this._grow$0(); + this._modificationCount = this._modificationCount + 1; + }, + _grow$0: function() { + var t1, newTable, t2, split; + t1 = Array(this._table.length * 2); + t1.fixed$length = init; + newTable = H.setRuntimeTypeInfo(t1, [H.getTypeArgumentByIndex(this, 0)]); + t1 = this._table; + t2 = this._head; + split = t1.length - t2; + H.IterableMixinWorkaround_setRangeList(newTable, 0, split, t1, t2); + t1 = this._head; + t2 = this._table; + H.IterableMixinWorkaround_setRangeList(newTable, split, split + t1, t2, 0); + this._head = 0; + this._tail = this._table.length; + this._table = newTable; + }, + ListQueue$1: function(initialCapacity, $E) { + var t1 = Array(8); + t1.fixed$length = init; + this._table = H.setRuntimeTypeInfo(t1, [$E]); + }, + $isEfficientLength: true, + static: {"^": "ListQueue__INITIAL_CAPACITY"} + }, + _ListQueueIterator: { + "^": "Object;_queue,_end,_modificationCount,_collection$_position,_collection$_current", + get$current: function() { + return this._collection$_current; + }, + moveNext$0: function() { + var t1, t2, t3; + t1 = this._queue; + if (this._modificationCount !== t1._modificationCount) + H.throwExpression(P.ConcurrentModificationError$(t1)); + t2 = this._collection$_position; + if (t2 === this._end) { + this._collection$_current = null; + return false; + } + t1 = t1._table; + t3 = t1.length; + if (t2 >= t3) + return H.ioore(t1, t2); + this._collection$_current = t1[t2]; + this._collection$_position = (t2 + 1 & t3 - 1) >>> 0; + return true; + } + } +}], +["dart.convert", "dart:convert", , P, { + "^": "", + _convertJsonToDart: function(json, reviver) { + var revive = new P._convertJsonToDart_closure(); + return revive.call$2(null, new P._convertJsonToDart_walk(revive).call$1(json)); + }, + _parseJson: function(source, reviver) { + var parsed, e, t1, exception; + t1 = source; + if (typeof t1 !== "string") + throw H.wrapException(new P.ArgumentError(source)); + parsed = null; + try { + parsed = JSON.parse(source); + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + throw H.wrapException(P.FormatException$(String(e))); + } + + return P._convertJsonToDart(parsed, reviver); + }, + _defaultToEncodable: [function(object) { + return object.toJson$0(); + }, "call$1", "_defaultToEncodable$closure", 2, 0, 5], + _convertJsonToDart_closure: { + "^": "Closure:10;", + call$2: function(key, value) { + return value; + } + }, + _convertJsonToDart_walk: { + "^": "Closure:11;revive_0", + call$1: function(e) { + var list, t1, i, keys, map, key, proto; + if (e == null || typeof e != "object") + return e; + if (Object.getPrototypeOf(e) === Array.prototype) { + list = e; + for (t1 = this.revive_0, i = 0; i < list.length; ++i) + list[i] = t1.call$2(i, this.call$1(list[i])); + return list; + } + keys = Object.keys(e); + map = H.fillLiteralMap([], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null)); + for (t1 = this.revive_0, i = 0; i < keys.length; ++i) { + key = keys[i]; + map.$indexSet(0, key, t1.call$2(key, this.call$1(e[key]))); + } + proto = e.__proto__; + if (typeof proto !== "undefined" && proto !== Object.prototype) + map.$indexSet(0, "__proto__", t1.call$2("__proto__", this.call$1(proto))); + return map; + } + }, + Codec: { + "^": "Object;" + }, + Converter: { + "^": "Object;" + }, + JsonUnsupportedObjectError: { + "^": "Error;unsupportedObject,cause", + toString$0: function(_) { + if (this.cause != null) + return "Converting object to an encodable object failed."; + else + return "Converting object did not return an encodable object."; + }, + static: {JsonUnsupportedObjectError$: function(unsupportedObject, cause) { + return new P.JsonUnsupportedObjectError(unsupportedObject, cause); + }} + }, + JsonCyclicError: { + "^": "JsonUnsupportedObjectError;unsupportedObject,cause", + toString$0: function(_) { + return "Cyclic error in JSON stringify"; + }, + static: {JsonCyclicError$: function(object) { + return new P.JsonCyclicError(object, null); + }} + }, + JsonCodec: { + "^": "Codec;_reviver,_toEncodable", + decode$2$reviver: function(source, reviver) { + return P._parseJson(source, this.get$decoder()._reviver); + }, + decode$1: function(source) { + return this.decode$2$reviver(source, null); + }, + encode$2$toEncodable: function(value, toEncodable) { + return P._JsonStringifier_stringify(value, this.get$encoder()._toEncodableFunction); + }, + encode$1: function(value) { + return this.encode$2$toEncodable(value, null); + }, + get$encoder: function() { + return C.JsonEncoder_null; + }, + get$decoder: function() { + return C.JsonDecoder_null; + } + }, + JsonEncoder: { + "^": "Converter;_toEncodableFunction" + }, + JsonDecoder: { + "^": "Converter;_reviver" + }, + _JsonStringifier: { + "^": "Object;_toEncodable,_sink,_seen", + _toEncodable$1: function(arg0) { + return this._toEncodable.call$1(arg0); + }, + escape$1: function(s) { + var t1, $length, t2, offset, i, charCode, t3, charCodes, str; + t1 = J.getInterceptor$asx(s); + $length = t1.get$length(s); + if (typeof $length !== "number") + return H.iae($length); + t2 = this._sink; + offset = 0; + i = 0; + for (; i < $length; ++i) { + charCode = t1.codeUnitAt$1(s, i); + if (charCode > 92) + continue; + if (charCode < 32) { + if (i > offset) { + t3 = C.JSString_methods.substring$2(s, offset, i); + t2._contents = t2._contents + t3; + } + offset = i + 1; + charCodes = P.List_List$filled(1, 92, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + switch (charCode) { + case 8: + charCodes = P.List_List$filled(1, 98, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + case 9: + charCodes = P.List_List$filled(1, 116, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + case 10: + charCodes = P.List_List$filled(1, 110, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + case 12: + charCodes = P.List_List$filled(1, 102, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + case 13: + charCodes = P.List_List$filled(1, 114, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + default: + charCodes = P.List_List$filled(1, 117, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + charCodes = P.List_List$filled(1, 48, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + charCodes = P.List_List$filled(1, 48, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + t3 = charCode >>> 4 & 15; + t3 = t3 < 10 ? 48 + t3 : 87 + t3; + charCodes = P.List_List$filled(1, t3, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + t3 = charCode & 15; + t3 = t3 < 10 ? 48 + t3 : 87 + t3; + charCodes = P.List_List$filled(1, t3, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + break; + } + } else if (charCode === 34 || charCode === 92) { + if (i > offset) { + t3 = C.JSString_methods.substring$2(s, offset, i); + t2._contents = t2._contents + t3; + } + offset = i + 1; + charCodes = P.List_List$filled(1, 92, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + charCodes = P.List_List$filled(1, charCode, J.JSInt); + t3 = H.Primitives_stringFromCharCodes(charCodes); + t2._contents = t2._contents + t3; + } + } + if (offset === 0) { + str = typeof s === "string" ? s : H.S(s); + t2._contents = t2._contents + str; + } else if (offset < $length) { + t1 = t1.substring$2(s, offset, $length); + t2._contents = t2._contents + t1; + } + }, + checkCycle$1: function(object) { + var t1, t2, i, t3; + for (t1 = this._seen, t2 = t1.length, i = 0; i < t2; ++i) { + t3 = t1[i]; + if (object == null ? t3 == null : object === t3) + throw H.wrapException(P.JsonCyclicError$(object)); + } + t1.push(object); + }, + stringifyValue$1: function(object) { + var customJson, e, t1, exception; + if (!this.stringifyJsonValue$1(object)) { + this.checkCycle$1(object); + try { + customJson = this._toEncodable$1(object); + if (!this.stringifyJsonValue$1(customJson)) { + t1 = P.JsonUnsupportedObjectError$(object, null); + throw H.wrapException(t1); + } + t1 = this._seen; + if (0 >= t1.length) + return H.ioore(t1, 0); + t1.pop(); + } catch (exception) { + t1 = H.unwrapException(exception); + e = t1; + throw H.wrapException(P.JsonUnsupportedObjectError$(object, e)); + } + + } + }, + stringifyJsonValue$1: function(object) { + var t1, t2, i, t3, separator, key; + if (typeof object === "number") { + if (!C.JSNumber_methods.get$isFinite(object)) + return false; + this._sink.write$1(C.JSNumber_methods.toString$0(object)); + return true; + } else if (object === true) { + this._sink.write$1("true"); + return true; + } else if (object === false) { + this._sink.write$1("false"); + return true; + } else if (object == null) { + this._sink.write$1("null"); + return true; + } else if (typeof object === "string") { + t1 = this._sink; + t1.write$1("\""); + this.escape$1(object); + t1.write$1("\""); + return true; + } else { + t1 = J.getInterceptor(object); + if (!!t1.$isList) { + this.checkCycle$1(object); + t2 = this._sink; + t2.write$1("["); + if (t1.get$length(object) > 0) { + this.stringifyValue$1(t1.$index(object, 0)); + for (i = 1; i < t1.get$length(object); ++i) { + t2._contents = t2._contents + ","; + this.stringifyValue$1(t1.$index(object, i)); + } + } + t2.write$1("]"); + this._removeSeen$1(object); + return true; + } else if (!!t1.$isMap) { + this.checkCycle$1(object); + t2 = this._sink; + t2.write$1("{"); + for (t3 = J.get$iterator$ax(t1.get$keys(object)), separator = "\""; t3.moveNext$0(); separator = ",\"") { + key = t3.get$current(); + t2._contents = t2._contents + separator; + this.escape$1(key); + t2._contents = t2._contents + "\":"; + this.stringifyValue$1(t1.$index(object, key)); + } + t2.write$1("}"); + this._removeSeen$1(object); + return true; + } else + return false; + } + }, + _removeSeen$1: function(object) { + var t1 = this._seen; + if (0 >= t1.length) + return H.ioore(t1, 0); + t1.pop(); + }, + static: {"^": "_JsonStringifier_BACKSPACE,_JsonStringifier_TAB,_JsonStringifier_NEWLINE,_JsonStringifier_CARRIAGE_RETURN,_JsonStringifier_FORM_FEED,_JsonStringifier_QUOTE,_JsonStringifier_CHAR_0,_JsonStringifier_BACKSLASH,_JsonStringifier_CHAR_b,_JsonStringifier_CHAR_f,_JsonStringifier_CHAR_n,_JsonStringifier_CHAR_r,_JsonStringifier_CHAR_t,_JsonStringifier_CHAR_u", _JsonStringifier_stringify: function(object, toEncodable) { + var output; + toEncodable = P._defaultToEncodable$closure(); + output = P.StringBuffer$(""); + new P._JsonStringifier(toEncodable, output, []).stringifyValue$1(object); + return output._contents; + }} + } +}], +["dart.core", "dart:core", , P, { + "^": "", + _symbolToString: function(symbol) { + return H.Symbol_getName(symbol); + }, + Error_safeToString: function(object) { + var buffer, t1, i, t2, codeUnit, charCodes; + if (typeof object === "number" || typeof object === "boolean" || null == object) + return J.toString$0(object); + if (typeof object === "string") { + buffer = new P.StringBuffer(""); + buffer._contents = "\""; + for (t1 = object.length, i = 0, t2 = "\""; i < t1; ++i) { + codeUnit = C.JSString_methods.codeUnitAt$1(object, i); + if (codeUnit <= 31) + if (codeUnit === 10) { + t2 = buffer._contents + "\\n"; + buffer._contents = t2; + } else if (codeUnit === 13) { + t2 = buffer._contents + "\\r"; + buffer._contents = t2; + } else if (codeUnit === 9) { + t2 = buffer._contents + "\\t"; + buffer._contents = t2; + } else { + t2 = buffer._contents + "\\x"; + buffer._contents = t2; + if (codeUnit < 16) + buffer._contents = t2 + "0"; + else { + buffer._contents = t2 + "1"; + codeUnit -= 16; + } + t2 = codeUnit < 10 ? 48 + codeUnit : 87 + codeUnit; + charCodes = P.List_List$filled(1, t2, J.JSInt); + t2 = H.Primitives_stringFromCharCodes(charCodes); + t2 = buffer._contents + t2; + buffer._contents = t2; + } + else if (codeUnit === 92) { + t2 = buffer._contents + "\\\\"; + buffer._contents = t2; + } else if (codeUnit === 34) { + t2 = buffer._contents + "\\\""; + buffer._contents = t2; + } else { + charCodes = P.List_List$filled(1, codeUnit, J.JSInt); + t2 = H.Primitives_stringFromCharCodes(charCodes); + t2 = buffer._contents + t2; + buffer._contents = t2; + } + } + t1 = t2 + "\""; + buffer._contents = t1; + return t1; + } + return "Instance of '" + H.Primitives_objectTypeName(object) + "'"; + }, + Exception_Exception: function(message) { + return new P._ExceptionImplementation(message); + }, + identical: [function(a, b) { + return a == null ? b == null : a === b; + }, "call$2", "identical$closure", 4, 0, 6], + identityHashCode: [function(object) { + return H.objectHashCode(object); + }, "call$1", "identityHashCode$closure", 2, 0, 7], + List_List$filled: function($length, fill, $E) { + var result, t1, i; + result = J.JSArray_JSArray$fixed($length, $E); + if ($length !== 0 && true) + for (t1 = result.length, i = 0; i < t1; ++i) + result[i] = fill; + return result; + }, + List_List$from: function(other, growable, $E) { + var list, t1; + list = H.setRuntimeTypeInfo([], [$E]); + for (t1 = J.get$iterator$ax(other); t1.moveNext$0();) + list.push(t1.get$current()); + if (growable) + return list; + list.fixed$length = init; + return list; + }, + print: function(object) { + var line = H.S(object); + H.printString(line); + }, + NoSuchMethodError_toString_closure: { + "^": "Closure:18;box_0", + call$2: function(key, value) { + var t1 = this.box_0; + if (t1.i_1 > 0) + t1.sb_0.write$1(", "); + t1.sb_0.write$1(P._symbolToString(key)); + } + }, + DateTime: { + "^": "Object;millisecondsSinceEpoch,isUtc", + $eq: function(_, other) { + if (other == null) + return false; + if (!J.getInterceptor(other).$isDateTime) + return false; + return this.millisecondsSinceEpoch === other.millisecondsSinceEpoch && this.isUtc === other.isUtc; + }, + get$hashCode: function(_) { + return this.millisecondsSinceEpoch; + }, + toString$0: function(_) { + var t1, y, m, d, h, min, sec, ms; + t1 = this.isUtc; + y = P.DateTime__fourDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCFullYear() + 0 : H.Primitives_lazyAsJsDate(this).getFullYear() + 0); + m = P.DateTime__twoDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCMonth() + 1 : H.Primitives_lazyAsJsDate(this).getMonth() + 1); + d = P.DateTime__twoDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCDate() + 0 : H.Primitives_lazyAsJsDate(this).getDate() + 0); + h = P.DateTime__twoDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCHours() + 0 : H.Primitives_lazyAsJsDate(this).getHours() + 0); + min = P.DateTime__twoDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCMinutes() + 0 : H.Primitives_lazyAsJsDate(this).getMinutes() + 0); + sec = P.DateTime__twoDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCSeconds() + 0 : H.Primitives_lazyAsJsDate(this).getSeconds() + 0); + ms = P.DateTime__threeDigits(t1 ? H.Primitives_lazyAsJsDate(this).getUTCMilliseconds() + 0 : H.Primitives_lazyAsJsDate(this).getMilliseconds() + 0); + if (t1) + return y + "-" + m + "-" + d + " " + h + ":" + min + ":" + sec + "." + ms + "Z"; + else + return y + "-" + m + "-" + d + " " + h + ":" + min + ":" + sec + "." + ms; + }, + DateTime$fromMillisecondsSinceEpoch$2$isUtc: function(millisecondsSinceEpoch, isUtc) { + if (Math.abs(millisecondsSinceEpoch) > 8640000000000000) + throw H.wrapException(new P.ArgumentError(millisecondsSinceEpoch)); + }, + DateTime$_now$0: function() { + H.Primitives_lazyAsJsDate(this); + }, + $isDateTime: true, + static: {"^": "DateTime_MONDAY,DateTime_TUESDAY,DateTime_WEDNESDAY,DateTime_THURSDAY,DateTime_FRIDAY,DateTime_SATURDAY,DateTime_SUNDAY,DateTime_DAYS_PER_WEEK,DateTime_JANUARY,DateTime_FEBRUARY,DateTime_MARCH,DateTime_APRIL,DateTime_MAY,DateTime_JUNE,DateTime_JULY,DateTime_AUGUST,DateTime_SEPTEMBER,DateTime_OCTOBER,DateTime_NOVEMBER,DateTime_DECEMBER,DateTime_MONTHS_PER_YEAR,DateTime__MAX_MILLISECONDS_SINCE_EPOCH", DateTime_parse: function(formattedString) { + var match, t1, t2, years, month, day, hour, minute, second, millisecond, addOneMillisecond, t3, sign, hourDifference, minuteDifference, isUtc, millisecondsSinceEpoch; + match = new H.JSSyntaxRegExp(H.JSSyntaxRegExp_makeNative("^([+-]?\\d{4,5})-?(\\d\\d)-?(\\d\\d)(?:[ T](\\d\\d)(?::?(\\d\\d)(?::?(\\d\\d)(.\\d{1,6})?)?)?( ?[zZ]| ?([-+])(\\d\\d)(?::?(\\d\\d))?)?)?$", false, true, false), null, null).firstMatch$1(formattedString); + if (match != null) { + t1 = new P.DateTime_parse_parseIntOrZero(); + t2 = match._match; + if (1 >= t2.length) + return H.ioore(t2, 1); + years = H.Primitives_parseInt(t2[1], null, null); + if (2 >= t2.length) + return H.ioore(t2, 2); + month = H.Primitives_parseInt(t2[2], null, null); + if (3 >= t2.length) + return H.ioore(t2, 3); + day = H.Primitives_parseInt(t2[3], null, null); + if (4 >= t2.length) + return H.ioore(t2, 4); + hour = t1.call$1(t2[4]); + if (5 >= t2.length) + return H.ioore(t2, 5); + minute = t1.call$1(t2[5]); + if (6 >= t2.length) + return H.ioore(t2, 6); + second = t1.call$1(t2[6]); + if (7 >= t2.length) + return H.ioore(t2, 7); + millisecond = J.round$0$n(J.$mul$ns(new P.DateTime_parse_parseDoubleOrZero().call$1(t2[7]), 1000)); + if (millisecond === 1000) { + addOneMillisecond = true; + millisecond = 999; + } else + addOneMillisecond = false; + t3 = t2.length; + if (8 >= t3) + return H.ioore(t2, 8); + if (t2[8] != null) { + if (9 >= t3) + return H.ioore(t2, 9); + t3 = t2[9]; + if (t3 != null) { + sign = J.$eq(t3, "-") ? -1 : 1; + if (10 >= t2.length) + return H.ioore(t2, 10); + hourDifference = H.Primitives_parseInt(t2[10], null, null); + if (11 >= t2.length) + return H.ioore(t2, 11); + minuteDifference = t1.call$1(t2[11]); + if (typeof hourDifference !== "number") + return H.iae(hourDifference); + minuteDifference = J.$add$ns(minuteDifference, 60 * hourDifference); + if (typeof minuteDifference !== "number") + return H.iae(minuteDifference); + minute = J.$sub$n(minute, sign * minuteDifference); + } + isUtc = true; + } else + isUtc = false; + millisecondsSinceEpoch = H.Primitives_valueFromDecomposedDate(years, month, day, hour, minute, second, millisecond, isUtc); + return P.DateTime$fromMillisecondsSinceEpoch(addOneMillisecond ? millisecondsSinceEpoch + 1 : millisecondsSinceEpoch, isUtc); + } else + throw H.wrapException(P.FormatException$(formattedString)); + }, DateTime$fromMillisecondsSinceEpoch: function(millisecondsSinceEpoch, isUtc) { + var t1 = new P.DateTime(millisecondsSinceEpoch, isUtc); + t1.DateTime$fromMillisecondsSinceEpoch$2$isUtc(millisecondsSinceEpoch, isUtc); + return t1; + }, DateTime__fourDigits: function(n) { + var absN, sign; + absN = Math.abs(n); + sign = n < 0 ? "-" : ""; + if (absN >= 1000) + return "" + n; + if (absN >= 100) + return sign + "0" + H.S(absN); + if (absN >= 10) + return sign + "00" + H.S(absN); + return sign + "000" + H.S(absN); + }, DateTime__threeDigits: function(n) { + if (n >= 100) + return "" + n; + if (n >= 10) + return "0" + n; + return "00" + n; + }, DateTime__twoDigits: function(n) { + if (n >= 10) + return "" + n; + return "0" + n; + }} + }, + DateTime_parse_parseIntOrZero: { + "^": "Closure:19;", + call$1: function(matched) { + if (matched == null) + return 0; + return H.Primitives_parseInt(matched, null, null); + } + }, + DateTime_parse_parseDoubleOrZero: { + "^": "Closure:20;", + call$1: function(matched) { + if (matched == null) + return 0; + return H.Primitives_parseDouble(matched, null); + } + }, + Duration: { + "^": "Object;_duration<", + $add: function(_, other) { + return P.Duration$(0, 0, this._duration + other.get$_duration(), 0, 0, 0); + }, + $sub: function(_, other) { + return P.Duration$(0, 0, C.JSInt_methods.$sub(this._duration, other.get$_duration()), 0, 0, 0); + }, + $mul: function(_, factor) { + return P.Duration$(0, 0, C.JSNumber_methods.toInt$0(C.JSInt_methods.roundToDouble$0(this._duration * factor)), 0, 0, 0); + }, + $lt: function(_, other) { + return C.JSInt_methods.$lt(this._duration, other.get$_duration()); + }, + $le: function(_, other) { + return C.JSInt_methods.$le(this._duration, other.get$_duration()); + }, + $ge: function(_, other) { + return C.JSInt_methods.$ge(this._duration, other.get$_duration()); + }, + $eq: function(_, other) { + if (other == null) + return false; + if (!J.getInterceptor(other).$isDuration) + return false; + return this._duration === other._duration; + }, + get$hashCode: function(_) { + return this._duration & 0x1FFFFFFF; + }, + toString$0: function(_) { + var t1, t2, twoDigitMinutes, twoDigitSeconds, sixDigitUs; + t1 = new P.Duration_toString_twoDigits(); + t2 = this._duration; + if (t2 < 0) + return "-" + H.S(P.Duration$(0, 0, -t2, 0, 0, 0)); + twoDigitMinutes = t1.call$1(C.JSInt_methods.remainder$1(C.JSInt_methods._tdivFast$1(t2, 60000000), 60)); + twoDigitSeconds = t1.call$1(C.JSInt_methods.remainder$1(C.JSInt_methods._tdivFast$1(t2, 1000000), 60)); + sixDigitUs = new P.Duration_toString_sixDigits().call$1(C.JSInt_methods.remainder$1(t2, 1000000)); + return "" + C.JSInt_methods._tdivFast$1(t2, 3600000000) + ":" + H.S(twoDigitMinutes) + ":" + H.S(twoDigitSeconds) + "." + H.S(sixDigitUs); + }, + $isDuration: true, + static: {"^": "Duration_MICROSECONDS_PER_MILLISECOND,Duration_MILLISECONDS_PER_SECOND,Duration_SECONDS_PER_MINUTE,Duration_MINUTES_PER_HOUR,Duration_HOURS_PER_DAY,Duration_MICROSECONDS_PER_SECOND,Duration_MICROSECONDS_PER_MINUTE,Duration_MICROSECONDS_PER_HOUR,Duration_MICROSECONDS_PER_DAY,Duration_MILLISECONDS_PER_MINUTE,Duration_MILLISECONDS_PER_HOUR,Duration_MILLISECONDS_PER_DAY,Duration_SECONDS_PER_HOUR,Duration_SECONDS_PER_DAY,Duration_MINUTES_PER_DAY,Duration_ZERO", Duration$: function(days, hours, microseconds, milliseconds, minutes, seconds) { + return new P.Duration(days * 86400000000 + hours * 3600000000 + minutes * 60000000 + seconds * 1000000 + milliseconds * 1000 + microseconds); + }} + }, + Duration_toString_sixDigits: { + "^": "Closure:21;", + call$1: function(n) { + if (n >= 100000) + return "" + n; + if (n >= 10000) + return "0" + n; + if (n >= 1000) + return "00" + n; + if (n >= 100) + return "000" + n; + if (n >= 10) + return "0000" + n; + return "00000" + n; + } + }, + Duration_toString_twoDigits: { + "^": "Closure:21;", + call$1: function(n) { + if (n >= 10) + return "" + n; + return "0" + n; + } + }, + Error: { + "^": "Object;", + get$stackTrace: function() { + return new H._StackTrace(this.$thrownJsError, null); + }, + $isError: true + }, + NullThrownError: { + "^": "Error;", + toString$0: function(_) { + return "Throw of null."; + } + }, + ArgumentError: { + "^": "Error;message", + toString$0: function(_) { + var t1 = this.message; + if (t1 != null) + return "Illegal argument(s): " + H.S(t1); + return "Illegal argument(s)"; + }, + static: {ArgumentError$: function(message) { + return new P.ArgumentError(message); + }} + }, + RangeError: { + "^": "ArgumentError;message", + toString$0: function(_) { + return "RangeError: " + H.S(this.message); + }, + static: {RangeError$: function(message) { + return new P.RangeError(message); + }, RangeError$value: function(value) { + return new P.RangeError("value " + H.S(value)); + }, RangeError$range: function(value, start, end) { + return new P.RangeError("value " + H.S(value) + " not in range " + start + ".." + H.S(end)); + }} + }, + UnsupportedError: { + "^": "Error;message", + toString$0: function(_) { + return "Unsupported operation: " + this.message; + }, + static: {UnsupportedError$: function(message) { + return new P.UnsupportedError(message); + }} + }, + UnimplementedError: { + "^": "Error;message", + toString$0: function(_) { + var t1 = this.message; + return t1 != null ? "UnimplementedError: " + H.S(t1) : "UnimplementedError"; + }, + $isError: true, + static: {UnimplementedError$: function(message) { + return new P.UnimplementedError(message); + }} + }, + StateError: { + "^": "Error;message", + toString$0: function(_) { + return "Bad state: " + this.message; + }, + static: {StateError$: function(message) { + return new P.StateError(message); + }} + }, + ConcurrentModificationError: { + "^": "Error;modifiedObject", + toString$0: function(_) { + var t1 = this.modifiedObject; + if (t1 == null) + return "Concurrent modification during iteration."; + return "Concurrent modification during iteration: " + H.S(P.Error_safeToString(t1)) + "."; + }, + static: {ConcurrentModificationError$: function(modifiedObject) { + return new P.ConcurrentModificationError(modifiedObject); + }} + }, + OutOfMemoryError: { + "^": "Object;", + toString$0: function(_) { + return "Out of Memory"; + }, + get$stackTrace: function() { + return; + }, + $isError: true + }, + StackOverflowError: { + "^": "Object;", + toString$0: function(_) { + return "Stack Overflow"; + }, + get$stackTrace: function() { + return; + }, + $isError: true + }, + CyclicInitializationError: { + "^": "Error;variableName", + toString$0: function(_) { + return "Reading static variable '" + this.variableName + "' during its initialization"; + }, + static: {CyclicInitializationError$: function(variableName) { + return new P.CyclicInitializationError(variableName); + }} + }, + _ExceptionImplementation: { + "^": "Object;message", + toString$0: function(_) { + var t1 = this.message; + if (t1 == null) + return "Exception"; + return "Exception: " + H.S(t1); + } + }, + FormatException: { + "^": "Object;message", + toString$0: function(_) { + return "FormatException: " + H.S(this.message); + }, + $isFormatException: true, + static: {FormatException$: function(message) { + return new P.FormatException(message); + }} + }, + Expando: { + "^": "Object;name", + toString$0: function(_) { + return "Expando:" + H.S(this.name); + }, + $index: function(_, object) { + var values = H.Primitives_getProperty(object, "expando$values"); + return values == null ? null : H.Primitives_getProperty(values, this._getKey$0()); + }, + $indexSet: function(_, object, value) { + var values = H.Primitives_getProperty(object, "expando$values"); + if (values == null) { + values = new P.Object(); + H.Primitives_setProperty(object, "expando$values", values); + } + H.Primitives_setProperty(values, this._getKey$0(), value); + }, + _getKey$0: function() { + var key, t1; + key = H.Primitives_getProperty(this, "expando$key"); + if (key == null) { + t1 = $.Expando__keyCount; + $.Expando__keyCount = t1 + 1; + key = "expando$key$" + t1; + H.Primitives_setProperty(this, "expando$key", key); + } + return key; + }, + static: {"^": "Expando__KEY_PROPERTY_NAME,Expando__EXPANDO_PROPERTY_NAME,Expando__keyCount"} + }, + Iterator: { + "^": "Object;" + }, + Null: { + "^": "Object;", + toString$0: function(_) { + return "null"; + } + }, + Object: { + "^": ";", + $eq: function(_, other) { + return this === other; + }, + get$hashCode: function(_) { + return H.Primitives_objectHashCode(this); + }, + toString$0: function(_) { + return H.Primitives_objectToString(this); + } + }, + StackTrace: { + "^": "Object;" + }, + StringBuffer: { + "^": "Object;_contents<", + get$length: function(_) { + return this._contents.length; + }, + get$isEmpty: function(_) { + return this._contents.length === 0; + }, + write$1: function(obj) { + var str = typeof obj === "string" ? obj : H.S(obj); + this._contents = this._contents + str; + }, + writeAll$2: function(objects, separator) { + var iterator, str; + iterator = J.get$iterator$ax(objects); + if (!iterator.moveNext$0()) + return; + if (separator.length === 0) + do { + str = iterator.get$current(); + str = typeof str === "string" ? str : H.S(str); + this._contents = this._contents + str; + } while (iterator.moveNext$0()); + else { + this.write$1(iterator.get$current()); + for (; iterator.moveNext$0();) { + this._contents = this._contents + separator; + str = iterator.get$current(); + str = typeof str === "string" ? str : H.S(str); + this._contents = this._contents + str; + } + } + }, + toString$0: function(_) { + return this._contents; + }, + StringBuffer$1: function($content) { + this._contents = $content; + }, + static: {StringBuffer$: function($content) { + var t1 = new P.StringBuffer(""); + t1.StringBuffer$1($content); + return t1; + }} + }, + Symbol: { + "^": "Object;" + } +}], +["dart.dom.html", "dart:html", , W, { + "^": "", + Element_Element$html: function(html, treeSanitizer, validator) { + var fragment, t1; + fragment = J.createFragment$3$treeSanitizer$validator$x(document.body, html, treeSanitizer, validator); + fragment.toString; + t1 = new W._ChildNodeListLazy(fragment); + t1 = t1.where$1(t1, new W.Element_Element$html_closure()); + return t1.get$single(t1); + }, + Window__isDartLocation: function(thing) { + var exception; + try { + return !!J.getInterceptor(thing).$isLocation; + } catch (exception) { + H.unwrapException(exception); + return false; + } + + }, + _wrapZone: function(callback) { + var t1 = $.Zone__current; + if (t1 === C.C__RootZone) + return callback; + return t1.bindUnaryCallback$2$runGuarded(callback, true); + }, + HtmlElement: { + "^": "Element;", + "%": "HTMLAppletElement|HTMLBRElement|HTMLCanvasElement|HTMLContentElement|HTMLDListElement|HTMLDataListElement|HTMLDetailsElement|HTMLDialogElement|HTMLDirectoryElement|HTMLDivElement|HTMLFontElement|HTMLFrameElement|HTMLFrameSetElement|HTMLHRElement|HTMLHeadElement|HTMLHeadingElement|HTMLHtmlElement|HTMLImageElement|HTMLLabelElement|HTMLLegendElement|HTMLMarqueeElement|HTMLMenuElement|HTMLModElement|HTMLOListElement|HTMLParagraphElement|HTMLPreElement|HTMLQuoteElement|HTMLScriptElement|HTMLShadowElement|HTMLSourceElement|HTMLSpanElement|HTMLTableCaptionElement|HTMLTableCellElement|HTMLTableColElement|HTMLTableDataCellElement|HTMLTableHeaderCellElement|HTMLTitleElement|HTMLTrackElement|HTMLUListElement|HTMLUnknownElement;HTMLElement" + }, + AnchorElement: { + "^": "HtmlElement;hostname=,href},port=,protocol=", + toString$0: function(receiver) { + return receiver.toString(); + }, + "%": "HTMLAnchorElement" + }, + AreaElement: { + "^": "HtmlElement;hostname=,href},port=,protocol=", + toString$0: function(receiver) { + return receiver.toString(); + }, + "%": "HTMLAreaElement" + }, + BaseElement: { + "^": "HtmlElement;href}", + "%": "HTMLBaseElement" + }, + BodyElement: { + "^": "HtmlElement;", + get$onBlur: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_blur._eventType, false), [null]); + }, + $isBodyElement: true, + "%": "HTMLBodyElement" + }, + ButtonElement: { + "^": "HtmlElement;disabled},name=,value%", + "%": "HTMLButtonElement" + }, + CharacterData: { + "^": "Node;length=", + "%": "CDATASection|CharacterData|Comment|ProcessingInstruction|Text" + }, + DomException: { + "^": "Interceptor;", + toString$0: function(receiver) { + return receiver.toString(); + }, + "%": "DOMException" + }, + Element: { + "^": "Node;", + get$attributes: function(receiver) { + return new W._ElementAttributeMap(receiver); + }, + toString$0: function(receiver) { + return receiver.localName; + }, + createFragment$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + var t1, t2, base, contextElement, fragment; + if (treeSanitizer == null) { + t1 = $.Element__defaultValidator; + if (t1 == null) { + t1 = H.setRuntimeTypeInfo([], [W.NodeValidator]); + t2 = new W.NodeValidatorBuilder(t1); + t1.push(W._Html5NodeValidator$(null)); + t1.push(W._TemplatingNodeValidator$()); + $.Element__defaultValidator = t2; + validator = t2; + } else + validator = t1; + t1 = $.Element__defaultSanitizer; + if (t1 == null) { + t1 = new W._ValidatingTreeSanitizer(validator); + $.Element__defaultSanitizer = t1; + treeSanitizer = t1; + } else { + t1.validator = validator; + treeSanitizer = t1; + } + } + if ($.Element__parseDocument == null) { + t1 = document.implementation.createHTMLDocument(""); + $.Element__parseDocument = t1; + $.Element__parseRange = t1.createRange(); + base = $.Element__parseDocument.createElement("base", null); + J.set$href$x(base, document.baseURI); + $.Element__parseDocument.head.appendChild(base); + } + t1 = $.Element__parseDocument; + if (!!this.$isBodyElement) + contextElement = t1.body; + else { + contextElement = t1.createElement(receiver.tagName, null); + $.Element__parseDocument.body.appendChild(contextElement); + } + if ("createContextualFragment" in window.Range.prototype) { + $.Element__parseRange.selectNodeContents(contextElement); + fragment = $.Element__parseRange.createContextualFragment(html); + } else { + contextElement.innerHTML = html; + fragment = $.Element__parseDocument.createDocumentFragment(); + for (; t1 = contextElement.firstChild, t1 != null;) + fragment.appendChild(t1); + } + t1 = $.Element__parseDocument.body; + if (contextElement == null ? t1 != null : contextElement !== t1) + J.remove$0$ax(contextElement); + treeSanitizer.sanitizeTree$1(fragment); + document.adoptNode(fragment); + return fragment; + }, + createFragment$2$treeSanitizer: function($receiver, html, treeSanitizer) { + return this.createFragment$3$treeSanitizer$validator($receiver, html, treeSanitizer, null); + }, + set$innerHtml: function(receiver, html) { + this.setInnerHtml$1(receiver, html); + }, + setInnerHtml$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + receiver.textContent = null; + receiver.appendChild(this.createFragment$3$treeSanitizer$validator(receiver, html, treeSanitizer, validator)); + }, + setInnerHtml$1: function($receiver, html) { + return this.setInnerHtml$3$treeSanitizer$validator($receiver, html, null, null); + }, + get$onBlur: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_blur._eventType, false), [null]); + }, + get$onChange: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_change._eventType, false), [null]); + }, + get$onClick: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_click._eventType, false), [null]); + }, + get$onInput: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_input._eventType, false), [null]); + }, + $isElement: true, + "%": ";Element" + }, + EmbedElement: { + "^": "HtmlElement;name=", + "%": "HTMLEmbedElement" + }, + ErrorEvent: { + "^": "Event;error=", + "%": "ErrorEvent" + }, + Event: { + "^": "Interceptor;", + preventDefault$0: function(receiver) { + return receiver.preventDefault(); + }, + "%": "AudioProcessingEvent|AutocompleteErrorEvent|BeforeLoadEvent|BeforeUnloadEvent|CSSFontFaceLoadEvent|CloseEvent|CustomEvent|DeviceMotionEvent|DeviceOrientationEvent|HashChangeEvent|IDBVersionChangeEvent|MIDIConnectionEvent|MIDIMessageEvent|MediaKeyEvent|MediaKeyMessageEvent|MediaKeyNeededEvent|MediaStreamEvent|MediaStreamTrackEvent|MessageEvent|MutationEvent|OfflineAudioCompletionEvent|OverflowEvent|PageTransitionEvent|PopStateEvent|ProgressEvent|RTCDTMFToneChangeEvent|RTCDataChannelEvent|RTCIceCandidateEvent|ResourceProgressEvent|SecurityPolicyViolationEvent|SpeechInputEvent|SpeechRecognitionEvent|SpeechSynthesisEvent|StorageEvent|TrackEvent|TransitionEvent|WebGLContextEvent|WebKitAnimationEvent|WebKitTransitionEvent|XMLHttpRequestProgressEvent;Event" + }, + EventTarget: { + "^": "Interceptor;", + addEventListener$3: function(receiver, type, listener, useCapture) { + return receiver.addEventListener(type, H.convertDartClosureToJS(listener, 1), useCapture); + }, + removeEventListener$3: function(receiver, type, listener, useCapture) { + return receiver.removeEventListener(type, H.convertDartClosureToJS(listener, 1), useCapture); + }, + "%": ";EventTarget" + }, + FieldSetElement: { + "^": "HtmlElement;disabled},name=", + "%": "HTMLFieldSetElement" + }, + FormElement: { + "^": "HtmlElement;length=,name=", + "%": "HTMLFormElement" + }, + IFrameElement: { + "^": "HtmlElement;name=", + "%": "HTMLIFrameElement" + }, + InputElement: { + "^": "HtmlElement;disabled},name=,value%", + $isElement: true, + "%": "HTMLInputElement" + }, + KeygenElement: { + "^": "HtmlElement;disabled},name=", + "%": "HTMLKeygenElement" + }, + LIElement: { + "^": "HtmlElement;value%", + "%": "HTMLLIElement" + }, + LinkElement: { + "^": "HtmlElement;disabled},href}", + "%": "HTMLLinkElement" + }, + Location: { + "^": "Interceptor;hostname=,port=,protocol=", + toString$0: function(receiver) { + return receiver.toString(); + }, + $isLocation: true, + "%": "Location" + }, + MapElement: { + "^": "HtmlElement;name=", + "%": "HTMLMapElement" + }, + MediaElement: { + "^": "HtmlElement;error=", + "%": "HTMLAudioElement|HTMLMediaElement|HTMLVideoElement" + }, + MetaElement: { + "^": "HtmlElement;name=", + "%": "HTMLMetaElement" + }, + MeterElement: { + "^": "HtmlElement;value%", + "%": "HTMLMeterElement" + }, + MidiOutput: { + "^": "MidiPort;", + send$2: function(receiver, data, timestamp) { + return receiver.send(data, timestamp); + }, + send$1: function($receiver, data) { + return $receiver.send(data); + }, + "%": "MIDIOutput" + }, + MidiPort: { + "^": "EventTarget;", + "%": "MIDIInput;MIDIPort" + }, + MouseEvent: { + "^": "UIEvent;", + "%": "DragEvent|MSPointerEvent|MouseEvent|MouseScrollEvent|MouseWheelEvent|PointerEvent|WheelEvent" + }, + Node: { + "^": "EventTarget;", + get$nodes: function(receiver) { + return new W._ChildNodeListLazy(receiver); + }, + remove$0: function(receiver) { + var t1 = receiver.parentNode; + if (t1 != null) + t1.removeChild(receiver); + }, + toString$0: function(receiver) { + var t1 = receiver.nodeValue; + return t1 == null ? J.Interceptor.prototype.toString$0.call(this, receiver) : t1; + }, + "%": "Document|DocumentFragment|DocumentType|HTMLDocument|Notation|ShadowRoot|XMLDocument;Node" + }, + NodeList: { + "^": "Interceptor_ListMixin_ImmutableListMixin;", + get$length: function(receiver) { + return receiver.length; + }, + $index: function(receiver, index) { + var t1 = receiver.length; + if (index >>> 0 !== index || index >= t1) + throw H.wrapException(P.RangeError$range(index, 0, t1)); + return receiver[index]; + }, + $indexSet: function(receiver, index, value) { + throw H.wrapException(P.UnsupportedError$("Cannot assign element of immutable List.")); + }, + elementAt$1: function(receiver, index) { + if (index < 0 || index >= receiver.length) + return H.ioore(receiver, index); + return receiver[index]; + }, + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true, + $isJavaScriptIndexingBehavior: true, + "%": "NodeList|RadioNodeList" + }, + ObjectElement: { + "^": "HtmlElement;name=", + "%": "HTMLObjectElement" + }, + OptGroupElement: { + "^": "HtmlElement;disabled}", + "%": "HTMLOptGroupElement" + }, + OptionElement: { + "^": "HtmlElement;disabled},value%", + "%": "HTMLOptionElement" + }, + OutputElement: { + "^": "HtmlElement;name=,value%", + "%": "HTMLOutputElement" + }, + ParamElement: { + "^": "HtmlElement;name=,value%", + "%": "HTMLParamElement" + }, + ProgressElement: { + "^": "HtmlElement;value%", + "%": "HTMLProgressElement" + }, + Range: { + "^": "Interceptor;", + toString$0: function(receiver) { + return receiver.toString(); + }, + "%": "Range" + }, + SelectElement: { + "^": "HtmlElement;disabled},length=,name=,value%", + "%": "HTMLSelectElement" + }, + SpeechRecognitionError: { + "^": "Event;error=", + "%": "SpeechRecognitionError" + }, + Storage: { + "^": "Interceptor;", + $index: function(receiver, key) { + return receiver.getItem(key); + }, + $indexSet: function(receiver, key, value) { + receiver.setItem(key, value); + }, + forEach$1: function(receiver, f) { + var i, key; + for (i = 0; true; ++i) { + key = receiver.key(i); + if (key == null) + return; + f.call$2(key, receiver.getItem(key)); + } + }, + get$keys: function(receiver) { + var keys = []; + this.forEach$1(receiver, new W.Storage_keys_closure(keys)); + return keys; + }, + get$values: function(receiver) { + var values = []; + this.forEach$1(receiver, new W.Storage_values_closure(values)); + return values; + }, + get$length: function(receiver) { + return receiver.length; + }, + get$isEmpty: function(receiver) { + return receiver.key(0) == null; + }, + $isMap: true, + $asMap: function() { + return [J.JSString, J.JSString]; + }, + "%": "Storage" + }, + StyleElement: { + "^": "HtmlElement;disabled}", + "%": "HTMLStyleElement" + }, + TableElement: { + "^": "HtmlElement;", + createFragment$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + var table, fragment; + if ("createContextualFragment" in window.Range.prototype) + return W.Element.prototype.createFragment$3$treeSanitizer$validator.call(this, receiver, html, treeSanitizer, validator); + table = W.Element_Element$html("" + html + "
", treeSanitizer, validator); + fragment = document.createDocumentFragment(); + fragment.toString; + new W._ChildNodeListLazy(fragment).addAll$1(0, J.get$nodes$x(table)); + return fragment; + }, + "%": "HTMLTableElement" + }, + TableRowElement: { + "^": "HtmlElement;", + createFragment$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + var fragment, t1, section, row; + if ("createContextualFragment" in window.Range.prototype) + return W.Element.prototype.createFragment$3$treeSanitizer$validator.call(this, receiver, html, treeSanitizer, validator); + fragment = document.createDocumentFragment(); + t1 = J.createFragment$3$treeSanitizer$validator$x(document.createElement("table", null), html, treeSanitizer, validator); + t1.toString; + t1 = new W._ChildNodeListLazy(t1); + section = t1.get$single(t1); + section.toString; + t1 = new W._ChildNodeListLazy(section); + row = t1.get$single(t1); + fragment.toString; + row.toString; + new W._ChildNodeListLazy(fragment).addAll$1(0, new W._ChildNodeListLazy(row)); + return fragment; + }, + "%": "HTMLTableRowElement" + }, + TableSectionElement: { + "^": "HtmlElement;", + createFragment$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + var fragment, t1, section; + if ("createContextualFragment" in window.Range.prototype) + return W.Element.prototype.createFragment$3$treeSanitizer$validator.call(this, receiver, html, treeSanitizer, validator); + fragment = document.createDocumentFragment(); + t1 = J.createFragment$3$treeSanitizer$validator$x(document.createElement("table", null), html, treeSanitizer, validator); + t1.toString; + t1 = new W._ChildNodeListLazy(t1); + section = t1.get$single(t1); + fragment.toString; + section.toString; + new W._ChildNodeListLazy(fragment).addAll$1(0, new W._ChildNodeListLazy(section)); + return fragment; + }, + "%": "HTMLTableSectionElement" + }, + TemplateElement: { + "^": "HtmlElement;", + setInnerHtml$3$treeSanitizer$validator: function(receiver, html, treeSanitizer, validator) { + var fragment; + receiver.textContent = null; + fragment = this.createFragment$3$treeSanitizer$validator(receiver, html, treeSanitizer, validator); + receiver.content.appendChild(fragment); + }, + setInnerHtml$1: function($receiver, html) { + return this.setInnerHtml$3$treeSanitizer$validator($receiver, html, null, null); + }, + $isTemplateElement: true, + "%": "HTMLTemplateElement" + }, + TextAreaElement: { + "^": "HtmlElement;disabled},name=,value%", + "%": "HTMLTextAreaElement" + }, + UIEvent: { + "^": "Event;", + "%": "CompositionEvent|FocusEvent|KeyboardEvent|SVGZoomEvent|TextEvent|TouchEvent;UIEvent" + }, + Window: { + "^": "EventTarget;", + get$location: function(receiver) { + var result = receiver.location; + if (W.Window__isDartLocation(result) === true) + return result; + if (null == receiver._location_wrapper) + receiver._location_wrapper = new W._LocationWrapper(result); + return receiver._location_wrapper; + }, + toString$0: function(receiver) { + return receiver.toString(); + }, + "%": "DOMWindow|Window" + }, + _Attr: { + "^": "Node;name=,value=", + "%": "Attr" + }, + _NamedNodeMap: { + "^": "Interceptor_ListMixin_ImmutableListMixin0;", + get$length: function(receiver) { + return receiver.length; + }, + $index: function(receiver, index) { + var t1 = receiver.length; + if (index >>> 0 !== index || index >= t1) + throw H.wrapException(P.RangeError$range(index, 0, t1)); + return receiver[index]; + }, + $indexSet: function(receiver, index, value) { + throw H.wrapException(P.UnsupportedError$("Cannot assign element of immutable List.")); + }, + elementAt$1: function(receiver, index) { + if (index < 0 || index >= receiver.length) + return H.ioore(receiver, index); + return receiver[index]; + }, + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true, + $isJavaScriptIndexingBehavior: true, + "%": "MozNamedAttrMap|NamedNodeMap" + }, + Element_Element$html_closure: { + "^": "Closure:11;", + call$1: function(e) { + return !!J.getInterceptor(e).$isElement; + } + }, + _ChildNodeListLazy: { + "^": "ListBase;_this", + get$single: function(_) { + var t1, l; + t1 = this._this; + l = t1.childNodes.length; + if (l === 0) + throw H.wrapException(P.StateError$("No elements")); + if (l > 1) + throw H.wrapException(P.StateError$("More than one element")); + return t1.firstChild; + }, + addAll$1: function(_, iterable) { + var t1, t2, len, i; + t1 = iterable._this; + t2 = this._this; + if (t1 !== t2) + for (len = t1.childNodes.length, i = 0; i < len; ++i) + t2.appendChild(t1.firstChild); + return; + }, + $indexSet: function(_, index, value) { + var t1, t2; + t1 = this._this; + t2 = t1.childNodes; + if (index >>> 0 !== index || index >= t2.length) + return H.ioore(t2, index); + t1.replaceChild(value, t2[index]); + }, + get$iterator: function(_) { + return C.NodeList_methods.get$iterator(this._this.childNodes); + }, + get$length: function(_) { + return this._this.childNodes.length; + }, + $index: function(_, index) { + var t1 = this._this.childNodes; + if (index >>> 0 !== index || index >= t1.length) + return H.ioore(t1, index); + return t1[index]; + }, + $asListBase: function() { + return [W.Node]; + }, + $asList: function() { + return [W.Node]; + } + }, + Interceptor_ListMixin: { + "^": "Interceptor+ListMixin;", + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true + }, + Interceptor_ListMixin_ImmutableListMixin: { + "^": "Interceptor_ListMixin+ImmutableListMixin;", + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true + }, + Storage_keys_closure: { + "^": "Closure:10;keys_0", + call$2: function(k, v) { + return this.keys_0.push(k); + } + }, + Storage_values_closure: { + "^": "Closure:10;values_0", + call$2: function(k, v) { + return this.values_0.push(v); + } + }, + Interceptor_ListMixin0: { + "^": "Interceptor+ListMixin;", + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true + }, + Interceptor_ListMixin_ImmutableListMixin0: { + "^": "Interceptor_ListMixin0+ImmutableListMixin;", + $isList: true, + $asList: function() { + return [W.Node]; + }, + $isEfficientLength: true + }, + _AttributeMap: { + "^": "Object;", + forEach$1: function(_, f) { + var t1, key; + for (t1 = this.get$keys(this), t1 = new H.ListIterator(t1, t1.length, 0, null); t1.moveNext$0();) { + key = t1._current; + f.call$2(key, this.$index(0, key)); + } + }, + get$keys: function(_) { + var attributes, keys, len, i, t1; + attributes = this._element.attributes; + keys = H.setRuntimeTypeInfo([], [J.JSString]); + for (len = attributes.length, i = 0; i < len; ++i) { + if (i >= attributes.length) + return H.ioore(attributes, i); + t1 = attributes[i]; + if (this._matches$1(t1)) + keys.push(J.get$name$x(t1)); + } + return keys; + }, + get$values: function(_) { + var attributes, values, len, i, t1; + attributes = this._element.attributes; + values = H.setRuntimeTypeInfo([], [J.JSString]); + for (len = attributes.length, i = 0; i < len; ++i) { + if (i >= attributes.length) + return H.ioore(attributes, i); + t1 = attributes[i]; + if (this._matches$1(t1)) + values.push(J.get$value$x(t1)); + } + return values; + }, + get$isEmpty: function(_) { + return this.get$length(this) === 0; + }, + $isMap: true, + $asMap: function() { + return [J.JSString, J.JSString]; + } + }, + _ElementAttributeMap: { + "^": "_AttributeMap;_element", + $index: function(_, key) { + return this._element.getAttribute(key); + }, + $indexSet: function(_, key, value) { + this._element.setAttribute(key, value); + }, + get$length: function(_) { + return this.get$keys(this).length; + }, + _matches$1: function(node) { + return node.namespaceURI == null; + } + }, + EventStreamProvider: { + "^": "Object;_eventType" + }, + _EventStream: { + "^": "Stream;", + listen$4$cancelOnError$onDone$onError: function(onData, cancelOnError, onDone, onError) { + var t1 = new W._EventStreamSubscription(0, this._target, this._eventType, W._wrapZone(onData), this._useCapture); + t1.$builtinTypeInfo = this.$builtinTypeInfo; + t1._tryResume$0(); + return t1; + } + }, + _ElementEventStreamImpl: { + "^": "_EventStream;_target,_eventType,_useCapture" + }, + _EventStreamSubscription: { + "^": "StreamSubscription;_pauseCount,_target,_eventType,_onData,_useCapture", + cancel$0: function() { + if (this._target == null) + return; + this._unlisten$0(); + this._target = null; + this._onData = null; + return; + }, + _tryResume$0: function() { + var t1 = this._onData; + if (t1 != null && this._pauseCount <= 0) + J.addEventListener$3$x(this._target, this._eventType, t1, this._useCapture); + }, + _unlisten$0: function() { + var t1 = this._onData; + if (t1 != null) + J.removeEventListener$3$x(this._target, this._eventType, t1, this._useCapture); + } + }, + _Html5NodeValidator: { + "^": "Object;uriPolicy<", + allowsElement$1: function(element) { + return $.get$_Html5NodeValidator__allowedElements().contains$1(0, element.tagName); + }, + allowsAttribute$3: function(element, attributeName, value) { + var tagName, t1, validator; + tagName = element.tagName; + t1 = $.get$_Html5NodeValidator__attributeValidators(); + validator = t1.$index(0, H.S(tagName) + "::" + attributeName); + if (validator == null) + validator = t1.$index(0, "*::" + attributeName); + if (validator == null) + return false; + return validator.call$4(element, attributeName, value, this); + }, + _Html5NodeValidator$1$uriPolicy: function(uriPolicy) { + var t1, t2; + t1 = $.get$_Html5NodeValidator__attributeValidators(); + if (t1.get$isEmpty(t1)) { + for (t2 = new H.ListIterator(C.List_1GN, 261, 0, null); t2.moveNext$0();) + t1.$indexSet(0, t2._current, W._Html5NodeValidator__standardAttributeValidator$closure()); + for (t2 = new H.ListIterator(C.List_yrN, 12, 0, null); t2.moveNext$0();) + t1.$indexSet(0, t2._current, W._Html5NodeValidator__uriAttributeValidator$closure()); + } + }, + static: {"^": "_Html5NodeValidator__allowedElements,_Html5NodeValidator__standardAttributes,_Html5NodeValidator__uriAttributes,_Html5NodeValidator__attributeValidators", _Html5NodeValidator$: function(uriPolicy) { + var e, t1; + e = document.createElement("a", null); + t1 = new W._SameOriginUriPolicy(e, C.Window_methods.get$location(window)); + t1 = new W._Html5NodeValidator(t1); + t1._Html5NodeValidator$1$uriPolicy(uriPolicy); + return t1; + }, _Html5NodeValidator__standardAttributeValidator: [function(element, attributeName, value, context) { + return true; + }, "call$4", "_Html5NodeValidator__standardAttributeValidator$closure", 8, 0, 8], _Html5NodeValidator__uriAttributeValidator: [function(element, attributeName, value, context) { + var t1, t2, t3, t4, t5, t6; + t1 = context.get$uriPolicy(); + t2 = t1._hiddenAnchor; + t3 = J.getInterceptor$x(t2); + t3.set$href(t2, value); + t4 = t3.get$hostname(t2); + t1 = t1._loc; + t5 = J.getInterceptor$x(t1); + t6 = t5.get$hostname(t1); + if (t4 == null ? t6 == null : t4 === t6) { + t4 = t3.get$port(t2); + t6 = t5.get$port(t1); + if (t4 == null ? t6 == null : t4 === t6) { + t4 = t3.get$protocol(t2); + t1 = t5.get$protocol(t1); + t1 = t4 == null ? t1 == null : t4 === t1; + } else + t1 = false; + } else + t1 = false; + if (!t1) + t1 = t3.get$hostname(t2) === "" && t3.get$port(t2) === "" && t3.get$protocol(t2) === ":"; + else + t1 = true; + return t1; + }, "call$4", "_Html5NodeValidator__uriAttributeValidator$closure", 8, 0, 8]} + }, + ImmutableListMixin: { + "^": "Object;", + get$iterator: function(receiver) { + return new W.FixedSizeListIterator(receiver, this.get$length(receiver), -1, null); + }, + $isList: true, + $asList: null, + $isEfficientLength: true + }, + NodeValidatorBuilder: { + "^": "Object;_validators", + allowsElement$1: function(element) { + return H.IterableMixinWorkaround_any(this._validators, new W.NodeValidatorBuilder_allowsElement_closure(element)); + }, + allowsAttribute$3: function(element, attributeName, value) { + return H.IterableMixinWorkaround_any(this._validators, new W.NodeValidatorBuilder_allowsAttribute_closure(element, attributeName, value)); + } + }, + NodeValidatorBuilder_allowsElement_closure: { + "^": "Closure:11;element_0", + call$1: function(v) { + return v.allowsElement$1(this.element_0); + } + }, + NodeValidatorBuilder_allowsAttribute_closure: { + "^": "Closure:11;element_0,attributeName_1,value_2", + call$1: function(v) { + return v.allowsAttribute$3(this.element_0, this.attributeName_1, this.value_2); + } + }, + _SimpleNodeValidator: { + "^": "Object;uriPolicy<", + allowsElement$1: function(element) { + return this.allowedElements.contains$1(0, element.tagName); + }, + allowsAttribute$3: function(element, attributeName, value) { + var tagName, t1; + tagName = element.tagName; + t1 = this.allowedUriAttributes; + if (t1.contains$1(0, H.S(tagName) + "::" + attributeName)) + return this.uriPolicy.allowsUri$1(value); + else if (t1.contains$1(0, "*::" + attributeName)) + return this.uriPolicy.allowsUri$1(value); + else { + t1 = this.allowedAttributes; + if (t1.contains$1(0, H.S(tagName) + "::" + attributeName)) + return true; + else if (t1.contains$1(0, "*::" + attributeName)) + return true; + else if (t1.contains$1(0, H.S(tagName) + "::*")) + return true; + else if (t1.contains$1(0, "*::*")) + return true; + } + return false; + } + }, + _TemplatingNodeValidator: { + "^": "_SimpleNodeValidator;_templateAttrs,allowedElements,allowedAttributes,allowedUriAttributes,uriPolicy", + allowsAttribute$3: function(element, attributeName, value) { + if (W._SimpleNodeValidator.prototype.allowsAttribute$3.call(this, element, attributeName, value)) + return true; + if (attributeName === "template" && value === "") + return true; + if (element.getAttribute("template") === "") + return this._templateAttrs.contains$1(0, attributeName); + return false; + }, + static: {"^": "_TemplatingNodeValidator__TEMPLATE_ATTRS", _TemplatingNodeValidator$: function() { + var t1, t2, t3, t4; + t1 = H.setRuntimeTypeInfo(new H.MappedListIterable(C.List_wSV, new W._TemplatingNodeValidator_closure()), [null, null]); + t2 = P.LinkedHashSet_LinkedHashSet(null, null, null, null); + t2.addAll$1(0, ["TEMPLATE"]); + t3 = P.LinkedHashSet_LinkedHashSet(null, null, null, null); + t3.addAll$1(0, t1); + t1 = t3; + t3 = P.LinkedHashSet_LinkedHashSet(null, null, null, null); + t4 = P.LinkedHashSet_LinkedHashSet(null, null, null, J.JSString); + t4.addAll$1(0, C.List_wSV); + return new W._TemplatingNodeValidator(t4, t2, t1, t3, null); + }} + }, + _TemplatingNodeValidator_closure: { + "^": "Closure:11;", + call$1: function(attr) { + return "TEMPLATE::" + H.S(attr); + } + }, + _SvgNodeValidator: { + "^": "Object;", + allowsElement$1: function(element) { + var t1 = J.getInterceptor(element); + if (!!t1.$isScriptElement) + return false; + if (!!t1.$isSvgElement) + return true; + return false; + }, + allowsAttribute$3: function(element, attributeName, value) { + if (attributeName === "is" || C.JSString_methods.startsWith$1(attributeName, "on")) + return false; + return this.allowsElement$1(element); + } + }, + FixedSizeListIterator: { + "^": "Object;_array,_html$_length,_position,_html$_current", + moveNext$0: function() { + var nextPosition, t1; + nextPosition = this._position + 1; + t1 = this._html$_length; + if (nextPosition < t1) { + this._html$_current = J.$index$asx(this._array, nextPosition); + this._position = nextPosition; + return true; + } + this._html$_current = null; + this._position = t1; + return false; + }, + get$current: function() { + return this._html$_current; + } + }, + _LocationWrapper: { + "^": "Object;_ptr", + get$hostname: function(_) { + return this._ptr.hostname; + }, + get$port: function(_) { + return this._ptr.port; + }, + get$protocol: function(_) { + return this._ptr.protocol; + }, + toString$0: function(_) { + return this._ptr.toString(); + }, + $isLocation: true + }, + NodeValidator: { + "^": "Object;" + }, + _SameOriginUriPolicy: { + "^": "Object;_hiddenAnchor,_loc" + }, + _ValidatingTreeSanitizer: { + "^": "Object;validator", + sanitizeTree$1: function(node) { + new W._ValidatingTreeSanitizer_sanitizeTree_walk(this).call$1(node); + }, + sanitizeNode$1: function(node) { + var t1, attrs, t2, isAttr, keys, i, $name, t3; + switch (node.nodeType) { + case 1: + t1 = J.getInterceptor$x(node); + attrs = t1.get$attributes(node); + if (!this.validator.allowsElement$1(node)) { + window; + t2 = "Removing disallowed element <" + H.S(node.tagName) + ">"; + if (typeof console != "undefined") + console.warn(t2); + t1.remove$0(node); + break; + } + t2 = attrs._element; + isAttr = t2.getAttribute("is"); + if (isAttr != null) + if (!this.validator.allowsAttribute$3(node, "is", isAttr)) { + window; + t2 = "Removing disallowed type extension <" + H.S(node.tagName) + " is=\"" + isAttr + "\">"; + if (typeof console != "undefined") + console.warn(t2); + t1.remove$0(node); + break; + } + keys = C.JSArray_methods.toList$0(attrs.get$keys(attrs)); + for (i = attrs.get$keys(attrs).length - 1; i >= 0; --i) { + if (i >= keys.length) + return H.ioore(keys, i); + $name = keys[i]; + if (!this.validator.allowsAttribute$3(node, J.toLowerCase$0$s($name), t2.getAttribute($name))) { + window; + t3 = "Removing disallowed attribute <" + H.S(node.tagName) + " " + $name + "=\"" + H.S(t2.getAttribute($name)) + "\">"; + if (typeof console != "undefined") + console.warn(t3); + t2.getAttribute($name); + t2.removeAttribute($name); + } + } + if (!!t1.$isTemplateElement) + this.sanitizeTree$1(node.content); + break; + case 8: + case 11: + case 3: + case 4: + break; + default: + J.remove$0$ax(node); + } + } + }, + _ValidatingTreeSanitizer_sanitizeTree_walk: { + "^": "Closure:22;this_0", + call$1: function(node) { + var child, nextChild; + this.this_0.sanitizeNode$1(node); + child = node.lastChild; + for (; child != null; child = nextChild) { + nextChild = child.previousSibling; + this.call$1(child); + } + } + } +}], +["dart.dom.svg", "dart:svg", , P, { + "^": "", + ScriptElement: { + "^": "SvgElement;", + $isScriptElement: true, + "%": "SVGScriptElement" + }, + StyleElement0: { + "^": "SvgElement;disabled}", + "%": "SVGStyleElement" + }, + SvgElement: { + "^": "Element;", + set$innerHtml: function(receiver, value) { + receiver.textContent = null; + receiver.appendChild(this.createFragment$3$treeSanitizer$validator(receiver, value, null, null)); + }, + createFragment$3$treeSanitizer$validator: function(receiver, svg, treeSanitizer, validator) { + var t1, html, fragment, svgFragment, root; + t1 = H.setRuntimeTypeInfo([], [W.NodeValidator]); + validator = new W.NodeValidatorBuilder(t1); + t1.push(W._Html5NodeValidator$(null)); + t1.push(W._TemplatingNodeValidator$()); + t1.push(new W._SvgNodeValidator()); + treeSanitizer = new W._ValidatingTreeSanitizer(validator); + html = "" + svg + ""; + fragment = J.createFragment$2$treeSanitizer$x(document.body, html, treeSanitizer); + svgFragment = document.createDocumentFragment(); + fragment.toString; + t1 = new W._ChildNodeListLazy(fragment); + root = t1.get$single(t1); + for (; t1 = root.firstChild, t1 != null;) + svgFragment.appendChild(t1); + return svgFragment; + }, + get$onBlur: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_blur._eventType, false), [null]); + }, + get$onChange: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_change._eventType, false), [null]); + }, + get$onClick: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_click._eventType, false), [null]); + }, + get$onInput: function(receiver) { + return H.setRuntimeTypeInfo(new W._ElementEventStreamImpl(receiver, C.EventStreamProvider_input._eventType, false), [null]); + }, + $isSvgElement: true, + "%": "SVGAElement|SVGAltGlyphDefElement|SVGAltGlyphElement|SVGAltGlyphItemElement|SVGAnimateElement|SVGAnimateMotionElement|SVGAnimateTransformElement|SVGAnimationElement|SVGCircleElement|SVGClipPathElement|SVGComponentTransferFunctionElement|SVGCursorElement|SVGDefsElement|SVGDescElement|SVGDiscardElement|SVGEllipseElement|SVGFEBlendElement|SVGFEColorMatrixElement|SVGFEComponentTransferElement|SVGFECompositeElement|SVGFEConvolveMatrixElement|SVGFEDiffuseLightingElement|SVGFEDisplacementMapElement|SVGFEDistantLightElement|SVGFEDropShadowElement|SVGFEFloodElement|SVGFEFuncAElement|SVGFEFuncBElement|SVGFEFuncGElement|SVGFEFuncRElement|SVGFEGaussianBlurElement|SVGFEImageElement|SVGFEMergeElement|SVGFEMergeNodeElement|SVGFEMorphologyElement|SVGFEOffsetElement|SVGFEPointLightElement|SVGFESpecularLightingElement|SVGFESpotLightElement|SVGFETileElement|SVGFETurbulenceElement|SVGFilterElement|SVGFontElement|SVGFontFaceElement|SVGFontFaceFormatElement|SVGFontFaceNameElement|SVGFontFaceSrcElement|SVGFontFaceUriElement|SVGForeignObjectElement|SVGGElement|SVGGeometryElement|SVGGlyphElement|SVGGlyphRefElement|SVGGradientElement|SVGGraphicsElement|SVGHKernElement|SVGImageElement|SVGLineElement|SVGLinearGradientElement|SVGMPathElement|SVGMarkerElement|SVGMaskElement|SVGMetadataElement|SVGMissingGlyphElement|SVGPathElement|SVGPatternElement|SVGPolygonElement|SVGPolylineElement|SVGRadialGradientElement|SVGRectElement|SVGSVGElement|SVGSetElement|SVGStopElement|SVGSwitchElement|SVGSymbolElement|SVGTSpanElement|SVGTextContentElement|SVGTextElement|SVGTextPathElement|SVGTextPositioningElement|SVGTitleElement|SVGUseElement|SVGVKernElement|SVGViewElement;SVGElement" + } +}], +["dart.isolate", "dart:isolate", , P, { + "^": "", + Capability: { + "^": "Object;", + $isCapability: true, + static: {Capability_Capability: function() { + return new H.CapabilityImpl((Math.random() * 0x100000000 >>> 0) + (Math.random() * 0x100000000 >>> 0) * 4294967296); + }} + } +}], +["dart.typed_data.implementation", "dart:_native_typed_data", , H, { + "^": "", + NativeTypedData: { + "^": "Interceptor;", + _invalidIndex$2: function(receiver, index, $length) { + var t1 = J.getInterceptor$n(index); + if (t1.$lt(index, 0) || t1.$ge(index, $length)) + throw H.wrapException(P.RangeError$range(index, 0, $length)); + else + throw H.wrapException(P.ArgumentError$("Invalid list index " + H.S(index))); + }, + "%": ";ArrayBufferView;NativeTypedArray|NativeTypedArray_ListMixin|NativeTypedArray_ListMixin_FixedLengthListMixin|NativeTypedArrayOfInt" + }, + NativeUint8List: { + "^": "NativeTypedArrayOfInt;", + get$length: function(receiver) { + return receiver.length; + }, + $index: function(receiver, index) { + var t1 = receiver.length; + if (index >>> 0 !== index || index >= t1) + this._invalidIndex$2(receiver, index, t1); + return receiver[index]; + }, + $indexSet: function(receiver, index, value) { + var t1 = receiver.length; + if (index >>> 0 !== index || index >= t1) + this._invalidIndex$2(receiver, index, t1); + receiver[index] = value; + }, + $isList: true, + $asList: function() { + return [J.JSInt]; + }, + $isEfficientLength: true, + "%": ";Uint8Array" + }, + NativeTypedArray: { + "^": "NativeTypedData;", + get$length: function(receiver) { + return receiver.length; + }, + $isJavaScriptIndexingBehavior: true + }, + NativeTypedArrayOfInt: { + "^": "NativeTypedArray_ListMixin_FixedLengthListMixin;", + $isList: true, + $asList: function() { + return [J.JSInt]; + }, + $isEfficientLength: true + }, + NativeTypedArray_ListMixin: { + "^": "NativeTypedArray+ListMixin;", + $isList: true, + $asList: function() { + return [J.JSInt]; + }, + $isEfficientLength: true + }, + NativeTypedArray_ListMixin_FixedLengthListMixin: { + "^": "NativeTypedArray_ListMixin+FixedLengthListMixin;" + } +}], +["dart2js._js_primitives", "dart:_js_primitives", , H, { + "^": "", + printString: function(string) { + if (typeof dartPrint == "function") { + dartPrint(string); + return; + } + if (typeof console == "object" && typeof console.log == "function") { + console.log(string); + return; + } + if (typeof window == "object") + return; + if (typeof print == "function") { + print(string); + return; + } + throw "Unable to print message: " + String(string); + } +}], +]); +Isolate.$finishClasses($$, $, null); +$$ = null; + +// Runtime type support +J.JSString.$isString = true; +J.JSString.$isObject = true; +J.JSInt.$isint = true; +J.JSInt.$isObject = true; +W.Node.$isNode = true; +W.Node.$isObject = true; +J.JSNumber.$isObject = true; +P.Duration.$isObject = true; +P.Object.$isObject = true; +W.NodeValidator.$isObject = true; +W.MouseEvent.$isEvent = true; +W.MouseEvent.$isObject = true; +W.Event.$isEvent = true; +W.Event.$isObject = true; +J.JSBool.$isbool = true; +J.JSBool.$isObject = true; +H.RawReceivePortImpl.$isObject = true; +H._IsolateEvent.$isObject = true; +H._IsolateContext.$isObject = true; +P.Symbol.$isSymbol = true; +P.Symbol.$isObject = true; +P.StackTrace.$isStackTrace = true; +P.StackTrace.$isObject = true; +J.JSDouble.$isdouble = true; +J.JSDouble.$isObject = true; +W.Element.$isElement = true; +W.Element.$isNode = true; +W.Element.$isObject = true; +W._Html5NodeValidator.$is_Html5NodeValidator = true; +W._Html5NodeValidator.$isObject = true; +P._EventSink.$is_EventSink = true; +P._EventSink.$isObject = true; +// getInterceptor methods +J.getInterceptor = function(receiver) { + if (typeof receiver == "number") { + if (Math.floor(receiver) == receiver) + return J.JSInt.prototype; + return J.JSDouble.prototype; + } + if (typeof receiver == "string") + return J.JSString.prototype; + if (receiver == null) + return J.JSNull.prototype; + if (typeof receiver == "boolean") + return J.JSBool.prototype; + if (receiver.constructor == Array) + return J.JSArray.prototype; + if (typeof receiver != "object") + return receiver; + if (receiver instanceof P.Object) + return receiver; + return J.getNativeInterceptor(receiver); +}; +J.getInterceptor$asx = function(receiver) { + if (typeof receiver == "string") + return J.JSString.prototype; + if (receiver == null) + return receiver; + if (receiver.constructor == Array) + return J.JSArray.prototype; + if (typeof receiver != "object") + return receiver; + if (receiver instanceof P.Object) + return receiver; + return J.getNativeInterceptor(receiver); +}; +J.getInterceptor$ax = function(receiver) { + if (receiver == null) + return receiver; + if (receiver.constructor == Array) + return J.JSArray.prototype; + if (typeof receiver != "object") + return receiver; + if (receiver instanceof P.Object) + return receiver; + return J.getNativeInterceptor(receiver); +}; +J.getInterceptor$n = function(receiver) { + if (typeof receiver == "number") + return J.JSNumber.prototype; + if (receiver == null) + return receiver; + if (!(receiver instanceof P.Object)) + return J.UnknownJavaScriptObject.prototype; + return receiver; +}; +J.getInterceptor$ns = function(receiver) { + if (typeof receiver == "number") + return J.JSNumber.prototype; + if (typeof receiver == "string") + return J.JSString.prototype; + if (receiver == null) + return receiver; + if (!(receiver instanceof P.Object)) + return J.UnknownJavaScriptObject.prototype; + return receiver; +}; +J.getInterceptor$s = function(receiver) { + if (typeof receiver == "string") + return J.JSString.prototype; + if (receiver == null) + return receiver; + if (!(receiver instanceof P.Object)) + return J.UnknownJavaScriptObject.prototype; + return receiver; +}; +J.getInterceptor$x = function(receiver) { + if (receiver == null) + return receiver; + if (typeof receiver != "object") + return receiver; + if (receiver instanceof P.Object) + return receiver; + return J.getNativeInterceptor(receiver); +}; +J.$add$ns = function(receiver, a0) { + if (typeof receiver == "number" && typeof a0 == "number") + return receiver + a0; + return J.getInterceptor$ns(receiver).$add(receiver, a0); +}; +J.$eq = function(receiver, a0) { + if (receiver == null) + return a0 == null; + if (typeof receiver != "object") + return a0 != null && receiver === a0; + return J.getInterceptor(receiver).$eq(receiver, a0); +}; +J.$ge$n = function(receiver, a0) { + if (typeof receiver == "number" && typeof a0 == "number") + return receiver >= a0; + return J.getInterceptor$n(receiver).$ge(receiver, a0); +}; +J.$index$asx = function(receiver, a0) { + if (receiver.constructor == Array || typeof receiver == "string" || H.isJsIndexable(receiver, receiver[init.dispatchPropertyName])) + if (a0 >>> 0 === a0 && a0 < receiver.length) + return receiver[a0]; + return J.getInterceptor$asx(receiver).$index(receiver, a0); +}; +J.$indexSet$ax = function(receiver, a0, a1) { + if ((receiver.constructor == Array || H.isJsIndexable(receiver, receiver[init.dispatchPropertyName])) && !receiver.immutable$list && a0 >>> 0 === a0 && a0 < receiver.length) + return receiver[a0] = a1; + return J.getInterceptor$ax(receiver).$indexSet(receiver, a0, a1); +}; +J.$mul$ns = function(receiver, a0) { + if (typeof receiver == "number" && typeof a0 == "number") + return receiver * a0; + return J.getInterceptor$ns(receiver).$mul(receiver, a0); +}; +J.$sub$n = function(receiver, a0) { + if (typeof receiver == "number" && typeof a0 == "number") + return receiver - a0; + return J.getInterceptor$n(receiver).$sub(receiver, a0); +}; +J.addEventListener$3$x = function(receiver, a0, a1, a2) { + return J.getInterceptor$x(receiver).addEventListener$3(receiver, a0, a1, a2); +}; +J.contains$1$asx = function(receiver, a0) { + return J.getInterceptor$asx(receiver).contains$1(receiver, a0); +}; +J.createFragment$2$treeSanitizer$x = function(receiver, a0, a1) { + return J.getInterceptor$x(receiver).createFragment$2$treeSanitizer(receiver, a0, a1); +}; +J.createFragment$3$treeSanitizer$validator$x = function(receiver, a0, a1, a2) { + return J.getInterceptor$x(receiver).createFragment$3$treeSanitizer$validator(receiver, a0, a1, a2); +}; +J.elementAt$1$ax = function(receiver, a0) { + return J.getInterceptor$ax(receiver).elementAt$1(receiver, a0); +}; +J.forEach$1$ax = function(receiver, a0) { + return J.getInterceptor$ax(receiver).forEach$1(receiver, a0); +}; +J.get$_key$x = function(receiver) { + return J.getInterceptor$x(receiver).get$_key(receiver); +}; +J.get$error$x = function(receiver) { + return J.getInterceptor$x(receiver).get$error(receiver); +}; +J.get$hashCode$ = function(receiver) { + return J.getInterceptor(receiver).get$hashCode(receiver); +}; +J.get$isEmpty$asx = function(receiver) { + return J.getInterceptor$asx(receiver).get$isEmpty(receiver); +}; +J.get$iterator$ax = function(receiver) { + return J.getInterceptor$ax(receiver).get$iterator(receiver); +}; +J.get$length$asx = function(receiver) { + return J.getInterceptor$asx(receiver).get$length(receiver); +}; +J.get$name$x = function(receiver) { + return J.getInterceptor$x(receiver).get$name(receiver); +}; +J.get$nodes$x = function(receiver) { + return J.getInterceptor$x(receiver).get$nodes(receiver); +}; +J.get$onBlur$x = function(receiver) { + return J.getInterceptor$x(receiver).get$onBlur(receiver); +}; +J.get$onChange$x = function(receiver) { + return J.getInterceptor$x(receiver).get$onChange(receiver); +}; +J.get$onClick$x = function(receiver) { + return J.getInterceptor$x(receiver).get$onClick(receiver); +}; +J.get$onInput$x = function(receiver) { + return J.getInterceptor$x(receiver).get$onInput(receiver); +}; +J.get$value$x = function(receiver) { + return J.getInterceptor$x(receiver).get$value(receiver); +}; +J.preventDefault$0$x = function(receiver) { + return J.getInterceptor$x(receiver).preventDefault$0(receiver); +}; +J.remove$0$ax = function(receiver) { + return J.getInterceptor$ax(receiver).remove$0(receiver); +}; +J.remove$1$ax = function(receiver, a0) { + return J.getInterceptor$ax(receiver).remove$1(receiver, a0); +}; +J.removeEventListener$3$x = function(receiver, a0, a1, a2) { + return J.getInterceptor$x(receiver).removeEventListener$3(receiver, a0, a1, a2); +}; +J.round$0$n = function(receiver) { + return J.getInterceptor$n(receiver).round$0(receiver); +}; +J.send$1$x = function(receiver, a0) { + return J.getInterceptor$x(receiver).send$1(receiver, a0); +}; +J.set$disabled$x = function(receiver, value) { + return J.getInterceptor$x(receiver).set$disabled(receiver, value); +}; +J.set$href$x = function(receiver, value) { + return J.getInterceptor$x(receiver).set$href(receiver, value); +}; +J.set$innerHtml$x = function(receiver, value) { + return J.getInterceptor$x(receiver).set$innerHtml(receiver, value); +}; +J.set$value$x = function(receiver, value) { + return J.getInterceptor$x(receiver).set$value(receiver, value); +}; +J.toList$0$ax = function(receiver) { + return J.getInterceptor$ax(receiver).toList$0(receiver); +}; +J.toLowerCase$0$s = function(receiver) { + return J.getInterceptor$s(receiver).toLowerCase$0(receiver); +}; +J.toString$0 = function(receiver) { + return J.getInterceptor(receiver).toString$0(receiver); +}; +J.toStringAsFixed$1$n = function(receiver, a0) { + return J.getInterceptor$n(receiver).toStringAsFixed$1(receiver, a0); +}; +J.trim$0$s = function(receiver) { + return J.getInterceptor$s(receiver).trim$0(receiver); +}; +C.C_DynamicRuntimeType = new H.DynamicRuntimeType(); +C.C_OutOfMemoryError = new P.OutOfMemoryError(); +C.C__RootZone = new P._RootZone(); +C.Duration_0 = new P.Duration(0); +C.EventStreamProvider_blur = new W.EventStreamProvider("blur"); +C.EventStreamProvider_change = new W.EventStreamProvider("change"); +C.EventStreamProvider_click = new W.EventStreamProvider("click"); +C.EventStreamProvider_input = new W.EventStreamProvider("input"); +C.JSArray_methods = J.JSArray.prototype; +C.JSInt_methods = J.JSInt.prototype; +C.JSNumber_methods = J.JSNumber.prototype; +C.JSString_methods = J.JSString.prototype; +C.JS_CONST_0 = function(hooks) { + if (typeof dartExperimentalFixupGetTag != "function") return hooks; + hooks.getTag = dartExperimentalFixupGetTag(hooks.getTag); +}; +C.JS_CONST_Fs4 = function(hooks) { return hooks; } +; +C.JS_CONST_IX5 = function getTagFallback(o) { + var constructor = o.constructor; + if (typeof constructor == "function") { + var name = constructor.name; + if (typeof name == "string" + && name !== "" + && name !== "Object" + && name !== "Function.prototype") { + return name; + } + } + var s = Object.prototype.toString.call(o); + return s.substring(8, s.length - 1); +}; +C.JS_CONST_QJm = function(getTagFallback) { + return function(hooks) { + if (typeof navigator != "object") return hooks; + var ua = navigator.userAgent; + if (ua.indexOf("DumpRenderTree") >= 0) return hooks; + if (ua.indexOf("Chrome") >= 0) { + function confirm(p) { + return typeof window == "object" && window[p] && window[p].name == p; + } + if (confirm("Window") && confirm("HTMLElement")) return hooks; + } + hooks.getTag = getTagFallback; + }; +}; +C.JS_CONST_U4w = function(hooks) { + var userAgent = typeof navigator == "object" ? navigator.userAgent : ""; + if (userAgent.indexOf("Firefox") == -1) return hooks; + var getTag = hooks.getTag; + var quickMap = { + "BeforeUnloadEvent": "Event", + "DataTransfer": "Clipboard", + "GeoGeolocation": "Geolocation", + "WorkerMessageEvent": "MessageEvent", + "XMLDocument": "!Document"}; + function getTagFirefox(o) { + var tag = getTag(o); + return quickMap[tag] || tag; + } + hooks.getTag = getTagFirefox; +}; +C.JS_CONST_aQP = function() { + function typeNameInChrome(o) { + var name = o.constructor.name; + if (name) return name; + var s = Object.prototype.toString.call(o); + return s.substring(8, s.length - 1); + } + function getUnknownTag(object, tag) { + if (/^HTML[A-Z].*Element$/.test(tag)) { + var name = Object.prototype.toString.call(object); + if (name == "[object Object]") return null; + return "HTMLElement"; + } + } + function getUnknownTagGenericBrowser(object, tag) { + if (object instanceof HTMLElement) return "HTMLElement"; + return getUnknownTag(object, tag); + } + function prototypeForTag(tag) { + if (typeof window == "undefined") return null; + if (typeof window[tag] == "undefined") return null; + var constructor = window[tag]; + if (typeof constructor != "function") return null; + return constructor.prototype; + } + function discriminator(tag) { return null; } + var isBrowser = typeof navigator == "object"; + return { + getTag: typeNameInChrome, + getUnknownTag: isBrowser ? getUnknownTagGenericBrowser : getUnknownTag, + prototypeForTag: prototypeForTag, + discriminator: discriminator }; +}; +C.JS_CONST_gkc = function(hooks) { + var userAgent = typeof navigator == "object" ? navigator.userAgent : ""; + if (userAgent.indexOf("Trident/") == -1) return hooks; + var getTag = hooks.getTag; + var quickMap = { + "BeforeUnloadEvent": "Event", + "DataTransfer": "Clipboard", + "HTMLDDElement": "HTMLElement", + "HTMLDTElement": "HTMLElement", + "HTMLPhraseElement": "HTMLElement", + "Position": "Geoposition" + }; + function getTagIE(o) { + var tag = getTag(o); + var newTag = quickMap[tag]; + if (newTag) return newTag; + if (tag == "Object") { + if (window.DataView && (o instanceof window.DataView)) return "DataView"; + } + return tag; + } + function prototypeForTagIE(tag) { + var constructor = window[tag]; + if (constructor == null) return null; + return constructor.prototype; + } + hooks.getTag = getTagIE; + hooks.prototypeForTag = prototypeForTagIE; +}; +C.JS_CONST_rr7 = function(hooks) { + var getTag = hooks.getTag; + var prototypeForTag = hooks.prototypeForTag; + function getTagFixed(o) { + var tag = getTag(o); + if (tag == "Document") { + if (!!o.xmlVersion) return "!Document"; + return "!HTMLDocument"; + } + return tag; + } + function prototypeForTagFixed(tag) { + if (tag == "Document") return null; + return prototypeForTag(tag); + } + hooks.getTag = getTagFixed; + hooks.prototypeForTag = prototypeForTagFixed; +}; +C.JsonCodec_null_null = new P.JsonCodec(null, null); +C.JsonDecoder_null = new P.JsonDecoder(null); +C.JsonEncoder_null = new P.JsonEncoder(null); +Isolate.makeConstantList = function(list) { + list.immutable$list = init; + list.fixed$length = init; + return list; +}; +C.List_1GN = H.setRuntimeTypeInfo(Isolate.makeConstantList(["*::class", "*::dir", "*::draggable", "*::hidden", "*::id", "*::inert", "*::itemprop", "*::itemref", "*::itemscope", "*::lang", "*::spellcheck", "*::title", "*::translate", "A::accesskey", "A::coords", "A::hreflang", "A::name", "A::shape", "A::tabindex", "A::target", "A::type", "AREA::accesskey", "AREA::alt", "AREA::coords", "AREA::nohref", "AREA::shape", "AREA::tabindex", "AREA::target", "AUDIO::controls", "AUDIO::loop", "AUDIO::mediagroup", "AUDIO::muted", "AUDIO::preload", "BDO::dir", "BODY::alink", "BODY::bgcolor", "BODY::link", "BODY::text", "BODY::vlink", "BR::clear", "BUTTON::accesskey", "BUTTON::disabled", "BUTTON::name", "BUTTON::tabindex", "BUTTON::type", "BUTTON::value", "CANVAS::height", "CANVAS::width", "CAPTION::align", "COL::align", "COL::char", "COL::charoff", "COL::span", "COL::valign", "COL::width", "COLGROUP::align", "COLGROUP::char", "COLGROUP::charoff", "COLGROUP::span", "COLGROUP::valign", "COLGROUP::width", "COMMAND::checked", "COMMAND::command", "COMMAND::disabled", "COMMAND::label", "COMMAND::radiogroup", "COMMAND::type", "DATA::value", "DEL::datetime", "DETAILS::open", "DIR::compact", "DIV::align", "DL::compact", "FIELDSET::disabled", "FONT::color", "FONT::face", "FONT::size", "FORM::accept", "FORM::autocomplete", "FORM::enctype", "FORM::method", "FORM::name", "FORM::novalidate", "FORM::target", "FRAME::name", "H1::align", "H2::align", "H3::align", "H4::align", "H5::align", "H6::align", "HR::align", "HR::noshade", "HR::size", "HR::width", "HTML::version", "IFRAME::align", "IFRAME::frameborder", "IFRAME::height", "IFRAME::marginheight", "IFRAME::marginwidth", "IFRAME::width", "IMG::align", "IMG::alt", "IMG::border", "IMG::height", "IMG::hspace", "IMG::ismap", "IMG::name", "IMG::usemap", "IMG::vspace", "IMG::width", "INPUT::accept", "INPUT::accesskey", "INPUT::align", "INPUT::alt", "INPUT::autocomplete", "INPUT::checked", "INPUT::disabled", "INPUT::inputmode", "INPUT::ismap", "INPUT::list", "INPUT::max", "INPUT::maxlength", "INPUT::min", "INPUT::multiple", "INPUT::name", "INPUT::placeholder", "INPUT::readonly", "INPUT::required", "INPUT::size", "INPUT::step", "INPUT::tabindex", "INPUT::type", "INPUT::usemap", "INPUT::value", "INS::datetime", "KEYGEN::disabled", "KEYGEN::keytype", "KEYGEN::name", "LABEL::accesskey", "LABEL::for", "LEGEND::accesskey", "LEGEND::align", "LI::type", "LI::value", "LINK::sizes", "MAP::name", "MENU::compact", "MENU::label", "MENU::type", "METER::high", "METER::low", "METER::max", "METER::min", "METER::value", "OBJECT::typemustmatch", "OL::compact", "OL::reversed", "OL::start", "OL::type", "OPTGROUP::disabled", "OPTGROUP::label", "OPTION::disabled", "OPTION::label", "OPTION::selected", "OPTION::value", "OUTPUT::for", "OUTPUT::name", "P::align", "PRE::width", "PROGRESS::max", "PROGRESS::min", "PROGRESS::value", "SELECT::autocomplete", "SELECT::disabled", "SELECT::multiple", "SELECT::name", "SELECT::required", "SELECT::size", "SELECT::tabindex", "SOURCE::type", "TABLE::align", "TABLE::bgcolor", "TABLE::border", "TABLE::cellpadding", "TABLE::cellspacing", "TABLE::frame", "TABLE::rules", "TABLE::summary", "TABLE::width", "TBODY::align", "TBODY::char", "TBODY::charoff", "TBODY::valign", "TD::abbr", "TD::align", "TD::axis", "TD::bgcolor", "TD::char", "TD::charoff", "TD::colspan", "TD::headers", "TD::height", "TD::nowrap", "TD::rowspan", "TD::scope", "TD::valign", "TD::width", "TEXTAREA::accesskey", "TEXTAREA::autocomplete", "TEXTAREA::cols", "TEXTAREA::disabled", "TEXTAREA::inputmode", "TEXTAREA::name", "TEXTAREA::placeholder", "TEXTAREA::readonly", "TEXTAREA::required", "TEXTAREA::rows", "TEXTAREA::tabindex", "TEXTAREA::wrap", "TFOOT::align", "TFOOT::char", "TFOOT::charoff", "TFOOT::valign", "TH::abbr", "TH::align", "TH::axis", "TH::bgcolor", "TH::char", "TH::charoff", "TH::colspan", "TH::headers", "TH::height", "TH::nowrap", "TH::rowspan", "TH::scope", "TH::valign", "TH::width", "THEAD::align", "THEAD::char", "THEAD::charoff", "THEAD::valign", "TR::align", "TR::bgcolor", "TR::char", "TR::charoff", "TR::valign", "TRACK::default", "TRACK::kind", "TRACK::label", "TRACK::srclang", "UL::compact", "UL::type", "VIDEO::controls", "VIDEO::height", "VIDEO::loop", "VIDEO::mediagroup", "VIDEO::muted", "VIDEO::preload", "VIDEO::width"]), [J.JSString]); +C.List_wSV = H.setRuntimeTypeInfo(Isolate.makeConstantList(["bind", "if", "ref", "repeat", "syntax"]), [J.JSString]); +C.List_yrN = H.setRuntimeTypeInfo(Isolate.makeConstantList(["A::href", "AREA::href", "BLOCKQUOTE::cite", "BODY::background", "COMMAND::icon", "DEL::cite", "FORM::action", "IMG::src", "INPUT::src", "INS::cite", "Q::cite", "VIDEO::poster"]), [J.JSString]); +C.NodeList_methods = W.NodeList.prototype; +C.PlainJavaScriptObject_methods = J.PlainJavaScriptObject.prototype; +C.UnknownJavaScriptObject_methods = J.UnknownJavaScriptObject.prototype; +C.Window_methods = W.Window.prototype; +$.libraries_to_load = {}; +$.Primitives_mirrorFunctionCacheName = "$cachedFunction"; +$.Primitives_mirrorInvokeCacheName = "$cachedInvocation"; +$.Closure_functionCounter = 0; +$.BoundClosure_selfFieldNameCache = null; +$.BoundClosure_receiverFieldNameCache = null; +$.RuntimeFunctionType_inAssert = false; +$.getTagFunction = null; +$.alternateTagFunction = null; +$.prototypeForTagFunction = null; +$.dispatchRecordsForInstanceTags = null; +$.interceptorsForUncacheableTags = null; +$.initNativeDispatchFlag = null; +$.owner = null; +$.balance = null; +$.error = null; +$.number = null; +$.amount = null; +$.btn_other = null; +$.btn_deposit = null; +$.btn_interest = null; +$.bac = null; +$.printToZone = null; +$._nextCallback = null; +$._lastCallback = null; +$.Zone__current = C.C__RootZone; +$.Expando__keyCount = 0; +$.Element__parseDocument = null; +$.Element__parseRange = null; +$.Element__defaultValidator = null; +$.Element__defaultSanitizer = null; +$.Device__isOpera = null; +$.Device__isWebKit = null; +Isolate.$lazy($, "globalThis", "globalThis", "get$globalThis", function() { + return function() { return this; }(); +}); +Isolate.$lazy($, "globalWindow", "globalWindow", "get$globalWindow", function() { + return $.get$globalThis().window; +}); +Isolate.$lazy($, "globalWorker", "globalWorker", "get$globalWorker", function() { + return $.get$globalThis().Worker; +}); +Isolate.$lazy($, "globalPostMessageDefined", "globalPostMessageDefined", "get$globalPostMessageDefined", function() { + return $.get$globalThis().postMessage !== void 0; +}); +Isolate.$lazy($, "thisScript", "IsolateNatives_thisScript", "get$IsolateNatives_thisScript", function() { + return H.IsolateNatives_computeThisScript(); +}); +Isolate.$lazy($, "workerIds", "IsolateNatives_workerIds", "get$IsolateNatives_workerIds", function() { + return new P.Expando(null); +}); +Isolate.$lazy($, "noSuchMethodPattern", "TypeErrorDecoder_noSuchMethodPattern", "get$TypeErrorDecoder_noSuchMethodPattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokeCallErrorOn({ toString: function() { return "$receiver$"; } })); +}); +Isolate.$lazy($, "notClosurePattern", "TypeErrorDecoder_notClosurePattern", "get$TypeErrorDecoder_notClosurePattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokeCallErrorOn({ $method$: null, toString: function() { return "$receiver$"; } })); +}); +Isolate.$lazy($, "nullCallPattern", "TypeErrorDecoder_nullCallPattern", "get$TypeErrorDecoder_nullCallPattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokeCallErrorOn(null)); +}); +Isolate.$lazy($, "nullLiteralCallPattern", "TypeErrorDecoder_nullLiteralCallPattern", "get$TypeErrorDecoder_nullLiteralCallPattern", function() { + return H.TypeErrorDecoder_extractPattern(function() { + var $argumentsExpr$ = '$arguments$' + try { + null.$method$($argumentsExpr$); + } catch (e) { + return e.message; + } +}()); +}); +Isolate.$lazy($, "undefinedCallPattern", "TypeErrorDecoder_undefinedCallPattern", "get$TypeErrorDecoder_undefinedCallPattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokeCallErrorOn(void 0)); +}); +Isolate.$lazy($, "undefinedLiteralCallPattern", "TypeErrorDecoder_undefinedLiteralCallPattern", "get$TypeErrorDecoder_undefinedLiteralCallPattern", function() { + return H.TypeErrorDecoder_extractPattern(function() { + var $argumentsExpr$ = '$arguments$' + try { + (void 0).$method$($argumentsExpr$); + } catch (e) { + return e.message; + } +}()); +}); +Isolate.$lazy($, "nullPropertyPattern", "TypeErrorDecoder_nullPropertyPattern", "get$TypeErrorDecoder_nullPropertyPattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokePropertyErrorOn(null)); +}); +Isolate.$lazy($, "nullLiteralPropertyPattern", "TypeErrorDecoder_nullLiteralPropertyPattern", "get$TypeErrorDecoder_nullLiteralPropertyPattern", function() { + return H.TypeErrorDecoder_extractPattern(function() { + try { + null.$method$; + } catch (e) { + return e.message; + } +}()); +}); +Isolate.$lazy($, "undefinedPropertyPattern", "TypeErrorDecoder_undefinedPropertyPattern", "get$TypeErrorDecoder_undefinedPropertyPattern", function() { + return H.TypeErrorDecoder_extractPattern(H.TypeErrorDecoder_provokePropertyErrorOn(void 0)); +}); +Isolate.$lazy($, "undefinedLiteralPropertyPattern", "TypeErrorDecoder_undefinedLiteralPropertyPattern", "get$TypeErrorDecoder_undefinedLiteralPropertyPattern", function() { + return H.TypeErrorDecoder_extractPattern(function() { + try { + (void 0).$method$; + } catch (e) { + return e.message; + } +}()); +}); +Isolate.$lazy($, "_toStringList", "IterableMixinWorkaround__toStringList", "get$IterableMixinWorkaround__toStringList", function() { + return []; +}); +Isolate.$lazy($, "_toStringVisiting", "_toStringVisiting", "get$_toStringVisiting", function() { + return P.HashSet_HashSet$identity(null); +}); +Isolate.$lazy($, "_toStringList", "Maps__toStringList", "get$Maps__toStringList", function() { + return []; +}); +Isolate.$lazy($, "_allowedElements", "_Html5NodeValidator__allowedElements", "get$_Html5NodeValidator__allowedElements", function() { + var t1 = P.LinkedHashSet_LinkedHashSet(null, null, null, null); + t1.addAll$1(0, ["A", "ABBR", "ACRONYM", "ADDRESS", "AREA", "ARTICLE", "ASIDE", "AUDIO", "B", "BDI", "BDO", "BIG", "BLOCKQUOTE", "BR", "BUTTON", "CANVAS", "CAPTION", "CENTER", "CITE", "CODE", "COL", "COLGROUP", "COMMAND", "DATA", "DATALIST", "DD", "DEL", "DETAILS", "DFN", "DIR", "DIV", "DL", "DT", "EM", "FIELDSET", "FIGCAPTION", "FIGURE", "FONT", "FOOTER", "FORM", "H1", "H2", "H3", "H4", "H5", "H6", "HEADER", "HGROUP", "HR", "I", "IFRAME", "IMG", "INPUT", "INS", "KBD", "LABEL", "LEGEND", "LI", "MAP", "MARK", "MENU", "METER", "NAV", "NOBR", "OL", "OPTGROUP", "OPTION", "OUTPUT", "P", "PRE", "PROGRESS", "Q", "S", "SAMP", "SECTION", "SELECT", "SMALL", "SOURCE", "SPAN", "STRIKE", "STRONG", "SUB", "SUMMARY", "SUP", "TABLE", "TBODY", "TD", "TEXTAREA", "TFOOT", "TH", "THEAD", "TIME", "TR", "TRACK", "TT", "U", "UL", "VAR", "VIDEO", "WBR"]); + return t1; +}); +Isolate.$lazy($, "_attributeValidators", "_Html5NodeValidator__attributeValidators", "get$_Html5NodeValidator__attributeValidators", function() { + return H.fillLiteralMap([], P.LinkedHashMap_LinkedHashMap(null, null, null, null, null)); +}); +// Native classes + +init.functionAliases = {}; +; +init.metadata = [{func: "dynamic__String", args: [J.JSString]}, +{func: "void_", void: true}, +{func: "dynamic__Event", args: [W.Event]}, +{func: "bool__dynamic_dynamic", ret: J.JSBool, args: [null, null]}, +{func: "int__dynamic", ret: J.JSInt, args: [null]}, +{func: "Object__dynamic", ret: P.Object, args: [null]}, +{func: "bool__Object_Object", ret: J.JSBool, args: [P.Object, P.Object]}, +{func: "int__Object", ret: J.JSInt, args: [P.Object]}, +{func: "bool__Element_String_String__Html5NodeValidator", ret: J.JSBool, args: [W.Element, J.JSString, J.JSString, W._Html5NodeValidator]}, +{func: "args0"}, +{func: "args2", args: [null, null]}, +{func: "args1", args: [null]}, +{func: "dynamic__dynamic_String", args: [null, J.JSString]}, +{func: "void__dynamic__StackTrace", void: true, args: [null], opt: [P.StackTrace]}, +, +{func: "dynamic__dynamic__dynamic", args: [null], opt: [null]}, +{func: "bool_", ret: J.JSBool}, +{func: "dynamic__dynamic_StackTrace", args: [null, P.StackTrace]}, +{func: "dynamic__Symbol_dynamic", args: [P.Symbol, null]}, +{func: "int__String", ret: J.JSInt, args: [J.JSString]}, +{func: "double__String", ret: J.JSDouble, args: [J.JSString]}, +{func: "String__int", ret: J.JSString, args: [J.JSInt]}, +{func: "void__Node", void: true, args: [W.Node]}, +]; +$ = null; +Isolate = Isolate.$finishIsolateConstructor(Isolate); +$ = new Isolate(); +function convertToFastObject(properties) { + function MyClass() {}; + MyClass.prototype = properties; + new MyClass(); + return properties; +} +A = convertToFastObject(A); +B = convertToFastObject(B); +C = convertToFastObject(C); +D = convertToFastObject(D); +E = convertToFastObject(E); +F = convertToFastObject(F); +G = convertToFastObject(G); +H = convertToFastObject(H); +J = convertToFastObject(J); +K = convertToFastObject(K); +L = convertToFastObject(L); +M = convertToFastObject(M); +N = convertToFastObject(N); +O = convertToFastObject(O); +P = convertToFastObject(P); +Q = convertToFastObject(Q); +R = convertToFastObject(R); +S = convertToFastObject(S); +T = convertToFastObject(T); +U = convertToFastObject(U); +V = convertToFastObject(V); +W = convertToFastObject(W); +X = convertToFastObject(X); +Y = convertToFastObject(Y); +Z = convertToFastObject(Z); +!function() { + function intern(s) { + var o = {}; + o[s] = 1; + return Object.keys(convertToFastObject(o))[0]; + } + init.getIsolateTag = function(name) { + return intern("___dart_" + name + init.isolateTag); + }; + var tableProperty = "___dart_isolate_tags_"; + var usedProperties = Object[tableProperty] || (Object[tableProperty] = Object.create(null)); + var rootProperty = "_ZxYxX"; + for (var i = 0;; i++) { + var property = intern(rootProperty + "_" + i + "_"); + if (!(property in usedProperties)) { + usedProperties[property] = 1; + init.isolateTag = property; + break; + } + } +}(); +init.dispatchPropertyName = init.getIsolateTag("dispatch_record"); +// BEGIN invoke [main]. +;(function (callback) { + if (typeof document === "undefined") { + callback(null); + return; + } + if (document.currentScript) { + callback(document.currentScript); + return; + } + + var scripts = document.scripts; + function onLoad(event) { + for (var i = 0; i < scripts.length; ++i) { + scripts[i].removeEventListener("load", onLoad, false); + } + callback(event.target); + } + for (var i = 0; i < scripts.length; ++i) { + scripts[i].addEventListener("load", onLoad, false); + } +})(function(currentScript) { + init.currentScript = currentScript; + + if (typeof dartMainRunner === "function") { + dartMainRunner((function(a){H.startRootIsolate(M.main$closure(),a)}), []); + } else { + (function(a){H.startRootIsolate(M.main$closure(),a)})([]); + } +}); +// END invoke [main]. +function init() { + Isolate.$isolateProperties = {}; + function generateAccessor(fieldDescriptor, accessors, cls) { + var fieldInformation = fieldDescriptor.split("-"); + var field = fieldInformation[0]; + var len = field.length; + var code = field.charCodeAt(len - 1); + var reflectable; + if (fieldInformation.length > 1) + reflectable = true; + else + reflectable = false; + code = code >= 60 && code <= 64 ? code - 59 : code >= 123 && code <= 126 ? code - 117 : code >= 37 && code <= 43 ? code - 27 : 0; + if (code) { + var getterCode = code & 3; + var setterCode = code >> 2; + var accessorName = field = field.substring(0, len - 1); + var divider = field.indexOf(":"); + if (divider > 0) { + accessorName = field.substring(0, divider); + field = field.substring(divider + 1); + } + if (getterCode) { + var args = getterCode & 2 ? "receiver" : ""; + var receiver = getterCode & 1 ? "this" : "receiver"; + var body = "return " + receiver + "." + field; + var property = cls + ".prototype.get$" + accessorName + "="; + var fn = "function(" + args + "){" + body + "}"; + if (reflectable) + accessors.push(property + "$reflectable(" + fn + ");\n"); + else + accessors.push(property + fn + ";\n"); + } + if (setterCode) { + var args = setterCode & 2 ? "receiver, value" : "value"; + var receiver = setterCode & 1 ? "this" : "receiver"; + var body = receiver + "." + field + " = value"; + var property = cls + ".prototype.set$" + accessorName + "="; + var fn = "function(" + args + "){" + body + "}"; + if (reflectable) + accessors.push(property + "$reflectable(" + fn + ");\n"); + else + accessors.push(property + fn + ";\n"); + } + } + return field; + } + Isolate.$isolateProperties.$generateAccessor = generateAccessor; + function defineClass(name, cls, fields) { + var accessors = []; + var str = "function " + cls + "("; + var body = ""; + for (var i = 0; i < fields.length; i++) { + if (i != 0) + str += ", "; + var field = generateAccessor(fields[i], accessors, cls); + var parameter = "parameter_" + field; + str += parameter; + body += "this." + field + " = " + parameter + ";\n"; + } + str += ") {\n" + body + "}\n"; + str += cls + ".builtin$cls=\"" + name + "\";\n"; + str += "$desc=$collectedClasses." + cls + ";\n"; + str += "if($desc instanceof Array) $desc = $desc[1];\n"; + str += cls + ".prototype = $desc;\n"; + if (typeof defineClass.name != "string") { + str += cls + ".name=\"" + cls + "\";\n"; + } + str += accessors.join(""); + return str; + } + var inheritFrom = function() { + function tmp() { + } + var hasOwnProperty = Object.prototype.hasOwnProperty; + return function(constructor, superConstructor) { + tmp.prototype = superConstructor.prototype; + var object = new tmp(); + var properties = constructor.prototype; + for (var member in properties) + if (hasOwnProperty.call(properties, member)) + object[member] = properties[member]; + object.constructor = constructor; + constructor.prototype = object; + return object; + }; + }(); + Isolate.$finishClasses = function(collectedClasses, isolateProperties, existingIsolateProperties) { + var pendingClasses = {}; + if (!init.allClasses) + init.allClasses = {}; + var allClasses = init.allClasses; + var hasOwnProperty = Object.prototype.hasOwnProperty; + if (typeof dart_precompiled == "function") { + var constructors = dart_precompiled(collectedClasses); + } else { + var combinedConstructorFunction = "function $reflectable(fn){fn.$reflectable=1;return fn};\n" + "var $desc;\n"; + var constructorsList = []; + } + for (var cls in collectedClasses) { + if (hasOwnProperty.call(collectedClasses, cls)) { + var desc = collectedClasses[cls]; + if (desc instanceof Array) + desc = desc[1]; + var classData = desc["^"], supr, name = cls, fields = classData; + if (typeof classData == "string") { + var split = classData.split("/"); + if (split.length == 2) { + name = split[0]; + fields = split[1]; + } + } + var s = fields.split(";"); + fields = s[1] == "" ? [] : s[1].split(","); + supr = s[0]; + split = supr.split(":"); + if (split.length == 2) { + supr = split[0]; + var functionSignature = split[1]; + if (functionSignature) + desc.$signature = function(s) { + return function() { + return init.metadata[s]; + }; + }(functionSignature); + } + if (supr && supr.indexOf("+") > 0) { + s = supr.split("+"); + supr = s[0]; + var mixin = collectedClasses[s[1]]; + if (mixin instanceof Array) + mixin = mixin[1]; + for (var d in mixin) { + if (hasOwnProperty.call(mixin, d) && !hasOwnProperty.call(desc, d)) + desc[d] = mixin[d]; + } + } + if (typeof dart_precompiled != "function") { + combinedConstructorFunction += defineClass(name, cls, fields); + constructorsList.push(cls); + } + if (supr) + pendingClasses[cls] = supr; + } + } + if (typeof dart_precompiled != "function") { + combinedConstructorFunction += "return [\n " + constructorsList.join(",\n ") + "\n]"; + var constructors = new Function("$collectedClasses", combinedConstructorFunction)(collectedClasses); + combinedConstructorFunction = null; + } + for (var i = 0; i < constructors.length; i++) { + var constructor = constructors[i]; + var cls = constructor.name; + var desc = collectedClasses[cls]; + var globalObject = isolateProperties; + if (desc instanceof Array) { + globalObject = desc[0] || isolateProperties; + desc = desc[1]; + } + allClasses[cls] = constructor; + globalObject[cls] = constructor; + } + constructors = null; + var finishedClasses = {}; + init.interceptorsByTag = Object.create(null); + init.leafTags = {}; + function finishClass(cls) { + var hasOwnProperty = Object.prototype.hasOwnProperty; + if (hasOwnProperty.call(finishedClasses, cls)) + return; + finishedClasses[cls] = true; + var superclass = pendingClasses[cls]; + if (!superclass || typeof superclass != "string") + return; + finishClass(superclass); + var constructor = allClasses[cls]; + var superConstructor = allClasses[superclass]; + if (!superConstructor) + superConstructor = existingIsolateProperties[superclass]; + var prototype = inheritFrom(constructor, superConstructor); + if (hasOwnProperty.call(prototype, "%")) { + var nativeSpec = prototype["%"].split(";"); + if (nativeSpec[0]) { + var tags = nativeSpec[0].split("|"); + for (var i = 0; i < tags.length; i++) { + init.interceptorsByTag[tags[i]] = constructor; + init.leafTags[tags[i]] = true; + } + } + if (nativeSpec[1]) { + tags = nativeSpec[1].split("|"); + if (nativeSpec[2]) { + var subclasses = nativeSpec[2].split("|"); + for (var i = 0; i < subclasses.length; i++) { + var subclass = allClasses[subclasses[i]]; + subclass.$nativeSuperclassTag = tags[0]; + } + } + for (i = 0; i < tags.length; i++) { + init.interceptorsByTag[tags[i]] = constructor; + init.leafTags[tags[i]] = false; + } + } + } + } + for (var cls in pendingClasses) + finishClass(cls); + }; + Isolate.$lazy = function(prototype, staticName, fieldName, getterName, lazyValue) { + var sentinelUndefined = {}; + var sentinelInProgress = {}; + prototype[fieldName] = sentinelUndefined; + prototype[getterName] = function() { + var result = $[fieldName]; + try { + if (result === sentinelUndefined) { + $[fieldName] = sentinelInProgress; + try { + result = $[fieldName] = lazyValue(); + } finally { + if (result === sentinelUndefined) { + if ($[fieldName] === sentinelInProgress) { + $[fieldName] = null; + } + } + } + } else { + if (result === sentinelInProgress) + H.throwCyclicInit(staticName); + } + return result; + } finally { + $[getterName] = function() { + return this[fieldName]; + }; + } + }; + }; + Isolate.$finishIsolateConstructor = function(oldIsolate) { + var isolateProperties = oldIsolate.$isolateProperties; + function Isolate() { + var hasOwnProperty = Object.prototype.hasOwnProperty; + for (var staticName in isolateProperties) + if (hasOwnProperty.call(isolateProperties, staticName)) + this[staticName] = isolateProperties[staticName]; + function ForceEfficientMap() { + } + ForceEfficientMap.prototype = this; + new ForceEfficientMap(); + } + Isolate.prototype = oldIsolate.prototype; + Isolate.prototype.constructor = Isolate; + Isolate.$isolateProperties = isolateProperties; + Isolate.$finishClasses = oldIsolate.$finishClasses; + Isolate.makeConstantList = oldIsolate.makeConstantList; + return Isolate; + }; +} +})() +function dart_precompiled($collectedClasses) { + var $desc; + function HtmlElement() { + } + HtmlElement.builtin$cls = "HtmlElement"; + if (!"name" in HtmlElement) + HtmlElement.name = "HtmlElement"; + $desc = $collectedClasses.HtmlElement; + if ($desc instanceof Array) + $desc = $desc[1]; + HtmlElement.prototype = $desc; + function AnchorElement() { + } + AnchorElement.builtin$cls = "AnchorElement"; + if (!"name" in AnchorElement) + AnchorElement.name = "AnchorElement"; + $desc = $collectedClasses.AnchorElement; + if ($desc instanceof Array) + $desc = $desc[1]; + AnchorElement.prototype = $desc; + AnchorElement.prototype.get$hostname = function(receiver) { + return receiver.hostname; + }; + AnchorElement.prototype.set$href = function(receiver, v) { + return receiver.href = v; + }; + AnchorElement.prototype.get$port = function(receiver) { + return receiver.port; + }; + AnchorElement.prototype.get$protocol = function(receiver) { + return receiver.protocol; + }; + function AnimationEvent() { + } + AnimationEvent.builtin$cls = "AnimationEvent"; + if (!"name" in AnimationEvent) + AnimationEvent.name = "AnimationEvent"; + $desc = $collectedClasses.AnimationEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + AnimationEvent.prototype = $desc; + function AreaElement() { + } + AreaElement.builtin$cls = "AreaElement"; + if (!"name" in AreaElement) + AreaElement.name = "AreaElement"; + $desc = $collectedClasses.AreaElement; + if ($desc instanceof Array) + $desc = $desc[1]; + AreaElement.prototype = $desc; + AreaElement.prototype.get$hostname = function(receiver) { + return receiver.hostname; + }; + AreaElement.prototype.set$href = function(receiver, v) { + return receiver.href = v; + }; + AreaElement.prototype.get$port = function(receiver) { + return receiver.port; + }; + AreaElement.prototype.get$protocol = function(receiver) { + return receiver.protocol; + }; + function AudioElement() { + } + AudioElement.builtin$cls = "AudioElement"; + if (!"name" in AudioElement) + AudioElement.name = "AudioElement"; + $desc = $collectedClasses.AudioElement; + if ($desc instanceof Array) + $desc = $desc[1]; + AudioElement.prototype = $desc; + function AutocompleteErrorEvent() { + } + AutocompleteErrorEvent.builtin$cls = "AutocompleteErrorEvent"; + if (!"name" in AutocompleteErrorEvent) + AutocompleteErrorEvent.name = "AutocompleteErrorEvent"; + $desc = $collectedClasses.AutocompleteErrorEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + AutocompleteErrorEvent.prototype = $desc; + function BRElement() { + } + BRElement.builtin$cls = "BRElement"; + if (!"name" in BRElement) + BRElement.name = "BRElement"; + $desc = $collectedClasses.BRElement; + if ($desc instanceof Array) + $desc = $desc[1]; + BRElement.prototype = $desc; + function BaseElement() { + } + BaseElement.builtin$cls = "BaseElement"; + if (!"name" in BaseElement) + BaseElement.name = "BaseElement"; + $desc = $collectedClasses.BaseElement; + if ($desc instanceof Array) + $desc = $desc[1]; + BaseElement.prototype = $desc; + BaseElement.prototype.set$href = function(receiver, v) { + return receiver.href = v; + }; + function BeforeLoadEvent() { + } + BeforeLoadEvent.builtin$cls = "BeforeLoadEvent"; + if (!"name" in BeforeLoadEvent) + BeforeLoadEvent.name = "BeforeLoadEvent"; + $desc = $collectedClasses.BeforeLoadEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + BeforeLoadEvent.prototype = $desc; + function BeforeUnloadEvent() { + } + BeforeUnloadEvent.builtin$cls = "BeforeUnloadEvent"; + if (!"name" in BeforeUnloadEvent) + BeforeUnloadEvent.name = "BeforeUnloadEvent"; + $desc = $collectedClasses.BeforeUnloadEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + BeforeUnloadEvent.prototype = $desc; + function BodyElement() { + } + BodyElement.builtin$cls = "BodyElement"; + if (!"name" in BodyElement) + BodyElement.name = "BodyElement"; + $desc = $collectedClasses.BodyElement; + if ($desc instanceof Array) + $desc = $desc[1]; + BodyElement.prototype = $desc; + function ButtonElement() { + } + ButtonElement.builtin$cls = "ButtonElement"; + if (!"name" in ButtonElement) + ButtonElement.name = "ButtonElement"; + $desc = $collectedClasses.ButtonElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ButtonElement.prototype = $desc; + ButtonElement.prototype.set$disabled = function(receiver, v) { + return receiver.disabled = v; + }; + ButtonElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + ButtonElement.prototype.get$value = function(receiver) { + return receiver.value; + }; + ButtonElement.prototype.set$value = function(receiver, v) { + return receiver.value = v; + }; + function CDataSection() { + } + CDataSection.builtin$cls = "CDataSection"; + if (!"name" in CDataSection) + CDataSection.name = "CDataSection"; + $desc = $collectedClasses.CDataSection; + if ($desc instanceof Array) + $desc = $desc[1]; + CDataSection.prototype = $desc; + function CanvasElement() { + } + CanvasElement.builtin$cls = "CanvasElement"; + if (!"name" in CanvasElement) + CanvasElement.name = "CanvasElement"; + $desc = $collectedClasses.CanvasElement; + if ($desc instanceof Array) + $desc = $desc[1]; + CanvasElement.prototype = $desc; + function CharacterData() { + } + CharacterData.builtin$cls = "CharacterData"; + if (!"name" in CharacterData) + CharacterData.name = "CharacterData"; + $desc = $collectedClasses.CharacterData; + if ($desc instanceof Array) + $desc = $desc[1]; + CharacterData.prototype = $desc; + CharacterData.prototype.get$length = function(receiver) { + return receiver.length; + }; + function CloseEvent() { + } + CloseEvent.builtin$cls = "CloseEvent"; + if (!"name" in CloseEvent) + CloseEvent.name = "CloseEvent"; + $desc = $collectedClasses.CloseEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + CloseEvent.prototype = $desc; + function Comment() { + } + Comment.builtin$cls = "Comment"; + if (!"name" in Comment) + Comment.name = "Comment"; + $desc = $collectedClasses.Comment; + if ($desc instanceof Array) + $desc = $desc[1]; + Comment.prototype = $desc; + function CompositionEvent() { + } + CompositionEvent.builtin$cls = "CompositionEvent"; + if (!"name" in CompositionEvent) + CompositionEvent.name = "CompositionEvent"; + $desc = $collectedClasses.CompositionEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + CompositionEvent.prototype = $desc; + function ContentElement() { + } + ContentElement.builtin$cls = "ContentElement"; + if (!"name" in ContentElement) + ContentElement.name = "ContentElement"; + $desc = $collectedClasses.ContentElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ContentElement.prototype = $desc; + function CssFontFaceLoadEvent() { + } + CssFontFaceLoadEvent.builtin$cls = "CssFontFaceLoadEvent"; + if (!"name" in CssFontFaceLoadEvent) + CssFontFaceLoadEvent.name = "CssFontFaceLoadEvent"; + $desc = $collectedClasses.CssFontFaceLoadEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + CssFontFaceLoadEvent.prototype = $desc; + function CustomEvent() { + } + CustomEvent.builtin$cls = "CustomEvent"; + if (!"name" in CustomEvent) + CustomEvent.name = "CustomEvent"; + $desc = $collectedClasses.CustomEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + CustomEvent.prototype = $desc; + function DListElement() { + } + DListElement.builtin$cls = "DListElement"; + if (!"name" in DListElement) + DListElement.name = "DListElement"; + $desc = $collectedClasses.DListElement; + if ($desc instanceof Array) + $desc = $desc[1]; + DListElement.prototype = $desc; + function DataListElement() { + } + DataListElement.builtin$cls = "DataListElement"; + if (!"name" in DataListElement) + DataListElement.name = "DataListElement"; + $desc = $collectedClasses.DataListElement; + if ($desc instanceof Array) + $desc = $desc[1]; + DataListElement.prototype = $desc; + function DetailsElement() { + } + DetailsElement.builtin$cls = "DetailsElement"; + if (!"name" in DetailsElement) + DetailsElement.name = "DetailsElement"; + $desc = $collectedClasses.DetailsElement; + if ($desc instanceof Array) + $desc = $desc[1]; + DetailsElement.prototype = $desc; + function DeviceMotionEvent() { + } + DeviceMotionEvent.builtin$cls = "DeviceMotionEvent"; + if (!"name" in DeviceMotionEvent) + DeviceMotionEvent.name = "DeviceMotionEvent"; + $desc = $collectedClasses.DeviceMotionEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + DeviceMotionEvent.prototype = $desc; + function DeviceOrientationEvent() { + } + DeviceOrientationEvent.builtin$cls = "DeviceOrientationEvent"; + if (!"name" in DeviceOrientationEvent) + DeviceOrientationEvent.name = "DeviceOrientationEvent"; + $desc = $collectedClasses.DeviceOrientationEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + DeviceOrientationEvent.prototype = $desc; + function DialogElement() { + } + DialogElement.builtin$cls = "DialogElement"; + if (!"name" in DialogElement) + DialogElement.name = "DialogElement"; + $desc = $collectedClasses.DialogElement; + if ($desc instanceof Array) + $desc = $desc[1]; + DialogElement.prototype = $desc; + function DivElement() { + } + DivElement.builtin$cls = "DivElement"; + if (!"name" in DivElement) + DivElement.name = "DivElement"; + $desc = $collectedClasses.DivElement; + if ($desc instanceof Array) + $desc = $desc[1]; + DivElement.prototype = $desc; + function Document() { + } + Document.builtin$cls = "Document"; + if (!"name" in Document) + Document.name = "Document"; + $desc = $collectedClasses.Document; + if ($desc instanceof Array) + $desc = $desc[1]; + Document.prototype = $desc; + function DocumentFragment() { + } + DocumentFragment.builtin$cls = "DocumentFragment"; + if (!"name" in DocumentFragment) + DocumentFragment.name = "DocumentFragment"; + $desc = $collectedClasses.DocumentFragment; + if ($desc instanceof Array) + $desc = $desc[1]; + DocumentFragment.prototype = $desc; + function DomError() { + } + DomError.builtin$cls = "DomError"; + if (!"name" in DomError) + DomError.name = "DomError"; + $desc = $collectedClasses.DomError; + if ($desc instanceof Array) + $desc = $desc[1]; + DomError.prototype = $desc; + function DomException() { + } + DomException.builtin$cls = "DomException"; + if (!"name" in DomException) + DomException.name = "DomException"; + $desc = $collectedClasses.DomException; + if ($desc instanceof Array) + $desc = $desc[1]; + DomException.prototype = $desc; + function DomImplementation() { + } + DomImplementation.builtin$cls = "DomImplementation"; + if (!"name" in DomImplementation) + DomImplementation.name = "DomImplementation"; + $desc = $collectedClasses.DomImplementation; + if ($desc instanceof Array) + $desc = $desc[1]; + DomImplementation.prototype = $desc; + function Element() { + } + Element.builtin$cls = "Element"; + if (!"name" in Element) + Element.name = "Element"; + $desc = $collectedClasses.Element; + if ($desc instanceof Array) + $desc = $desc[1]; + Element.prototype = $desc; + function EmbedElement() { + } + EmbedElement.builtin$cls = "EmbedElement"; + if (!"name" in EmbedElement) + EmbedElement.name = "EmbedElement"; + $desc = $collectedClasses.EmbedElement; + if ($desc instanceof Array) + $desc = $desc[1]; + EmbedElement.prototype = $desc; + EmbedElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + function ErrorEvent() { + } + ErrorEvent.builtin$cls = "ErrorEvent"; + if (!"name" in ErrorEvent) + ErrorEvent.name = "ErrorEvent"; + $desc = $collectedClasses.ErrorEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + ErrorEvent.prototype = $desc; + ErrorEvent.prototype.get$error = function(receiver) { + return receiver.error; + }; + function Event() { + } + Event.builtin$cls = "Event"; + if (!"name" in Event) + Event.name = "Event"; + $desc = $collectedClasses.Event; + if ($desc instanceof Array) + $desc = $desc[1]; + Event.prototype = $desc; + function EventTarget() { + } + EventTarget.builtin$cls = "EventTarget"; + if (!"name" in EventTarget) + EventTarget.name = "EventTarget"; + $desc = $collectedClasses.EventTarget; + if ($desc instanceof Array) + $desc = $desc[1]; + EventTarget.prototype = $desc; + function FieldSetElement() { + } + FieldSetElement.builtin$cls = "FieldSetElement"; + if (!"name" in FieldSetElement) + FieldSetElement.name = "FieldSetElement"; + $desc = $collectedClasses.FieldSetElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FieldSetElement.prototype = $desc; + FieldSetElement.prototype.set$disabled = function(receiver, v) { + return receiver.disabled = v; + }; + FieldSetElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + function FileError() { + } + FileError.builtin$cls = "FileError"; + if (!"name" in FileError) + FileError.name = "FileError"; + $desc = $collectedClasses.FileError; + if ($desc instanceof Array) + $desc = $desc[1]; + FileError.prototype = $desc; + function FocusEvent() { + } + FocusEvent.builtin$cls = "FocusEvent"; + if (!"name" in FocusEvent) + FocusEvent.name = "FocusEvent"; + $desc = $collectedClasses.FocusEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + FocusEvent.prototype = $desc; + function FormElement() { + } + FormElement.builtin$cls = "FormElement"; + if (!"name" in FormElement) + FormElement.name = "FormElement"; + $desc = $collectedClasses.FormElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FormElement.prototype = $desc; + FormElement.prototype.get$length = function(receiver) { + return receiver.length; + }; + FormElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + function HRElement() { + } + HRElement.builtin$cls = "HRElement"; + if (!"name" in HRElement) + HRElement.name = "HRElement"; + $desc = $collectedClasses.HRElement; + if ($desc instanceof Array) + $desc = $desc[1]; + HRElement.prototype = $desc; + function HashChangeEvent() { + } + HashChangeEvent.builtin$cls = "HashChangeEvent"; + if (!"name" in HashChangeEvent) + HashChangeEvent.name = "HashChangeEvent"; + $desc = $collectedClasses.HashChangeEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + HashChangeEvent.prototype = $desc; + function HeadElement() { + } + HeadElement.builtin$cls = "HeadElement"; + if (!"name" in HeadElement) + HeadElement.name = "HeadElement"; + $desc = $collectedClasses.HeadElement; + if ($desc instanceof Array) + $desc = $desc[1]; + HeadElement.prototype = $desc; + function HeadingElement() { + } + HeadingElement.builtin$cls = "HeadingElement"; + if (!"name" in HeadingElement) + HeadingElement.name = "HeadingElement"; + $desc = $collectedClasses.HeadingElement; + if ($desc instanceof Array) + $desc = $desc[1]; + HeadingElement.prototype = $desc; + function HtmlDocument() { + } + HtmlDocument.builtin$cls = "HtmlDocument"; + if (!"name" in HtmlDocument) + HtmlDocument.name = "HtmlDocument"; + $desc = $collectedClasses.HtmlDocument; + if ($desc instanceof Array) + $desc = $desc[1]; + HtmlDocument.prototype = $desc; + function HtmlHtmlElement() { + } + HtmlHtmlElement.builtin$cls = "HtmlHtmlElement"; + if (!"name" in HtmlHtmlElement) + HtmlHtmlElement.name = "HtmlHtmlElement"; + $desc = $collectedClasses.HtmlHtmlElement; + if ($desc instanceof Array) + $desc = $desc[1]; + HtmlHtmlElement.prototype = $desc; + function IFrameElement() { + } + IFrameElement.builtin$cls = "IFrameElement"; + if (!"name" in IFrameElement) + IFrameElement.name = "IFrameElement"; + $desc = $collectedClasses.IFrameElement; + if ($desc instanceof Array) + $desc = $desc[1]; + IFrameElement.prototype = $desc; + IFrameElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + function ImageElement() { + } + ImageElement.builtin$cls = "ImageElement"; + if (!"name" in ImageElement) + ImageElement.name = "ImageElement"; + $desc = $collectedClasses.ImageElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ImageElement.prototype = $desc; + function InputElement() { + } + InputElement.builtin$cls = "InputElement"; + if (!"name" in InputElement) + InputElement.name = "InputElement"; + $desc = $collectedClasses.InputElement; + if ($desc instanceof Array) + $desc = $desc[1]; + InputElement.prototype = $desc; + InputElement.prototype.set$disabled = function(receiver, v) { + return receiver.disabled = v; + }; + InputElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + InputElement.prototype.get$value = function(receiver) { + return receiver.value; + }; + InputElement.prototype.set$value = function(receiver, v) { + return receiver.value = v; + }; + function KeyboardEvent() { + } + KeyboardEvent.builtin$cls = "KeyboardEvent"; + if (!"name" in KeyboardEvent) + KeyboardEvent.name = "KeyboardEvent"; + $desc = $collectedClasses.KeyboardEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + KeyboardEvent.prototype = $desc; + function KeygenElement() { + } + KeygenElement.builtin$cls = "KeygenElement"; + if (!"name" in KeygenElement) + KeygenElement.name = "KeygenElement"; + $desc = $collectedClasses.KeygenElement; + if ($desc instanceof Array) + $desc = $desc[1]; + KeygenElement.prototype = $desc; + KeygenElement.prototype.set$disabled = function(receiver, v) { + return receiver.disabled = v; + }; + KeygenElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + function LIElement() { + } + LIElement.builtin$cls = "LIElement"; + if (!"name" in LIElement) + LIElement.name = "LIElement"; + $desc = $collectedClasses.LIElement; + if ($desc instanceof Array) + $desc = $desc[1]; + LIElement.prototype = $desc; + LIElement.prototype.get$value = function(receiver) { + return receiver.value; + }; + LIElement.prototype.set$value = function(receiver, v) { + return receiver.value = v; + }; + function LabelElement() { + } + LabelElement.builtin$cls = "LabelElement"; + if (!"name" in LabelElement) + LabelElement.name = "LabelElement"; + $desc = $collectedClasses.LabelElement; + if ($desc instanceof Array) + $desc = $desc[1]; + LabelElement.prototype = $desc; + function LegendElement() { + } + LegendElement.builtin$cls = "LegendElement"; + if (!"name" in LegendElement) + LegendElement.name = "LegendElement"; + $desc = $collectedClasses.LegendElement; + if ($desc instanceof Array) + $desc = $desc[1]; + LegendElement.prototype = $desc; + function LinkElement() { + } + LinkElement.builtin$cls = "LinkElement"; + if (!"name" in LinkElement) + LinkElement.name = "LinkElement"; + $desc = $collectedClasses.LinkElement; + if ($desc instanceof Array) + $desc = $desc[1]; + LinkElement.prototype = $desc; + LinkElement.prototype.set$disabled = function(receiver, v) { + return receiver.disabled = v; + }; + LinkElement.prototype.set$href = function(receiver, v) { + return receiver.href = v; + }; + function Location() { + } + Location.builtin$cls = "Location"; + if (!"name" in Location) + Location.name = "Location"; + $desc = $collectedClasses.Location; + if ($desc instanceof Array) + $desc = $desc[1]; + Location.prototype = $desc; + Location.prototype.get$hostname = function(receiver) { + return receiver.hostname; + }; + Location.prototype.get$port = function(receiver) { + return receiver.port; + }; + Location.prototype.get$protocol = function(receiver) { + return receiver.protocol; + }; + function MapElement() { + } + MapElement.builtin$cls = "MapElement"; + if (!"name" in MapElement) + MapElement.name = "MapElement"; + $desc = $collectedClasses.MapElement; + if ($desc instanceof Array) + $desc = $desc[1]; + MapElement.prototype = $desc; + MapElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + function MediaElement() { + } + MediaElement.builtin$cls = "MediaElement"; + if (!"name" in MediaElement) + MediaElement.name = "MediaElement"; + $desc = $collectedClasses.MediaElement; + if ($desc instanceof Array) + $desc = $desc[1]; + MediaElement.prototype = $desc; + MediaElement.prototype.get$error = function(receiver) { + return receiver.error; + }; + function MediaError() { + } + MediaError.builtin$cls = "MediaError"; + if (!"name" in MediaError) + MediaError.name = "MediaError"; + $desc = $collectedClasses.MediaError; + if ($desc instanceof Array) + $desc = $desc[1]; + MediaError.prototype = $desc; + function MediaKeyError() { + } + MediaKeyError.builtin$cls = "MediaKeyError"; + if (!"name" in MediaKeyError) + MediaKeyError.name = "MediaKeyError"; + $desc = $collectedClasses.MediaKeyError; + if ($desc instanceof Array) + $desc = $desc[1]; + MediaKeyError.prototype = $desc; + function MediaKeyEvent() { + } + MediaKeyEvent.builtin$cls = "MediaKeyEvent"; + if (!"name" in MediaKeyEvent) + MediaKeyEvent.name = "MediaKeyEvent"; + $desc = $collectedClasses.MediaKeyEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + MediaKeyEvent.prototype = $desc; + function MediaKeyMessageEvent() { + } + MediaKeyMessageEvent.builtin$cls = "MediaKeyMessageEvent"; + if (!"name" in MediaKeyMessageEvent) + MediaKeyMessageEvent.name = "MediaKeyMessageEvent"; + $desc = $collectedClasses.MediaKeyMessageEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + MediaKeyMessageEvent.prototype = $desc; + function MediaKeyNeededEvent() { + } + MediaKeyNeededEvent.builtin$cls = "MediaKeyNeededEvent"; + if (!"name" in MediaKeyNeededEvent) + MediaKeyNeededEvent.name = "MediaKeyNeededEvent"; + $desc = $collectedClasses.MediaKeyNeededEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + MediaKeyNeededEvent.prototype = $desc; + function MediaStreamEvent() { + } + MediaStreamEvent.builtin$cls = "MediaStreamEvent"; + if (!"name" in MediaStreamEvent) + MediaStreamEvent.name = "MediaStreamEvent"; + $desc = $collectedClasses.MediaStreamEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + MediaStreamEvent.prototype = $desc; + function MediaStreamTrackEvent() { + } + MediaStreamTrackEvent.builtin$cls = "MediaStreamTrackEvent"; + if (!"name" in MediaStreamTrackEvent) + MediaStreamTrackEvent.name = "MediaStreamTrackEvent"; + $desc = $collectedClasses.MediaStreamTrackEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + MediaStreamTrackEvent.prototype = $desc; + function MenuElement() { + } + MenuElement.builtin$cls = "MenuElement"; + if (!"name" in MenuElement) + MenuElement.name = "MenuElement"; + $desc = $collectedClasses.MenuElement; + if ($desc instanceof Array) + $desc = $desc[1]; + MenuElement.prototype = $desc; + function MessageEvent() { + } + MessageEvent.builtin$cls = "MessageEvent"; + if (!"name" in MessageEvent) + MessageEvent.name = "MessageEvent"; + $desc = $collectedClasses.MessageEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + MessageEvent.prototype = $desc; + function MetaElement() { + } + MetaElement.builtin$cls = "MetaElement"; + if (!"name" in MetaElement) + MetaElement.name = "MetaElement"; + $desc = $collectedClasses.MetaElement; + if ($desc instanceof Array) + $desc = $desc[1]; + MetaElement.prototype = $desc; + MetaElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + function MeterElement() { + } + MeterElement.builtin$cls = "MeterElement"; + if (!"name" in MeterElement) + MeterElement.name = "MeterElement"; + $desc = $collectedClasses.MeterElement; + if ($desc instanceof Array) + $desc = $desc[1]; + MeterElement.prototype = $desc; + MeterElement.prototype.get$value = function(receiver) { + return receiver.value; + }; + MeterElement.prototype.set$value = function(receiver, v) { + return receiver.value = v; + }; + function MidiConnectionEvent() { + } + MidiConnectionEvent.builtin$cls = "MidiConnectionEvent"; + if (!"name" in MidiConnectionEvent) + MidiConnectionEvent.name = "MidiConnectionEvent"; + $desc = $collectedClasses.MidiConnectionEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + MidiConnectionEvent.prototype = $desc; + function MidiInput() { + } + MidiInput.builtin$cls = "MidiInput"; + if (!"name" in MidiInput) + MidiInput.name = "MidiInput"; + $desc = $collectedClasses.MidiInput; + if ($desc instanceof Array) + $desc = $desc[1]; + MidiInput.prototype = $desc; + function MidiMessageEvent() { + } + MidiMessageEvent.builtin$cls = "MidiMessageEvent"; + if (!"name" in MidiMessageEvent) + MidiMessageEvent.name = "MidiMessageEvent"; + $desc = $collectedClasses.MidiMessageEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + MidiMessageEvent.prototype = $desc; + function MidiOutput() { + } + MidiOutput.builtin$cls = "MidiOutput"; + if (!"name" in MidiOutput) + MidiOutput.name = "MidiOutput"; + $desc = $collectedClasses.MidiOutput; + if ($desc instanceof Array) + $desc = $desc[1]; + MidiOutput.prototype = $desc; + function MidiPort() { + } + MidiPort.builtin$cls = "MidiPort"; + if (!"name" in MidiPort) + MidiPort.name = "MidiPort"; + $desc = $collectedClasses.MidiPort; + if ($desc instanceof Array) + $desc = $desc[1]; + MidiPort.prototype = $desc; + function ModElement() { + } + ModElement.builtin$cls = "ModElement"; + if (!"name" in ModElement) + ModElement.name = "ModElement"; + $desc = $collectedClasses.ModElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ModElement.prototype = $desc; + function MouseEvent() { + } + MouseEvent.builtin$cls = "MouseEvent"; + if (!"name" in MouseEvent) + MouseEvent.name = "MouseEvent"; + $desc = $collectedClasses.MouseEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + MouseEvent.prototype = $desc; + function Navigator() { + } + Navigator.builtin$cls = "Navigator"; + if (!"name" in Navigator) + Navigator.name = "Navigator"; + $desc = $collectedClasses.Navigator; + if ($desc instanceof Array) + $desc = $desc[1]; + Navigator.prototype = $desc; + function NavigatorUserMediaError() { + } + NavigatorUserMediaError.builtin$cls = "NavigatorUserMediaError"; + if (!"name" in NavigatorUserMediaError) + NavigatorUserMediaError.name = "NavigatorUserMediaError"; + $desc = $collectedClasses.NavigatorUserMediaError; + if ($desc instanceof Array) + $desc = $desc[1]; + NavigatorUserMediaError.prototype = $desc; + function Node() { + } + Node.builtin$cls = "Node"; + if (!"name" in Node) + Node.name = "Node"; + $desc = $collectedClasses.Node; + if ($desc instanceof Array) + $desc = $desc[1]; + Node.prototype = $desc; + function NodeList() { + } + NodeList.builtin$cls = "NodeList"; + if (!"name" in NodeList) + NodeList.name = "NodeList"; + $desc = $collectedClasses.NodeList; + if ($desc instanceof Array) + $desc = $desc[1]; + NodeList.prototype = $desc; + function OListElement() { + } + OListElement.builtin$cls = "OListElement"; + if (!"name" in OListElement) + OListElement.name = "OListElement"; + $desc = $collectedClasses.OListElement; + if ($desc instanceof Array) + $desc = $desc[1]; + OListElement.prototype = $desc; + function ObjectElement() { + } + ObjectElement.builtin$cls = "ObjectElement"; + if (!"name" in ObjectElement) + ObjectElement.name = "ObjectElement"; + $desc = $collectedClasses.ObjectElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ObjectElement.prototype = $desc; + ObjectElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + function OptGroupElement() { + } + OptGroupElement.builtin$cls = "OptGroupElement"; + if (!"name" in OptGroupElement) + OptGroupElement.name = "OptGroupElement"; + $desc = $collectedClasses.OptGroupElement; + if ($desc instanceof Array) + $desc = $desc[1]; + OptGroupElement.prototype = $desc; + OptGroupElement.prototype.set$disabled = function(receiver, v) { + return receiver.disabled = v; + }; + function OptionElement() { + } + OptionElement.builtin$cls = "OptionElement"; + if (!"name" in OptionElement) + OptionElement.name = "OptionElement"; + $desc = $collectedClasses.OptionElement; + if ($desc instanceof Array) + $desc = $desc[1]; + OptionElement.prototype = $desc; + OptionElement.prototype.set$disabled = function(receiver, v) { + return receiver.disabled = v; + }; + OptionElement.prototype.get$value = function(receiver) { + return receiver.value; + }; + OptionElement.prototype.set$value = function(receiver, v) { + return receiver.value = v; + }; + function OutputElement() { + } + OutputElement.builtin$cls = "OutputElement"; + if (!"name" in OutputElement) + OutputElement.name = "OutputElement"; + $desc = $collectedClasses.OutputElement; + if ($desc instanceof Array) + $desc = $desc[1]; + OutputElement.prototype = $desc; + OutputElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + OutputElement.prototype.get$value = function(receiver) { + return receiver.value; + }; + OutputElement.prototype.set$value = function(receiver, v) { + return receiver.value = v; + }; + function OverflowEvent() { + } + OverflowEvent.builtin$cls = "OverflowEvent"; + if (!"name" in OverflowEvent) + OverflowEvent.name = "OverflowEvent"; + $desc = $collectedClasses.OverflowEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + OverflowEvent.prototype = $desc; + function PageTransitionEvent() { + } + PageTransitionEvent.builtin$cls = "PageTransitionEvent"; + if (!"name" in PageTransitionEvent) + PageTransitionEvent.name = "PageTransitionEvent"; + $desc = $collectedClasses.PageTransitionEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + PageTransitionEvent.prototype = $desc; + function ParagraphElement() { + } + ParagraphElement.builtin$cls = "ParagraphElement"; + if (!"name" in ParagraphElement) + ParagraphElement.name = "ParagraphElement"; + $desc = $collectedClasses.ParagraphElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ParagraphElement.prototype = $desc; + function ParamElement() { + } + ParamElement.builtin$cls = "ParamElement"; + if (!"name" in ParamElement) + ParamElement.name = "ParamElement"; + $desc = $collectedClasses.ParamElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ParamElement.prototype = $desc; + ParamElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + ParamElement.prototype.get$value = function(receiver) { + return receiver.value; + }; + ParamElement.prototype.set$value = function(receiver, v) { + return receiver.value = v; + }; + function PopStateEvent() { + } + PopStateEvent.builtin$cls = "PopStateEvent"; + if (!"name" in PopStateEvent) + PopStateEvent.name = "PopStateEvent"; + $desc = $collectedClasses.PopStateEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + PopStateEvent.prototype = $desc; + function PositionError() { + } + PositionError.builtin$cls = "PositionError"; + if (!"name" in PositionError) + PositionError.name = "PositionError"; + $desc = $collectedClasses.PositionError; + if ($desc instanceof Array) + $desc = $desc[1]; + PositionError.prototype = $desc; + function PreElement() { + } + PreElement.builtin$cls = "PreElement"; + if (!"name" in PreElement) + PreElement.name = "PreElement"; + $desc = $collectedClasses.PreElement; + if ($desc instanceof Array) + $desc = $desc[1]; + PreElement.prototype = $desc; + function ProcessingInstruction() { + } + ProcessingInstruction.builtin$cls = "ProcessingInstruction"; + if (!"name" in ProcessingInstruction) + ProcessingInstruction.name = "ProcessingInstruction"; + $desc = $collectedClasses.ProcessingInstruction; + if ($desc instanceof Array) + $desc = $desc[1]; + ProcessingInstruction.prototype = $desc; + function ProgressElement() { + } + ProgressElement.builtin$cls = "ProgressElement"; + if (!"name" in ProgressElement) + ProgressElement.name = "ProgressElement"; + $desc = $collectedClasses.ProgressElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ProgressElement.prototype = $desc; + ProgressElement.prototype.get$value = function(receiver) { + return receiver.value; + }; + ProgressElement.prototype.set$value = function(receiver, v) { + return receiver.value = v; + }; + function ProgressEvent() { + } + ProgressEvent.builtin$cls = "ProgressEvent"; + if (!"name" in ProgressEvent) + ProgressEvent.name = "ProgressEvent"; + $desc = $collectedClasses.ProgressEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + ProgressEvent.prototype = $desc; + function QuoteElement() { + } + QuoteElement.builtin$cls = "QuoteElement"; + if (!"name" in QuoteElement) + QuoteElement.name = "QuoteElement"; + $desc = $collectedClasses.QuoteElement; + if ($desc instanceof Array) + $desc = $desc[1]; + QuoteElement.prototype = $desc; + function Range() { + } + Range.builtin$cls = "Range"; + if (!"name" in Range) + Range.name = "Range"; + $desc = $collectedClasses.Range; + if ($desc instanceof Array) + $desc = $desc[1]; + Range.prototype = $desc; + function ResourceProgressEvent() { + } + ResourceProgressEvent.builtin$cls = "ResourceProgressEvent"; + if (!"name" in ResourceProgressEvent) + ResourceProgressEvent.name = "ResourceProgressEvent"; + $desc = $collectedClasses.ResourceProgressEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + ResourceProgressEvent.prototype = $desc; + function RtcDataChannelEvent() { + } + RtcDataChannelEvent.builtin$cls = "RtcDataChannelEvent"; + if (!"name" in RtcDataChannelEvent) + RtcDataChannelEvent.name = "RtcDataChannelEvent"; + $desc = $collectedClasses.RtcDataChannelEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + RtcDataChannelEvent.prototype = $desc; + function RtcDtmfToneChangeEvent() { + } + RtcDtmfToneChangeEvent.builtin$cls = "RtcDtmfToneChangeEvent"; + if (!"name" in RtcDtmfToneChangeEvent) + RtcDtmfToneChangeEvent.name = "RtcDtmfToneChangeEvent"; + $desc = $collectedClasses.RtcDtmfToneChangeEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + RtcDtmfToneChangeEvent.prototype = $desc; + function RtcIceCandidateEvent() { + } + RtcIceCandidateEvent.builtin$cls = "RtcIceCandidateEvent"; + if (!"name" in RtcIceCandidateEvent) + RtcIceCandidateEvent.name = "RtcIceCandidateEvent"; + $desc = $collectedClasses.RtcIceCandidateEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + RtcIceCandidateEvent.prototype = $desc; + function ScriptElement0() { + } + ScriptElement0.builtin$cls = "ScriptElement0"; + if (!"name" in ScriptElement0) + ScriptElement0.name = "ScriptElement0"; + $desc = $collectedClasses.ScriptElement0; + if ($desc instanceof Array) + $desc = $desc[1]; + ScriptElement0.prototype = $desc; + function SecurityPolicyViolationEvent() { + } + SecurityPolicyViolationEvent.builtin$cls = "SecurityPolicyViolationEvent"; + if (!"name" in SecurityPolicyViolationEvent) + SecurityPolicyViolationEvent.name = "SecurityPolicyViolationEvent"; + $desc = $collectedClasses.SecurityPolicyViolationEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + SecurityPolicyViolationEvent.prototype = $desc; + function SelectElement() { + } + SelectElement.builtin$cls = "SelectElement"; + if (!"name" in SelectElement) + SelectElement.name = "SelectElement"; + $desc = $collectedClasses.SelectElement; + if ($desc instanceof Array) + $desc = $desc[1]; + SelectElement.prototype = $desc; + SelectElement.prototype.set$disabled = function(receiver, v) { + return receiver.disabled = v; + }; + SelectElement.prototype.get$length = function(receiver) { + return receiver.length; + }; + SelectElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + SelectElement.prototype.get$value = function(receiver) { + return receiver.value; + }; + SelectElement.prototype.set$value = function(receiver, v) { + return receiver.value = v; + }; + function ShadowElement() { + } + ShadowElement.builtin$cls = "ShadowElement"; + if (!"name" in ShadowElement) + ShadowElement.name = "ShadowElement"; + $desc = $collectedClasses.ShadowElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ShadowElement.prototype = $desc; + function ShadowRoot() { + } + ShadowRoot.builtin$cls = "ShadowRoot"; + if (!"name" in ShadowRoot) + ShadowRoot.name = "ShadowRoot"; + $desc = $collectedClasses.ShadowRoot; + if ($desc instanceof Array) + $desc = $desc[1]; + ShadowRoot.prototype = $desc; + function SourceElement() { + } + SourceElement.builtin$cls = "SourceElement"; + if (!"name" in SourceElement) + SourceElement.name = "SourceElement"; + $desc = $collectedClasses.SourceElement; + if ($desc instanceof Array) + $desc = $desc[1]; + SourceElement.prototype = $desc; + function SpanElement() { + } + SpanElement.builtin$cls = "SpanElement"; + if (!"name" in SpanElement) + SpanElement.name = "SpanElement"; + $desc = $collectedClasses.SpanElement; + if ($desc instanceof Array) + $desc = $desc[1]; + SpanElement.prototype = $desc; + function SpeechInputEvent() { + } + SpeechInputEvent.builtin$cls = "SpeechInputEvent"; + if (!"name" in SpeechInputEvent) + SpeechInputEvent.name = "SpeechInputEvent"; + $desc = $collectedClasses.SpeechInputEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + SpeechInputEvent.prototype = $desc; + function SpeechRecognitionError() { + } + SpeechRecognitionError.builtin$cls = "SpeechRecognitionError"; + if (!"name" in SpeechRecognitionError) + SpeechRecognitionError.name = "SpeechRecognitionError"; + $desc = $collectedClasses.SpeechRecognitionError; + if ($desc instanceof Array) + $desc = $desc[1]; + SpeechRecognitionError.prototype = $desc; + SpeechRecognitionError.prototype.get$error = function(receiver) { + return receiver.error; + }; + function SpeechRecognitionEvent() { + } + SpeechRecognitionEvent.builtin$cls = "SpeechRecognitionEvent"; + if (!"name" in SpeechRecognitionEvent) + SpeechRecognitionEvent.name = "SpeechRecognitionEvent"; + $desc = $collectedClasses.SpeechRecognitionEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + SpeechRecognitionEvent.prototype = $desc; + function SpeechSynthesisEvent() { + } + SpeechSynthesisEvent.builtin$cls = "SpeechSynthesisEvent"; + if (!"name" in SpeechSynthesisEvent) + SpeechSynthesisEvent.name = "SpeechSynthesisEvent"; + $desc = $collectedClasses.SpeechSynthesisEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + SpeechSynthesisEvent.prototype = $desc; + function Storage() { + } + Storage.builtin$cls = "Storage"; + if (!"name" in Storage) + Storage.name = "Storage"; + $desc = $collectedClasses.Storage; + if ($desc instanceof Array) + $desc = $desc[1]; + Storage.prototype = $desc; + function StorageEvent() { + } + StorageEvent.builtin$cls = "StorageEvent"; + if (!"name" in StorageEvent) + StorageEvent.name = "StorageEvent"; + $desc = $collectedClasses.StorageEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + StorageEvent.prototype = $desc; + function StyleElement() { + } + StyleElement.builtin$cls = "StyleElement"; + if (!"name" in StyleElement) + StyleElement.name = "StyleElement"; + $desc = $collectedClasses.StyleElement; + if ($desc instanceof Array) + $desc = $desc[1]; + StyleElement.prototype = $desc; + StyleElement.prototype.set$disabled = function(receiver, v) { + return receiver.disabled = v; + }; + function TableCaptionElement() { + } + TableCaptionElement.builtin$cls = "TableCaptionElement"; + if (!"name" in TableCaptionElement) + TableCaptionElement.name = "TableCaptionElement"; + $desc = $collectedClasses.TableCaptionElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TableCaptionElement.prototype = $desc; + function TableCellElement() { + } + TableCellElement.builtin$cls = "TableCellElement"; + if (!"name" in TableCellElement) + TableCellElement.name = "TableCellElement"; + $desc = $collectedClasses.TableCellElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TableCellElement.prototype = $desc; + function TableColElement() { + } + TableColElement.builtin$cls = "TableColElement"; + if (!"name" in TableColElement) + TableColElement.name = "TableColElement"; + $desc = $collectedClasses.TableColElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TableColElement.prototype = $desc; + function TableElement() { + } + TableElement.builtin$cls = "TableElement"; + if (!"name" in TableElement) + TableElement.name = "TableElement"; + $desc = $collectedClasses.TableElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TableElement.prototype = $desc; + function TableRowElement() { + } + TableRowElement.builtin$cls = "TableRowElement"; + if (!"name" in TableRowElement) + TableRowElement.name = "TableRowElement"; + $desc = $collectedClasses.TableRowElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TableRowElement.prototype = $desc; + function TableSectionElement() { + } + TableSectionElement.builtin$cls = "TableSectionElement"; + if (!"name" in TableSectionElement) + TableSectionElement.name = "TableSectionElement"; + $desc = $collectedClasses.TableSectionElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TableSectionElement.prototype = $desc; + function TemplateElement() { + } + TemplateElement.builtin$cls = "TemplateElement"; + if (!"name" in TemplateElement) + TemplateElement.name = "TemplateElement"; + $desc = $collectedClasses.TemplateElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TemplateElement.prototype = $desc; + function Text() { + } + Text.builtin$cls = "Text"; + if (!"name" in Text) + Text.name = "Text"; + $desc = $collectedClasses.Text; + if ($desc instanceof Array) + $desc = $desc[1]; + Text.prototype = $desc; + function TextAreaElement() { + } + TextAreaElement.builtin$cls = "TextAreaElement"; + if (!"name" in TextAreaElement) + TextAreaElement.name = "TextAreaElement"; + $desc = $collectedClasses.TextAreaElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TextAreaElement.prototype = $desc; + TextAreaElement.prototype.set$disabled = function(receiver, v) { + return receiver.disabled = v; + }; + TextAreaElement.prototype.get$name = function(receiver) { + return receiver.name; + }; + TextAreaElement.prototype.get$value = function(receiver) { + return receiver.value; + }; + TextAreaElement.prototype.set$value = function(receiver, v) { + return receiver.value = v; + }; + function TextEvent() { + } + TextEvent.builtin$cls = "TextEvent"; + if (!"name" in TextEvent) + TextEvent.name = "TextEvent"; + $desc = $collectedClasses.TextEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + TextEvent.prototype = $desc; + function TitleElement() { + } + TitleElement.builtin$cls = "TitleElement"; + if (!"name" in TitleElement) + TitleElement.name = "TitleElement"; + $desc = $collectedClasses.TitleElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TitleElement.prototype = $desc; + function TouchEvent() { + } + TouchEvent.builtin$cls = "TouchEvent"; + if (!"name" in TouchEvent) + TouchEvent.name = "TouchEvent"; + $desc = $collectedClasses.TouchEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + TouchEvent.prototype = $desc; + function TrackElement() { + } + TrackElement.builtin$cls = "TrackElement"; + if (!"name" in TrackElement) + TrackElement.name = "TrackElement"; + $desc = $collectedClasses.TrackElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TrackElement.prototype = $desc; + function TrackEvent() { + } + TrackEvent.builtin$cls = "TrackEvent"; + if (!"name" in TrackEvent) + TrackEvent.name = "TrackEvent"; + $desc = $collectedClasses.TrackEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + TrackEvent.prototype = $desc; + function TransitionEvent() { + } + TransitionEvent.builtin$cls = "TransitionEvent"; + if (!"name" in TransitionEvent) + TransitionEvent.name = "TransitionEvent"; + $desc = $collectedClasses.TransitionEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + TransitionEvent.prototype = $desc; + function UIEvent() { + } + UIEvent.builtin$cls = "UIEvent"; + if (!"name" in UIEvent) + UIEvent.name = "UIEvent"; + $desc = $collectedClasses.UIEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + UIEvent.prototype = $desc; + function UListElement() { + } + UListElement.builtin$cls = "UListElement"; + if (!"name" in UListElement) + UListElement.name = "UListElement"; + $desc = $collectedClasses.UListElement; + if ($desc instanceof Array) + $desc = $desc[1]; + UListElement.prototype = $desc; + function UnknownElement() { + } + UnknownElement.builtin$cls = "UnknownElement"; + if (!"name" in UnknownElement) + UnknownElement.name = "UnknownElement"; + $desc = $collectedClasses.UnknownElement; + if ($desc instanceof Array) + $desc = $desc[1]; + UnknownElement.prototype = $desc; + function VideoElement() { + } + VideoElement.builtin$cls = "VideoElement"; + if (!"name" in VideoElement) + VideoElement.name = "VideoElement"; + $desc = $collectedClasses.VideoElement; + if ($desc instanceof Array) + $desc = $desc[1]; + VideoElement.prototype = $desc; + function WheelEvent() { + } + WheelEvent.builtin$cls = "WheelEvent"; + if (!"name" in WheelEvent) + WheelEvent.name = "WheelEvent"; + $desc = $collectedClasses.WheelEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + WheelEvent.prototype = $desc; + function Window() { + } + Window.builtin$cls = "Window"; + if (!"name" in Window) + Window.name = "Window"; + $desc = $collectedClasses.Window; + if ($desc instanceof Array) + $desc = $desc[1]; + Window.prototype = $desc; + function XmlDocument() { + } + XmlDocument.builtin$cls = "XmlDocument"; + if (!"name" in XmlDocument) + XmlDocument.name = "XmlDocument"; + $desc = $collectedClasses.XmlDocument; + if ($desc instanceof Array) + $desc = $desc[1]; + XmlDocument.prototype = $desc; + function _Attr() { + } + _Attr.builtin$cls = "_Attr"; + if (!"name" in _Attr) + _Attr.name = "_Attr"; + $desc = $collectedClasses._Attr; + if ($desc instanceof Array) + $desc = $desc[1]; + _Attr.prototype = $desc; + _Attr.prototype.get$name = function(receiver) { + return receiver.name; + }; + _Attr.prototype.get$value = function(receiver) { + return receiver.value; + }; + function _DocumentType() { + } + _DocumentType.builtin$cls = "_DocumentType"; + if (!"name" in _DocumentType) + _DocumentType.name = "_DocumentType"; + $desc = $collectedClasses._DocumentType; + if ($desc instanceof Array) + $desc = $desc[1]; + _DocumentType.prototype = $desc; + function _HTMLAppletElement() { + } + _HTMLAppletElement.builtin$cls = "_HTMLAppletElement"; + if (!"name" in _HTMLAppletElement) + _HTMLAppletElement.name = "_HTMLAppletElement"; + $desc = $collectedClasses._HTMLAppletElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _HTMLAppletElement.prototype = $desc; + function _HTMLDirectoryElement() { + } + _HTMLDirectoryElement.builtin$cls = "_HTMLDirectoryElement"; + if (!"name" in _HTMLDirectoryElement) + _HTMLDirectoryElement.name = "_HTMLDirectoryElement"; + $desc = $collectedClasses._HTMLDirectoryElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _HTMLDirectoryElement.prototype = $desc; + function _HTMLFontElement() { + } + _HTMLFontElement.builtin$cls = "_HTMLFontElement"; + if (!"name" in _HTMLFontElement) + _HTMLFontElement.name = "_HTMLFontElement"; + $desc = $collectedClasses._HTMLFontElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _HTMLFontElement.prototype = $desc; + function _HTMLFrameElement() { + } + _HTMLFrameElement.builtin$cls = "_HTMLFrameElement"; + if (!"name" in _HTMLFrameElement) + _HTMLFrameElement.name = "_HTMLFrameElement"; + $desc = $collectedClasses._HTMLFrameElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _HTMLFrameElement.prototype = $desc; + function _HTMLFrameSetElement() { + } + _HTMLFrameSetElement.builtin$cls = "_HTMLFrameSetElement"; + if (!"name" in _HTMLFrameSetElement) + _HTMLFrameSetElement.name = "_HTMLFrameSetElement"; + $desc = $collectedClasses._HTMLFrameSetElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _HTMLFrameSetElement.prototype = $desc; + function _HTMLMarqueeElement() { + } + _HTMLMarqueeElement.builtin$cls = "_HTMLMarqueeElement"; + if (!"name" in _HTMLMarqueeElement) + _HTMLMarqueeElement.name = "_HTMLMarqueeElement"; + $desc = $collectedClasses._HTMLMarqueeElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _HTMLMarqueeElement.prototype = $desc; + function _MutationEvent() { + } + _MutationEvent.builtin$cls = "_MutationEvent"; + if (!"name" in _MutationEvent) + _MutationEvent.name = "_MutationEvent"; + $desc = $collectedClasses._MutationEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + _MutationEvent.prototype = $desc; + function _NamedNodeMap() { + } + _NamedNodeMap.builtin$cls = "_NamedNodeMap"; + if (!"name" in _NamedNodeMap) + _NamedNodeMap.name = "_NamedNodeMap"; + $desc = $collectedClasses._NamedNodeMap; + if ($desc instanceof Array) + $desc = $desc[1]; + _NamedNodeMap.prototype = $desc; + function _Notation() { + } + _Notation.builtin$cls = "_Notation"; + if (!"name" in _Notation) + _Notation.name = "_Notation"; + $desc = $collectedClasses._Notation; + if ($desc instanceof Array) + $desc = $desc[1]; + _Notation.prototype = $desc; + function _XMLHttpRequestProgressEvent() { + } + _XMLHttpRequestProgressEvent.builtin$cls = "_XMLHttpRequestProgressEvent"; + if (!"name" in _XMLHttpRequestProgressEvent) + _XMLHttpRequestProgressEvent.name = "_XMLHttpRequestProgressEvent"; + $desc = $collectedClasses._XMLHttpRequestProgressEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + _XMLHttpRequestProgressEvent.prototype = $desc; + function VersionChangeEvent() { + } + VersionChangeEvent.builtin$cls = "VersionChangeEvent"; + if (!"name" in VersionChangeEvent) + VersionChangeEvent.name = "VersionChangeEvent"; + $desc = $collectedClasses.VersionChangeEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + VersionChangeEvent.prototype = $desc; + function AElement() { + } + AElement.builtin$cls = "AElement"; + if (!"name" in AElement) + AElement.name = "AElement"; + $desc = $collectedClasses.AElement; + if ($desc instanceof Array) + $desc = $desc[1]; + AElement.prototype = $desc; + function AltGlyphElement() { + } + AltGlyphElement.builtin$cls = "AltGlyphElement"; + if (!"name" in AltGlyphElement) + AltGlyphElement.name = "AltGlyphElement"; + $desc = $collectedClasses.AltGlyphElement; + if ($desc instanceof Array) + $desc = $desc[1]; + AltGlyphElement.prototype = $desc; + function AnimateElement() { + } + AnimateElement.builtin$cls = "AnimateElement"; + if (!"name" in AnimateElement) + AnimateElement.name = "AnimateElement"; + $desc = $collectedClasses.AnimateElement; + if ($desc instanceof Array) + $desc = $desc[1]; + AnimateElement.prototype = $desc; + function AnimateMotionElement() { + } + AnimateMotionElement.builtin$cls = "AnimateMotionElement"; + if (!"name" in AnimateMotionElement) + AnimateMotionElement.name = "AnimateMotionElement"; + $desc = $collectedClasses.AnimateMotionElement; + if ($desc instanceof Array) + $desc = $desc[1]; + AnimateMotionElement.prototype = $desc; + function AnimateTransformElement() { + } + AnimateTransformElement.builtin$cls = "AnimateTransformElement"; + if (!"name" in AnimateTransformElement) + AnimateTransformElement.name = "AnimateTransformElement"; + $desc = $collectedClasses.AnimateTransformElement; + if ($desc instanceof Array) + $desc = $desc[1]; + AnimateTransformElement.prototype = $desc; + function AnimatedNumberList() { + } + AnimatedNumberList.builtin$cls = "AnimatedNumberList"; + if (!"name" in AnimatedNumberList) + AnimatedNumberList.name = "AnimatedNumberList"; + $desc = $collectedClasses.AnimatedNumberList; + if ($desc instanceof Array) + $desc = $desc[1]; + AnimatedNumberList.prototype = $desc; + function AnimationElement() { + } + AnimationElement.builtin$cls = "AnimationElement"; + if (!"name" in AnimationElement) + AnimationElement.name = "AnimationElement"; + $desc = $collectedClasses.AnimationElement; + if ($desc instanceof Array) + $desc = $desc[1]; + AnimationElement.prototype = $desc; + function CircleElement() { + } + CircleElement.builtin$cls = "CircleElement"; + if (!"name" in CircleElement) + CircleElement.name = "CircleElement"; + $desc = $collectedClasses.CircleElement; + if ($desc instanceof Array) + $desc = $desc[1]; + CircleElement.prototype = $desc; + function ClipPathElement() { + } + ClipPathElement.builtin$cls = "ClipPathElement"; + if (!"name" in ClipPathElement) + ClipPathElement.name = "ClipPathElement"; + $desc = $collectedClasses.ClipPathElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ClipPathElement.prototype = $desc; + function DefsElement() { + } + DefsElement.builtin$cls = "DefsElement"; + if (!"name" in DefsElement) + DefsElement.name = "DefsElement"; + $desc = $collectedClasses.DefsElement; + if ($desc instanceof Array) + $desc = $desc[1]; + DefsElement.prototype = $desc; + function DescElement() { + } + DescElement.builtin$cls = "DescElement"; + if (!"name" in DescElement) + DescElement.name = "DescElement"; + $desc = $collectedClasses.DescElement; + if ($desc instanceof Array) + $desc = $desc[1]; + DescElement.prototype = $desc; + function DiscardElement() { + } + DiscardElement.builtin$cls = "DiscardElement"; + if (!"name" in DiscardElement) + DiscardElement.name = "DiscardElement"; + $desc = $collectedClasses.DiscardElement; + if ($desc instanceof Array) + $desc = $desc[1]; + DiscardElement.prototype = $desc; + function EllipseElement() { + } + EllipseElement.builtin$cls = "EllipseElement"; + if (!"name" in EllipseElement) + EllipseElement.name = "EllipseElement"; + $desc = $collectedClasses.EllipseElement; + if ($desc instanceof Array) + $desc = $desc[1]; + EllipseElement.prototype = $desc; + function FEBlendElement() { + } + FEBlendElement.builtin$cls = "FEBlendElement"; + if (!"name" in FEBlendElement) + FEBlendElement.name = "FEBlendElement"; + $desc = $collectedClasses.FEBlendElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEBlendElement.prototype = $desc; + function FEColorMatrixElement() { + } + FEColorMatrixElement.builtin$cls = "FEColorMatrixElement"; + if (!"name" in FEColorMatrixElement) + FEColorMatrixElement.name = "FEColorMatrixElement"; + $desc = $collectedClasses.FEColorMatrixElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEColorMatrixElement.prototype = $desc; + function FEComponentTransferElement() { + } + FEComponentTransferElement.builtin$cls = "FEComponentTransferElement"; + if (!"name" in FEComponentTransferElement) + FEComponentTransferElement.name = "FEComponentTransferElement"; + $desc = $collectedClasses.FEComponentTransferElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEComponentTransferElement.prototype = $desc; + function FECompositeElement() { + } + FECompositeElement.builtin$cls = "FECompositeElement"; + if (!"name" in FECompositeElement) + FECompositeElement.name = "FECompositeElement"; + $desc = $collectedClasses.FECompositeElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FECompositeElement.prototype = $desc; + function FEConvolveMatrixElement() { + } + FEConvolveMatrixElement.builtin$cls = "FEConvolveMatrixElement"; + if (!"name" in FEConvolveMatrixElement) + FEConvolveMatrixElement.name = "FEConvolveMatrixElement"; + $desc = $collectedClasses.FEConvolveMatrixElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEConvolveMatrixElement.prototype = $desc; + function FEDiffuseLightingElement() { + } + FEDiffuseLightingElement.builtin$cls = "FEDiffuseLightingElement"; + if (!"name" in FEDiffuseLightingElement) + FEDiffuseLightingElement.name = "FEDiffuseLightingElement"; + $desc = $collectedClasses.FEDiffuseLightingElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEDiffuseLightingElement.prototype = $desc; + function FEDisplacementMapElement() { + } + FEDisplacementMapElement.builtin$cls = "FEDisplacementMapElement"; + if (!"name" in FEDisplacementMapElement) + FEDisplacementMapElement.name = "FEDisplacementMapElement"; + $desc = $collectedClasses.FEDisplacementMapElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEDisplacementMapElement.prototype = $desc; + function FEDistantLightElement() { + } + FEDistantLightElement.builtin$cls = "FEDistantLightElement"; + if (!"name" in FEDistantLightElement) + FEDistantLightElement.name = "FEDistantLightElement"; + $desc = $collectedClasses.FEDistantLightElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEDistantLightElement.prototype = $desc; + function FEFloodElement() { + } + FEFloodElement.builtin$cls = "FEFloodElement"; + if (!"name" in FEFloodElement) + FEFloodElement.name = "FEFloodElement"; + $desc = $collectedClasses.FEFloodElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEFloodElement.prototype = $desc; + function FEFuncAElement() { + } + FEFuncAElement.builtin$cls = "FEFuncAElement"; + if (!"name" in FEFuncAElement) + FEFuncAElement.name = "FEFuncAElement"; + $desc = $collectedClasses.FEFuncAElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEFuncAElement.prototype = $desc; + function FEFuncBElement() { + } + FEFuncBElement.builtin$cls = "FEFuncBElement"; + if (!"name" in FEFuncBElement) + FEFuncBElement.name = "FEFuncBElement"; + $desc = $collectedClasses.FEFuncBElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEFuncBElement.prototype = $desc; + function FEFuncGElement() { + } + FEFuncGElement.builtin$cls = "FEFuncGElement"; + if (!"name" in FEFuncGElement) + FEFuncGElement.name = "FEFuncGElement"; + $desc = $collectedClasses.FEFuncGElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEFuncGElement.prototype = $desc; + function FEFuncRElement() { + } + FEFuncRElement.builtin$cls = "FEFuncRElement"; + if (!"name" in FEFuncRElement) + FEFuncRElement.name = "FEFuncRElement"; + $desc = $collectedClasses.FEFuncRElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEFuncRElement.prototype = $desc; + function FEGaussianBlurElement() { + } + FEGaussianBlurElement.builtin$cls = "FEGaussianBlurElement"; + if (!"name" in FEGaussianBlurElement) + FEGaussianBlurElement.name = "FEGaussianBlurElement"; + $desc = $collectedClasses.FEGaussianBlurElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEGaussianBlurElement.prototype = $desc; + function FEImageElement() { + } + FEImageElement.builtin$cls = "FEImageElement"; + if (!"name" in FEImageElement) + FEImageElement.name = "FEImageElement"; + $desc = $collectedClasses.FEImageElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEImageElement.prototype = $desc; + function FEMergeElement() { + } + FEMergeElement.builtin$cls = "FEMergeElement"; + if (!"name" in FEMergeElement) + FEMergeElement.name = "FEMergeElement"; + $desc = $collectedClasses.FEMergeElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEMergeElement.prototype = $desc; + function FEMergeNodeElement() { + } + FEMergeNodeElement.builtin$cls = "FEMergeNodeElement"; + if (!"name" in FEMergeNodeElement) + FEMergeNodeElement.name = "FEMergeNodeElement"; + $desc = $collectedClasses.FEMergeNodeElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEMergeNodeElement.prototype = $desc; + function FEMorphologyElement() { + } + FEMorphologyElement.builtin$cls = "FEMorphologyElement"; + if (!"name" in FEMorphologyElement) + FEMorphologyElement.name = "FEMorphologyElement"; + $desc = $collectedClasses.FEMorphologyElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEMorphologyElement.prototype = $desc; + function FEOffsetElement() { + } + FEOffsetElement.builtin$cls = "FEOffsetElement"; + if (!"name" in FEOffsetElement) + FEOffsetElement.name = "FEOffsetElement"; + $desc = $collectedClasses.FEOffsetElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEOffsetElement.prototype = $desc; + function FEPointLightElement() { + } + FEPointLightElement.builtin$cls = "FEPointLightElement"; + if (!"name" in FEPointLightElement) + FEPointLightElement.name = "FEPointLightElement"; + $desc = $collectedClasses.FEPointLightElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FEPointLightElement.prototype = $desc; + function FESpecularLightingElement() { + } + FESpecularLightingElement.builtin$cls = "FESpecularLightingElement"; + if (!"name" in FESpecularLightingElement) + FESpecularLightingElement.name = "FESpecularLightingElement"; + $desc = $collectedClasses.FESpecularLightingElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FESpecularLightingElement.prototype = $desc; + function FESpotLightElement() { + } + FESpotLightElement.builtin$cls = "FESpotLightElement"; + if (!"name" in FESpotLightElement) + FESpotLightElement.name = "FESpotLightElement"; + $desc = $collectedClasses.FESpotLightElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FESpotLightElement.prototype = $desc; + function FETileElement() { + } + FETileElement.builtin$cls = "FETileElement"; + if (!"name" in FETileElement) + FETileElement.name = "FETileElement"; + $desc = $collectedClasses.FETileElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FETileElement.prototype = $desc; + function FETurbulenceElement() { + } + FETurbulenceElement.builtin$cls = "FETurbulenceElement"; + if (!"name" in FETurbulenceElement) + FETurbulenceElement.name = "FETurbulenceElement"; + $desc = $collectedClasses.FETurbulenceElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FETurbulenceElement.prototype = $desc; + function FilterElement() { + } + FilterElement.builtin$cls = "FilterElement"; + if (!"name" in FilterElement) + FilterElement.name = "FilterElement"; + $desc = $collectedClasses.FilterElement; + if ($desc instanceof Array) + $desc = $desc[1]; + FilterElement.prototype = $desc; + function ForeignObjectElement() { + } + ForeignObjectElement.builtin$cls = "ForeignObjectElement"; + if (!"name" in ForeignObjectElement) + ForeignObjectElement.name = "ForeignObjectElement"; + $desc = $collectedClasses.ForeignObjectElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ForeignObjectElement.prototype = $desc; + function GElement() { + } + GElement.builtin$cls = "GElement"; + if (!"name" in GElement) + GElement.name = "GElement"; + $desc = $collectedClasses.GElement; + if ($desc instanceof Array) + $desc = $desc[1]; + GElement.prototype = $desc; + function GeometryElement() { + } + GeometryElement.builtin$cls = "GeometryElement"; + if (!"name" in GeometryElement) + GeometryElement.name = "GeometryElement"; + $desc = $collectedClasses.GeometryElement; + if ($desc instanceof Array) + $desc = $desc[1]; + GeometryElement.prototype = $desc; + function GraphicsElement() { + } + GraphicsElement.builtin$cls = "GraphicsElement"; + if (!"name" in GraphicsElement) + GraphicsElement.name = "GraphicsElement"; + $desc = $collectedClasses.GraphicsElement; + if ($desc instanceof Array) + $desc = $desc[1]; + GraphicsElement.prototype = $desc; + function ImageElement0() { + } + ImageElement0.builtin$cls = "ImageElement0"; + if (!"name" in ImageElement0) + ImageElement0.name = "ImageElement0"; + $desc = $collectedClasses.ImageElement0; + if ($desc instanceof Array) + $desc = $desc[1]; + ImageElement0.prototype = $desc; + function LineElement() { + } + LineElement.builtin$cls = "LineElement"; + if (!"name" in LineElement) + LineElement.name = "LineElement"; + $desc = $collectedClasses.LineElement; + if ($desc instanceof Array) + $desc = $desc[1]; + LineElement.prototype = $desc; + function LinearGradientElement() { + } + LinearGradientElement.builtin$cls = "LinearGradientElement"; + if (!"name" in LinearGradientElement) + LinearGradientElement.name = "LinearGradientElement"; + $desc = $collectedClasses.LinearGradientElement; + if ($desc instanceof Array) + $desc = $desc[1]; + LinearGradientElement.prototype = $desc; + function MarkerElement() { + } + MarkerElement.builtin$cls = "MarkerElement"; + if (!"name" in MarkerElement) + MarkerElement.name = "MarkerElement"; + $desc = $collectedClasses.MarkerElement; + if ($desc instanceof Array) + $desc = $desc[1]; + MarkerElement.prototype = $desc; + function MaskElement() { + } + MaskElement.builtin$cls = "MaskElement"; + if (!"name" in MaskElement) + MaskElement.name = "MaskElement"; + $desc = $collectedClasses.MaskElement; + if ($desc instanceof Array) + $desc = $desc[1]; + MaskElement.prototype = $desc; + function MetadataElement() { + } + MetadataElement.builtin$cls = "MetadataElement"; + if (!"name" in MetadataElement) + MetadataElement.name = "MetadataElement"; + $desc = $collectedClasses.MetadataElement; + if ($desc instanceof Array) + $desc = $desc[1]; + MetadataElement.prototype = $desc; + function PathElement() { + } + PathElement.builtin$cls = "PathElement"; + if (!"name" in PathElement) + PathElement.name = "PathElement"; + $desc = $collectedClasses.PathElement; + if ($desc instanceof Array) + $desc = $desc[1]; + PathElement.prototype = $desc; + function PatternElement() { + } + PatternElement.builtin$cls = "PatternElement"; + if (!"name" in PatternElement) + PatternElement.name = "PatternElement"; + $desc = $collectedClasses.PatternElement; + if ($desc instanceof Array) + $desc = $desc[1]; + PatternElement.prototype = $desc; + function PolygonElement() { + } + PolygonElement.builtin$cls = "PolygonElement"; + if (!"name" in PolygonElement) + PolygonElement.name = "PolygonElement"; + $desc = $collectedClasses.PolygonElement; + if ($desc instanceof Array) + $desc = $desc[1]; + PolygonElement.prototype = $desc; + function PolylineElement() { + } + PolylineElement.builtin$cls = "PolylineElement"; + if (!"name" in PolylineElement) + PolylineElement.name = "PolylineElement"; + $desc = $collectedClasses.PolylineElement; + if ($desc instanceof Array) + $desc = $desc[1]; + PolylineElement.prototype = $desc; + function RadialGradientElement() { + } + RadialGradientElement.builtin$cls = "RadialGradientElement"; + if (!"name" in RadialGradientElement) + RadialGradientElement.name = "RadialGradientElement"; + $desc = $collectedClasses.RadialGradientElement; + if ($desc instanceof Array) + $desc = $desc[1]; + RadialGradientElement.prototype = $desc; + function RectElement() { + } + RectElement.builtin$cls = "RectElement"; + if (!"name" in RectElement) + RectElement.name = "RectElement"; + $desc = $collectedClasses.RectElement; + if ($desc instanceof Array) + $desc = $desc[1]; + RectElement.prototype = $desc; + function ScriptElement() { + } + ScriptElement.builtin$cls = "ScriptElement"; + if (!"name" in ScriptElement) + ScriptElement.name = "ScriptElement"; + $desc = $collectedClasses.ScriptElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ScriptElement.prototype = $desc; + function SetElement() { + } + SetElement.builtin$cls = "SetElement"; + if (!"name" in SetElement) + SetElement.name = "SetElement"; + $desc = $collectedClasses.SetElement; + if ($desc instanceof Array) + $desc = $desc[1]; + SetElement.prototype = $desc; + function StopElement() { + } + StopElement.builtin$cls = "StopElement"; + if (!"name" in StopElement) + StopElement.name = "StopElement"; + $desc = $collectedClasses.StopElement; + if ($desc instanceof Array) + $desc = $desc[1]; + StopElement.prototype = $desc; + function StyleElement0() { + } + StyleElement0.builtin$cls = "StyleElement0"; + if (!"name" in StyleElement0) + StyleElement0.name = "StyleElement0"; + $desc = $collectedClasses.StyleElement0; + if ($desc instanceof Array) + $desc = $desc[1]; + StyleElement0.prototype = $desc; + StyleElement0.prototype.set$disabled = function(receiver, v) { + return receiver.disabled = v; + }; + function SvgElement() { + } + SvgElement.builtin$cls = "SvgElement"; + if (!"name" in SvgElement) + SvgElement.name = "SvgElement"; + $desc = $collectedClasses.SvgElement; + if ($desc instanceof Array) + $desc = $desc[1]; + SvgElement.prototype = $desc; + function SvgSvgElement() { + } + SvgSvgElement.builtin$cls = "SvgSvgElement"; + if (!"name" in SvgSvgElement) + SvgSvgElement.name = "SvgSvgElement"; + $desc = $collectedClasses.SvgSvgElement; + if ($desc instanceof Array) + $desc = $desc[1]; + SvgSvgElement.prototype = $desc; + function SwitchElement() { + } + SwitchElement.builtin$cls = "SwitchElement"; + if (!"name" in SwitchElement) + SwitchElement.name = "SwitchElement"; + $desc = $collectedClasses.SwitchElement; + if ($desc instanceof Array) + $desc = $desc[1]; + SwitchElement.prototype = $desc; + function SymbolElement() { + } + SymbolElement.builtin$cls = "SymbolElement"; + if (!"name" in SymbolElement) + SymbolElement.name = "SymbolElement"; + $desc = $collectedClasses.SymbolElement; + if ($desc instanceof Array) + $desc = $desc[1]; + SymbolElement.prototype = $desc; + function TSpanElement() { + } + TSpanElement.builtin$cls = "TSpanElement"; + if (!"name" in TSpanElement) + TSpanElement.name = "TSpanElement"; + $desc = $collectedClasses.TSpanElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TSpanElement.prototype = $desc; + function TextContentElement() { + } + TextContentElement.builtin$cls = "TextContentElement"; + if (!"name" in TextContentElement) + TextContentElement.name = "TextContentElement"; + $desc = $collectedClasses.TextContentElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TextContentElement.prototype = $desc; + function TextElement() { + } + TextElement.builtin$cls = "TextElement"; + if (!"name" in TextElement) + TextElement.name = "TextElement"; + $desc = $collectedClasses.TextElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TextElement.prototype = $desc; + function TextPathElement() { + } + TextPathElement.builtin$cls = "TextPathElement"; + if (!"name" in TextPathElement) + TextPathElement.name = "TextPathElement"; + $desc = $collectedClasses.TextPathElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TextPathElement.prototype = $desc; + function TextPositioningElement() { + } + TextPositioningElement.builtin$cls = "TextPositioningElement"; + if (!"name" in TextPositioningElement) + TextPositioningElement.name = "TextPositioningElement"; + $desc = $collectedClasses.TextPositioningElement; + if ($desc instanceof Array) + $desc = $desc[1]; + TextPositioningElement.prototype = $desc; + function TitleElement0() { + } + TitleElement0.builtin$cls = "TitleElement0"; + if (!"name" in TitleElement0) + TitleElement0.name = "TitleElement0"; + $desc = $collectedClasses.TitleElement0; + if ($desc instanceof Array) + $desc = $desc[1]; + TitleElement0.prototype = $desc; + function UseElement() { + } + UseElement.builtin$cls = "UseElement"; + if (!"name" in UseElement) + UseElement.name = "UseElement"; + $desc = $collectedClasses.UseElement; + if ($desc instanceof Array) + $desc = $desc[1]; + UseElement.prototype = $desc; + function ViewElement() { + } + ViewElement.builtin$cls = "ViewElement"; + if (!"name" in ViewElement) + ViewElement.name = "ViewElement"; + $desc = $collectedClasses.ViewElement; + if ($desc instanceof Array) + $desc = $desc[1]; + ViewElement.prototype = $desc; + function ZoomEvent() { + } + ZoomEvent.builtin$cls = "ZoomEvent"; + if (!"name" in ZoomEvent) + ZoomEvent.name = "ZoomEvent"; + $desc = $collectedClasses.ZoomEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + ZoomEvent.prototype = $desc; + function _GradientElement() { + } + _GradientElement.builtin$cls = "_GradientElement"; + if (!"name" in _GradientElement) + _GradientElement.name = "_GradientElement"; + $desc = $collectedClasses._GradientElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _GradientElement.prototype = $desc; + function _SVGAltGlyphDefElement() { + } + _SVGAltGlyphDefElement.builtin$cls = "_SVGAltGlyphDefElement"; + if (!"name" in _SVGAltGlyphDefElement) + _SVGAltGlyphDefElement.name = "_SVGAltGlyphDefElement"; + $desc = $collectedClasses._SVGAltGlyphDefElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGAltGlyphDefElement.prototype = $desc; + function _SVGAltGlyphItemElement() { + } + _SVGAltGlyphItemElement.builtin$cls = "_SVGAltGlyphItemElement"; + if (!"name" in _SVGAltGlyphItemElement) + _SVGAltGlyphItemElement.name = "_SVGAltGlyphItemElement"; + $desc = $collectedClasses._SVGAltGlyphItemElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGAltGlyphItemElement.prototype = $desc; + function _SVGComponentTransferFunctionElement() { + } + _SVGComponentTransferFunctionElement.builtin$cls = "_SVGComponentTransferFunctionElement"; + if (!"name" in _SVGComponentTransferFunctionElement) + _SVGComponentTransferFunctionElement.name = "_SVGComponentTransferFunctionElement"; + $desc = $collectedClasses._SVGComponentTransferFunctionElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGComponentTransferFunctionElement.prototype = $desc; + function _SVGCursorElement() { + } + _SVGCursorElement.builtin$cls = "_SVGCursorElement"; + if (!"name" in _SVGCursorElement) + _SVGCursorElement.name = "_SVGCursorElement"; + $desc = $collectedClasses._SVGCursorElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGCursorElement.prototype = $desc; + function _SVGFEDropShadowElement() { + } + _SVGFEDropShadowElement.builtin$cls = "_SVGFEDropShadowElement"; + if (!"name" in _SVGFEDropShadowElement) + _SVGFEDropShadowElement.name = "_SVGFEDropShadowElement"; + $desc = $collectedClasses._SVGFEDropShadowElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGFEDropShadowElement.prototype = $desc; + function _SVGFontElement() { + } + _SVGFontElement.builtin$cls = "_SVGFontElement"; + if (!"name" in _SVGFontElement) + _SVGFontElement.name = "_SVGFontElement"; + $desc = $collectedClasses._SVGFontElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGFontElement.prototype = $desc; + function _SVGFontFaceElement() { + } + _SVGFontFaceElement.builtin$cls = "_SVGFontFaceElement"; + if (!"name" in _SVGFontFaceElement) + _SVGFontFaceElement.name = "_SVGFontFaceElement"; + $desc = $collectedClasses._SVGFontFaceElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGFontFaceElement.prototype = $desc; + function _SVGFontFaceFormatElement() { + } + _SVGFontFaceFormatElement.builtin$cls = "_SVGFontFaceFormatElement"; + if (!"name" in _SVGFontFaceFormatElement) + _SVGFontFaceFormatElement.name = "_SVGFontFaceFormatElement"; + $desc = $collectedClasses._SVGFontFaceFormatElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGFontFaceFormatElement.prototype = $desc; + function _SVGFontFaceNameElement() { + } + _SVGFontFaceNameElement.builtin$cls = "_SVGFontFaceNameElement"; + if (!"name" in _SVGFontFaceNameElement) + _SVGFontFaceNameElement.name = "_SVGFontFaceNameElement"; + $desc = $collectedClasses._SVGFontFaceNameElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGFontFaceNameElement.prototype = $desc; + function _SVGFontFaceSrcElement() { + } + _SVGFontFaceSrcElement.builtin$cls = "_SVGFontFaceSrcElement"; + if (!"name" in _SVGFontFaceSrcElement) + _SVGFontFaceSrcElement.name = "_SVGFontFaceSrcElement"; + $desc = $collectedClasses._SVGFontFaceSrcElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGFontFaceSrcElement.prototype = $desc; + function _SVGFontFaceUriElement() { + } + _SVGFontFaceUriElement.builtin$cls = "_SVGFontFaceUriElement"; + if (!"name" in _SVGFontFaceUriElement) + _SVGFontFaceUriElement.name = "_SVGFontFaceUriElement"; + $desc = $collectedClasses._SVGFontFaceUriElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGFontFaceUriElement.prototype = $desc; + function _SVGGlyphElement() { + } + _SVGGlyphElement.builtin$cls = "_SVGGlyphElement"; + if (!"name" in _SVGGlyphElement) + _SVGGlyphElement.name = "_SVGGlyphElement"; + $desc = $collectedClasses._SVGGlyphElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGGlyphElement.prototype = $desc; + function _SVGGlyphRefElement() { + } + _SVGGlyphRefElement.builtin$cls = "_SVGGlyphRefElement"; + if (!"name" in _SVGGlyphRefElement) + _SVGGlyphRefElement.name = "_SVGGlyphRefElement"; + $desc = $collectedClasses._SVGGlyphRefElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGGlyphRefElement.prototype = $desc; + function _SVGHKernElement() { + } + _SVGHKernElement.builtin$cls = "_SVGHKernElement"; + if (!"name" in _SVGHKernElement) + _SVGHKernElement.name = "_SVGHKernElement"; + $desc = $collectedClasses._SVGHKernElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGHKernElement.prototype = $desc; + function _SVGMPathElement() { + } + _SVGMPathElement.builtin$cls = "_SVGMPathElement"; + if (!"name" in _SVGMPathElement) + _SVGMPathElement.name = "_SVGMPathElement"; + $desc = $collectedClasses._SVGMPathElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGMPathElement.prototype = $desc; + function _SVGMissingGlyphElement() { + } + _SVGMissingGlyphElement.builtin$cls = "_SVGMissingGlyphElement"; + if (!"name" in _SVGMissingGlyphElement) + _SVGMissingGlyphElement.name = "_SVGMissingGlyphElement"; + $desc = $collectedClasses._SVGMissingGlyphElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGMissingGlyphElement.prototype = $desc; + function _SVGVKernElement() { + } + _SVGVKernElement.builtin$cls = "_SVGVKernElement"; + if (!"name" in _SVGVKernElement) + _SVGVKernElement.name = "_SVGVKernElement"; + $desc = $collectedClasses._SVGVKernElement; + if ($desc instanceof Array) + $desc = $desc[1]; + _SVGVKernElement.prototype = $desc; + function AudioProcessingEvent() { + } + AudioProcessingEvent.builtin$cls = "AudioProcessingEvent"; + if (!"name" in AudioProcessingEvent) + AudioProcessingEvent.name = "AudioProcessingEvent"; + $desc = $collectedClasses.AudioProcessingEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + AudioProcessingEvent.prototype = $desc; + function OfflineAudioCompletionEvent() { + } + OfflineAudioCompletionEvent.builtin$cls = "OfflineAudioCompletionEvent"; + if (!"name" in OfflineAudioCompletionEvent) + OfflineAudioCompletionEvent.name = "OfflineAudioCompletionEvent"; + $desc = $collectedClasses.OfflineAudioCompletionEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + OfflineAudioCompletionEvent.prototype = $desc; + function ContextEvent() { + } + ContextEvent.builtin$cls = "ContextEvent"; + if (!"name" in ContextEvent) + ContextEvent.name = "ContextEvent"; + $desc = $collectedClasses.ContextEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + ContextEvent.prototype = $desc; + function SqlError() { + } + SqlError.builtin$cls = "SqlError"; + if (!"name" in SqlError) + SqlError.name = "SqlError"; + $desc = $collectedClasses.SqlError; + if ($desc instanceof Array) + $desc = $desc[1]; + SqlError.prototype = $desc; + function NativeTypedData() { + } + NativeTypedData.builtin$cls = "NativeTypedData"; + if (!"name" in NativeTypedData) + NativeTypedData.name = "NativeTypedData"; + $desc = $collectedClasses.NativeTypedData; + if ($desc instanceof Array) + $desc = $desc[1]; + NativeTypedData.prototype = $desc; + function NativeUint8List() { + } + NativeUint8List.builtin$cls = "NativeUint8List"; + if (!"name" in NativeUint8List) + NativeUint8List.name = "NativeUint8List"; + $desc = $collectedClasses.NativeUint8List; + if ($desc instanceof Array) + $desc = $desc[1]; + NativeUint8List.prototype = $desc; + function JS_CONST(code) { + this.code = code; + } + JS_CONST.builtin$cls = "JS_CONST"; + if (!"name" in JS_CONST) + JS_CONST.name = "JS_CONST"; + $desc = $collectedClasses.JS_CONST; + if ($desc instanceof Array) + $desc = $desc[1]; + JS_CONST.prototype = $desc; + function Interceptor() { + } + Interceptor.builtin$cls = "Interceptor"; + if (!"name" in Interceptor) + Interceptor.name = "Interceptor"; + $desc = $collectedClasses.Interceptor; + if ($desc instanceof Array) + $desc = $desc[1]; + Interceptor.prototype = $desc; + function JSBool() { + } + JSBool.builtin$cls = "bool"; + if (!"name" in JSBool) + JSBool.name = "JSBool"; + $desc = $collectedClasses.JSBool; + if ($desc instanceof Array) + $desc = $desc[1]; + JSBool.prototype = $desc; + function JSNull() { + } + JSNull.builtin$cls = "Null"; + if (!"name" in JSNull) + JSNull.name = "JSNull"; + $desc = $collectedClasses.JSNull; + if ($desc instanceof Array) + $desc = $desc[1]; + JSNull.prototype = $desc; + function JavaScriptObject() { + } + JavaScriptObject.builtin$cls = "JavaScriptObject"; + if (!"name" in JavaScriptObject) + JavaScriptObject.name = "JavaScriptObject"; + $desc = $collectedClasses.JavaScriptObject; + if ($desc instanceof Array) + $desc = $desc[1]; + JavaScriptObject.prototype = $desc; + function PlainJavaScriptObject() { + } + PlainJavaScriptObject.builtin$cls = "PlainJavaScriptObject"; + if (!"name" in PlainJavaScriptObject) + PlainJavaScriptObject.name = "PlainJavaScriptObject"; + $desc = $collectedClasses.PlainJavaScriptObject; + if ($desc instanceof Array) + $desc = $desc[1]; + PlainJavaScriptObject.prototype = $desc; + function UnknownJavaScriptObject() { + } + UnknownJavaScriptObject.builtin$cls = "UnknownJavaScriptObject"; + if (!"name" in UnknownJavaScriptObject) + UnknownJavaScriptObject.name = "UnknownJavaScriptObject"; + $desc = $collectedClasses.UnknownJavaScriptObject; + if ($desc instanceof Array) + $desc = $desc[1]; + UnknownJavaScriptObject.prototype = $desc; + function JSArray() { + } + JSArray.builtin$cls = "List"; + if (!"name" in JSArray) + JSArray.name = "JSArray"; + $desc = $collectedClasses.JSArray; + if ($desc instanceof Array) + $desc = $desc[1]; + JSArray.prototype = $desc; + function JSNumber() { + } + JSNumber.builtin$cls = "num"; + if (!"name" in JSNumber) + JSNumber.name = "JSNumber"; + $desc = $collectedClasses.JSNumber; + if ($desc instanceof Array) + $desc = $desc[1]; + JSNumber.prototype = $desc; + function JSInt() { + } + JSInt.builtin$cls = "int"; + if (!"name" in JSInt) + JSInt.name = "JSInt"; + $desc = $collectedClasses.JSInt; + if ($desc instanceof Array) + $desc = $desc[1]; + JSInt.prototype = $desc; + function JSDouble() { + } + JSDouble.builtin$cls = "double"; + if (!"name" in JSDouble) + JSDouble.name = "JSDouble"; + $desc = $collectedClasses.JSDouble; + if ($desc instanceof Array) + $desc = $desc[1]; + JSDouble.prototype = $desc; + function JSString() { + } + JSString.builtin$cls = "String"; + if (!"name" in JSString) + JSString.name = "JSString"; + $desc = $collectedClasses.JSString; + if ($desc instanceof Array) + $desc = $desc[1]; + JSString.prototype = $desc; + function startRootIsolate_closure(box_0, entry_1) { + this.box_0 = box_0; + this.entry_1 = entry_1; + } + startRootIsolate_closure.builtin$cls = "startRootIsolate_closure"; + if (!"name" in startRootIsolate_closure) + startRootIsolate_closure.name = "startRootIsolate_closure"; + $desc = $collectedClasses.startRootIsolate_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + startRootIsolate_closure.prototype = $desc; + function startRootIsolate_closure0(box_0, entry_2) { + this.box_0 = box_0; + this.entry_2 = entry_2; + } + startRootIsolate_closure0.builtin$cls = "startRootIsolate_closure0"; + if (!"name" in startRootIsolate_closure0) + startRootIsolate_closure0.name = "startRootIsolate_closure0"; + $desc = $collectedClasses.startRootIsolate_closure0; + if ($desc instanceof Array) + $desc = $desc[1]; + startRootIsolate_closure0.prototype = $desc; + function _Manager(nextIsolateId, currentManagerId, nextManagerId, currentContext, rootContext, topEventLoop, fromCommandLine, isWorker, supportsWorkers, isolates, mainManager, managers, entry) { + this.nextIsolateId = nextIsolateId; + this.currentManagerId = currentManagerId; + this.nextManagerId = nextManagerId; + this.currentContext = currentContext; + this.rootContext = rootContext; + this.topEventLoop = topEventLoop; + this.fromCommandLine = fromCommandLine; + this.isWorker = isWorker; + this.supportsWorkers = supportsWorkers; + this.isolates = isolates; + this.mainManager = mainManager; + this.managers = managers; + this.entry = entry; + } + _Manager.builtin$cls = "_Manager"; + if (!"name" in _Manager) + _Manager.name = "_Manager"; + $desc = $collectedClasses._Manager; + if ($desc instanceof Array) + $desc = $desc[1]; + _Manager.prototype = $desc; + function _IsolateContext(id, ports, weakPorts, isolateStatics, controlPort, pauseCapability, terminateCapability, isPaused, delayedEvents, pauseTokens, doneHandlers, errorsAreFatal) { + this.id = id; + this.ports = ports; + this.weakPorts = weakPorts; + this.isolateStatics = isolateStatics; + this.controlPort = controlPort; + this.pauseCapability = pauseCapability; + this.terminateCapability = terminateCapability; + this.isPaused = isPaused; + this.delayedEvents = delayedEvents; + this.pauseTokens = pauseTokens; + this.doneHandlers = doneHandlers; + this.errorsAreFatal = errorsAreFatal; + } + _IsolateContext.builtin$cls = "_IsolateContext"; + if (!"name" in _IsolateContext) + _IsolateContext.name = "_IsolateContext"; + $desc = $collectedClasses._IsolateContext; + if ($desc instanceof Array) + $desc = $desc[1]; + _IsolateContext.prototype = $desc; + _IsolateContext.prototype.get$isolateStatics = function() { + return this.isolateStatics; + }; + _IsolateContext.prototype.get$controlPort = function() { + return this.controlPort; + }; + function _IsolateContext_handlePing_closure(responsePort_0) { + this.responsePort_0 = responsePort_0; + } + _IsolateContext_handlePing_closure.builtin$cls = "_IsolateContext_handlePing_closure"; + if (!"name" in _IsolateContext_handlePing_closure) + _IsolateContext_handlePing_closure.name = "_IsolateContext_handlePing_closure"; + $desc = $collectedClasses._IsolateContext_handlePing_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _IsolateContext_handlePing_closure.prototype = $desc; + function _EventLoop(events, _activeJsAsyncCount) { + this.events = events; + this._activeJsAsyncCount = _activeJsAsyncCount; + } + _EventLoop.builtin$cls = "_EventLoop"; + if (!"name" in _EventLoop) + _EventLoop.name = "_EventLoop"; + $desc = $collectedClasses._EventLoop; + if ($desc instanceof Array) + $desc = $desc[1]; + _EventLoop.prototype = $desc; + function _EventLoop__runHelper_next(this_0) { + this.this_0 = this_0; + } + _EventLoop__runHelper_next.builtin$cls = "_EventLoop__runHelper_next"; + if (!"name" in _EventLoop__runHelper_next) + _EventLoop__runHelper_next.name = "_EventLoop__runHelper_next"; + $desc = $collectedClasses._EventLoop__runHelper_next; + if ($desc instanceof Array) + $desc = $desc[1]; + _EventLoop__runHelper_next.prototype = $desc; + function _IsolateEvent(isolate, fn, message) { + this.isolate = isolate; + this.fn = fn; + this.message = message; + } + _IsolateEvent.builtin$cls = "_IsolateEvent"; + if (!"name" in _IsolateEvent) + _IsolateEvent.name = "_IsolateEvent"; + $desc = $collectedClasses._IsolateEvent; + if ($desc instanceof Array) + $desc = $desc[1]; + _IsolateEvent.prototype = $desc; + function _MainManagerStub() { + } + _MainManagerStub.builtin$cls = "_MainManagerStub"; + if (!"name" in _MainManagerStub) + _MainManagerStub.name = "_MainManagerStub"; + $desc = $collectedClasses._MainManagerStub; + if ($desc instanceof Array) + $desc = $desc[1]; + _MainManagerStub.prototype = $desc; + function IsolateNatives__processWorkerMessage_closure(entryPoint_0, args_1, message_2, isSpawnUri_3, startPaused_4, replyTo_5) { + this.entryPoint_0 = entryPoint_0; + this.args_1 = args_1; + this.message_2 = message_2; + this.isSpawnUri_3 = isSpawnUri_3; + this.startPaused_4 = startPaused_4; + this.replyTo_5 = replyTo_5; + } + IsolateNatives__processWorkerMessage_closure.builtin$cls = "IsolateNatives__processWorkerMessage_closure"; + if (!"name" in IsolateNatives__processWorkerMessage_closure) + IsolateNatives__processWorkerMessage_closure.name = "IsolateNatives__processWorkerMessage_closure"; + $desc = $collectedClasses.IsolateNatives__processWorkerMessage_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + IsolateNatives__processWorkerMessage_closure.prototype = $desc; + function IsolateNatives__startIsolate_runStartFunction(topLevel_0, args_1, message_2, isSpawnUri_3) { + this.topLevel_0 = topLevel_0; + this.args_1 = args_1; + this.message_2 = message_2; + this.isSpawnUri_3 = isSpawnUri_3; + } + IsolateNatives__startIsolate_runStartFunction.builtin$cls = "IsolateNatives__startIsolate_runStartFunction"; + if (!"name" in IsolateNatives__startIsolate_runStartFunction) + IsolateNatives__startIsolate_runStartFunction.name = "IsolateNatives__startIsolate_runStartFunction"; + $desc = $collectedClasses.IsolateNatives__startIsolate_runStartFunction; + if ($desc instanceof Array) + $desc = $desc[1]; + IsolateNatives__startIsolate_runStartFunction.prototype = $desc; + function _BaseSendPort() { + } + _BaseSendPort.builtin$cls = "_BaseSendPort"; + if (!"name" in _BaseSendPort) + _BaseSendPort.name = "_BaseSendPort"; + $desc = $collectedClasses._BaseSendPort; + if ($desc instanceof Array) + $desc = $desc[1]; + _BaseSendPort.prototype = $desc; + function _NativeJsSendPort(_receivePort, _isolateId) { + this._receivePort = _receivePort; + this._isolateId = _isolateId; + } + _NativeJsSendPort.builtin$cls = "_NativeJsSendPort"; + if (!"name" in _NativeJsSendPort) + _NativeJsSendPort.name = "_NativeJsSendPort"; + $desc = $collectedClasses._NativeJsSendPort; + if ($desc instanceof Array) + $desc = $desc[1]; + _NativeJsSendPort.prototype = $desc; + function _NativeJsSendPort_send_closure(box_0, this_1, shouldSerialize_2) { + this.box_0 = box_0; + this.this_1 = this_1; + this.shouldSerialize_2 = shouldSerialize_2; + } + _NativeJsSendPort_send_closure.builtin$cls = "_NativeJsSendPort_send_closure"; + if (!"name" in _NativeJsSendPort_send_closure) + _NativeJsSendPort_send_closure.name = "_NativeJsSendPort_send_closure"; + $desc = $collectedClasses._NativeJsSendPort_send_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _NativeJsSendPort_send_closure.prototype = $desc; + function _WorkerSendPort(_workerId, _receivePortId, _isolateId) { + this._workerId = _workerId; + this._receivePortId = _receivePortId; + this._isolateId = _isolateId; + } + _WorkerSendPort.builtin$cls = "_WorkerSendPort"; + if (!"name" in _WorkerSendPort) + _WorkerSendPort.name = "_WorkerSendPort"; + $desc = $collectedClasses._WorkerSendPort; + if ($desc instanceof Array) + $desc = $desc[1]; + _WorkerSendPort.prototype = $desc; + function RawReceivePortImpl(_id, _handler, _isClosed) { + this._id = _id; + this._handler = _handler; + this._isClosed = _isClosed; + } + RawReceivePortImpl.builtin$cls = "RawReceivePortImpl"; + if (!"name" in RawReceivePortImpl) + RawReceivePortImpl.name = "RawReceivePortImpl"; + $desc = $collectedClasses.RawReceivePortImpl; + if ($desc instanceof Array) + $desc = $desc[1]; + RawReceivePortImpl.prototype = $desc; + RawReceivePortImpl.prototype.get$_id = function() { + return this._id; + }; + RawReceivePortImpl.prototype.get$_isClosed = function() { + return this._isClosed; + }; + function _JsSerializer(_nextFreeRefId, _visited) { + this._nextFreeRefId = _nextFreeRefId; + this._visited = _visited; + } + _JsSerializer.builtin$cls = "_JsSerializer"; + if (!"name" in _JsSerializer) + _JsSerializer.name = "_JsSerializer"; + $desc = $collectedClasses._JsSerializer; + if ($desc instanceof Array) + $desc = $desc[1]; + _JsSerializer.prototype = $desc; + function _JsCopier(_visited) { + this._visited = _visited; + } + _JsCopier.builtin$cls = "_JsCopier"; + if (!"name" in _JsCopier) + _JsCopier.name = "_JsCopier"; + $desc = $collectedClasses._JsCopier; + if ($desc instanceof Array) + $desc = $desc[1]; + _JsCopier.prototype = $desc; + function _JsDeserializer(_deserialized) { + this._deserialized = _deserialized; + } + _JsDeserializer.builtin$cls = "_JsDeserializer"; + if (!"name" in _JsDeserializer) + _JsDeserializer.name = "_JsDeserializer"; + $desc = $collectedClasses._JsDeserializer; + if ($desc instanceof Array) + $desc = $desc[1]; + _JsDeserializer.prototype = $desc; + function _JsVisitedMap(tagged) { + this.tagged = tagged; + } + _JsVisitedMap.builtin$cls = "_JsVisitedMap"; + if (!"name" in _JsVisitedMap) + _JsVisitedMap.name = "_JsVisitedMap"; + $desc = $collectedClasses._JsVisitedMap; + if ($desc instanceof Array) + $desc = $desc[1]; + _JsVisitedMap.prototype = $desc; + function _MessageTraverserVisitedMap() { + } + _MessageTraverserVisitedMap.builtin$cls = "_MessageTraverserVisitedMap"; + if (!"name" in _MessageTraverserVisitedMap) + _MessageTraverserVisitedMap.name = "_MessageTraverserVisitedMap"; + $desc = $collectedClasses._MessageTraverserVisitedMap; + if ($desc instanceof Array) + $desc = $desc[1]; + _MessageTraverserVisitedMap.prototype = $desc; + function _MessageTraverser() { + } + _MessageTraverser.builtin$cls = "_MessageTraverser"; + if (!"name" in _MessageTraverser) + _MessageTraverser.name = "_MessageTraverser"; + $desc = $collectedClasses._MessageTraverser; + if ($desc instanceof Array) + $desc = $desc[1]; + _MessageTraverser.prototype = $desc; + function _Copier() { + } + _Copier.builtin$cls = "_Copier"; + if (!"name" in _Copier) + _Copier.name = "_Copier"; + $desc = $collectedClasses._Copier; + if ($desc instanceof Array) + $desc = $desc[1]; + _Copier.prototype = $desc; + function _Copier_visitMap_closure(box_0, this_1) { + this.box_0 = box_0; + this.this_1 = this_1; + } + _Copier_visitMap_closure.builtin$cls = "_Copier_visitMap_closure"; + if (!"name" in _Copier_visitMap_closure) + _Copier_visitMap_closure.name = "_Copier_visitMap_closure"; + $desc = $collectedClasses._Copier_visitMap_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _Copier_visitMap_closure.prototype = $desc; + function _Serializer() { + } + _Serializer.builtin$cls = "_Serializer"; + if (!"name" in _Serializer) + _Serializer.name = "_Serializer"; + $desc = $collectedClasses._Serializer; + if ($desc instanceof Array) + $desc = $desc[1]; + _Serializer.prototype = $desc; + function _Deserializer() { + } + _Deserializer.builtin$cls = "_Deserializer"; + if (!"name" in _Deserializer) + _Deserializer.name = "_Deserializer"; + $desc = $collectedClasses._Deserializer; + if ($desc instanceof Array) + $desc = $desc[1]; + _Deserializer.prototype = $desc; + function TimerImpl(_once, _inEventLoop, _handle) { + this._once = _once; + this._inEventLoop = _inEventLoop; + this._handle = _handle; + } + TimerImpl.builtin$cls = "TimerImpl"; + if (!"name" in TimerImpl) + TimerImpl.name = "TimerImpl"; + $desc = $collectedClasses.TimerImpl; + if ($desc instanceof Array) + $desc = $desc[1]; + TimerImpl.prototype = $desc; + function TimerImpl_internalCallback(this_0, callback_1) { + this.this_0 = this_0; + this.callback_1 = callback_1; + } + TimerImpl_internalCallback.builtin$cls = "TimerImpl_internalCallback"; + if (!"name" in TimerImpl_internalCallback) + TimerImpl_internalCallback.name = "TimerImpl_internalCallback"; + $desc = $collectedClasses.TimerImpl_internalCallback; + if ($desc instanceof Array) + $desc = $desc[1]; + TimerImpl_internalCallback.prototype = $desc; + function TimerImpl_internalCallback0(this_2, callback_3) { + this.this_2 = this_2; + this.callback_3 = callback_3; + } + TimerImpl_internalCallback0.builtin$cls = "TimerImpl_internalCallback0"; + if (!"name" in TimerImpl_internalCallback0) + TimerImpl_internalCallback0.name = "TimerImpl_internalCallback0"; + $desc = $collectedClasses.TimerImpl_internalCallback0; + if ($desc instanceof Array) + $desc = $desc[1]; + TimerImpl_internalCallback0.prototype = $desc; + function CapabilityImpl(_id) { + this._id = _id; + } + CapabilityImpl.builtin$cls = "CapabilityImpl"; + if (!"name" in CapabilityImpl) + CapabilityImpl.name = "CapabilityImpl"; + $desc = $collectedClasses.CapabilityImpl; + if ($desc instanceof Array) + $desc = $desc[1]; + CapabilityImpl.prototype = $desc; + CapabilityImpl.prototype.get$_id = function() { + return this._id; + }; + function ReflectionInfo(jsFunction, data, isAccessor, requiredParameterCount, optionalParameterCount, areOptionalParametersNamed, functionType, cachedSortedIndices) { + this.jsFunction = jsFunction; + this.data = data; + this.isAccessor = isAccessor; + this.requiredParameterCount = requiredParameterCount; + this.optionalParameterCount = optionalParameterCount; + this.areOptionalParametersNamed = areOptionalParametersNamed; + this.functionType = functionType; + this.cachedSortedIndices = cachedSortedIndices; + } + ReflectionInfo.builtin$cls = "ReflectionInfo"; + if (!"name" in ReflectionInfo) + ReflectionInfo.name = "ReflectionInfo"; + $desc = $collectedClasses.ReflectionInfo; + if ($desc instanceof Array) + $desc = $desc[1]; + ReflectionInfo.prototype = $desc; + function TypeErrorDecoder(_pattern, _arguments, _argumentsExpr, _expr, _method, _receiver) { + this._pattern = _pattern; + this._arguments = _arguments; + this._argumentsExpr = _argumentsExpr; + this._expr = _expr; + this._method = _method; + this._receiver = _receiver; + } + TypeErrorDecoder.builtin$cls = "TypeErrorDecoder"; + if (!"name" in TypeErrorDecoder) + TypeErrorDecoder.name = "TypeErrorDecoder"; + $desc = $collectedClasses.TypeErrorDecoder; + if ($desc instanceof Array) + $desc = $desc[1]; + TypeErrorDecoder.prototype = $desc; + function NullError(_message, _method) { + this._message = _message; + this._method = _method; + } + NullError.builtin$cls = "NullError"; + if (!"name" in NullError) + NullError.name = "NullError"; + $desc = $collectedClasses.NullError; + if ($desc instanceof Array) + $desc = $desc[1]; + NullError.prototype = $desc; + function JsNoSuchMethodError(_message, _method, _receiver) { + this._message = _message; + this._method = _method; + this._receiver = _receiver; + } + JsNoSuchMethodError.builtin$cls = "JsNoSuchMethodError"; + if (!"name" in JsNoSuchMethodError) + JsNoSuchMethodError.name = "JsNoSuchMethodError"; + $desc = $collectedClasses.JsNoSuchMethodError; + if ($desc instanceof Array) + $desc = $desc[1]; + JsNoSuchMethodError.prototype = $desc; + function UnknownJsTypeError(_message) { + this._message = _message; + } + UnknownJsTypeError.builtin$cls = "UnknownJsTypeError"; + if (!"name" in UnknownJsTypeError) + UnknownJsTypeError.name = "UnknownJsTypeError"; + $desc = $collectedClasses.UnknownJsTypeError; + if ($desc instanceof Array) + $desc = $desc[1]; + UnknownJsTypeError.prototype = $desc; + function unwrapException_saveStackTrace(ex_0) { + this.ex_0 = ex_0; + } + unwrapException_saveStackTrace.builtin$cls = "unwrapException_saveStackTrace"; + if (!"name" in unwrapException_saveStackTrace) + unwrapException_saveStackTrace.name = "unwrapException_saveStackTrace"; + $desc = $collectedClasses.unwrapException_saveStackTrace; + if ($desc instanceof Array) + $desc = $desc[1]; + unwrapException_saveStackTrace.prototype = $desc; + function _StackTrace(_exception, _trace) { + this._exception = _exception; + this._trace = _trace; + } + _StackTrace.builtin$cls = "_StackTrace"; + if (!"name" in _StackTrace) + _StackTrace.name = "_StackTrace"; + $desc = $collectedClasses._StackTrace; + if ($desc instanceof Array) + $desc = $desc[1]; + _StackTrace.prototype = $desc; + function invokeClosure_closure(closure_0) { + this.closure_0 = closure_0; + } + invokeClosure_closure.builtin$cls = "invokeClosure_closure"; + if (!"name" in invokeClosure_closure) + invokeClosure_closure.name = "invokeClosure_closure"; + $desc = $collectedClasses.invokeClosure_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + invokeClosure_closure.prototype = $desc; + function invokeClosure_closure0(closure_1, arg1_2) { + this.closure_1 = closure_1; + this.arg1_2 = arg1_2; + } + invokeClosure_closure0.builtin$cls = "invokeClosure_closure0"; + if (!"name" in invokeClosure_closure0) + invokeClosure_closure0.name = "invokeClosure_closure0"; + $desc = $collectedClasses.invokeClosure_closure0; + if ($desc instanceof Array) + $desc = $desc[1]; + invokeClosure_closure0.prototype = $desc; + function invokeClosure_closure1(closure_3, arg1_4, arg2_5) { + this.closure_3 = closure_3; + this.arg1_4 = arg1_4; + this.arg2_5 = arg2_5; + } + invokeClosure_closure1.builtin$cls = "invokeClosure_closure1"; + if (!"name" in invokeClosure_closure1) + invokeClosure_closure1.name = "invokeClosure_closure1"; + $desc = $collectedClasses.invokeClosure_closure1; + if ($desc instanceof Array) + $desc = $desc[1]; + invokeClosure_closure1.prototype = $desc; + function invokeClosure_closure2(closure_6, arg1_7, arg2_8, arg3_9) { + this.closure_6 = closure_6; + this.arg1_7 = arg1_7; + this.arg2_8 = arg2_8; + this.arg3_9 = arg3_9; + } + invokeClosure_closure2.builtin$cls = "invokeClosure_closure2"; + if (!"name" in invokeClosure_closure2) + invokeClosure_closure2.name = "invokeClosure_closure2"; + $desc = $collectedClasses.invokeClosure_closure2; + if ($desc instanceof Array) + $desc = $desc[1]; + invokeClosure_closure2.prototype = $desc; + function invokeClosure_closure3(closure_10, arg1_11, arg2_12, arg3_13, arg4_14) { + this.closure_10 = closure_10; + this.arg1_11 = arg1_11; + this.arg2_12 = arg2_12; + this.arg3_13 = arg3_13; + this.arg4_14 = arg4_14; + } + invokeClosure_closure3.builtin$cls = "invokeClosure_closure3"; + if (!"name" in invokeClosure_closure3) + invokeClosure_closure3.name = "invokeClosure_closure3"; + $desc = $collectedClasses.invokeClosure_closure3; + if ($desc instanceof Array) + $desc = $desc[1]; + invokeClosure_closure3.prototype = $desc; + function Closure() { + } + Closure.builtin$cls = "Closure"; + if (!"name" in Closure) + Closure.name = "Closure"; + $desc = $collectedClasses.Closure; + if ($desc instanceof Array) + $desc = $desc[1]; + Closure.prototype = $desc; + function TearOffClosure() { + } + TearOffClosure.builtin$cls = "TearOffClosure"; + if (!"name" in TearOffClosure) + TearOffClosure.name = "TearOffClosure"; + $desc = $collectedClasses.TearOffClosure; + if ($desc instanceof Array) + $desc = $desc[1]; + TearOffClosure.prototype = $desc; + function BoundClosure(_self, __js_helper$_target, _receiver, __js_helper$_name) { + this._self = _self; + this.__js_helper$_target = __js_helper$_target; + this._receiver = _receiver; + this.__js_helper$_name = __js_helper$_name; + } + BoundClosure.builtin$cls = "BoundClosure"; + if (!"name" in BoundClosure) + BoundClosure.name = "BoundClosure"; + $desc = $collectedClasses.BoundClosure; + if ($desc instanceof Array) + $desc = $desc[1]; + BoundClosure.prototype = $desc; + function RuntimeError(message) { + this.message = message; + } + RuntimeError.builtin$cls = "RuntimeError"; + if (!"name" in RuntimeError) + RuntimeError.name = "RuntimeError"; + $desc = $collectedClasses.RuntimeError; + if ($desc instanceof Array) + $desc = $desc[1]; + RuntimeError.prototype = $desc; + function RuntimeType() { + } + RuntimeType.builtin$cls = "RuntimeType"; + if (!"name" in RuntimeType) + RuntimeType.name = "RuntimeType"; + $desc = $collectedClasses.RuntimeType; + if ($desc instanceof Array) + $desc = $desc[1]; + RuntimeType.prototype = $desc; + function RuntimeFunctionType(returnType, parameterTypes, optionalParameterTypes, namedParameters) { + this.returnType = returnType; + this.parameterTypes = parameterTypes; + this.optionalParameterTypes = optionalParameterTypes; + this.namedParameters = namedParameters; + } + RuntimeFunctionType.builtin$cls = "RuntimeFunctionType"; + if (!"name" in RuntimeFunctionType) + RuntimeFunctionType.name = "RuntimeFunctionType"; + $desc = $collectedClasses.RuntimeFunctionType; + if ($desc instanceof Array) + $desc = $desc[1]; + RuntimeFunctionType.prototype = $desc; + function DynamicRuntimeType() { + } + DynamicRuntimeType.builtin$cls = "DynamicRuntimeType"; + if (!"name" in DynamicRuntimeType) + DynamicRuntimeType.name = "DynamicRuntimeType"; + $desc = $collectedClasses.DynamicRuntimeType; + if ($desc instanceof Array) + $desc = $desc[1]; + DynamicRuntimeType.prototype = $desc; + function initHooks_closure(getTag_0) { + this.getTag_0 = getTag_0; + } + initHooks_closure.builtin$cls = "initHooks_closure"; + if (!"name" in initHooks_closure) + initHooks_closure.name = "initHooks_closure"; + $desc = $collectedClasses.initHooks_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + initHooks_closure.prototype = $desc; + function initHooks_closure0(getUnknownTag_1) { + this.getUnknownTag_1 = getUnknownTag_1; + } + initHooks_closure0.builtin$cls = "initHooks_closure0"; + if (!"name" in initHooks_closure0) + initHooks_closure0.name = "initHooks_closure0"; + $desc = $collectedClasses.initHooks_closure0; + if ($desc instanceof Array) + $desc = $desc[1]; + initHooks_closure0.prototype = $desc; + function initHooks_closure1(prototypeForTag_2) { + this.prototypeForTag_2 = prototypeForTag_2; + } + initHooks_closure1.builtin$cls = "initHooks_closure1"; + if (!"name" in initHooks_closure1) + initHooks_closure1.name = "initHooks_closure1"; + $desc = $collectedClasses.initHooks_closure1; + if ($desc instanceof Array) + $desc = $desc[1]; + initHooks_closure1.prototype = $desc; + function JSSyntaxRegExp(_nativeRegExp, _nativeGlobalRegExp, _nativeAnchoredRegExp) { + this._nativeRegExp = _nativeRegExp; + this._nativeGlobalRegExp = _nativeGlobalRegExp; + this._nativeAnchoredRegExp = _nativeAnchoredRegExp; + } + JSSyntaxRegExp.builtin$cls = "JSSyntaxRegExp"; + if (!"name" in JSSyntaxRegExp) + JSSyntaxRegExp.name = "JSSyntaxRegExp"; + $desc = $collectedClasses.JSSyntaxRegExp; + if ($desc instanceof Array) + $desc = $desc[1]; + JSSyntaxRegExp.prototype = $desc; + function _MatchImplementation(pattern, _match) { + this.pattern = pattern; + this._match = _match; + } + _MatchImplementation.builtin$cls = "_MatchImplementation"; + if (!"name" in _MatchImplementation) + _MatchImplementation.name = "_MatchImplementation"; + $desc = $collectedClasses._MatchImplementation; + if ($desc instanceof Array) + $desc = $desc[1]; + _MatchImplementation.prototype = $desc; + function BankAccount(_number, owner, _balance, _pin_code, date_created, date_modified) { + this._number = _number; + this.owner = owner; + this._balance = _balance; + this._pin_code = _pin_code; + this.date_created = date_created; + this.date_modified = date_modified; + } + BankAccount.builtin$cls = "BankAccount"; + if (!"name" in BankAccount) + BankAccount.name = "BankAccount"; + $desc = $collectedClasses.BankAccount; + if ($desc instanceof Array) + $desc = $desc[1]; + BankAccount.prototype = $desc; + function Person(_bank_terminal$_name, address, _email, _gender, _date_birth) { + this._bank_terminal$_name = _bank_terminal$_name; + this.address = address; + this._email = _email; + this._gender = _gender; + this._date_birth = _date_birth; + } + Person.builtin$cls = "Person"; + if (!"name" in Person) + Person.name = "Person"; + $desc = $collectedClasses.Person; + if ($desc instanceof Array) + $desc = $desc[1]; + Person.prototype = $desc; + function ListIterable() { + } + ListIterable.builtin$cls = "ListIterable"; + if (!"name" in ListIterable) + ListIterable.name = "ListIterable"; + $desc = $collectedClasses.ListIterable; + if ($desc instanceof Array) + $desc = $desc[1]; + ListIterable.prototype = $desc; + function ListIterator(_iterable, _length, _index, _current) { + this._iterable = _iterable; + this._length = _length; + this._index = _index; + this._current = _current; + } + ListIterator.builtin$cls = "ListIterator"; + if (!"name" in ListIterator) + ListIterator.name = "ListIterator"; + $desc = $collectedClasses.ListIterator; + if ($desc instanceof Array) + $desc = $desc[1]; + ListIterator.prototype = $desc; + function MappedIterable(_iterable, _f) { + this._iterable = _iterable; + this._f = _f; + } + MappedIterable.builtin$cls = "MappedIterable"; + if (!"name" in MappedIterable) + MappedIterable.name = "MappedIterable"; + $desc = $collectedClasses.MappedIterable; + if ($desc instanceof Array) + $desc = $desc[1]; + MappedIterable.prototype = $desc; + function EfficientLengthMappedIterable(_iterable, _f) { + this._iterable = _iterable; + this._f = _f; + } + EfficientLengthMappedIterable.builtin$cls = "EfficientLengthMappedIterable"; + if (!"name" in EfficientLengthMappedIterable) + EfficientLengthMappedIterable.name = "EfficientLengthMappedIterable"; + $desc = $collectedClasses.EfficientLengthMappedIterable; + if ($desc instanceof Array) + $desc = $desc[1]; + EfficientLengthMappedIterable.prototype = $desc; + function MappedIterator(_current, _iterator, _f) { + this._current = _current; + this._iterator = _iterator; + this._f = _f; + } + MappedIterator.builtin$cls = "MappedIterator"; + if (!"name" in MappedIterator) + MappedIterator.name = "MappedIterator"; + $desc = $collectedClasses.MappedIterator; + if ($desc instanceof Array) + $desc = $desc[1]; + MappedIterator.prototype = $desc; + function MappedListIterable(_source, _f) { + this._source = _source; + this._f = _f; + } + MappedListIterable.builtin$cls = "MappedListIterable"; + if (!"name" in MappedListIterable) + MappedListIterable.name = "MappedListIterable"; + $desc = $collectedClasses.MappedListIterable; + if ($desc instanceof Array) + $desc = $desc[1]; + MappedListIterable.prototype = $desc; + function WhereIterable(_iterable, _f) { + this._iterable = _iterable; + this._f = _f; + } + WhereIterable.builtin$cls = "WhereIterable"; + if (!"name" in WhereIterable) + WhereIterable.name = "WhereIterable"; + $desc = $collectedClasses.WhereIterable; + if ($desc instanceof Array) + $desc = $desc[1]; + WhereIterable.prototype = $desc; + function WhereIterator(_iterator, _f) { + this._iterator = _iterator; + this._f = _f; + } + WhereIterator.builtin$cls = "WhereIterator"; + if (!"name" in WhereIterator) + WhereIterator.name = "WhereIterator"; + $desc = $collectedClasses.WhereIterator; + if ($desc instanceof Array) + $desc = $desc[1]; + WhereIterator.prototype = $desc; + function FixedLengthListMixin() { + } + FixedLengthListMixin.builtin$cls = "FixedLengthListMixin"; + if (!"name" in FixedLengthListMixin) + FixedLengthListMixin.name = "FixedLengthListMixin"; + $desc = $collectedClasses.FixedLengthListMixin; + if ($desc instanceof Array) + $desc = $desc[1]; + FixedLengthListMixin.prototype = $desc; + function _AsyncError(error, stackTrace) { + this.error = error; + this.stackTrace = stackTrace; + } + _AsyncError.builtin$cls = "_AsyncError"; + if (!"name" in _AsyncError) + _AsyncError.name = "_AsyncError"; + $desc = $collectedClasses._AsyncError; + if ($desc instanceof Array) + $desc = $desc[1]; + _AsyncError.prototype = $desc; + _AsyncError.prototype.get$error = function(receiver) { + return this.error; + }; + _AsyncError.prototype.get$stackTrace = function() { + return this.stackTrace; + }; + function _Future(_state, _zone, _resultOrListeners, _nextListener, _onValueCallback, _errorTestCallback, _onErrorCallback, _whenCompleteActionCallback) { + this._state = _state; + this._zone = _zone; + this._resultOrListeners = _resultOrListeners; + this._nextListener = _nextListener; + this._onValueCallback = _onValueCallback; + this._errorTestCallback = _errorTestCallback; + this._onErrorCallback = _onErrorCallback; + this._whenCompleteActionCallback = _whenCompleteActionCallback; + } + _Future.builtin$cls = "_Future"; + if (!"name" in _Future) + _Future.name = "_Future"; + $desc = $collectedClasses._Future; + if ($desc instanceof Array) + $desc = $desc[1]; + _Future.prototype = $desc; + _Future.prototype.get$_zone = function() { + return this._zone; + }; + _Future.prototype.get$_nextListener = function() { + return this._nextListener; + }; + function _Future__addListener_closure(this_0, listener_1) { + this.this_0 = this_0; + this.listener_1 = listener_1; + } + _Future__addListener_closure.builtin$cls = "_Future__addListener_closure"; + if (!"name" in _Future__addListener_closure) + _Future__addListener_closure.name = "_Future__addListener_closure"; + $desc = $collectedClasses._Future__addListener_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _Future__addListener_closure.prototype = $desc; + function _Future__chainForeignFuture_closure(target_0) { + this.target_0 = target_0; + } + _Future__chainForeignFuture_closure.builtin$cls = "_Future__chainForeignFuture_closure"; + if (!"name" in _Future__chainForeignFuture_closure) + _Future__chainForeignFuture_closure.name = "_Future__chainForeignFuture_closure"; + $desc = $collectedClasses._Future__chainForeignFuture_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _Future__chainForeignFuture_closure.prototype = $desc; + function _Future__chainForeignFuture_closure0(target_1) { + this.target_1 = target_1; + } + _Future__chainForeignFuture_closure0.builtin$cls = "_Future__chainForeignFuture_closure0"; + if (!"name" in _Future__chainForeignFuture_closure0) + _Future__chainForeignFuture_closure0.name = "_Future__chainForeignFuture_closure0"; + $desc = $collectedClasses._Future__chainForeignFuture_closure0; + if ($desc instanceof Array) + $desc = $desc[1]; + _Future__chainForeignFuture_closure0.prototype = $desc; + function _Future__propagateToListeners_handleValueCallback(box_1, listener_3, sourceValue_4, zone_5) { + this.box_1 = box_1; + this.listener_3 = listener_3; + this.sourceValue_4 = sourceValue_4; + this.zone_5 = zone_5; + } + _Future__propagateToListeners_handleValueCallback.builtin$cls = "_Future__propagateToListeners_handleValueCallback"; + if (!"name" in _Future__propagateToListeners_handleValueCallback) + _Future__propagateToListeners_handleValueCallback.name = "_Future__propagateToListeners_handleValueCallback"; + $desc = $collectedClasses._Future__propagateToListeners_handleValueCallback; + if ($desc instanceof Array) + $desc = $desc[1]; + _Future__propagateToListeners_handleValueCallback.prototype = $desc; + function _Future__propagateToListeners_handleError(box_2, box_1, listener_6, zone_7) { + this.box_2 = box_2; + this.box_1 = box_1; + this.listener_6 = listener_6; + this.zone_7 = zone_7; + } + _Future__propagateToListeners_handleError.builtin$cls = "_Future__propagateToListeners_handleError"; + if (!"name" in _Future__propagateToListeners_handleError) + _Future__propagateToListeners_handleError.name = "_Future__propagateToListeners_handleError"; + $desc = $collectedClasses._Future__propagateToListeners_handleError; + if ($desc instanceof Array) + $desc = $desc[1]; + _Future__propagateToListeners_handleError.prototype = $desc; + function _Future__propagateToListeners_handleWhenCompleteCallback(box_2, box_1, hasError_8, listener_9, zone_10) { + this.box_2 = box_2; + this.box_1 = box_1; + this.hasError_8 = hasError_8; + this.listener_9 = listener_9; + this.zone_10 = zone_10; + } + _Future__propagateToListeners_handleWhenCompleteCallback.builtin$cls = "_Future__propagateToListeners_handleWhenCompleteCallback"; + if (!"name" in _Future__propagateToListeners_handleWhenCompleteCallback) + _Future__propagateToListeners_handleWhenCompleteCallback.name = "_Future__propagateToListeners_handleWhenCompleteCallback"; + $desc = $collectedClasses._Future__propagateToListeners_handleWhenCompleteCallback; + if ($desc instanceof Array) + $desc = $desc[1]; + _Future__propagateToListeners_handleWhenCompleteCallback.prototype = $desc; + function _Future__propagateToListeners_handleWhenCompleteCallback_closure(box_2, listener_11) { + this.box_2 = box_2; + this.listener_11 = listener_11; + } + _Future__propagateToListeners_handleWhenCompleteCallback_closure.builtin$cls = "_Future__propagateToListeners_handleWhenCompleteCallback_closure"; + if (!"name" in _Future__propagateToListeners_handleWhenCompleteCallback_closure) + _Future__propagateToListeners_handleWhenCompleteCallback_closure.name = "_Future__propagateToListeners_handleWhenCompleteCallback_closure"; + $desc = $collectedClasses._Future__propagateToListeners_handleWhenCompleteCallback_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _Future__propagateToListeners_handleWhenCompleteCallback_closure.prototype = $desc; + function _Future__propagateToListeners_handleWhenCompleteCallback_closure0(box_0, listener_12) { + this.box_0 = box_0; + this.listener_12 = listener_12; + } + _Future__propagateToListeners_handleWhenCompleteCallback_closure0.builtin$cls = "_Future__propagateToListeners_handleWhenCompleteCallback_closure0"; + if (!"name" in _Future__propagateToListeners_handleWhenCompleteCallback_closure0) + _Future__propagateToListeners_handleWhenCompleteCallback_closure0.name = "_Future__propagateToListeners_handleWhenCompleteCallback_closure0"; + $desc = $collectedClasses._Future__propagateToListeners_handleWhenCompleteCallback_closure0; + if ($desc instanceof Array) + $desc = $desc[1]; + _Future__propagateToListeners_handleWhenCompleteCallback_closure0.prototype = $desc; + function _AsyncCallbackEntry(callback, next) { + this.callback = callback; + this.next = next; + } + _AsyncCallbackEntry.builtin$cls = "_AsyncCallbackEntry"; + if (!"name" in _AsyncCallbackEntry) + _AsyncCallbackEntry.name = "_AsyncCallbackEntry"; + $desc = $collectedClasses._AsyncCallbackEntry; + if ($desc instanceof Array) + $desc = $desc[1]; + _AsyncCallbackEntry.prototype = $desc; + function Stream() { + } + Stream.builtin$cls = "Stream"; + if (!"name" in Stream) + Stream.name = "Stream"; + $desc = $collectedClasses.Stream; + if ($desc instanceof Array) + $desc = $desc[1]; + Stream.prototype = $desc; + function Stream_forEach_closure(box_0, this_1, action_2, future_3) { + this.box_0 = box_0; + this.this_1 = this_1; + this.action_2 = action_2; + this.future_3 = future_3; + } + Stream_forEach_closure.builtin$cls = "Stream_forEach_closure"; + if (!"name" in Stream_forEach_closure) + Stream_forEach_closure.name = "Stream_forEach_closure"; + $desc = $collectedClasses.Stream_forEach_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + Stream_forEach_closure.prototype = $desc; + function Stream_forEach__closure(action_4, element_5) { + this.action_4 = action_4; + this.element_5 = element_5; + } + Stream_forEach__closure.builtin$cls = "Stream_forEach__closure"; + if (!"name" in Stream_forEach__closure) + Stream_forEach__closure.name = "Stream_forEach__closure"; + $desc = $collectedClasses.Stream_forEach__closure; + if ($desc instanceof Array) + $desc = $desc[1]; + Stream_forEach__closure.prototype = $desc; + function Stream_forEach__closure0() { + } + Stream_forEach__closure0.builtin$cls = "Stream_forEach__closure0"; + if (!"name" in Stream_forEach__closure0) + Stream_forEach__closure0.name = "Stream_forEach__closure0"; + $desc = $collectedClasses.Stream_forEach__closure0; + if ($desc instanceof Array) + $desc = $desc[1]; + Stream_forEach__closure0.prototype = $desc; + function Stream_forEach_closure0(future_6) { + this.future_6 = future_6; + } + Stream_forEach_closure0.builtin$cls = "Stream_forEach_closure0"; + if (!"name" in Stream_forEach_closure0) + Stream_forEach_closure0.name = "Stream_forEach_closure0"; + $desc = $collectedClasses.Stream_forEach_closure0; + if ($desc instanceof Array) + $desc = $desc[1]; + Stream_forEach_closure0.prototype = $desc; + function Stream_length_closure(box_0) { + this.box_0 = box_0; + } + Stream_length_closure.builtin$cls = "Stream_length_closure"; + if (!"name" in Stream_length_closure) + Stream_length_closure.name = "Stream_length_closure"; + $desc = $collectedClasses.Stream_length_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + Stream_length_closure.prototype = $desc; + function Stream_length_closure0(box_0, future_1) { + this.box_0 = box_0; + this.future_1 = future_1; + } + Stream_length_closure0.builtin$cls = "Stream_length_closure0"; + if (!"name" in Stream_length_closure0) + Stream_length_closure0.name = "Stream_length_closure0"; + $desc = $collectedClasses.Stream_length_closure0; + if ($desc instanceof Array) + $desc = $desc[1]; + Stream_length_closure0.prototype = $desc; + function Stream_isEmpty_closure(box_0, future_1) { + this.box_0 = box_0; + this.future_1 = future_1; + } + Stream_isEmpty_closure.builtin$cls = "Stream_isEmpty_closure"; + if (!"name" in Stream_isEmpty_closure) + Stream_isEmpty_closure.name = "Stream_isEmpty_closure"; + $desc = $collectedClasses.Stream_isEmpty_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + Stream_isEmpty_closure.prototype = $desc; + function Stream_isEmpty_closure0(future_2) { + this.future_2 = future_2; + } + Stream_isEmpty_closure0.builtin$cls = "Stream_isEmpty_closure0"; + if (!"name" in Stream_isEmpty_closure0) + Stream_isEmpty_closure0.name = "Stream_isEmpty_closure0"; + $desc = $collectedClasses.Stream_isEmpty_closure0; + if ($desc instanceof Array) + $desc = $desc[1]; + Stream_isEmpty_closure0.prototype = $desc; + function StreamSubscription() { + } + StreamSubscription.builtin$cls = "StreamSubscription"; + if (!"name" in StreamSubscription) + StreamSubscription.name = "StreamSubscription"; + $desc = $collectedClasses.StreamSubscription; + if ($desc instanceof Array) + $desc = $desc[1]; + StreamSubscription.prototype = $desc; + function _EventSink() { + } + _EventSink.builtin$cls = "_EventSink"; + if (!"name" in _EventSink) + _EventSink.name = "_EventSink"; + $desc = $collectedClasses._EventSink; + if ($desc instanceof Array) + $desc = $desc[1]; + _EventSink.prototype = $desc; + function _cancelAndError_closure(future_0, error_1, stackTrace_2) { + this.future_0 = future_0; + this.error_1 = error_1; + this.stackTrace_2 = stackTrace_2; + } + _cancelAndError_closure.builtin$cls = "_cancelAndError_closure"; + if (!"name" in _cancelAndError_closure) + _cancelAndError_closure.name = "_cancelAndError_closure"; + $desc = $collectedClasses._cancelAndError_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _cancelAndError_closure.prototype = $desc; + function _cancelAndErrorClosure_closure(subscription_0, future_1) { + this.subscription_0 = subscription_0; + this.future_1 = future_1; + } + _cancelAndErrorClosure_closure.builtin$cls = "_cancelAndErrorClosure_closure"; + if (!"name" in _cancelAndErrorClosure_closure) + _cancelAndErrorClosure_closure.name = "_cancelAndErrorClosure_closure"; + $desc = $collectedClasses._cancelAndErrorClosure_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _cancelAndErrorClosure_closure.prototype = $desc; + function _cancelAndValue_closure(future_0, value_1) { + this.future_0 = future_0; + this.value_1 = value_1; + } + _cancelAndValue_closure.builtin$cls = "_cancelAndValue_closure"; + if (!"name" in _cancelAndValue_closure) + _cancelAndValue_closure.name = "_cancelAndValue_closure"; + $desc = $collectedClasses._cancelAndValue_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _cancelAndValue_closure.prototype = $desc; + function _BaseZone() { + } + _BaseZone.builtin$cls = "_BaseZone"; + if (!"name" in _BaseZone) + _BaseZone.name = "_BaseZone"; + $desc = $collectedClasses._BaseZone; + if ($desc instanceof Array) + $desc = $desc[1]; + _BaseZone.prototype = $desc; + function _BaseZone_bindCallback_closure(this_0, registered_1) { + this.this_0 = this_0; + this.registered_1 = registered_1; + } + _BaseZone_bindCallback_closure.builtin$cls = "_BaseZone_bindCallback_closure"; + if (!"name" in _BaseZone_bindCallback_closure) + _BaseZone_bindCallback_closure.name = "_BaseZone_bindCallback_closure"; + $desc = $collectedClasses._BaseZone_bindCallback_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _BaseZone_bindCallback_closure.prototype = $desc; + function _BaseZone_bindCallback_closure0(this_2, registered_3) { + this.this_2 = this_2; + this.registered_3 = registered_3; + } + _BaseZone_bindCallback_closure0.builtin$cls = "_BaseZone_bindCallback_closure0"; + if (!"name" in _BaseZone_bindCallback_closure0) + _BaseZone_bindCallback_closure0.name = "_BaseZone_bindCallback_closure0"; + $desc = $collectedClasses._BaseZone_bindCallback_closure0; + if ($desc instanceof Array) + $desc = $desc[1]; + _BaseZone_bindCallback_closure0.prototype = $desc; + function _BaseZone_bindUnaryCallback_closure(this_0, registered_1) { + this.this_0 = this_0; + this.registered_1 = registered_1; + } + _BaseZone_bindUnaryCallback_closure.builtin$cls = "_BaseZone_bindUnaryCallback_closure"; + if (!"name" in _BaseZone_bindUnaryCallback_closure) + _BaseZone_bindUnaryCallback_closure.name = "_BaseZone_bindUnaryCallback_closure"; + $desc = $collectedClasses._BaseZone_bindUnaryCallback_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _BaseZone_bindUnaryCallback_closure.prototype = $desc; + function _BaseZone_bindUnaryCallback_closure0(this_2, registered_3) { + this.this_2 = this_2; + this.registered_3 = registered_3; + } + _BaseZone_bindUnaryCallback_closure0.builtin$cls = "_BaseZone_bindUnaryCallback_closure0"; + if (!"name" in _BaseZone_bindUnaryCallback_closure0) + _BaseZone_bindUnaryCallback_closure0.name = "_BaseZone_bindUnaryCallback_closure0"; + $desc = $collectedClasses._BaseZone_bindUnaryCallback_closure0; + if ($desc instanceof Array) + $desc = $desc[1]; + _BaseZone_bindUnaryCallback_closure0.prototype = $desc; + function _rootHandleUncaughtError_closure(error_0, stackTrace_1) { + this.error_0 = error_0; + this.stackTrace_1 = stackTrace_1; + } + _rootHandleUncaughtError_closure.builtin$cls = "_rootHandleUncaughtError_closure"; + if (!"name" in _rootHandleUncaughtError_closure) + _rootHandleUncaughtError_closure.name = "_rootHandleUncaughtError_closure"; + $desc = $collectedClasses._rootHandleUncaughtError_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _rootHandleUncaughtError_closure.prototype = $desc; + function _rootHandleUncaughtError__closure(error_2, stackTrace_3) { + this.error_2 = error_2; + this.stackTrace_3 = stackTrace_3; + } + _rootHandleUncaughtError__closure.builtin$cls = "_rootHandleUncaughtError__closure"; + if (!"name" in _rootHandleUncaughtError__closure) + _rootHandleUncaughtError__closure.name = "_rootHandleUncaughtError__closure"; + $desc = $collectedClasses._rootHandleUncaughtError__closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _rootHandleUncaughtError__closure.prototype = $desc; + function _RootZone() { + } + _RootZone.builtin$cls = "_RootZone"; + if (!"name" in _RootZone) + _RootZone.name = "_RootZone"; + $desc = $collectedClasses._RootZone; + if ($desc instanceof Array) + $desc = $desc[1]; + _RootZone.prototype = $desc; + function _HashMap(_collection$_length, _strings, _nums, _rest, _keys) { + this._collection$_length = _collection$_length; + this._strings = _strings; + this._nums = _nums; + this._rest = _rest; + this._keys = _keys; + } + _HashMap.builtin$cls = "_HashMap"; + if (!"name" in _HashMap) + _HashMap.name = "_HashMap"; + $desc = $collectedClasses._HashMap; + if ($desc instanceof Array) + $desc = $desc[1]; + _HashMap.prototype = $desc; + function _HashMap_values_closure(this_0) { + this.this_0 = this_0; + } + _HashMap_values_closure.builtin$cls = "_HashMap_values_closure"; + if (!"name" in _HashMap_values_closure) + _HashMap_values_closure.name = "_HashMap_values_closure"; + $desc = $collectedClasses._HashMap_values_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _HashMap_values_closure.prototype = $desc; + function HashMapKeyIterable(_map) { + this._map = _map; + } + HashMapKeyIterable.builtin$cls = "HashMapKeyIterable"; + if (!"name" in HashMapKeyIterable) + HashMapKeyIterable.name = "HashMapKeyIterable"; + $desc = $collectedClasses.HashMapKeyIterable; + if ($desc instanceof Array) + $desc = $desc[1]; + HashMapKeyIterable.prototype = $desc; + function HashMapKeyIterator(_map, _keys, _offset, _collection$_current) { + this._map = _map; + this._keys = _keys; + this._offset = _offset; + this._collection$_current = _collection$_current; + } + HashMapKeyIterator.builtin$cls = "HashMapKeyIterator"; + if (!"name" in HashMapKeyIterator) + HashMapKeyIterator.name = "HashMapKeyIterator"; + $desc = $collectedClasses.HashMapKeyIterator; + if ($desc instanceof Array) + $desc = $desc[1]; + HashMapKeyIterator.prototype = $desc; + function _LinkedHashMap(_collection$_length, _strings, _nums, _rest, _first, _last, _modifications) { + this._collection$_length = _collection$_length; + this._strings = _strings; + this._nums = _nums; + this._rest = _rest; + this._first = _first; + this._last = _last; + this._modifications = _modifications; + } + _LinkedHashMap.builtin$cls = "_LinkedHashMap"; + if (!"name" in _LinkedHashMap) + _LinkedHashMap.name = "_LinkedHashMap"; + $desc = $collectedClasses._LinkedHashMap; + if ($desc instanceof Array) + $desc = $desc[1]; + _LinkedHashMap.prototype = $desc; + function _LinkedHashMap_values_closure(this_0) { + this.this_0 = this_0; + } + _LinkedHashMap_values_closure.builtin$cls = "_LinkedHashMap_values_closure"; + if (!"name" in _LinkedHashMap_values_closure) + _LinkedHashMap_values_closure.name = "_LinkedHashMap_values_closure"; + $desc = $collectedClasses._LinkedHashMap_values_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _LinkedHashMap_values_closure.prototype = $desc; + function LinkedHashMapCell(_key, _value, _next, _previous) { + this._key = _key; + this._value = _value; + this._next = _next; + this._previous = _previous; + } + LinkedHashMapCell.builtin$cls = "LinkedHashMapCell"; + if (!"name" in LinkedHashMapCell) + LinkedHashMapCell.name = "LinkedHashMapCell"; + $desc = $collectedClasses.LinkedHashMapCell; + if ($desc instanceof Array) + $desc = $desc[1]; + LinkedHashMapCell.prototype = $desc; + LinkedHashMapCell.prototype.get$_key = function(receiver) { + return this._key; + }; + LinkedHashMapCell.prototype.get$_value = function() { + return this._value; + }; + LinkedHashMapCell.prototype.set$_value = function(v) { + return this._value = v; + }; + LinkedHashMapCell.prototype.get$_next = function() { + return this._next; + }; + LinkedHashMapCell.prototype.set$_next = function(v) { + return this._next = v; + }; + LinkedHashMapCell.prototype.get$_previous = function() { + return this._previous; + }; + LinkedHashMapCell.prototype.set$_previous = function(v) { + return this._previous = v; + }; + function LinkedHashMapKeyIterable(_map) { + this._map = _map; + } + LinkedHashMapKeyIterable.builtin$cls = "LinkedHashMapKeyIterable"; + if (!"name" in LinkedHashMapKeyIterable) + LinkedHashMapKeyIterable.name = "LinkedHashMapKeyIterable"; + $desc = $collectedClasses.LinkedHashMapKeyIterable; + if ($desc instanceof Array) + $desc = $desc[1]; + LinkedHashMapKeyIterable.prototype = $desc; + function LinkedHashMapKeyIterator(_map, _modifications, _cell, _collection$_current) { + this._map = _map; + this._modifications = _modifications; + this._cell = _cell; + this._collection$_current = _collection$_current; + } + LinkedHashMapKeyIterator.builtin$cls = "LinkedHashMapKeyIterator"; + if (!"name" in LinkedHashMapKeyIterator) + LinkedHashMapKeyIterator.name = "LinkedHashMapKeyIterator"; + $desc = $collectedClasses.LinkedHashMapKeyIterator; + if ($desc instanceof Array) + $desc = $desc[1]; + LinkedHashMapKeyIterator.prototype = $desc; + function _HashSet() { + } + _HashSet.builtin$cls = "_HashSet"; + if (!"name" in _HashSet) + _HashSet.name = "_HashSet"; + $desc = $collectedClasses._HashSet; + if ($desc instanceof Array) + $desc = $desc[1]; + _HashSet.prototype = $desc; + function _IdentityHashSet(_collection$_length, _strings, _nums, _rest, _elements) { + this._collection$_length = _collection$_length; + this._strings = _strings; + this._nums = _nums; + this._rest = _rest; + this._elements = _elements; + } + _IdentityHashSet.builtin$cls = "_IdentityHashSet"; + if (!"name" in _IdentityHashSet) + _IdentityHashSet.name = "_IdentityHashSet"; + $desc = $collectedClasses._IdentityHashSet; + if ($desc instanceof Array) + $desc = $desc[1]; + _IdentityHashSet.prototype = $desc; + function HashSetIterator(_set, _elements, _offset, _collection$_current) { + this._set = _set; + this._elements = _elements; + this._offset = _offset; + this._collection$_current = _collection$_current; + } + HashSetIterator.builtin$cls = "HashSetIterator"; + if (!"name" in HashSetIterator) + HashSetIterator.name = "HashSetIterator"; + $desc = $collectedClasses.HashSetIterator; + if ($desc instanceof Array) + $desc = $desc[1]; + HashSetIterator.prototype = $desc; + function _LinkedHashSet(_collection$_length, _strings, _nums, _rest, _first, _last, _modifications) { + this._collection$_length = _collection$_length; + this._strings = _strings; + this._nums = _nums; + this._rest = _rest; + this._first = _first; + this._last = _last; + this._modifications = _modifications; + } + _LinkedHashSet.builtin$cls = "_LinkedHashSet"; + if (!"name" in _LinkedHashSet) + _LinkedHashSet.name = "_LinkedHashSet"; + $desc = $collectedClasses._LinkedHashSet; + if ($desc instanceof Array) + $desc = $desc[1]; + _LinkedHashSet.prototype = $desc; + function LinkedHashSetCell(_collection$_element, _next, _previous) { + this._collection$_element = _collection$_element; + this._next = _next; + this._previous = _previous; + } + LinkedHashSetCell.builtin$cls = "LinkedHashSetCell"; + if (!"name" in LinkedHashSetCell) + LinkedHashSetCell.name = "LinkedHashSetCell"; + $desc = $collectedClasses.LinkedHashSetCell; + if ($desc instanceof Array) + $desc = $desc[1]; + LinkedHashSetCell.prototype = $desc; + LinkedHashSetCell.prototype.get$_collection$_element = function() { + return this._collection$_element; + }; + LinkedHashSetCell.prototype.get$_next = function() { + return this._next; + }; + LinkedHashSetCell.prototype.set$_next = function(v) { + return this._next = v; + }; + LinkedHashSetCell.prototype.get$_previous = function() { + return this._previous; + }; + LinkedHashSetCell.prototype.set$_previous = function(v) { + return this._previous = v; + }; + function LinkedHashSetIterator(_set, _modifications, _cell, _collection$_current) { + this._set = _set; + this._modifications = _modifications; + this._cell = _cell; + this._collection$_current = _collection$_current; + } + LinkedHashSetIterator.builtin$cls = "LinkedHashSetIterator"; + if (!"name" in LinkedHashSetIterator) + LinkedHashSetIterator.name = "LinkedHashSetIterator"; + $desc = $collectedClasses.LinkedHashSetIterator; + if ($desc instanceof Array) + $desc = $desc[1]; + LinkedHashSetIterator.prototype = $desc; + function _HashSetBase() { + } + _HashSetBase.builtin$cls = "_HashSetBase"; + if (!"name" in _HashSetBase) + _HashSetBase.name = "_HashSetBase"; + $desc = $collectedClasses._HashSetBase; + if ($desc instanceof Array) + $desc = $desc[1]; + _HashSetBase.prototype = $desc; + function IterableBase() { + } + IterableBase.builtin$cls = "IterableBase"; + if (!"name" in IterableBase) + IterableBase.name = "IterableBase"; + $desc = $collectedClasses.IterableBase; + if ($desc instanceof Array) + $desc = $desc[1]; + IterableBase.prototype = $desc; + function ListBase() { + } + ListBase.builtin$cls = "ListBase"; + if (!"name" in ListBase) + ListBase.name = "ListBase"; + $desc = $collectedClasses.ListBase; + if ($desc instanceof Array) + $desc = $desc[1]; + ListBase.prototype = $desc; + function ListMixin() { + } + ListMixin.builtin$cls = "ListMixin"; + if (!"name" in ListMixin) + ListMixin.name = "ListMixin"; + $desc = $collectedClasses.ListMixin; + if ($desc instanceof Array) + $desc = $desc[1]; + ListMixin.prototype = $desc; + function Maps_mapToString_closure(box_0, result_1) { + this.box_0 = box_0; + this.result_1 = result_1; + } + Maps_mapToString_closure.builtin$cls = "Maps_mapToString_closure"; + if (!"name" in Maps_mapToString_closure) + Maps_mapToString_closure.name = "Maps_mapToString_closure"; + $desc = $collectedClasses.Maps_mapToString_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + Maps_mapToString_closure.prototype = $desc; + function ListQueue(_table, _head, _tail, _modificationCount) { + this._table = _table; + this._head = _head; + this._tail = _tail; + this._modificationCount = _modificationCount; + } + ListQueue.builtin$cls = "ListQueue"; + if (!"name" in ListQueue) + ListQueue.name = "ListQueue"; + $desc = $collectedClasses.ListQueue; + if ($desc instanceof Array) + $desc = $desc[1]; + ListQueue.prototype = $desc; + function _ListQueueIterator(_queue, _end, _modificationCount, _collection$_position, _collection$_current) { + this._queue = _queue; + this._end = _end; + this._modificationCount = _modificationCount; + this._collection$_position = _collection$_position; + this._collection$_current = _collection$_current; + } + _ListQueueIterator.builtin$cls = "_ListQueueIterator"; + if (!"name" in _ListQueueIterator) + _ListQueueIterator.name = "_ListQueueIterator"; + $desc = $collectedClasses._ListQueueIterator; + if ($desc instanceof Array) + $desc = $desc[1]; + _ListQueueIterator.prototype = $desc; + function _convertJsonToDart_closure() { + } + _convertJsonToDart_closure.builtin$cls = "_convertJsonToDart_closure"; + if (!"name" in _convertJsonToDart_closure) + _convertJsonToDart_closure.name = "_convertJsonToDart_closure"; + $desc = $collectedClasses._convertJsonToDart_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _convertJsonToDart_closure.prototype = $desc; + function _convertJsonToDart_walk(revive_0) { + this.revive_0 = revive_0; + } + _convertJsonToDart_walk.builtin$cls = "_convertJsonToDart_walk"; + if (!"name" in _convertJsonToDart_walk) + _convertJsonToDart_walk.name = "_convertJsonToDart_walk"; + $desc = $collectedClasses._convertJsonToDart_walk; + if ($desc instanceof Array) + $desc = $desc[1]; + _convertJsonToDart_walk.prototype = $desc; + function Codec() { + } + Codec.builtin$cls = "Codec"; + if (!"name" in Codec) + Codec.name = "Codec"; + $desc = $collectedClasses.Codec; + if ($desc instanceof Array) + $desc = $desc[1]; + Codec.prototype = $desc; + function Converter() { + } + Converter.builtin$cls = "Converter"; + if (!"name" in Converter) + Converter.name = "Converter"; + $desc = $collectedClasses.Converter; + if ($desc instanceof Array) + $desc = $desc[1]; + Converter.prototype = $desc; + function JsonUnsupportedObjectError(unsupportedObject, cause) { + this.unsupportedObject = unsupportedObject; + this.cause = cause; + } + JsonUnsupportedObjectError.builtin$cls = "JsonUnsupportedObjectError"; + if (!"name" in JsonUnsupportedObjectError) + JsonUnsupportedObjectError.name = "JsonUnsupportedObjectError"; + $desc = $collectedClasses.JsonUnsupportedObjectError; + if ($desc instanceof Array) + $desc = $desc[1]; + JsonUnsupportedObjectError.prototype = $desc; + function JsonCyclicError(unsupportedObject, cause) { + this.unsupportedObject = unsupportedObject; + this.cause = cause; + } + JsonCyclicError.builtin$cls = "JsonCyclicError"; + if (!"name" in JsonCyclicError) + JsonCyclicError.name = "JsonCyclicError"; + $desc = $collectedClasses.JsonCyclicError; + if ($desc instanceof Array) + $desc = $desc[1]; + JsonCyclicError.prototype = $desc; + function JsonCodec(_reviver, _toEncodable) { + this._reviver = _reviver; + this._toEncodable = _toEncodable; + } + JsonCodec.builtin$cls = "JsonCodec"; + if (!"name" in JsonCodec) + JsonCodec.name = "JsonCodec"; + $desc = $collectedClasses.JsonCodec; + if ($desc instanceof Array) + $desc = $desc[1]; + JsonCodec.prototype = $desc; + function JsonEncoder(_toEncodableFunction) { + this._toEncodableFunction = _toEncodableFunction; + } + JsonEncoder.builtin$cls = "JsonEncoder"; + if (!"name" in JsonEncoder) + JsonEncoder.name = "JsonEncoder"; + $desc = $collectedClasses.JsonEncoder; + if ($desc instanceof Array) + $desc = $desc[1]; + JsonEncoder.prototype = $desc; + function JsonDecoder(_reviver) { + this._reviver = _reviver; + } + JsonDecoder.builtin$cls = "JsonDecoder"; + if (!"name" in JsonDecoder) + JsonDecoder.name = "JsonDecoder"; + $desc = $collectedClasses.JsonDecoder; + if ($desc instanceof Array) + $desc = $desc[1]; + JsonDecoder.prototype = $desc; + function _JsonStringifier(_toEncodable, _sink, _seen) { + this._toEncodable = _toEncodable; + this._sink = _sink; + this._seen = _seen; + } + _JsonStringifier.builtin$cls = "_JsonStringifier"; + if (!"name" in _JsonStringifier) + _JsonStringifier.name = "_JsonStringifier"; + $desc = $collectedClasses._JsonStringifier; + if ($desc instanceof Array) + $desc = $desc[1]; + _JsonStringifier.prototype = $desc; + function NoSuchMethodError_toString_closure(box_0) { + this.box_0 = box_0; + } + NoSuchMethodError_toString_closure.builtin$cls = "NoSuchMethodError_toString_closure"; + if (!"name" in NoSuchMethodError_toString_closure) + NoSuchMethodError_toString_closure.name = "NoSuchMethodError_toString_closure"; + $desc = $collectedClasses.NoSuchMethodError_toString_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + NoSuchMethodError_toString_closure.prototype = $desc; + function DateTime(millisecondsSinceEpoch, isUtc) { + this.millisecondsSinceEpoch = millisecondsSinceEpoch; + this.isUtc = isUtc; + } + DateTime.builtin$cls = "DateTime"; + if (!"name" in DateTime) + DateTime.name = "DateTime"; + $desc = $collectedClasses.DateTime; + if ($desc instanceof Array) + $desc = $desc[1]; + DateTime.prototype = $desc; + function DateTime_parse_parseIntOrZero() { + } + DateTime_parse_parseIntOrZero.builtin$cls = "DateTime_parse_parseIntOrZero"; + if (!"name" in DateTime_parse_parseIntOrZero) + DateTime_parse_parseIntOrZero.name = "DateTime_parse_parseIntOrZero"; + $desc = $collectedClasses.DateTime_parse_parseIntOrZero; + if ($desc instanceof Array) + $desc = $desc[1]; + DateTime_parse_parseIntOrZero.prototype = $desc; + function DateTime_parse_parseDoubleOrZero() { + } + DateTime_parse_parseDoubleOrZero.builtin$cls = "DateTime_parse_parseDoubleOrZero"; + if (!"name" in DateTime_parse_parseDoubleOrZero) + DateTime_parse_parseDoubleOrZero.name = "DateTime_parse_parseDoubleOrZero"; + $desc = $collectedClasses.DateTime_parse_parseDoubleOrZero; + if ($desc instanceof Array) + $desc = $desc[1]; + DateTime_parse_parseDoubleOrZero.prototype = $desc; + function Duration(_duration) { + this._duration = _duration; + } + Duration.builtin$cls = "Duration"; + if (!"name" in Duration) + Duration.name = "Duration"; + $desc = $collectedClasses.Duration; + if ($desc instanceof Array) + $desc = $desc[1]; + Duration.prototype = $desc; + Duration.prototype.get$_duration = function() { + return this._duration; + }; + function Duration_toString_sixDigits() { + } + Duration_toString_sixDigits.builtin$cls = "Duration_toString_sixDigits"; + if (!"name" in Duration_toString_sixDigits) + Duration_toString_sixDigits.name = "Duration_toString_sixDigits"; + $desc = $collectedClasses.Duration_toString_sixDigits; + if ($desc instanceof Array) + $desc = $desc[1]; + Duration_toString_sixDigits.prototype = $desc; + function Duration_toString_twoDigits() { + } + Duration_toString_twoDigits.builtin$cls = "Duration_toString_twoDigits"; + if (!"name" in Duration_toString_twoDigits) + Duration_toString_twoDigits.name = "Duration_toString_twoDigits"; + $desc = $collectedClasses.Duration_toString_twoDigits; + if ($desc instanceof Array) + $desc = $desc[1]; + Duration_toString_twoDigits.prototype = $desc; + function Error() { + } + Error.builtin$cls = "Error"; + if (!"name" in Error) + Error.name = "Error"; + $desc = $collectedClasses.Error; + if ($desc instanceof Array) + $desc = $desc[1]; + Error.prototype = $desc; + function NullThrownError() { + } + NullThrownError.builtin$cls = "NullThrownError"; + if (!"name" in NullThrownError) + NullThrownError.name = "NullThrownError"; + $desc = $collectedClasses.NullThrownError; + if ($desc instanceof Array) + $desc = $desc[1]; + NullThrownError.prototype = $desc; + function ArgumentError(message) { + this.message = message; + } + ArgumentError.builtin$cls = "ArgumentError"; + if (!"name" in ArgumentError) + ArgumentError.name = "ArgumentError"; + $desc = $collectedClasses.ArgumentError; + if ($desc instanceof Array) + $desc = $desc[1]; + ArgumentError.prototype = $desc; + function RangeError(message) { + this.message = message; + } + RangeError.builtin$cls = "RangeError"; + if (!"name" in RangeError) + RangeError.name = "RangeError"; + $desc = $collectedClasses.RangeError; + if ($desc instanceof Array) + $desc = $desc[1]; + RangeError.prototype = $desc; + function UnsupportedError(message) { + this.message = message; + } + UnsupportedError.builtin$cls = "UnsupportedError"; + if (!"name" in UnsupportedError) + UnsupportedError.name = "UnsupportedError"; + $desc = $collectedClasses.UnsupportedError; + if ($desc instanceof Array) + $desc = $desc[1]; + UnsupportedError.prototype = $desc; + function UnimplementedError(message) { + this.message = message; + } + UnimplementedError.builtin$cls = "UnimplementedError"; + if (!"name" in UnimplementedError) + UnimplementedError.name = "UnimplementedError"; + $desc = $collectedClasses.UnimplementedError; + if ($desc instanceof Array) + $desc = $desc[1]; + UnimplementedError.prototype = $desc; + function StateError(message) { + this.message = message; + } + StateError.builtin$cls = "StateError"; + if (!"name" in StateError) + StateError.name = "StateError"; + $desc = $collectedClasses.StateError; + if ($desc instanceof Array) + $desc = $desc[1]; + StateError.prototype = $desc; + function ConcurrentModificationError(modifiedObject) { + this.modifiedObject = modifiedObject; + } + ConcurrentModificationError.builtin$cls = "ConcurrentModificationError"; + if (!"name" in ConcurrentModificationError) + ConcurrentModificationError.name = "ConcurrentModificationError"; + $desc = $collectedClasses.ConcurrentModificationError; + if ($desc instanceof Array) + $desc = $desc[1]; + ConcurrentModificationError.prototype = $desc; + function OutOfMemoryError() { + } + OutOfMemoryError.builtin$cls = "OutOfMemoryError"; + if (!"name" in OutOfMemoryError) + OutOfMemoryError.name = "OutOfMemoryError"; + $desc = $collectedClasses.OutOfMemoryError; + if ($desc instanceof Array) + $desc = $desc[1]; + OutOfMemoryError.prototype = $desc; + function StackOverflowError() { + } + StackOverflowError.builtin$cls = "StackOverflowError"; + if (!"name" in StackOverflowError) + StackOverflowError.name = "StackOverflowError"; + $desc = $collectedClasses.StackOverflowError; + if ($desc instanceof Array) + $desc = $desc[1]; + StackOverflowError.prototype = $desc; + function CyclicInitializationError(variableName) { + this.variableName = variableName; + } + CyclicInitializationError.builtin$cls = "CyclicInitializationError"; + if (!"name" in CyclicInitializationError) + CyclicInitializationError.name = "CyclicInitializationError"; + $desc = $collectedClasses.CyclicInitializationError; + if ($desc instanceof Array) + $desc = $desc[1]; + CyclicInitializationError.prototype = $desc; + function _ExceptionImplementation(message) { + this.message = message; + } + _ExceptionImplementation.builtin$cls = "_ExceptionImplementation"; + if (!"name" in _ExceptionImplementation) + _ExceptionImplementation.name = "_ExceptionImplementation"; + $desc = $collectedClasses._ExceptionImplementation; + if ($desc instanceof Array) + $desc = $desc[1]; + _ExceptionImplementation.prototype = $desc; + function FormatException(message) { + this.message = message; + } + FormatException.builtin$cls = "FormatException"; + if (!"name" in FormatException) + FormatException.name = "FormatException"; + $desc = $collectedClasses.FormatException; + if ($desc instanceof Array) + $desc = $desc[1]; + FormatException.prototype = $desc; + function Expando(name) { + this.name = name; + } + Expando.builtin$cls = "Expando"; + if (!"name" in Expando) + Expando.name = "Expando"; + $desc = $collectedClasses.Expando; + if ($desc instanceof Array) + $desc = $desc[1]; + Expando.prototype = $desc; + function Iterator() { + } + Iterator.builtin$cls = "Iterator"; + if (!"name" in Iterator) + Iterator.name = "Iterator"; + $desc = $collectedClasses.Iterator; + if ($desc instanceof Array) + $desc = $desc[1]; + Iterator.prototype = $desc; + function Null() { + } + Null.builtin$cls = "Null"; + if (!"name" in Null) + Null.name = "Null"; + $desc = $collectedClasses.Null; + if ($desc instanceof Array) + $desc = $desc[1]; + Null.prototype = $desc; + function Object() { + } + Object.builtin$cls = "Object"; + if (!"name" in Object) + Object.name = "Object"; + $desc = $collectedClasses.Object; + if ($desc instanceof Array) + $desc = $desc[1]; + Object.prototype = $desc; + function StackTrace() { + } + StackTrace.builtin$cls = "StackTrace"; + if (!"name" in StackTrace) + StackTrace.name = "StackTrace"; + $desc = $collectedClasses.StackTrace; + if ($desc instanceof Array) + $desc = $desc[1]; + StackTrace.prototype = $desc; + function StringBuffer(_contents) { + this._contents = _contents; + } + StringBuffer.builtin$cls = "StringBuffer"; + if (!"name" in StringBuffer) + StringBuffer.name = "StringBuffer"; + $desc = $collectedClasses.StringBuffer; + if ($desc instanceof Array) + $desc = $desc[1]; + StringBuffer.prototype = $desc; + StringBuffer.prototype.get$_contents = function() { + return this._contents; + }; + function Symbol() { + } + Symbol.builtin$cls = "Symbol"; + if (!"name" in Symbol) + Symbol.name = "Symbol"; + $desc = $collectedClasses.Symbol; + if ($desc instanceof Array) + $desc = $desc[1]; + Symbol.prototype = $desc; + function Element_Element$html_closure() { + } + Element_Element$html_closure.builtin$cls = "Element_Element$html_closure"; + if (!"name" in Element_Element$html_closure) + Element_Element$html_closure.name = "Element_Element$html_closure"; + $desc = $collectedClasses.Element_Element$html_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + Element_Element$html_closure.prototype = $desc; + function _ChildNodeListLazy(_this) { + this._this = _this; + } + _ChildNodeListLazy.builtin$cls = "_ChildNodeListLazy"; + if (!"name" in _ChildNodeListLazy) + _ChildNodeListLazy.name = "_ChildNodeListLazy"; + $desc = $collectedClasses._ChildNodeListLazy; + if ($desc instanceof Array) + $desc = $desc[1]; + _ChildNodeListLazy.prototype = $desc; + function Interceptor_ListMixin() { + } + Interceptor_ListMixin.builtin$cls = "Interceptor_ListMixin"; + if (!"name" in Interceptor_ListMixin) + Interceptor_ListMixin.name = "Interceptor_ListMixin"; + $desc = $collectedClasses.Interceptor_ListMixin; + if ($desc instanceof Array) + $desc = $desc[1]; + Interceptor_ListMixin.prototype = $desc; + function Interceptor_ListMixin_ImmutableListMixin() { + } + Interceptor_ListMixin_ImmutableListMixin.builtin$cls = "Interceptor_ListMixin_ImmutableListMixin"; + if (!"name" in Interceptor_ListMixin_ImmutableListMixin) + Interceptor_ListMixin_ImmutableListMixin.name = "Interceptor_ListMixin_ImmutableListMixin"; + $desc = $collectedClasses.Interceptor_ListMixin_ImmutableListMixin; + if ($desc instanceof Array) + $desc = $desc[1]; + Interceptor_ListMixin_ImmutableListMixin.prototype = $desc; + function Storage_keys_closure(keys_0) { + this.keys_0 = keys_0; + } + Storage_keys_closure.builtin$cls = "Storage_keys_closure"; + if (!"name" in Storage_keys_closure) + Storage_keys_closure.name = "Storage_keys_closure"; + $desc = $collectedClasses.Storage_keys_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + Storage_keys_closure.prototype = $desc; + function Storage_values_closure(values_0) { + this.values_0 = values_0; + } + Storage_values_closure.builtin$cls = "Storage_values_closure"; + if (!"name" in Storage_values_closure) + Storage_values_closure.name = "Storage_values_closure"; + $desc = $collectedClasses.Storage_values_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + Storage_values_closure.prototype = $desc; + function Interceptor_ListMixin0() { + } + Interceptor_ListMixin0.builtin$cls = "Interceptor_ListMixin0"; + if (!"name" in Interceptor_ListMixin0) + Interceptor_ListMixin0.name = "Interceptor_ListMixin0"; + $desc = $collectedClasses.Interceptor_ListMixin0; + if ($desc instanceof Array) + $desc = $desc[1]; + Interceptor_ListMixin0.prototype = $desc; + function Interceptor_ListMixin_ImmutableListMixin0() { + } + Interceptor_ListMixin_ImmutableListMixin0.builtin$cls = "Interceptor_ListMixin_ImmutableListMixin0"; + if (!"name" in Interceptor_ListMixin_ImmutableListMixin0) + Interceptor_ListMixin_ImmutableListMixin0.name = "Interceptor_ListMixin_ImmutableListMixin0"; + $desc = $collectedClasses.Interceptor_ListMixin_ImmutableListMixin0; + if ($desc instanceof Array) + $desc = $desc[1]; + Interceptor_ListMixin_ImmutableListMixin0.prototype = $desc; + function _AttributeMap() { + } + _AttributeMap.builtin$cls = "_AttributeMap"; + if (!"name" in _AttributeMap) + _AttributeMap.name = "_AttributeMap"; + $desc = $collectedClasses._AttributeMap; + if ($desc instanceof Array) + $desc = $desc[1]; + _AttributeMap.prototype = $desc; + function _ElementAttributeMap(_element) { + this._element = _element; + } + _ElementAttributeMap.builtin$cls = "_ElementAttributeMap"; + if (!"name" in _ElementAttributeMap) + _ElementAttributeMap.name = "_ElementAttributeMap"; + $desc = $collectedClasses._ElementAttributeMap; + if ($desc instanceof Array) + $desc = $desc[1]; + _ElementAttributeMap.prototype = $desc; + function EventStreamProvider(_eventType) { + this._eventType = _eventType; + } + EventStreamProvider.builtin$cls = "EventStreamProvider"; + if (!"name" in EventStreamProvider) + EventStreamProvider.name = "EventStreamProvider"; + $desc = $collectedClasses.EventStreamProvider; + if ($desc instanceof Array) + $desc = $desc[1]; + EventStreamProvider.prototype = $desc; + function _EventStream() { + } + _EventStream.builtin$cls = "_EventStream"; + if (!"name" in _EventStream) + _EventStream.name = "_EventStream"; + $desc = $collectedClasses._EventStream; + if ($desc instanceof Array) + $desc = $desc[1]; + _EventStream.prototype = $desc; + function _ElementEventStreamImpl(_target, _eventType, _useCapture) { + this._target = _target; + this._eventType = _eventType; + this._useCapture = _useCapture; + } + _ElementEventStreamImpl.builtin$cls = "_ElementEventStreamImpl"; + if (!"name" in _ElementEventStreamImpl) + _ElementEventStreamImpl.name = "_ElementEventStreamImpl"; + $desc = $collectedClasses._ElementEventStreamImpl; + if ($desc instanceof Array) + $desc = $desc[1]; + _ElementEventStreamImpl.prototype = $desc; + function _EventStreamSubscription(_pauseCount, _target, _eventType, _onData, _useCapture) { + this._pauseCount = _pauseCount; + this._target = _target; + this._eventType = _eventType; + this._onData = _onData; + this._useCapture = _useCapture; + } + _EventStreamSubscription.builtin$cls = "_EventStreamSubscription"; + if (!"name" in _EventStreamSubscription) + _EventStreamSubscription.name = "_EventStreamSubscription"; + $desc = $collectedClasses._EventStreamSubscription; + if ($desc instanceof Array) + $desc = $desc[1]; + _EventStreamSubscription.prototype = $desc; + function _Html5NodeValidator(uriPolicy) { + this.uriPolicy = uriPolicy; + } + _Html5NodeValidator.builtin$cls = "_Html5NodeValidator"; + if (!"name" in _Html5NodeValidator) + _Html5NodeValidator.name = "_Html5NodeValidator"; + $desc = $collectedClasses._Html5NodeValidator; + if ($desc instanceof Array) + $desc = $desc[1]; + _Html5NodeValidator.prototype = $desc; + _Html5NodeValidator.prototype.get$uriPolicy = function() { + return this.uriPolicy; + }; + function ImmutableListMixin() { + } + ImmutableListMixin.builtin$cls = "ImmutableListMixin"; + if (!"name" in ImmutableListMixin) + ImmutableListMixin.name = "ImmutableListMixin"; + $desc = $collectedClasses.ImmutableListMixin; + if ($desc instanceof Array) + $desc = $desc[1]; + ImmutableListMixin.prototype = $desc; + function NodeValidatorBuilder(_validators) { + this._validators = _validators; + } + NodeValidatorBuilder.builtin$cls = "NodeValidatorBuilder"; + if (!"name" in NodeValidatorBuilder) + NodeValidatorBuilder.name = "NodeValidatorBuilder"; + $desc = $collectedClasses.NodeValidatorBuilder; + if ($desc instanceof Array) + $desc = $desc[1]; + NodeValidatorBuilder.prototype = $desc; + function NodeValidatorBuilder_allowsElement_closure(element_0) { + this.element_0 = element_0; + } + NodeValidatorBuilder_allowsElement_closure.builtin$cls = "NodeValidatorBuilder_allowsElement_closure"; + if (!"name" in NodeValidatorBuilder_allowsElement_closure) + NodeValidatorBuilder_allowsElement_closure.name = "NodeValidatorBuilder_allowsElement_closure"; + $desc = $collectedClasses.NodeValidatorBuilder_allowsElement_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + NodeValidatorBuilder_allowsElement_closure.prototype = $desc; + function NodeValidatorBuilder_allowsAttribute_closure(element_0, attributeName_1, value_2) { + this.element_0 = element_0; + this.attributeName_1 = attributeName_1; + this.value_2 = value_2; + } + NodeValidatorBuilder_allowsAttribute_closure.builtin$cls = "NodeValidatorBuilder_allowsAttribute_closure"; + if (!"name" in NodeValidatorBuilder_allowsAttribute_closure) + NodeValidatorBuilder_allowsAttribute_closure.name = "NodeValidatorBuilder_allowsAttribute_closure"; + $desc = $collectedClasses.NodeValidatorBuilder_allowsAttribute_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + NodeValidatorBuilder_allowsAttribute_closure.prototype = $desc; + function _SimpleNodeValidator(uriPolicy) { + this.uriPolicy = uriPolicy; + } + _SimpleNodeValidator.builtin$cls = "_SimpleNodeValidator"; + if (!"name" in _SimpleNodeValidator) + _SimpleNodeValidator.name = "_SimpleNodeValidator"; + $desc = $collectedClasses._SimpleNodeValidator; + if ($desc instanceof Array) + $desc = $desc[1]; + _SimpleNodeValidator.prototype = $desc; + _SimpleNodeValidator.prototype.get$uriPolicy = function() { + return this.uriPolicy; + }; + function _TemplatingNodeValidator(_templateAttrs, allowedElements, allowedAttributes, allowedUriAttributes, uriPolicy) { + this._templateAttrs = _templateAttrs; + this.allowedElements = allowedElements; + this.allowedAttributes = allowedAttributes; + this.allowedUriAttributes = allowedUriAttributes; + this.uriPolicy = uriPolicy; + } + _TemplatingNodeValidator.builtin$cls = "_TemplatingNodeValidator"; + if (!"name" in _TemplatingNodeValidator) + _TemplatingNodeValidator.name = "_TemplatingNodeValidator"; + $desc = $collectedClasses._TemplatingNodeValidator; + if ($desc instanceof Array) + $desc = $desc[1]; + _TemplatingNodeValidator.prototype = $desc; + function _TemplatingNodeValidator_closure() { + } + _TemplatingNodeValidator_closure.builtin$cls = "_TemplatingNodeValidator_closure"; + if (!"name" in _TemplatingNodeValidator_closure) + _TemplatingNodeValidator_closure.name = "_TemplatingNodeValidator_closure"; + $desc = $collectedClasses._TemplatingNodeValidator_closure; + if ($desc instanceof Array) + $desc = $desc[1]; + _TemplatingNodeValidator_closure.prototype = $desc; + function _SvgNodeValidator() { + } + _SvgNodeValidator.builtin$cls = "_SvgNodeValidator"; + if (!"name" in _SvgNodeValidator) + _SvgNodeValidator.name = "_SvgNodeValidator"; + $desc = $collectedClasses._SvgNodeValidator; + if ($desc instanceof Array) + $desc = $desc[1]; + _SvgNodeValidator.prototype = $desc; + function FixedSizeListIterator(_array, _html$_length, _position, _html$_current) { + this._array = _array; + this._html$_length = _html$_length; + this._position = _position; + this._html$_current = _html$_current; + } + FixedSizeListIterator.builtin$cls = "FixedSizeListIterator"; + if (!"name" in FixedSizeListIterator) + FixedSizeListIterator.name = "FixedSizeListIterator"; + $desc = $collectedClasses.FixedSizeListIterator; + if ($desc instanceof Array) + $desc = $desc[1]; + FixedSizeListIterator.prototype = $desc; + function _LocationWrapper(_ptr) { + this._ptr = _ptr; + } + _LocationWrapper.builtin$cls = "_LocationWrapper"; + if (!"name" in _LocationWrapper) + _LocationWrapper.name = "_LocationWrapper"; + $desc = $collectedClasses._LocationWrapper; + if ($desc instanceof Array) + $desc = $desc[1]; + _LocationWrapper.prototype = $desc; + function NodeValidator() { + } + NodeValidator.builtin$cls = "NodeValidator"; + if (!"name" in NodeValidator) + NodeValidator.name = "NodeValidator"; + $desc = $collectedClasses.NodeValidator; + if ($desc instanceof Array) + $desc = $desc[1]; + NodeValidator.prototype = $desc; + function _SameOriginUriPolicy(_hiddenAnchor, _loc) { + this._hiddenAnchor = _hiddenAnchor; + this._loc = _loc; + } + _SameOriginUriPolicy.builtin$cls = "_SameOriginUriPolicy"; + if (!"name" in _SameOriginUriPolicy) + _SameOriginUriPolicy.name = "_SameOriginUriPolicy"; + $desc = $collectedClasses._SameOriginUriPolicy; + if ($desc instanceof Array) + $desc = $desc[1]; + _SameOriginUriPolicy.prototype = $desc; + function _ValidatingTreeSanitizer(validator) { + this.validator = validator; + } + _ValidatingTreeSanitizer.builtin$cls = "_ValidatingTreeSanitizer"; + if (!"name" in _ValidatingTreeSanitizer) + _ValidatingTreeSanitizer.name = "_ValidatingTreeSanitizer"; + $desc = $collectedClasses._ValidatingTreeSanitizer; + if ($desc instanceof Array) + $desc = $desc[1]; + _ValidatingTreeSanitizer.prototype = $desc; + function _ValidatingTreeSanitizer_sanitizeTree_walk(this_0) { + this.this_0 = this_0; + } + _ValidatingTreeSanitizer_sanitizeTree_walk.builtin$cls = "_ValidatingTreeSanitizer_sanitizeTree_walk"; + if (!"name" in _ValidatingTreeSanitizer_sanitizeTree_walk) + _ValidatingTreeSanitizer_sanitizeTree_walk.name = "_ValidatingTreeSanitizer_sanitizeTree_walk"; + $desc = $collectedClasses._ValidatingTreeSanitizer_sanitizeTree_walk; + if ($desc instanceof Array) + $desc = $desc[1]; + _ValidatingTreeSanitizer_sanitizeTree_walk.prototype = $desc; + function Capability() { + } + Capability.builtin$cls = "Capability"; + if (!"name" in Capability) + Capability.name = "Capability"; + $desc = $collectedClasses.Capability; + if ($desc instanceof Array) + $desc = $desc[1]; + Capability.prototype = $desc; + function NativeTypedArray() { + } + NativeTypedArray.builtin$cls = "NativeTypedArray"; + if (!"name" in NativeTypedArray) + NativeTypedArray.name = "NativeTypedArray"; + $desc = $collectedClasses.NativeTypedArray; + if ($desc instanceof Array) + $desc = $desc[1]; + NativeTypedArray.prototype = $desc; + function NativeTypedArrayOfInt() { + } + NativeTypedArrayOfInt.builtin$cls = "NativeTypedArrayOfInt"; + if (!"name" in NativeTypedArrayOfInt) + NativeTypedArrayOfInt.name = "NativeTypedArrayOfInt"; + $desc = $collectedClasses.NativeTypedArrayOfInt; + if ($desc instanceof Array) + $desc = $desc[1]; + NativeTypedArrayOfInt.prototype = $desc; + function NativeTypedArray_ListMixin() { + } + NativeTypedArray_ListMixin.builtin$cls = "NativeTypedArray_ListMixin"; + if (!"name" in NativeTypedArray_ListMixin) + NativeTypedArray_ListMixin.name = "NativeTypedArray_ListMixin"; + $desc = $collectedClasses.NativeTypedArray_ListMixin; + if ($desc instanceof Array) + $desc = $desc[1]; + NativeTypedArray_ListMixin.prototype = $desc; + function NativeTypedArray_ListMixin_FixedLengthListMixin() { + } + NativeTypedArray_ListMixin_FixedLengthListMixin.builtin$cls = "NativeTypedArray_ListMixin_FixedLengthListMixin"; + if (!"name" in NativeTypedArray_ListMixin_FixedLengthListMixin) + NativeTypedArray_ListMixin_FixedLengthListMixin.name = "NativeTypedArray_ListMixin_FixedLengthListMixin"; + $desc = $collectedClasses.NativeTypedArray_ListMixin_FixedLengthListMixin; + if ($desc instanceof Array) + $desc = $desc[1]; + NativeTypedArray_ListMixin_FixedLengthListMixin.prototype = $desc; + return [HtmlElement, AnchorElement, AnimationEvent, AreaElement, AudioElement, AutocompleteErrorEvent, BRElement, BaseElement, BeforeLoadEvent, BeforeUnloadEvent, BodyElement, ButtonElement, CDataSection, CanvasElement, CharacterData, CloseEvent, Comment, CompositionEvent, ContentElement, CssFontFaceLoadEvent, CustomEvent, DListElement, DataListElement, DetailsElement, DeviceMotionEvent, DeviceOrientationEvent, DialogElement, DivElement, Document, DocumentFragment, DomError, DomException, DomImplementation, Element, EmbedElement, ErrorEvent, Event, EventTarget, FieldSetElement, FileError, FocusEvent, FormElement, HRElement, HashChangeEvent, HeadElement, HeadingElement, HtmlDocument, HtmlHtmlElement, IFrameElement, ImageElement, InputElement, KeyboardEvent, KeygenElement, LIElement, LabelElement, LegendElement, LinkElement, Location, MapElement, MediaElement, MediaError, MediaKeyError, MediaKeyEvent, MediaKeyMessageEvent, MediaKeyNeededEvent, MediaStreamEvent, MediaStreamTrackEvent, MenuElement, MessageEvent, MetaElement, MeterElement, MidiConnectionEvent, MidiInput, MidiMessageEvent, MidiOutput, MidiPort, ModElement, MouseEvent, Navigator, NavigatorUserMediaError, Node, NodeList, OListElement, ObjectElement, OptGroupElement, OptionElement, OutputElement, OverflowEvent, PageTransitionEvent, ParagraphElement, ParamElement, PopStateEvent, PositionError, PreElement, ProcessingInstruction, ProgressElement, ProgressEvent, QuoteElement, Range, ResourceProgressEvent, RtcDataChannelEvent, RtcDtmfToneChangeEvent, RtcIceCandidateEvent, ScriptElement0, SecurityPolicyViolationEvent, SelectElement, ShadowElement, ShadowRoot, SourceElement, SpanElement, SpeechInputEvent, SpeechRecognitionError, SpeechRecognitionEvent, SpeechSynthesisEvent, Storage, StorageEvent, StyleElement, TableCaptionElement, TableCellElement, TableColElement, TableElement, TableRowElement, TableSectionElement, TemplateElement, Text, TextAreaElement, TextEvent, TitleElement, TouchEvent, TrackElement, TrackEvent, TransitionEvent, UIEvent, UListElement, UnknownElement, VideoElement, WheelEvent, Window, XmlDocument, _Attr, _DocumentType, _HTMLAppletElement, _HTMLDirectoryElement, _HTMLFontElement, _HTMLFrameElement, _HTMLFrameSetElement, _HTMLMarqueeElement, _MutationEvent, _NamedNodeMap, _Notation, _XMLHttpRequestProgressEvent, VersionChangeEvent, AElement, AltGlyphElement, AnimateElement, AnimateMotionElement, AnimateTransformElement, AnimatedNumberList, AnimationElement, CircleElement, ClipPathElement, DefsElement, DescElement, DiscardElement, EllipseElement, FEBlendElement, FEColorMatrixElement, FEComponentTransferElement, FECompositeElement, FEConvolveMatrixElement, FEDiffuseLightingElement, FEDisplacementMapElement, FEDistantLightElement, FEFloodElement, FEFuncAElement, FEFuncBElement, FEFuncGElement, FEFuncRElement, FEGaussianBlurElement, FEImageElement, FEMergeElement, FEMergeNodeElement, FEMorphologyElement, FEOffsetElement, FEPointLightElement, FESpecularLightingElement, FESpotLightElement, FETileElement, FETurbulenceElement, FilterElement, ForeignObjectElement, GElement, GeometryElement, GraphicsElement, ImageElement0, LineElement, LinearGradientElement, MarkerElement, MaskElement, MetadataElement, PathElement, PatternElement, PolygonElement, PolylineElement, RadialGradientElement, RectElement, ScriptElement, SetElement, StopElement, StyleElement0, SvgElement, SvgSvgElement, SwitchElement, SymbolElement, TSpanElement, TextContentElement, TextElement, TextPathElement, TextPositioningElement, TitleElement0, UseElement, ViewElement, ZoomEvent, _GradientElement, _SVGAltGlyphDefElement, _SVGAltGlyphItemElement, _SVGComponentTransferFunctionElement, _SVGCursorElement, _SVGFEDropShadowElement, _SVGFontElement, _SVGFontFaceElement, _SVGFontFaceFormatElement, _SVGFontFaceNameElement, _SVGFontFaceSrcElement, _SVGFontFaceUriElement, _SVGGlyphElement, _SVGGlyphRefElement, _SVGHKernElement, _SVGMPathElement, _SVGMissingGlyphElement, _SVGVKernElement, AudioProcessingEvent, OfflineAudioCompletionEvent, ContextEvent, SqlError, NativeTypedData, NativeUint8List, JS_CONST, Interceptor, JSBool, JSNull, JavaScriptObject, PlainJavaScriptObject, UnknownJavaScriptObject, JSArray, JSNumber, JSInt, JSDouble, JSString, startRootIsolate_closure, startRootIsolate_closure0, _Manager, _IsolateContext, _IsolateContext_handlePing_closure, _EventLoop, _EventLoop__runHelper_next, _IsolateEvent, _MainManagerStub, IsolateNatives__processWorkerMessage_closure, IsolateNatives__startIsolate_runStartFunction, _BaseSendPort, _NativeJsSendPort, _NativeJsSendPort_send_closure, _WorkerSendPort, RawReceivePortImpl, _JsSerializer, _JsCopier, _JsDeserializer, _JsVisitedMap, _MessageTraverserVisitedMap, _MessageTraverser, _Copier, _Copier_visitMap_closure, _Serializer, _Deserializer, TimerImpl, TimerImpl_internalCallback, TimerImpl_internalCallback0, CapabilityImpl, ReflectionInfo, TypeErrorDecoder, NullError, JsNoSuchMethodError, UnknownJsTypeError, unwrapException_saveStackTrace, _StackTrace, invokeClosure_closure, invokeClosure_closure0, invokeClosure_closure1, invokeClosure_closure2, invokeClosure_closure3, Closure, TearOffClosure, BoundClosure, RuntimeError, RuntimeType, RuntimeFunctionType, DynamicRuntimeType, initHooks_closure, initHooks_closure0, initHooks_closure1, JSSyntaxRegExp, _MatchImplementation, BankAccount, Person, ListIterable, ListIterator, MappedIterable, EfficientLengthMappedIterable, MappedIterator, MappedListIterable, WhereIterable, WhereIterator, FixedLengthListMixin, _AsyncError, _Future, _Future__addListener_closure, _Future__chainForeignFuture_closure, _Future__chainForeignFuture_closure0, _Future__propagateToListeners_handleValueCallback, _Future__propagateToListeners_handleError, _Future__propagateToListeners_handleWhenCompleteCallback, _Future__propagateToListeners_handleWhenCompleteCallback_closure, _Future__propagateToListeners_handleWhenCompleteCallback_closure0, _AsyncCallbackEntry, Stream, Stream_forEach_closure, Stream_forEach__closure, Stream_forEach__closure0, Stream_forEach_closure0, Stream_length_closure, Stream_length_closure0, Stream_isEmpty_closure, Stream_isEmpty_closure0, StreamSubscription, _EventSink, _cancelAndError_closure, _cancelAndErrorClosure_closure, _cancelAndValue_closure, _BaseZone, _BaseZone_bindCallback_closure, _BaseZone_bindCallback_closure0, _BaseZone_bindUnaryCallback_closure, _BaseZone_bindUnaryCallback_closure0, _rootHandleUncaughtError_closure, _rootHandleUncaughtError__closure, _RootZone, _HashMap, _HashMap_values_closure, HashMapKeyIterable, HashMapKeyIterator, _LinkedHashMap, _LinkedHashMap_values_closure, LinkedHashMapCell, LinkedHashMapKeyIterable, LinkedHashMapKeyIterator, _HashSet, _IdentityHashSet, HashSetIterator, _LinkedHashSet, LinkedHashSetCell, LinkedHashSetIterator, _HashSetBase, IterableBase, ListBase, ListMixin, Maps_mapToString_closure, ListQueue, _ListQueueIterator, _convertJsonToDart_closure, _convertJsonToDart_walk, Codec, Converter, JsonUnsupportedObjectError, JsonCyclicError, JsonCodec, JsonEncoder, JsonDecoder, _JsonStringifier, NoSuchMethodError_toString_closure, DateTime, DateTime_parse_parseIntOrZero, DateTime_parse_parseDoubleOrZero, Duration, Duration_toString_sixDigits, Duration_toString_twoDigits, Error, NullThrownError, ArgumentError, RangeError, UnsupportedError, UnimplementedError, StateError, ConcurrentModificationError, OutOfMemoryError, StackOverflowError, CyclicInitializationError, _ExceptionImplementation, FormatException, Expando, Iterator, Null, Object, StackTrace, StringBuffer, Symbol, Element_Element$html_closure, _ChildNodeListLazy, Interceptor_ListMixin, Interceptor_ListMixin_ImmutableListMixin, Storage_keys_closure, Storage_values_closure, Interceptor_ListMixin0, Interceptor_ListMixin_ImmutableListMixin0, _AttributeMap, _ElementAttributeMap, EventStreamProvider, _EventStream, _ElementEventStreamImpl, _EventStreamSubscription, _Html5NodeValidator, ImmutableListMixin, NodeValidatorBuilder, NodeValidatorBuilder_allowsElement_closure, NodeValidatorBuilder_allowsAttribute_closure, _SimpleNodeValidator, _TemplatingNodeValidator, _TemplatingNodeValidator_closure, _SvgNodeValidator, FixedSizeListIterator, _LocationWrapper, NodeValidator, _SameOriginUriPolicy, _ValidatingTreeSanitizer, _ValidatingTreeSanitizer_sanitizeTree_walk, Capability, NativeTypedArray, NativeTypedArrayOfInt, NativeTypedArray_ListMixin, NativeTypedArray_ListMixin_FixedLengthListMixin]; +} diff --git a/Chapter 1/bank_terminal/build/web/bank_terminal_s5.html b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.html new file mode 100644 index 0000000..01eadb4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/bank_terminal_s5.html @@ -0,0 +1,52 @@ + + + + + + Bank terminal s5 + + + +

Bank terminal s5

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Number + + + +
Owner
Balance
Amount transaction
+ + + +
+ +
+ + + + diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_chrome/dart2js/chrome_dart2js.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_chrome/dart2js/chrome_dart2js.dart new file mode 100644 index 0000000..93f8f75 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_chrome/dart2js/chrome_dart2js.dart @@ -0,0 +1,1252 @@ +/// Native wrappers for the Chrome packaged app APIs. +/// +/// These functions allow direct access to the chrome.* APIs, allowing +/// Chrome packaged apps to be written using Dart. +/// +/// For more information on these APIs, see the +/// [chrome.* API documentation](http://developer.chrome.com/apps/api_index.html). +library _chrome; + +import 'dart:_foreign_helper' show JS; +import 'dart:_js_helper'; +import 'dart:html_common'; +import 'dart:html'; + +// Copyright (c) 2013, 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. + +// DO NOT EDIT +// Auto-generated dart:_chrome library. + + +/* TODO(sashab): Add "show convertDartClosureToJS" once 'show' works. */ + + +// Generated files below this line. +// Copyright (c) 2013, 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. + +/** + * A set of utilities for use with the Chrome Extension APIs. + * + * Allows for easy access to required JS objects. + */ + +/** + * A dart object, that is convertible to JS. Used for creating objects in dart, + * then passing them to JS. + * + * Objects that are passable to JS need to implement this interface. + */ +abstract class ChromeObject { + /* + * Default Constructor + * + * Called by child objects during their regular construction. + */ + ChromeObject() : _jsObject = JS('var', '{}'); + + /* + * Internal proxy constructor + * + * Creates a new Dart object using this existing proxy. + */ + ChromeObject._proxy(this._jsObject); + + /* + * JS Object Representation + */ + final Object _jsObject; +} + +/** + * Useful functions for converting arguments. + */ + +/** + * Converts the given map-type argument to js-friendly format, recursively. + * Returns the new Map object. + */ +Object _convertMapArgument(Map argument) { + Map m = new Map(); + for (Object key in argument.keys) + m[key] = convertArgument(argument[key]); + return convertDartToNative_Dictionary(m); +} + +/** + * Converts the given list-type argument to js-friendly format, recursively. + * Returns the new List object. + */ +List _convertListArgument(List argument) { + List l = new List(); + for (var i = 0; i < argument.length; i ++) + l.add(convertArgument(argument[i])); + return l; +} + +/** + * Converts the given argument Object to js-friendly format, recursively. + * + * Flattens out all Chrome objects into their corresponding ._toMap() + * definitions, then converts them to JS objects. + * + * Returns the new argument. + * + * Cannot be used for functions. + */ +Object convertArgument(var argument) { + if (argument == null) + return argument; + + if (argument is num || argument is String || argument is bool) + return argument; + + if (argument is ChromeObject) + return argument._jsObject; + + if (argument is List) + return _convertListArgument(argument); + + if (argument is Map) + return _convertMapArgument(argument); + + if (argument is Function) + throw new Exception("Cannot serialize Function argument ${argument}."); + + // TODO(sashab): Try and detect whether the argument is already serialized. + return argument; +} + +/// Description of a declarative rule for handling events. +class Rule extends ChromeObject { + /* + * Public (Dart) constructor + */ + Rule({String id, List conditions, List actions, int priority}) { + this.id = id; + this.conditions = conditions; + this.actions = actions; + this.priority = priority; + } + + /* + * Private (JS) constructor + */ + Rule._proxy(_jsObject) : super._proxy(_jsObject); + + /* + * Public accessors + */ + String get id => JS('String', '#.id', this._jsObject); + + void set id(String id) { + JS('void', '#.id = #', this._jsObject, id); + } + + // TODO(sashab): Wrap these generic Lists somehow. + List get conditions => JS('List', '#.conditions', this._jsObject); + + void set conditions(List conditions) { + JS('void', '#.conditions = #', this._jsObject, convertArgument(conditions)); + } + + // TODO(sashab): Wrap these generic Lists somehow. + List get actions => JS('List', '#.actions', this._jsObject); + + void set actions(List actions) { + JS('void', '#.actions = #', this._jsObject, convertArgument(actions)); + } + + int get priority => JS('int', '#.priority', this._jsObject); + + void set priority(int priority) { + JS('void', '#.priority = #', this._jsObject, priority); + } + +} + +/** + * The Event class. + * + * Chrome Event classes extend this interface. + * + * e.g. + * + * // chrome.app.runtime.onLaunched + * class Event_ChromeAppRuntimeOnLaunched extends Event { + * // constructor, passing the arity of the callback + * Event_ChromeAppRuntimeOnLaunched(jsObject) : + * super._(jsObject, 1); + * + * // methods, strengthening the Function parameter specificity + * void addListener(void callback(LaunchData launchData)) + * => super.addListener(callback); + * void removeListener(void callback(LaunchData launchData)) + * => super.removeListener(callback); + * bool hasListener(void callback(LaunchData launchData)) + * => super.hasListener(callback); + * } + * + */ +class Event { + /* + * JS Object Representation + */ + final Object _jsObject; + + /* + * Number of arguments the callback takes. + */ + final int _callbackArity; + + /* + * Private constructor + */ + Event._(this._jsObject, this._callbackArity); + + /* + * Methods + */ + + /// Registers an event listener callback to an event. + void addListener(Function callback) => + JS('void', + '#.addListener(#)', + this._jsObject, + convertDartClosureToJS(callback, this._callbackArity) + ); + + /// Deregisters an event listener callback from an event. + void removeListener(Function callback) => + JS('void', + '#.removeListener(#)', + this._jsObject, + convertDartClosureToJS(callback, this._callbackArity) + ); + + /// Returns True if callback is registered to the event. + bool hasListener(Function callback) => + JS('bool', + '#.hasListener(#)', + this._jsObject, + convertDartClosureToJS(callback, this._callbackArity) + ); + + /// Returns true if any event listeners are registered to the event. + bool hasListeners() => + JS('bool', + '#.hasListeners()', + this._jsObject + ); + + /// Registers rules to handle events. + /// + /// [eventName] is the name of the event this function affects and [rules] are + /// the rules to be registered. These do not replace previously registered + /// rules. [callback] is called with registered rules. + /// + void addRules(String eventName, List rules, + [void callback(List rules)]) { + // proxy the callback + void __proxy_callback(List rules) { + if (callback != null) { + List __proxy_rules = new List(); + + for (Object o in rules) + __proxy_rules.add(new Rule._proxy(o)); + + callback(__proxy_rules); + } + } + + JS('void', + '#.addRules(#, #, #)', + this._jsObject, + convertArgument(eventName), + convertArgument(rules), + convertDartClosureToJS(__proxy_callback, 1) + ); + } + + /// Returns currently registered rules. + /// + /// [eventName] is the name of the event this function affects and, if an array + /// is passed as [ruleIdentifiers], only rules with identifiers contained in + /// this array are returned. [callback] is called with registered rules. + /// + void getRules(String eventName, [List ruleIdentifiers, + void callback(List rules)]) { + // proxy the callback + void __proxy_callback(List rules) { + if (callback != null) { + List __proxy_rules = new List(); + + for (Object o in rules) + __proxy_rules.add(new Rule._proxy(o)); + + callback(__proxy_rules); + } + } + + JS('void', + '#.getRules(#, #, #)', + this._jsObject, + convertArgument(eventName), + convertArgument(ruleIdentifiers), + convertDartClosureToJS(__proxy_callback, 1) + ); + } + + /// Unregisters currently registered rules. + /// + /// [eventName] is the name of the event this function affects and, if an array + /// is passed as [ruleIdentifiers], only rules with identifiers contained in + /// this array are unregistered. [callback] is called when the rules are + /// unregistered. + /// + void removeRules(String eventName, [List ruleIdentifiers, + void callback()]) => + JS('void', + '#.removeRules(#, #, #)', + this._jsObject, + convertArgument(eventName), + convertArgument(ruleIdentifiers), + convertDartClosureToJS(callback, 0) + ); +} + +// Copyright (c) 2012, 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. + + +// chrome.app +class API_ChromeApp { + /* + * JS Variable + */ + final Object _jsObject; + + /* + * Members + */ + API_app_window window; + API_app_runtime runtime; + + /* + * Constructor + */ + API_ChromeApp(this._jsObject) { + var window_object = JS('', '#.window', this._jsObject); + if (window_object == null) + throw new UnsupportedError('Not supported by current browser.'); + window = new API_app_window(window_object); + + var runtime_object = JS('', '#.runtime', this._jsObject); + if (runtime_object == null) + throw new UnsupportedError('Not supported by current browser.'); + runtime = new API_app_runtime(runtime_object); + } +} + +// chrome +class API_Chrome { + /* + * JS Variable + */ + Object _jsObject; + + /* + * Members + */ + API_ChromeApp app; + API_file_system fileSystem; + + /* + * Constructor + */ + API_Chrome() { + this._jsObject = JS("Object", "chrome"); + if (this._jsObject == null) + throw new UnsupportedError('Not supported by current browser.'); + + var app_object = JS('', '#.app', this._jsObject); + if (app_object == null) + throw new UnsupportedError('Not supported by current browser.'); + app = new API_ChromeApp(app_object); + + var file_system_object = JS('', '#.fileSystem', this._jsObject); + if (file_system_object == null) + throw new UnsupportedError('Not supported by current browser.'); + fileSystem = new API_file_system(file_system_object); + } +} + +// The final chrome objects +final API_Chrome chrome = new API_Chrome(); +// Copyright (c) 2013, 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. + +// Generated from namespace: app.window + + +/** + * Types + */ + +class AppWindowBounds extends ChromeObject { + /* + * Public constructor + */ + AppWindowBounds({int left, int top, int width, int height}) { + if (left != null) + this.left = left; + if (top != null) + this.top = top; + if (width != null) + this.width = width; + if (height != null) + this.height = height; + } + + /* + * Private constructor + */ + AppWindowBounds._proxy(_jsObject) : super._proxy(_jsObject); + + /* + * Public accessors + */ + int get left => JS('int', '#.left', this._jsObject); + + void set left(int left) { + JS('void', '#.left = #', this._jsObject, left); + } + + int get top => JS('int', '#.top', this._jsObject); + + void set top(int top) { + JS('void', '#.top = #', this._jsObject, top); + } + + int get width => JS('int', '#.width', this._jsObject); + + void set width(int width) { + JS('void', '#.width = #', this._jsObject, width); + } + + int get height => JS('int', '#.height', this._jsObject); + + void set height(int height) { + JS('void', '#.height = #', this._jsObject, height); + } + +} + +class AppWindowCreateWindowOptions extends ChromeObject { + /* + * Public constructor + */ + AppWindowCreateWindowOptions({String id, int defaultWidth, int defaultHeight, int defaultLeft, int defaultTop, int width, int height, int left, int top, int minWidth, int minHeight, int maxWidth, int maxHeight, String type, String frame, AppWindowBounds bounds, bool transparentBackground, String state, bool hidden, bool resizable, bool singleton}) { + if (id != null) + this.id = id; + if (defaultWidth != null) + this.defaultWidth = defaultWidth; + if (defaultHeight != null) + this.defaultHeight = defaultHeight; + if (defaultLeft != null) + this.defaultLeft = defaultLeft; + if (defaultTop != null) + this.defaultTop = defaultTop; + if (width != null) + this.width = width; + if (height != null) + this.height = height; + if (left != null) + this.left = left; + if (top != null) + this.top = top; + if (minWidth != null) + this.minWidth = minWidth; + if (minHeight != null) + this.minHeight = minHeight; + if (maxWidth != null) + this.maxWidth = maxWidth; + if (maxHeight != null) + this.maxHeight = maxHeight; + if (type != null) + this.type = type; + if (frame != null) + this.frame = frame; + if (bounds != null) + this.bounds = bounds; + if (transparentBackground != null) + this.transparentBackground = transparentBackground; + if (state != null) + this.state = state; + if (hidden != null) + this.hidden = hidden; + if (resizable != null) + this.resizable = resizable; + if (singleton != null) + this.singleton = singleton; + } + + /* + * Private constructor + */ + AppWindowCreateWindowOptions._proxy(_jsObject) : super._proxy(_jsObject); + + /* + * Public accessors + */ + /// Id to identify the window. This will be used to remember the size and + /// position of the window and restore that geometry when a window with the + /// same id is later opened. + String get id => JS('String', '#.id', this._jsObject); + + void set id(String id) { + JS('void', '#.id = #', this._jsObject, id); + } + + /// Default width of the window. (Deprecated; regular bounds act like this + /// now.) + int get defaultWidth => JS('int', '#.defaultWidth', this._jsObject); + + void set defaultWidth(int defaultWidth) { + JS('void', '#.defaultWidth = #', this._jsObject, defaultWidth); + } + + /// Default height of the window. (Deprecated; regular bounds act like this + /// now.) + int get defaultHeight => JS('int', '#.defaultHeight', this._jsObject); + + void set defaultHeight(int defaultHeight) { + JS('void', '#.defaultHeight = #', this._jsObject, defaultHeight); + } + + /// Default X coordinate of the window. (Deprecated; regular bounds act like + /// this now.) + int get defaultLeft => JS('int', '#.defaultLeft', this._jsObject); + + void set defaultLeft(int defaultLeft) { + JS('void', '#.defaultLeft = #', this._jsObject, defaultLeft); + } + + /// Default Y coordinate of the window. (Deprecated; regular bounds act like + /// this now.) + int get defaultTop => JS('int', '#.defaultTop', this._jsObject); + + void set defaultTop(int defaultTop) { + JS('void', '#.defaultTop = #', this._jsObject, defaultTop); + } + + /// Width of the window. (Deprecated; use 'bounds'.) + int get width => JS('int', '#.width', this._jsObject); + + void set width(int width) { + JS('void', '#.width = #', this._jsObject, width); + } + + /// Height of the window. (Deprecated; use 'bounds'.) + int get height => JS('int', '#.height', this._jsObject); + + void set height(int height) { + JS('void', '#.height = #', this._jsObject, height); + } + + /// X coordinate of the window. (Deprecated; use 'bounds'.) + int get left => JS('int', '#.left', this._jsObject); + + void set left(int left) { + JS('void', '#.left = #', this._jsObject, left); + } + + /// Y coordinate of the window. (Deprecated; use 'bounds'.) + int get top => JS('int', '#.top', this._jsObject); + + void set top(int top) { + JS('void', '#.top = #', this._jsObject, top); + } + + /// Minimum width for the lifetime of the window. + int get minWidth => JS('int', '#.minWidth', this._jsObject); + + void set minWidth(int minWidth) { + JS('void', '#.minWidth = #', this._jsObject, minWidth); + } + + /// Minimum height for the lifetime of the window. + int get minHeight => JS('int', '#.minHeight', this._jsObject); + + void set minHeight(int minHeight) { + JS('void', '#.minHeight = #', this._jsObject, minHeight); + } + + /// Maximum width for the lifetime of the window. + int get maxWidth => JS('int', '#.maxWidth', this._jsObject); + + void set maxWidth(int maxWidth) { + JS('void', '#.maxWidth = #', this._jsObject, maxWidth); + } + + /// Maximum height for the lifetime of the window. + int get maxHeight => JS('int', '#.maxHeight', this._jsObject); + + void set maxHeight(int maxHeight) { + JS('void', '#.maxHeight = #', this._jsObject, maxHeight); + } + + /// Type of window to create. + String get type => JS('String', '#.type', this._jsObject); + + void set type(String type) { + JS('void', '#.type = #', this._jsObject, type); + } + + /// Frame type: 'none' or 'chrome' (defaults to 'chrome'). + String get frame => JS('String', '#.frame', this._jsObject); + + void set frame(String frame) { + JS('void', '#.frame = #', this._jsObject, frame); + } + + /// Size and position of the content in the window (excluding the titlebar). If + /// an id is also specified and a window with a matching id has been shown + /// before, the remembered bounds of the window will be used instead. + AppWindowBounds get bounds => new AppWindowBounds._proxy(JS('', '#.bounds', this._jsObject)); + + void set bounds(AppWindowBounds bounds) { + JS('void', '#.bounds = #', this._jsObject, convertArgument(bounds)); + } + + /// Enable window background transparency. Only supported in ash. Requires + /// experimental API permission. + bool get transparentBackground => JS('bool', '#.transparentBackground', this._jsObject); + + void set transparentBackground(bool transparentBackground) { + JS('void', '#.transparentBackground = #', this._jsObject, transparentBackground); + } + + /// The initial state of the window, allowing it to be created already + /// fullscreen, maximized, or minimized. Defaults to 'normal'. + String get state => JS('String', '#.state', this._jsObject); + + void set state(String state) { + JS('void', '#.state = #', this._jsObject, state); + } + + /// If true, the window will be created in a hidden state. Call show() on the + /// window to show it once it has been created. Defaults to false. + bool get hidden => JS('bool', '#.hidden', this._jsObject); + + void set hidden(bool hidden) { + JS('void', '#.hidden = #', this._jsObject, hidden); + } + + /// If true, the window will be resizable by the user. Defaults to true. + bool get resizable => JS('bool', '#.resizable', this._jsObject); + + void set resizable(bool resizable) { + JS('void', '#.resizable = #', this._jsObject, resizable); + } + + /// By default if you specify an id for the window, the window will only be + /// created if another window with the same id doesn't already exist. If a + /// window with the same id already exists that window is activated instead. If + /// you do want to create multiple windows with the same id, you can set this + /// property to false. + bool get singleton => JS('bool', '#.singleton', this._jsObject); + + void set singleton(bool singleton) { + JS('void', '#.singleton = #', this._jsObject, singleton); + } + +} + +class AppWindowAppWindow extends ChromeObject { + /* + * Private constructor + */ + AppWindowAppWindow._proxy(_jsObject) : super._proxy(_jsObject); + + /* + * Public accessors + */ + /// The JavaScript 'window' object for the created child. + // Copyright (c) 2013, 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. + + // TODO(sashab, sra): Detect whether this is the current window, or an + // external one, and return an appropriately-typed object + WindowBase get contentWindow => + JS("Window", "#.contentWindow", this._jsObject); + + /* + * Methods + */ + /// Focus the window. + void focus() => JS('void', '#.focus()', this._jsObject); + + /// Fullscreens the window. + void fullscreen() => JS('void', '#.fullscreen()', this._jsObject); + + /// Is the window fullscreen? + bool isFullscreen() => JS('bool', '#.isFullscreen()', this._jsObject); + + /// Minimize the window. + void minimize() => JS('void', '#.minimize()', this._jsObject); + + /// Is the window minimized? + bool isMinimized() => JS('bool', '#.isMinimized()', this._jsObject); + + /// Maximize the window. + void maximize() => JS('void', '#.maximize()', this._jsObject); + + /// Is the window maximized? + bool isMaximized() => JS('bool', '#.isMaximized()', this._jsObject); + + /// Restore the window, exiting a maximized, minimized, or fullscreen state. + void restore() => JS('void', '#.restore()', this._jsObject); + + /// Move the window to the position (|left|, |top|). + void moveTo(int left, int top) => JS('void', '#.moveTo(#, #)', this._jsObject, left, top); + + /// Resize the window to |width|x|height| pixels in size. + void resizeTo(int width, int height) => JS('void', '#.resizeTo(#, #)', this._jsObject, width, height); + + /// Draw attention to the window. + void drawAttention() => JS('void', '#.drawAttention()', this._jsObject); + + /// Clear attention to the window. + void clearAttention() => JS('void', '#.clearAttention()', this._jsObject); + + /// Close the window. + void close() => JS('void', '#.close()', this._jsObject); + + /// Show the window. Does nothing if the window is already visible. + void show() => JS('void', '#.show()', this._jsObject); + + /// Hide the window. Does nothing if the window is already hidden. + void hide() => JS('void', '#.hide()', this._jsObject); + + /// Get the window's bounds as a $ref:Bounds object. + // Copyright (c) 2013, 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. + + // TODO(sashab, kalman): Fix IDL parser to read function return values + // correctly. Currently, it just reads void for all functions. + AppWindowBounds getBounds() => + new AppWindowBounds._proxy(JS('void', '#.getBounds()', this._jsObject)); + + /// Set the window's bounds. + void setBounds(AppWindowBounds bounds) => JS('void', '#.setBounds(#)', this._jsObject, convertArgument(bounds)); + + /// Set the app icon for the window (experimental). Currently this is only + /// being implemented on Ash. TODO(stevenjb): Investigate implementing this on + /// Windows and OSX. + void setIcon(String icon_url) => JS('void', '#.setIcon(#)', this._jsObject, icon_url); + +} + +/** + * Events + */ + +/// Fired when the window is resized. +class Event_app_window_onBoundsChanged extends Event { + void addListener(void callback()) => super.addListener(callback); + + void removeListener(void callback()) => super.removeListener(callback); + + bool hasListener(void callback()) => super.hasListener(callback); + + Event_app_window_onBoundsChanged(jsObject) : super._(jsObject, 0); +} + +/// Fired when the window is closed. +class Event_app_window_onClosed extends Event { + void addListener(void callback()) => super.addListener(callback); + + void removeListener(void callback()) => super.removeListener(callback); + + bool hasListener(void callback()) => super.hasListener(callback); + + Event_app_window_onClosed(jsObject) : super._(jsObject, 0); +} + +/// Fired when the window is fullscreened. +class Event_app_window_onFullscreened extends Event { + void addListener(void callback()) => super.addListener(callback); + + void removeListener(void callback()) => super.removeListener(callback); + + bool hasListener(void callback()) => super.hasListener(callback); + + Event_app_window_onFullscreened(jsObject) : super._(jsObject, 0); +} + +/// Fired when the window is maximized. +class Event_app_window_onMaximized extends Event { + void addListener(void callback()) => super.addListener(callback); + + void removeListener(void callback()) => super.removeListener(callback); + + bool hasListener(void callback()) => super.hasListener(callback); + + Event_app_window_onMaximized(jsObject) : super._(jsObject, 0); +} + +/// Fired when the window is minimized. +class Event_app_window_onMinimized extends Event { + void addListener(void callback()) => super.addListener(callback); + + void removeListener(void callback()) => super.removeListener(callback); + + bool hasListener(void callback()) => super.hasListener(callback); + + Event_app_window_onMinimized(jsObject) : super._(jsObject, 0); +} + +/// Fired when the window is restored from being minimized or maximized. +class Event_app_window_onRestored extends Event { + void addListener(void callback()) => super.addListener(callback); + + void removeListener(void callback()) => super.removeListener(callback); + + bool hasListener(void callback()) => super.hasListener(callback); + + Event_app_window_onRestored(jsObject) : super._(jsObject, 0); +} + +/** + * Functions + */ + +class API_app_window { + /* + * API connection + */ + Object _jsObject; + + /* + * Events + */ + Event_app_window_onBoundsChanged onBoundsChanged; + Event_app_window_onClosed onClosed; + Event_app_window_onFullscreened onFullscreened; + Event_app_window_onMaximized onMaximized; + Event_app_window_onMinimized onMinimized; + Event_app_window_onRestored onRestored; + + /* + * Functions + */ + /// The size and position of a window can be specified in a number of different + /// ways. The most simple option is not specifying anything at all, in which + /// case a default size and platform dependent position will be used.

+ /// Another option is to use the bounds property, which will put the window at + /// the specified coordinates with the specified size. If the window has a + /// frame, it's total size will be the size given plus the size of the frame; + /// that is, the size in bounds is the content size, not the window + /// size.

To automatically remember the positions of windows you can + /// give them ids. If a window has an id, This id is used to remember the size + /// and position of the window whenever it is moved or resized. This size and + /// position is then used instead of the specified bounds on subsequent opening + /// of a window with the same id. If you need to open a window with an id at a + /// location other than the remembered default, you can create it hidden, move + /// it to the desired location, then show it. + // Copyright (c) 2013, 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. + + // TODO(sashab): This override is no longer needed once prefixes are removed. + void create(String url, + [AppWindowCreateWindowOptions options, + void callback(AppWindowAppWindow created_window)]) { + void __proxy_callback(created_window) { + if (callback != null) + callback(new AppWindowAppWindow._proxy(created_window)); + } + JS('void', '#.create(#, #, #)', this._jsObject, url, convertArgument(options), + convertDartClosureToJS(__proxy_callback, 1)); + } + + /// Returns an $ref:AppWindow object for the current script context (ie + /// JavaScript 'window' object). This can also be called on a handle to a + /// script context for another page, for example: + /// otherWindow.chrome.app.window.current(). + // Copyright (c) 2013, 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. + + // TODO(sashab, kalman): Fix IDL parser to read function return values + // correctly. Currently, it just reads void for all functions. + AppWindowAppWindow current() => + new AppWindowAppWindow._proxy(JS('void', '#.current()', this._jsObject)); + + void initializeAppWindow(Object state) => JS('void', '#.initializeAppWindow(#)', this._jsObject, convertArgument(state)); + + API_app_window(this._jsObject) { + onBoundsChanged = new Event_app_window_onBoundsChanged(JS('', '#.onBoundsChanged', this._jsObject)); + onClosed = new Event_app_window_onClosed(JS('', '#.onClosed', this._jsObject)); + onFullscreened = new Event_app_window_onFullscreened(JS('', '#.onFullscreened', this._jsObject)); + onMaximized = new Event_app_window_onMaximized(JS('', '#.onMaximized', this._jsObject)); + onMinimized = new Event_app_window_onMinimized(JS('', '#.onMinimized', this._jsObject)); + onRestored = new Event_app_window_onRestored(JS('', '#.onRestored', this._jsObject)); + } +} +// Copyright (c) 2013, 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. + +// Generated from namespace: app.runtime + + +/** + * Types + */ + +class AppRuntimeLaunchItem extends ChromeObject { + /* + * Public constructor + */ + AppRuntimeLaunchItem({FileEntry entry, String type}) { + if (entry != null) + this.entry = entry; + if (type != null) + this.type = type; + } + + /* + * Private constructor + */ + AppRuntimeLaunchItem._proxy(_jsObject) : super._proxy(_jsObject); + + /* + * Public accessors + */ + /// FileEntry for the file. + FileEntry get entry => JS('FileEntry', '#.entry', this._jsObject); + + void set entry(FileEntry entry) { + JS('void', '#.entry = #', this._jsObject, convertArgument(entry)); + } + + /// The MIME type of the file. + String get type => JS('String', '#.type', this._jsObject); + + void set type(String type) { + JS('void', '#.type = #', this._jsObject, type); + } + +} + +class AppRuntimeLaunchData extends ChromeObject { + /* + * Public constructor + */ + AppRuntimeLaunchData({String id, List items}) { + if (id != null) + this.id = id; + if (items != null) + this.items = items; + } + + /* + * Private constructor + */ + AppRuntimeLaunchData._proxy(_jsObject) : super._proxy(_jsObject); + + /* + * Public accessors + */ + /// The id of the file handler that the app is being invoked with. + String get id => JS('String', '#.id', this._jsObject); + + void set id(String id) { + JS('void', '#.id = #', this._jsObject, id); + } + + List get items { + List __proxy_items = new List(); + int count = JS('int', '#.items.length', this._jsObject); + for (int i = 0; i < count; i++) { + var item = JS('', '#.items[#]', this._jsObject, i); + __proxy_items.add(new AppRuntimeLaunchItem._proxy(item)); + } + return __proxy_items; + } + + void set items(List items) { + JS('void', '#.items = #', this._jsObject, convertArgument(items)); + } + +} + +/** + * Events + */ + +/// Fired when an app is launched from the launcher. +class Event_app_runtime_onLaunched extends Event { + void addListener(void callback(AppRuntimeLaunchData launchData)) { + void __proxy_callback(launchData) { + if (callback != null) { + callback(new AppRuntimeLaunchData._proxy(launchData)); + } + } + super.addListener(__proxy_callback); + } + + void removeListener(void callback(AppRuntimeLaunchData launchData)) { + void __proxy_callback(launchData) { + if (callback != null) { + callback(new AppRuntimeLaunchData._proxy(launchData)); + } + } + super.removeListener(__proxy_callback); + } + + bool hasListener(void callback(AppRuntimeLaunchData launchData)) { + void __proxy_callback(launchData) { + if (callback != null) { + callback(new AppRuntimeLaunchData._proxy(launchData)); + } + } + super.hasListener(__proxy_callback); + } + + Event_app_runtime_onLaunched(jsObject) : super._(jsObject, 1); +} + +/// Fired at Chrome startup to apps that were running when Chrome last shut +/// down. +class Event_app_runtime_onRestarted extends Event { + void addListener(void callback()) => super.addListener(callback); + + void removeListener(void callback()) => super.removeListener(callback); + + bool hasListener(void callback()) => super.hasListener(callback); + + Event_app_runtime_onRestarted(jsObject) : super._(jsObject, 0); +} + +/** + * Functions + */ + +class API_app_runtime { + /* + * API connection + */ + Object _jsObject; + + /* + * Events + */ + Event_app_runtime_onLaunched onLaunched; + Event_app_runtime_onRestarted onRestarted; + API_app_runtime(this._jsObject) { + onLaunched = new Event_app_runtime_onLaunched(JS('', '#.onLaunched', this._jsObject)); + onRestarted = new Event_app_runtime_onRestarted(JS('', '#.onRestarted', this._jsObject)); + } +} +// Copyright (c) 2013, 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. + +// Generated from namespace: fileSystem + + +/** + * Types + */ + +class FilesystemAcceptOption extends ChromeObject { + /* + * Public constructor + */ + FilesystemAcceptOption({String description, List mimeTypes, List extensions}) { + if (description != null) + this.description = description; + if (mimeTypes != null) + this.mimeTypes = mimeTypes; + if (extensions != null) + this.extensions = extensions; + } + + /* + * Private constructor + */ + FilesystemAcceptOption._proxy(_jsObject) : super._proxy(_jsObject); + + /* + * Public accessors + */ + /// This is the optional text description for this option. If not present, a + /// description will be automatically generated; typically containing an + /// expanded list of valid extensions (e.g. "text/html" may expand to "*.html, + /// *.htm"). + String get description => JS('String', '#.description', this._jsObject); + + void set description(String description) { + JS('void', '#.description = #', this._jsObject, description); + } + + /// Mime-types to accept, e.g. "image/jpeg" or "audio/*". One of mimeTypes or + /// extensions must contain at least one valid element. + List get mimeTypes => JS('List', '#.mimeTypes', this._jsObject); + + void set mimeTypes(List mimeTypes) { + JS('void', '#.mimeTypes = #', this._jsObject, mimeTypes); + } + + /// Extensions to accept, e.g. "jpg", "gif", "crx". + List get extensions => JS('List', '#.extensions', this._jsObject); + + void set extensions(List extensions) { + JS('void', '#.extensions = #', this._jsObject, extensions); + } + +} + +class FilesystemChooseEntryOptions extends ChromeObject { + /* + * Public constructor + */ + FilesystemChooseEntryOptions({String type, String suggestedName, List accepts, bool acceptsAllTypes}) { + if (type != null) + this.type = type; + if (suggestedName != null) + this.suggestedName = suggestedName; + if (accepts != null) + this.accepts = accepts; + if (acceptsAllTypes != null) + this.acceptsAllTypes = acceptsAllTypes; + } + + /* + * Private constructor + */ + FilesystemChooseEntryOptions._proxy(_jsObject) : super._proxy(_jsObject); + + /* + * Public accessors + */ + /// Type of the prompt to show. The default is 'openFile'. + String get type => JS('String', '#.type', this._jsObject); + + void set type(String type) { + JS('void', '#.type = #', this._jsObject, type); + } + + /// The suggested file name that will be presented to the user as the default + /// name to read or write. This is optional. + String get suggestedName => JS('String', '#.suggestedName', this._jsObject); + + void set suggestedName(String suggestedName) { + JS('void', '#.suggestedName = #', this._jsObject, suggestedName); + } + + /// The optional list of accept options for this file opener. Each option will + /// be presented as a unique group to the end-user. + List get accepts { + List __proxy_accepts = new List(); + int count = JS('int', '#.accepts.length', this._jsObject); + for (int i = 0; i < count; i++) { + var item = JS('', '#.accepts[#]', this._jsObject, i); + __proxy_accepts.add(new FilesystemAcceptOption._proxy(item)); + } + return __proxy_accepts; + } + + void set accepts(List accepts) { + JS('void', '#.accepts = #', this._jsObject, convertArgument(accepts)); + } + + /// Whether to accept all file types, in addition to the options specified in + /// the accepts argument. The default is true. If the accepts field is unset or + /// contains no valid entries, this will always be reset to true. + bool get acceptsAllTypes => JS('bool', '#.acceptsAllTypes', this._jsObject); + + void set acceptsAllTypes(bool acceptsAllTypes) { + JS('void', '#.acceptsAllTypes = #', this._jsObject, acceptsAllTypes); + } + +} + +/** + * Functions + */ + +class API_file_system { + /* + * API connection + */ + Object _jsObject; + + /* + * Functions + */ + /// Get the display path of a FileEntry object. The display path is based on + /// the full path of the file on the local file system, but may be made more + /// readable for display purposes. + void getDisplayPath(FileEntry fileEntry, void callback(String displayPath)) => JS('void', '#.getDisplayPath(#, #)', this._jsObject, convertArgument(fileEntry), convertDartClosureToJS(callback, 1)); + + /// Get a writable FileEntry from another FileEntry. This call will fail if the + /// application does not have the 'write' permission under 'fileSystem'. + void getWritableEntry(FileEntry fileEntry, void callback(FileEntry fileEntry)) { + void __proxy_callback(fileEntry) { + if (callback != null) { + callback(fileEntry); + } + } + JS('void', '#.getWritableEntry(#, #)', this._jsObject, convertArgument(fileEntry), convertDartClosureToJS(__proxy_callback, 1)); + } + + /// Gets whether this FileEntry is writable or not. + void isWritableEntry(FileEntry fileEntry, void callback(bool isWritable)) => JS('void', '#.isWritableEntry(#, #)', this._jsObject, convertArgument(fileEntry), convertDartClosureToJS(callback, 1)); + + /// Ask the user to choose a file. + void chooseEntry(void callback(FileEntry fileEntry), [FilesystemChooseEntryOptions options]) { + void __proxy_callback(fileEntry) { + if (callback != null) { + callback(fileEntry); + } + } + JS('void', '#.chooseEntry(#, #)', this._jsObject, convertArgument(options), convertDartClosureToJS(__proxy_callback, 1)); + } + + /// Returns the file entry with the given id if it can be restored. This call + /// will fail otherwise. + void restoreEntry(String id, void callback(FileEntry fileEntry)) { + void __proxy_callback(fileEntry) { + if (callback != null) { + callback(fileEntry); + } + } + JS('void', '#.restoreEntry(#, #)', this._jsObject, id, convertDartClosureToJS(__proxy_callback, 1)); + } + + /// Returns whether a file entry for the given id can be restored, i.e. whether + /// restoreEntry would succeed with this id now. + void isRestorable(String id, void callback(bool isRestorable)) => JS('void', '#.isRestorable(#, #)', this._jsObject, id, convertDartClosureToJS(callback, 1)); + + /// Returns an id that can be passed to restoreEntry to regain access to a + /// given file entry. Only the 500 most recently used entries are retained, + /// where calls to retainEntry and restoreEntry count as use. If the app has + /// the 'retainEntries' permission under 'fileSystem', entries are retained + /// indefinitely. Otherwise, entries are retained only while the app is running + /// and across restarts. + String retainEntry(FileEntry fileEntry) => JS('String', '#.retainEntry(#)', this._jsObject, convertArgument(fileEntry)); + + API_file_system(this._jsObject) { + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_chrome/dartium/chrome_dartium.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_chrome/dartium/chrome_dartium.dart new file mode 100644 index 0000000..c16c9e5 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_chrome/dartium/chrome_dartium.dart @@ -0,0 +1,16 @@ +/// Native wrappers for the Chrome Packaged App APIs. +/// +/// These functions allow direct access to the Packaged App APIs, allowing +/// Chrome Packaged Apps to be written using Dart. +/// +/// For more information on these APIs, see the +/// [Chrome APIs Documentation](http://developer.chrome.com/extensions/api_index.html) +library _chrome; + +// Copyright (c) 2013, 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. + +// DO NOT EDIT +// Auto-generated dart:_chrome library. + diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/compiler.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/compiler.dart new file mode 100644 index 0000000..b79ab22 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/compiler.dart @@ -0,0 +1,187 @@ +// Copyright (c) 2012, 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. + +library compiler; + +import 'dart:async'; +import 'implementation/apiimpl.dart'; + +// Unless explicitly allowed, passing [:null:] for any argument to the +// methods of library will result in an Error being thrown. + +/** + * Returns a future that completes to the source corresponding to [uri]. + * If an exception occurs, the future completes with this exception. + * + * The source can be represented either as a [:List:] of UTF-8 bytes or as + * a [String]. + * + * The following text is non-normative: + * + * It is recommended to return a UTF-8 encoded list of bytes because the scanner + * is more efficient in this case. In either case, the data structure is + * expected to hold a zero element at the last position. If this is not the + * case, the entire data structure is copied before scanning. + */ +typedef Future/*>*/ CompilerInputProvider(Uri uri); + +/// Deprecated, please use [CompilerInputProvider] instead. +typedef Future ReadStringFromUri(Uri uri); + +/** + * Returns an [EventSink] that will serve as compiler output for the given + * component. + * + * Components are identified by [name] and [extension]. By convention, + * the empty string [:"":] will represent the main script + * (corresponding to the script parameter of [compile]) even if the + * main script is a library. For libraries that are compiled + * separately, the library name is used. + * + * At least the following extensions can be expected: + * + * * "js" for JavaScript output. + * * "js.map" for source maps. + * * "dart" for Dart output. + * * "dart.map" for source maps. + * + * As more features are added to the compiler, new names and + * extensions may be introduced. + */ +typedef EventSink CompilerOutputProvider(String name, + String extension); + +/** + * Invoked by the compiler to report diagnostics. If [uri] is + * [:null:], so are [begin] and [end]. No other arguments may be + * [:null:]. If [uri] is not [:null:], neither are [begin] and + * [end]. [uri] indicates the compilation unit from where the + * diagnostic originates. [begin] and [end] are zero-based character + * offsets from the beginning of the compilaton unit. [message] is the + * diagnostic message, and [kind] indicates indicates what kind of + * diagnostic it is. + */ +typedef void DiagnosticHandler(Uri uri, int begin, int end, + String message, Diagnostic kind); + +/** + * Returns a future that completes to a non-null String when [script] + * has been successfully compiled. + * + * The compiler output is obtained by providing an [outputProvider]. + * + * If the compilation fails, the future's value will be [:null:] and + * [handler] will have been invoked at least once with [:kind == + * Diagnostic.ERROR:] or [:kind == Diagnostic.CRASH:]. + * + * Deprecated: if no [outputProvider] is given, the future completes + * to the compiled script. This behavior will be removed in the future + * as the compiler may create multiple files to support lazy loading + * of libraries. + */ +Future compile(Uri script, + Uri libraryRoot, + Uri packageRoot, + CompilerInputProvider inputProvider, + DiagnosticHandler handler, + [List options = const [], + CompilerOutputProvider outputProvider, + Map environment = const {}]) { + if (!libraryRoot.path.endsWith("/")) { + throw new ArgumentError("libraryRoot must end with a /"); + } + if (packageRoot != null && !packageRoot.path.endsWith("/")) { + throw new ArgumentError("packageRoot must end with a /"); + } + // TODO(ahe): Consider completing the future with an exception if + // code is null. + Compiler compiler = new Compiler(inputProvider, + outputProvider, + handler, + libraryRoot, + packageRoot, + options, + environment); + // TODO(ahe): Use the value of the future (which signals success or failure). + return compiler.run(script).then((_) { + String code = compiler.assembledCode; + if (code != null && outputProvider != null) { + code = ''; // Non-null signals success. + } + return code; + }); +} + +/** + * Kind of diagnostics that the compiler can report. + */ +class Diagnostic { + /** + * An error as identified by the "Dart Programming Language + * Specification" [http://www.dartlang.org/docs/spec/]. + * + * Note: the compiler may still produce an executable result after + * reporting a compilation error. The specification says: + * + * "A compile-time error must be reported by a Dart compiler before + * the erroneous code is executed." and "If a compile-time error + * occurs within the code of a running isolate A, A is immediately + * suspended." + * + * This means that the compiler can generate code that when executed + * terminates execution. + */ + static const Diagnostic ERROR = const Diagnostic(1, 'error'); + + /** + * A warning as identified by the "Dart Programming Language + * Specification" [http://www.dartlang.org/docs/spec/]. + */ + static const Diagnostic WARNING = const Diagnostic(2, 'warning'); + + /** + * Any other warning that is not covered by [WARNING]. + */ + static const Diagnostic HINT = const Diagnostic(4, 'hint'); + + /** + * Additional information about the preceding non-info diagnostic from the + * compiler. + * + * For example, consider a duplicated definition. The compiler first emits a + * message about the duplicated definition, then emits an info message about + * the location of the existing definition. + */ + static const Diagnostic INFO = const Diagnostic(8, 'info'); + + /** + * Informational messages that shouldn't be printed unless + * explicitly requested by the user of a compiler. + */ + static const Diagnostic VERBOSE_INFO = const Diagnostic(16, 'verbose info'); + + /** + * An internal error in the compiler. + */ + static const Diagnostic CRASH = const Diagnostic(32, 'crash'); + + /** + * An [int] representation of this kind. The ordinals are designed + * to be used as bitsets. + */ + final int ordinal; + + /** + * The name of this kind. + */ + final String name; + + /** + * This constructor is not private to support user-defined + * diagnostic kinds. + */ + const Diagnostic(this.ordinal, this.name); + + String toString() => name; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/apiimpl.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/apiimpl.dart new file mode 100644 index 0000000..bde352e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/apiimpl.dart @@ -0,0 +1,342 @@ +// Copyright (c) 2012, 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. + +library leg_apiimpl; + +import 'dart:async'; + +import '../compiler.dart' as api; +import 'dart2jslib.dart' as leg; +import 'tree/tree.dart' as tree; +import 'elements/elements.dart' as elements; +import 'ssa/tracer.dart' as ssa; +import '../../libraries.dart'; +import 'source_file.dart'; + + +class Compiler extends leg.Compiler { + api.CompilerInputProvider provider; + api.DiagnosticHandler handler; + final Uri libraryRoot; + final Uri packageRoot; + List options; + Map environment; + bool mockableLibraryUsed = false; + final Set allowedLibraryCategories; + + Compiler(this.provider, + api.CompilerOutputProvider outputProvider, + this.handler, + this.libraryRoot, + this.packageRoot, + List options, + this.environment) + : this.options = options, + this.allowedLibraryCategories = getAllowedLibraryCategories(options), + super( + tracer: new ssa.HTracer( + ssa.GENERATE_SSA_TRACE ? outputProvider('dart', 'cfg') : null), + outputProvider: outputProvider, + enableTypeAssertions: hasOption(options, '--enable-checked-mode'), + enableUserAssertions: hasOption(options, '--enable-checked-mode'), + trustTypeAnnotations: + hasOption(options, '--trust-type-annotations'), + enableMinification: hasOption(options, '--minify'), + enableNativeLiveTypeAnalysis: + !hasOption(options, '--disable-native-live-type-analysis'), + emitJavaScript: !hasOption(options, '--output-type=dart'), + analyzeAllFlag: hasOption(options, '--analyze-all'), + analyzeOnly: hasOption(options, '--analyze-only'), + analyzeSignaturesOnly: + hasOption(options, '--analyze-signatures-only'), + strips: extractCsvOption(options, '--force-strip='), + enableConcreteTypeInference: + hasOption(options, '--enable-concrete-type-inference'), + disableTypeInferenceFlag: + hasOption(options, '--disable-type-inference'), + preserveComments: hasOption(options, '--preserve-comments'), + verbose: hasOption(options, '--verbose'), + sourceMapUri: extractUriOption(options, '--source-map='), + outputUri: extractUriOption(options, '--out='), + terseDiagnostics: hasOption(options, '--terse'), + dumpInfo: hasOption(options, '--dump-info'), + buildId: extractStringOption( + options, '--build-id=', + "build number could not be determined"), + showPackageWarnings: + hasOption(options, '--show-package-warnings')) { + if (!libraryRoot.path.endsWith("/")) { + throw new ArgumentError("libraryRoot must end with a /"); + } + if (packageRoot != null && !packageRoot.path.endsWith("/")) { + throw new ArgumentError("packageRoot must end with a /"); + } + } + + static String extractStringOption(List options, + String prefix, + String defaultValue) { + for (String option in options) { + if (option.startsWith(prefix)) { + return option.substring(prefix.length); + } + } + return defaultValue; + } + + static Uri extractUriOption(List options, String prefix) { + var option = extractStringOption(options, prefix, null); + return (option == null) ? null : Uri.parse(option); + } + + // CSV: Comma separated values. + static List extractCsvOption(List options, String prefix) { + for (String option in options) { + if (option.startsWith(prefix)) { + return option.substring(prefix.length).split(','); + } + } + return const []; + } + + static Set getAllowedLibraryCategories(List options) { + var result = extractCsvOption(options, '--categories='); + if (result.isEmpty) { + result = ['Client']; + } + result.add('Shared'); + result.add('Internal'); + return new Set.from(result); + } + + static bool hasOption(List options, String option) { + return options.indexOf(option) >= 0; + } + + // TODO(johnniwinther): Merge better with [translateDartUri] when + // [scanBuiltinLibrary] is removed. + String lookupLibraryPath(String dartLibraryName) { + LibraryInfo info = LIBRARIES[dartLibraryName]; + if (info == null) return null; + if (!info.isDart2jsLibrary) return null; + if (!allowedLibraryCategories.contains(info.category)) return null; + String path = info.dart2jsPath; + if (path == null) { + path = info.path; + } + return "lib/$path"; + } + + String lookupPatchPath(String dartLibraryName) { + LibraryInfo info = LIBRARIES[dartLibraryName]; + if (info == null) return null; + if (!info.isDart2jsLibrary) return null; + String path = info.dart2jsPatchPath; + if (path == null) return null; + return "lib/$path"; + } + + Future scanBuiltinLibrary(String path) { + Uri uri = libraryRoot.resolve(lookupLibraryPath(path)); + Uri canonicalUri = new Uri(scheme: "dart", path: path); + return libraryLoader.loadLibrary(uri, null, canonicalUri); + } + + void log(message) { + handler(null, null, null, message, api.Diagnostic.VERBOSE_INFO); + } + + /// See [leg.Compiler.translateResolvedUri]. + Uri translateResolvedUri(elements.LibraryElement importingLibrary, + Uri resolvedUri, tree.Node node) { + if (resolvedUri.scheme == 'dart') { + return translateDartUri(importingLibrary, resolvedUri, node); + } + return resolvedUri; + } + + /** + * Reads the script designated by [readableUri]. + */ + Future readScript(leg.Spannable node, Uri readableUri) { + if (!readableUri.isAbsolute) { + internalError(node, + 'Relative uri $readableUri provided to readScript(Uri).'); + } + + // We need to store the current element since we are reporting read errors + // asynchronously and therefore need to restore the current element for + // [node] to be valid. + elements.Element element = currentElement; + void reportReadError(exception) { + withCurrentElement(element, () { + reportError(node, + leg.MessageKind.READ_SCRIPT_ERROR, + {'uri': readableUri, 'exception': exception}); + }); + } + + Uri resourceUri = translateUri(node, readableUri); + // TODO(johnniwinther): Wrap the result from [provider] in a specialized + // [Future] to ensure that we never execute an asynchronous action without + // setting up the current element of the compiler. + return new Future.sync(() => callUserProvider(resourceUri)).then((data) { + SourceFile sourceFile; + String resourceUriString = resourceUri.toString(); + if (data is List) { + sourceFile = new Utf8BytesSourceFile(resourceUriString, data); + } else if (data is String) { + sourceFile = new StringSourceFile(resourceUriString, data); + } else { + String message = "Expected a 'String' or a 'List' from the input " + "provider, but got: ${Error.safeToString(data)}."; + reportReadError(message); + } + // We use [readableUri] as the URI for the script since need to preserve + // the scheme in the script because [Script.uri] is used for resolving + // relative URIs mentioned in the script. See the comment on + // [LibraryLoader] for more details. + return new leg.Script(readableUri, resourceUri, sourceFile); + }).catchError((error) { + reportReadError(error); + return null; + }); + } + + /** + * Translates a readable URI into a resource URI. + * + * See [LibraryLoader] for terminology on URIs. + */ + Uri translateUri(leg.Spannable node, Uri readableUri) { + switch (readableUri.scheme) { + case 'package': return translatePackageUri(node, readableUri); + default: return readableUri; + } + } + + Uri translateDartUri(elements.LibraryElement importingLibrary, + Uri resolvedUri, tree.Node node) { + LibraryInfo libraryInfo = LIBRARIES[resolvedUri.path]; + String path = lookupLibraryPath(resolvedUri.path); + if (libraryInfo != null && + libraryInfo.category == "Internal") { + bool allowInternalLibraryAccess = false; + if (importingLibrary != null) { + if (importingLibrary.isPlatformLibrary || importingLibrary.isPatch) { + allowInternalLibraryAccess = true; + } else if (importingLibrary.canonicalUri.path.contains( + 'dart/tests/compiler/dart2js_native')) { + allowInternalLibraryAccess = true; + } + } + if (!allowInternalLibraryAccess) { + if (importingLibrary != null) { + reportError( + node, + leg.MessageKind.INTERNAL_LIBRARY_FROM, + {'resolvedUri': resolvedUri, + 'importingUri': importingLibrary.canonicalUri}); + } else { + reportError( + node, + leg.MessageKind.INTERNAL_LIBRARY, + {'resolvedUri': resolvedUri}); + } + } + } + if (path == null) { + reportError(node, leg.MessageKind.LIBRARY_NOT_FOUND, + {'resolvedUri': resolvedUri}); + return null; + } + if (resolvedUri.path == 'html' || + resolvedUri.path == 'io') { + // TODO(ahe): Get rid of mockableLibraryUsed when test.dart + // supports this use case better. + mockableLibraryUsed = true; + } + return libraryRoot.resolve(path); + } + + Uri resolvePatchUri(String dartLibraryPath) { + String patchPath = lookupPatchPath(dartLibraryPath); + if (patchPath == null) return null; + return libraryRoot.resolve(patchPath); + } + + Uri translatePackageUri(leg.Spannable node, Uri uri) { + if (packageRoot == null) { + reportFatalError( + node, leg.MessageKind.PACKAGE_ROOT_NOT_SET, {'uri': uri}); + } + return packageRoot.resolve(uri.path); + } + + Future run(Uri uri) { + log('Allowed library categories: $allowedLibraryCategories'); + return super.run(uri).then((bool success) { + int cumulated = 0; + for (final task in tasks) { + cumulated += task.timing; + log('${task.name} took ${task.timing}msec'); + } + int total = totalCompileTime.elapsedMilliseconds; + log('Total compile-time ${total}msec;' + ' unaccounted ${total - cumulated}msec'); + return success; + }); + } + + void reportDiagnostic(leg.Spannable node, + leg.Message message, + api.Diagnostic kind) { + leg.SourceSpan span = spanFromSpannable(node); + if (identical(kind, api.Diagnostic.ERROR) + || identical(kind, api.Diagnostic.CRASH)) { + compilationFailed = true; + } + // [:span.uri:] might be [:null:] in case of a [Script] with no [uri]. For + // instance in the [Types] constructor in typechecker.dart. + if (span == null || span.uri == null) { + callUserHandler(null, null, null, '$message', kind); + } else { + callUserHandler( + translateUri(null, span.uri), span.begin, span.end, '$message', kind); + } + } + + bool get isMockCompilation { + return mockableLibraryUsed + && (options.indexOf('--allow-mock-compilation') != -1); + } + + void callUserHandler(Uri uri, int begin, int end, + String message, api.Diagnostic kind) { + try { + handler(uri, begin, end, message, kind); + } catch (ex, s) { + diagnoseCrashInUserCode( + 'Uncaught exception in diagnostic handler', ex, s); + rethrow; + } + } + + Future callUserProvider(Uri uri) { + try { + return provider(uri); + } catch (ex, s) { + diagnoseCrashInUserCode('Uncaught exception in input provider', ex, s); + rethrow; + } + } + + void diagnoseCrashInUserCode(String message, exception, stackTrace) { + hasCrashed = true; + print('$message: ${tryToString(exception)}'); + print(tryToString(stackTrace)); + } + + fromEnvironment(String name) => environment[name]; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/closure.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/closure.dart new file mode 100644 index 0000000..7b1f02d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/closure.dart @@ -0,0 +1,840 @@ +// Copyright (c) 2012, 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. + +library closureToClassMapper; + +import "elements/elements.dart"; +import "dart2jslib.dart"; +import "dart_types.dart"; +import "scanner/scannerlib.dart" show Token; +import "tree/tree.dart"; +import "util/util.dart"; +import "elements/modelx.dart" show ElementX, FunctionElementX, ClassElementX; +import "elements/visitor.dart" show ElementVisitor; + +class ClosureNamer { + String getClosureVariableName(String name, int id) { + return "${name}_$id"; + } +} + +class ClosureTask extends CompilerTask { + Map closureMappingCache; + ClosureNamer namer; + ClosureTask(Compiler compiler, this.namer) + : closureMappingCache = new Map(), + super(compiler); + + String get name => "Closure Simplifier"; + + ClosureClassMap computeClosureToClassMapping(Element element, + Node node, + TreeElements elements) { + return measure(() { + ClosureClassMap cached = closureMappingCache[node]; + if (cached != null) return cached; + + ClosureTranslator translator = + new ClosureTranslator(compiler, elements, closureMappingCache, namer); + + // The translator will store the computed closure-mappings inside the + // cache. One for given node and one for each nested closure. + if (node is FunctionExpression) { + translator.translateFunction(element, node); + } else if (element.isSynthesized) { + return new ClosureClassMap(null, null, null, + new ThisElement(element, compiler.types.dynamicType)); + } else { + assert(element.isField()); + VariableElement field = element; + if (field.initializer != null) { + // The lazy initializer of a static. + translator.translateLazyInitializer(element, node, field.initializer); + } else { + assert(element.isInstanceMember()); + closureMappingCache[node] = + new ClosureClassMap(null, null, null, + new ThisElement(element, compiler.types.dynamicType)); + } + } + assert(closureMappingCache[node] != null); + return closureMappingCache[node]; + }); + } + + ClosureClassMap getMappingForNestedFunction(FunctionExpression node) { + return measure(() { + ClosureClassMap nestedClosureData = closureMappingCache[node]; + if (nestedClosureData == null) { + compiler.internalError(node, "No closure cache."); + } + return nestedClosureData; + }); + } +} + +// TODO(ahe): These classes continuously cause problems. We need to +// move these classes to elements/modelx.dart or see if we can find a +// more general solution. +class ClosureFieldElement extends ElementX implements VariableElement { + /// The source variable this element refers to. + final TypedElement variableElement; + + ClosureFieldElement(String name, + this.variableElement, + ClassElement enclosing) + : super(name, ElementKind.FIELD, enclosing); + + Expression get initializer { + throw new SpannableAssertionFailure( + variableElement, + 'Should not access initializer of ClosureFieldElement.'); + } + + bool isInstanceMember() => true; + bool isAssignable() => false; + + DartType computeType(Compiler compiler) { + return variableElement.type; + } + + DartType get type => variableElement.type; + + String toString() => "ClosureFieldElement($name)"; + + accept(ElementVisitor visitor) => visitor.visitClosureFieldElement(this); +} + +// TODO(ahe): These classes continuously cause problems. We need to +// move these classes to elements/modelx.dart or see if we can find a +// more general solution. +class ClosureClassElement extends ClassElementX { + DartType rawType; + DartType thisType; + FunctionType callType; + /// Node that corresponds to this closure, used for source position. + final FunctionExpression node; + + ClosureClassElement(this.node, + String name, + Compiler compiler, + this.methodElement, + Element enclosingElement) + : super(name, + enclosingElement, + // By assigning a fresh class-id we make sure that the hashcode + // is unique, but also emit closure classes after all other + // classes (since the emitter sorts classes by their id). + compiler.getNextFreeClassId(), + STATE_DONE) { + ClassElement superclass = methodElement.isInstanceMember() + ? compiler.boundClosureClass + : compiler.closureClass; + superclass.ensureResolved(compiler); + supertype = superclass.thisType; + interfaces = const Link(); + thisType = rawType = new InterfaceType(this); + allSupertypesAndSelf = + superclass.allSupertypesAndSelf.extendClass(thisType); + callType = methodElement.type; + } + + bool isClosure() => true; + + Token position() => node.getBeginToken(); + + Node parseNode(DiagnosticListener listener) => node; + + /** + * The most outer method this closure is declared into. + */ + final TypedElement methodElement; + + // A [ClosureClassElement] is nested inside a function or initializer in terms + // of [enclosingElement], but still has to be treated as a top-level + // element. + bool isTopLevel() => true; + + get enclosingElement => methodElement; + + accept(ElementVisitor visitor) => visitor.visitClosureClassElement(this); +} + +// TODO(ahe): These classes continuously cause problems. We need to +// move these classes to elements/modelx.dart or see if we can find a +// more general solution. +class BoxElement extends ElementX implements TypedElement { + final DartType type; + + BoxElement(String name, Element enclosingElement, this.type) + : super(name, ElementKind.VARIABLE_LIST, enclosingElement); + + DartType computeType(Compiler compiler) => type; + + accept(ElementVisitor visitor) => visitor.visitBoxElement(this); +} + +// TODO(ngeoffray, ahe): These classes continuously cause problems. We need to +// move these classes to elements/modelx.dart or see if we can find a +// more general solution. +class BoxFieldElement extends ElementX implements TypedElement { + BoxFieldElement(String name, + this.variableElement, + BoxElement enclosingBox) + : super(name, ElementKind.FIELD, enclosingBox); + + DartType computeType(Compiler compiler) => type; + + DartType get type => variableElement.type; + + final VariableElement variableElement; + + accept(ElementVisitor visitor) => visitor.visitBoxFieldElement(this); +} + +// TODO(ahe): These classes continuously cause problems. We need to +// move these classes to elements/modelx.dart or see if we can find a +// more general solution. +class ThisElement extends ElementX implements TypedElement { + final DartType type; + + ThisElement(Element enclosing, this.type) + : super('this', ElementKind.PARAMETER, enclosing); + + bool isAssignable() => false; + + DartType computeType(Compiler compiler) => compiler.types.dynamicType; + + // Since there is no declaration corresponding to 'this', use the position of + // the enclosing method. + Token position() => enclosingElement.position(); + + accept(ElementVisitor visitor) => visitor.visitThisElement(this); +} + +// The box-element for a scope, and the captured variables that need to be +// stored in the box. +class ClosureScope { + Element boxElement; + Map capturedVariableMapping; + // If the scope is attached to a [For] contains the variables that are + // declared in the initializer of the [For] and that need to be boxed. + // Otherwise contains the empty List. + List boxedLoopVariables; + + ClosureScope(this.boxElement, this.capturedVariableMapping) + : boxedLoopVariables = const []; + + bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty; +} + +class ClosureClassMap { + // The closure's element before any translation. Will be null for methods. + final Element closureElement; + // The closureClassElement will be null for methods that are not local + // closures. + final ClassElement closureClassElement; + // The callElement will be null for methods that are not local closures. + final FunctionElement callElement; + // The [thisElement] makes handling 'this' easier by treating it like any + // other argument. It is only set for instance-members. + final ThisElement thisElement; + + // Maps free locals, arguments and function elements to their captured + // copies. + final Map freeVariableMapping; + // Maps closure-fields to their captured elements. This is somehow the inverse + // mapping of [freeVariableMapping], but whereas [freeVariableMapping] does + // not deal with boxes, here we map instance-fields (which might represent + // boxes) to their boxElement. + final Map capturedFieldMapping; + + // Maps scopes ([Loop] and [FunctionExpression] nodes) to their + // [ClosureScope] which contains their box and the + // captured variables that are stored in the box. + // This map will be empty if the method/closure of this [ClosureData] does not + // contain any nested closure. + final Map capturingScopes; + + final Set usedVariablesInTry; + + ClosureClassMap(this.closureElement, + this.closureClassElement, + this.callElement, + this.thisElement) + : this.freeVariableMapping = new Map(), + this.capturedFieldMapping = new Map(), + this.capturingScopes = new Map(), + this.usedVariablesInTry = new Set(); + + bool isClosure() => closureElement != null; + + bool capturingScopesBox(Element element) { + return capturingScopes.values.any((scope) { + return scope.boxedLoopVariables.contains(element); + }); + } + + bool isVariableBoxed(Element element) { + Element copy = freeVariableMapping[element]; + if (copy != null && !copy.isMember()) return true; + return capturingScopesBox(element); + } + + void forEachCapturedVariable(void f(Element local, Element field)) { + freeVariableMapping.forEach((variable, copy) { + if (variable is BoxElement) return; + f(variable, copy); + }); + capturingScopes.values.forEach((scope) { + scope.capturedVariableMapping.forEach(f); + }); + } + + void forEachBoxedVariable(void f(Element local, Element field)) { + freeVariableMapping.forEach((variable, copy) { + if (!isVariableBoxed(variable)) return; + f(variable, copy); + }); + capturingScopes.values.forEach((scope) { + scope.capturedVariableMapping.forEach(f); + }); + } +} + +class ClosureTranslator extends Visitor { + final Compiler compiler; + final TreeElements elements; + int closureFieldCounter = 0; + int boxedFieldCounter = 0; + bool inTryStatement = false; + final Map closureMappingCache; + + // Map of captured variables. Initially they will map to themselves. If + // a variable needs to be boxed then the scope declaring the variable + // will update this mapping. + Map capturedVariableMapping; + // List of encountered closures. + List closures; + + // The variables that have been declared in the current scope. + List scopeVariables; + + // Keep track of the mutated variables so that we don't need to box + // non-mutated variables. + Set mutatedVariables; + + Element outermostElement; + Element currentElement; + + // The closureData of the currentFunctionElement. + ClosureClassMap closureData; + + ClosureNamer namer; + + bool insideClosure = false; + + ClosureTranslator(this.compiler, this.elements, this.closureMappingCache, + this.namer) + : capturedVariableMapping = new Map(), + closures = [], + mutatedVariables = new Set(); + + void translateFunction(Element element, FunctionExpression node) { + // For constructors the [element] and the [:elements[node]:] may differ. + // The [:elements[node]:] always points to the generative-constructor + // element, whereas the [element] might be the constructor-body element. + visit(node); // [visitFunctionExpression] will call [visitInvokable]. + // When variables need to be boxed their [capturedVariableMapping] is + // updated, but we delay updating the similar freeVariableMapping in the + // closure datas that capture these variables. + // The closures don't have their fields (in the closure class) set, either. + updateClosures(); + } + + void translateLazyInitializer(VariableElement element, + VariableDefinitions node, + Expression initializer) { + visitInvokable(element, node, () { visit(initializer); }); + updateClosures(); + } + + // This function runs through all of the existing closures and updates their + // free variables to the boxed value. It also adds the field-elements to the + // class representing the closure. At the same time it fills the + // [capturedFieldMapping]. + void updateClosures() { + for (Expression closure in closures) { + // The captured variables that need to be stored in a field of the closure + // class. + Set fieldCaptures = new Set(); + Set boxes = new Set(); + ClosureClassMap data = closureMappingCache[closure]; + Map freeVariableMapping = data.freeVariableMapping; + // We get a copy of the keys and iterate over it, to avoid modifications + // to the map while iterating over it. + freeVariableMapping.keys.toList().forEach((Element fromElement) { + assert(fromElement == freeVariableMapping[fromElement]); + Element updatedElement = capturedVariableMapping[fromElement]; + assert(updatedElement != null); + if (fromElement == updatedElement) { + assert(freeVariableMapping[fromElement] == updatedElement); + assert(Elements.isLocal(updatedElement) + || updatedElement.isTypeVariable()); + // The variable has not been boxed. + fieldCaptures.add(updatedElement); + } else { + // A boxed element. + freeVariableMapping[fromElement] = updatedElement; + Element boxElement = updatedElement.enclosingElement; + assert(boxElement is BoxElement); + boxes.add(boxElement); + } + }); + ClassElement closureElement = data.closureClassElement; + assert(closureElement != null || + (fieldCaptures.isEmpty && boxes.isEmpty)); + void addElement(Element element, String name) { + Element fieldElement = new ClosureFieldElement( + name, element, closureElement); + closureElement.addMember(fieldElement, compiler); + data.capturedFieldMapping[fieldElement] = element; + freeVariableMapping[element] = fieldElement; + } + // Add the box elements first so we get the same ordering. + // TODO(sra): What is the canonical order of multiple boxes? + for (Element capturedElement in boxes) { + addElement(capturedElement, capturedElement.name); + } + for (Element capturedElement in + Elements.sortedByPosition(fieldCaptures)) { + int id = closureFieldCounter++; + String name = namer.getClosureVariableName(capturedElement.name, id); + addElement(capturedElement, name); + } + closureElement.reverseBackendMembers(); + } + } + + void useLocal(Element element) { + // If the element is not declared in the current function and the element + // is not the closure itself we need to mark the element as free variable. + // Note that the check on [insideClosure] is not just an + // optimization: factories have type parameters as function + // parameters, and type parameters are declared in the class, not + // the factory. + if (insideClosure && + element.enclosingElement != currentElement && + element != currentElement) { + assert(closureData.freeVariableMapping[element] == null || + closureData.freeVariableMapping[element] == element); + closureData.freeVariableMapping[element] = element; + } else if (inTryStatement) { + // Don't mark the this-element. This would complicate things in the + // builder. + if (element != closureData.thisElement) { + // TODO(ngeoffray): only do this if the variable is mutated. + closureData.usedVariablesInTry.add(element); + } + } + } + + void declareLocal(Element element) { + scopeVariables.add(element); + } + + void registerNeedsThis() { + if (closureData.thisElement != null) { + useLocal(closureData.thisElement); + } + } + + visit(Node node) => node.accept(this); + + visitNode(Node node) => node.visitChildren(this); + + visitVariableDefinitions(VariableDefinitions node) { + if (node.type != null) { + visit(node.type); + } + for (Link link = node.definitions.nodes; + !link.isEmpty; + link = link.tail) { + Node definition = link.head; + VariableElement element = elements[definition]; + assert(element != null); + declareLocal(element); + // We still need to visit the right-hand sides of the init-assignments. + // For SendSets don't visit the left again. Otherwise it would be marked + // as mutated. + if (definition is Send) { + Send assignment = definition; + Node arguments = assignment.argumentsNode; + if (arguments != null) { + visit(arguments); + } + } else { + visit(definition); + } + } + } + + visitTypeAnnotation(TypeAnnotation node) { + Element member = currentElement.getEnclosingMember(); + DartType type = elements.getType(node); + // TODO(karlklose,johnniwinther): if the type is null, the annotation is + // from a parameter which has been analyzed before the method has been + // resolved and the result has been thrown away. + if (compiler.enableTypeAssertions && type != null && + type.containsTypeVariables) { + if (insideClosure && member.isFactoryConstructor()) { + // This is a closure in a factory constructor. Since there is no + // [:this:], we have to mark the type arguments as free variables to + // capture them in the closure. + type.forEachTypeVariable((variable) => useLocal(variable.element)); + } + if (member.isInstanceMember() && !member.isField()) { + // In checked mode, using a type variable in a type annotation may lead + // to a runtime type check that needs to access the type argument and + // therefore the closure needs a this-element, if it is not in a field + // initializer; field initatializers are evaluated in a context where + // the type arguments are available in locals. + registerNeedsThis(); + } + } + } + + visitIdentifier(Identifier node) { + if (node.isThis()) { + registerNeedsThis(); + } else { + Element element = elements[node]; + if (element != null && element.kind == ElementKind.TYPE_VARIABLE) { + if (outermostElement.isConstructor()) { + useLocal(element); + } else { + registerNeedsThis(); + } + } + } + node.visitChildren(this); + } + + visitSend(Send node) { + Element element = elements[node]; + if (Elements.isLocal(element)) { + useLocal(element); + } else if (element != null && element.isTypeVariable()) { + TypeVariableElement variable = element; + analyzeType(variable.type); + } else if (node.receiver == null && + Elements.isInstanceSend(node, elements)) { + registerNeedsThis(); + } else if (node.isSuperCall) { + registerNeedsThis(); + } else if (node.isTypeTest || node.isTypeCast) { + TypeAnnotation annotation = node.typeAnnotationFromIsCheckOrCast; + DartType type = elements.getType(annotation); + analyzeType(type); + } else if (node.isTypeTest) { + DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); + analyzeType(type); + } else if (node.isTypeCast) { + DartType type = elements.getType(node.arguments.head); + analyzeType(type); + } else if (element == compiler.assertMethod + && !compiler.enableUserAssertions) { + return; + } + node.visitChildren(this); + } + + visitSendSet(SendSet node) { + Element element = elements[node]; + if (Elements.isLocal(element)) { + mutatedVariables.add(element); + if (compiler.enableTypeAssertions) { + TypedElement typedElement = element; + analyzeTypeVariables(typedElement.type); + } + } + super.visitSendSet(node); + } + + visitNewExpression(NewExpression node) { + DartType type = elements.getType(node); + analyzeType(type); + node.visitChildren(this); + } + + void analyzeTypeVariables(DartType type) { + type.forEachTypeVariable((TypeVariableType typeVariable) { + // Field initializers are inlined and access the type variable as + // normal parameters. + if (!outermostElement.isField() && + !outermostElement.isConstructor()) { + registerNeedsThis(); + } else { + useLocal(typeVariable.element); + } + }); + } + + void analyzeType(DartType type) { + // TODO(johnniwinther): Find out why this can be null. + if (type == null) return; + if (outermostElement.isMember() && + compiler.backend.classNeedsRti(outermostElement.getEnclosingClass())) { + if (outermostElement.isConstructor() || + outermostElement.isField()) { + analyzeTypeVariables(type); + } else if (outermostElement.isInstanceMember()) { + if (type.containsTypeVariables) { + registerNeedsThis(); + } + } + } + } + + // If variables that are declared in the [node] scope are captured and need + // to be boxed create a box-element and update the [capturingScopes] in the + // current [closureData]. + // The boxed variables are updated in the [capturedVariableMapping]. + void attachCapturedScopeVariables(Node node) { + Element box = null; + Map scopeMapping = new Map(); + for (Element element in scopeVariables) { + // No need to box non-assignable elements. + if (!element.isAssignable()) continue; + if (!mutatedVariables.contains(element)) continue; + if (capturedVariableMapping.containsKey(element)) { + if (box == null) { + // TODO(floitsch): construct better box names. + String boxName = + namer.getClosureVariableName('box', closureFieldCounter++); + box = new BoxElement( + boxName, currentElement, compiler.types.dynamicType); + } + String elementName = element.name; + String boxedName = + namer.getClosureVariableName(elementName, boxedFieldCounter++); + // TODO(kasperl): Should this be a FieldElement instead? + Element boxed = new BoxFieldElement(boxedName, element, box); + // No need to rename the fields of a box, so we give them a native name + // right now. + boxed.setFixedBackendName(boxedName); + scopeMapping[element] = boxed; + capturedVariableMapping[element] = boxed; + } + } + if (!scopeMapping.isEmpty) { + ClosureScope scope = new ClosureScope(box, scopeMapping); + closureData.capturingScopes[node] = scope; + } + } + + void inNewScope(Node node, Function action) { + List oldScopeVariables = scopeVariables; + scopeVariables = new List(); + action(); + attachCapturedScopeVariables(node); + for (Element element in scopeVariables) { + mutatedVariables.remove(element); + } + scopeVariables = oldScopeVariables; + } + + visitLoop(Loop node) { + inNewScope(node, () { + node.visitChildren(this); + }); + } + + visitFor(For node) { + visitLoop(node); + // See if we have declared loop variables that need to be boxed. + if (node.initializer == null) return; + VariableDefinitions definitions = node.initializer.asVariableDefinitions(); + if (definitions == null) return; + ClosureScope scopeData = closureData.capturingScopes[node]; + if (scopeData == null) return; + List result = []; + for (Link link = definitions.definitions.nodes; + !link.isEmpty; + link = link.tail) { + Node definition = link.head; + Element element = elements[definition]; + if (capturedVariableMapping.containsKey(element)) { + result.add(element); + }; + } + scopeData.boxedLoopVariables = result; + } + + /** Returns a non-unique name for the given closure element. */ + String computeClosureName(Element element) { + Link parts = const Link(); + String ownName = element.name; + if (ownName == null || ownName == "") { + parts = parts.prepend("closure"); + } else { + parts = parts.prepend(ownName); + } + for (Element enclosingElement = element.enclosingElement; + enclosingElement != null && + (enclosingElement.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY + || enclosingElement.kind == ElementKind.GENERATIVE_CONSTRUCTOR + || enclosingElement.kind == ElementKind.CLASS + || enclosingElement.kind == ElementKind.FUNCTION + || enclosingElement.kind == ElementKind.GETTER + || enclosingElement.kind == ElementKind.SETTER); + enclosingElement = enclosingElement.enclosingElement) { + // TODO(johnniwinther): Simplify computed names. + if (enclosingElement.isGenerativeConstructor() || + enclosingElement.isGenerativeConstructorBody() || + enclosingElement.isFactoryConstructor()) { + parts = parts.prepend( + Elements.reconstructConstructorName(enclosingElement)); + } else { + String surroundingName = + Elements.operatorNameToIdentifier(enclosingElement.name); + parts = parts.prepend(surroundingName); + } + // A generative constructors's parent is the class; the class name is + // already part of the generative constructor's name. + if (enclosingElement.kind == ElementKind.GENERATIVE_CONSTRUCTOR) break; + } + StringBuffer sb = new StringBuffer(); + parts.printOn(sb, '_'); + return sb.toString(); + } + + ClosureClassMap globalizeClosure(FunctionExpression node, + TypedElement element) { + String closureName = computeClosureName(element); + ClassElement globalizedElement = new ClosureClassElement( + node, closureName, compiler, element, element.getCompilationUnit()); + FunctionElement callElement = + new FunctionElementX.from(Compiler.CALL_OPERATOR_NAME, + element, + globalizedElement); + ClosureContainer enclosing = element.enclosingElement; + enclosing.nestedClosures.add(callElement); + globalizedElement.addMember(callElement, compiler); + // The nested function's 'this' is the same as the one for the outer + // function. It could be [null] if we are inside a static method. + Element thisElement = closureData.thisElement; + return new ClosureClassMap(element, globalizedElement, + callElement, thisElement); + } + + void visitInvokable(TypedElement element, Node node, void visitChildren()) { + bool oldInsideClosure = insideClosure; + Element oldFunctionElement = currentElement; + ClosureClassMap oldClosureData = closureData; + + insideClosure = outermostElement != null; + currentElement = element; + if (insideClosure) { + closures.add(node); + closureData = globalizeClosure(node, element); + } else { + outermostElement = element; + Element thisElement = null; + if (element.isInstanceMember() || element.isGenerativeConstructor()) { + thisElement = new ThisElement(element, compiler.types.dynamicType); + } + closureData = new ClosureClassMap(null, null, null, thisElement); + } + closureMappingCache[node] = closureData; + + inNewScope(node, () { + // We have to declare the implicit 'this' parameter. + if (!insideClosure && closureData.thisElement != null) { + declareLocal(closureData.thisElement); + } + // If we are inside a named closure we have to declare ourselve. For + // simplicity we declare the local even if the closure does not have a + // name. + // It will simply not be used. + if (insideClosure) { + declareLocal(element); + } + + if (currentElement.isFactoryConstructor() && + compiler.backend.classNeedsRti(currentElement.enclosingElement)) { + // Declare the type parameters in the scope. Generative + // constructors just use 'this'. + ClassElement cls = currentElement.enclosingElement; + cls.typeVariables.forEach((TypeVariableType typeVariable) { + declareLocal(typeVariable.element); + }); + } + + DartType type = element.computeType(compiler); + // If the method needs RTI, or checked mode is set, we need to + // escape the potential type variables used in that closure. + if (element is FunctionElement + && (compiler.backend.methodNeedsRti(element) || + compiler.enableTypeAssertions)) { + analyzeTypeVariables(type); + } + + visitChildren(); + }); + + + ClosureClassMap savedClosureData = closureData; + bool savedInsideClosure = insideClosure; + + // Restore old values. + insideClosure = oldInsideClosure; + closureData = oldClosureData; + currentElement = oldFunctionElement; + + // Mark all free variables as captured and use them in the outer function. + Iterable freeVariables = savedClosureData.freeVariableMapping.keys; + assert(freeVariables.isEmpty || savedInsideClosure); + for (Element freeElement in freeVariables) { + if (capturedVariableMapping[freeElement] != null && + capturedVariableMapping[freeElement] != freeElement) { + compiler.internalError(node, 'In closure analyzer.'); + } + capturedVariableMapping[freeElement] = freeElement; + useLocal(freeElement); + } + } + + visitFunctionExpression(FunctionExpression node) { + Element element = elements[node]; + + if (element.isParameter()) { + // TODO(ahe): This is a hack. This method should *not* call + // visitChildren. + return node.name.accept(this); + } + + visitInvokable(element, node, () { + // TODO(ahe): This is problematic. The backend should not repeat + // the work of the resolver. It is the resolver's job to create + // parameters, etc. Other phases should only visit statements. + if (node.parameters != null) node.parameters.accept(this); + if (node.initializers != null) node.initializers.accept(this); + if (node.body != null) node.body.accept(this); + }); + } + + visitFunctionDeclaration(FunctionDeclaration node) { + node.visitChildren(this); + declareLocal(elements[node]); + } + + visitTryStatement(TryStatement node) { + // TODO(ngeoffray): implement finer grain state. + bool oldInTryStatement = inTryStatement; + inTryStatement = true; + node.visitChildren(this); + inTryStatement = oldInTryStatement; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/code_buffer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/code_buffer.dart new file mode 100644 index 0000000..4662eb7 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/code_buffer.dart @@ -0,0 +1,126 @@ +// Copyright (c) 2012, 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. + +part of dart2js; + +class CodeBuffer implements StringBuffer { + + StringBuffer buffer = new StringBuffer(); + List markers = new List(); + + int lastBufferOffset = 0; + int mappedRangeCounter = 0; + + CodeBuffer(); + + int get length => buffer.length; + bool get isEmpty => buffer.isEmpty; + bool get isNotEmpty => buffer.isNotEmpty; + + CodeBuffer add(var object) { + write(object); + return this; + } + /** + * Converts [object] to a string and adds it to the buffer. If [object] is a + * [CodeBuffer], adds its markers to [markers]. + */ + CodeBuffer write(var object) { + if (object is CodeBuffer) { + return addBuffer(object); + } + if (mappedRangeCounter == 0) setSourceLocation(null); + buffer.write(object); + return this; + } + + CodeBuffer writeAll(Iterable objects, [String separator = ""]) { + Iterator iterator = objects.iterator; + if (!iterator.moveNext()) return this; + if (separator.isEmpty) { + do { + write(iterator.current); + } while (iterator.moveNext()); + } else { + write(iterator.current); + while (iterator.moveNext()) { + write(separator); + write(iterator.current); + } + } + return this; + } + + CodeBuffer writeln([var object = ""]) { + return write(object).write("\n"); + } + + CodeBuffer addBuffer(CodeBuffer other) { + if (other.markers.length > 0) { + CodeBufferMarker firstMarker = other.markers[0]; + int offsetDelta = + buffer.length + firstMarker.offsetDelta - lastBufferOffset; + markers.add(new CodeBufferMarker(offsetDelta, + firstMarker.sourcePosition)); + for (int i = 1; i < other.markers.length; ++i) { + markers.add(other.markers[i]); + } + lastBufferOffset = buffer.length + other.lastBufferOffset; + } + buffer.write(other.getText()); + return this; + } + + CodeBuffer addAll(Iterable iterable) => writeAll(iterable); + + CodeBuffer writeCharCode(int charCode) { + buffer.writeCharCode(charCode); + return this; + } + + CodeBuffer clear() { + buffer = new StringBuffer(); + markers.clear(); + lastBufferOffset = 0; + return this; + } + + String toString() { + throw "Don't use CodeBuffer.toString() since it drops sourcemap data."; + } + + String getText() { + return buffer.toString(); + } + + void beginMappedRange() { + ++mappedRangeCounter; + } + + void endMappedRange() { + assert(mappedRangeCounter > 0); + --mappedRangeCounter; + } + + void setSourceLocation(var sourcePosition) { + int offsetDelta = buffer.length - lastBufferOffset; + markers.add(new CodeBufferMarker(offsetDelta, sourcePosition)); + lastBufferOffset = buffer.length; + } + + void forEachSourceLocation(void f(int targetOffset, var sourcePosition)) { + int targetOffset = 0; + markers.forEach((marker) { + targetOffset += marker.offsetDelta; + f(targetOffset, marker.sourcePosition); + }); + } +} + +class CodeBufferMarker { + final int offsetDelta; + final sourcePosition; + + CodeBufferMarker(this.offsetDelta, this.sourcePosition); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/colors.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/colors.dart new file mode 100644 index 0000000..0c10085 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/colors.dart @@ -0,0 +1,29 @@ +// Copyright (c) 2012, 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. + +library colors; + +// See http://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes +const String RESET = '\u001b[0m'; + +// See http://en.wikipedia.org/wiki/ANSI_escape_code#Colors +const String BLACK_COLOR = '\u001b[30m'; +const String RED_COLOR = '\u001b[31m'; +const String GREEN_COLOR = '\u001b[32m'; +const String YELLOW_COLOR = '\u001b[33m'; +const String BLUE_COLOR = '\u001b[34m'; +const String MAGENTA_COLOR = '\u001b[35m'; +const String CYAN_COLOR = '\u001b[36m'; +const String WHITE_COLOR = '\u001b[37m'; + +String wrap(String string, String color) => "${color}$string${RESET}"; + +String black(String string) => wrap(string, BLACK_COLOR); +String red(String string) => wrap(string, RED_COLOR); +String green(String string) => wrap(string, GREEN_COLOR); +String yellow(String string) => wrap(string, YELLOW_COLOR); +String blue(String string) => wrap(string, BLUE_COLOR); +String magenta(String string) => wrap(string, MAGENTA_COLOR); +String cyan(String string) => wrap(string, CYAN_COLOR); +String white(String string) => wrap(string, WHITE_COLOR); diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/common.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/common.dart new file mode 100644 index 0000000..2826d9e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/common.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2013, 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. + +library dart2js.common; + +export 'dart2jslib.dart' show + CompilerTask, + Compiler, + Constant, + ConstantHandler, + InterceptorConstant, + MessageKind, + NullConstant, + Selector, + TreeElements, + TypeConstant, + invariant; + +export 'dart_types.dart' show + DartType, + FunctionType, + InterfaceType, + TypeVariableType, + Types; + +export 'elements/elements.dart' show + ClassElement, + ClosureFieldElement, + Element, + Elements, + FunctionElement, + FunctionSignature, + LibraryElement, + MetadataAnnotation, + MixinApplicationElement, + TypedefElement, + VariableElement; + +export 'tree/tree.dart' show + Node; + +export 'types/types.dart' show + TypeMask; + +export 'universe/universe.dart' show + SelectorKind; + +export 'util/util.dart' show + Link, + SpannableAssertionFailure; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/compile_time_constants.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/compile_time_constants.dart new file mode 100644 index 0000000..121fe5b --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/compile_time_constants.dart @@ -0,0 +1,1010 @@ +// Copyright (c) 2012, 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. + +part of dart2js; + +/** + * The [ConstantHandler] keeps track of compile-time constants, + * initializations of global and static fields, and default values of + * optional parameters. + */ +class ConstantHandler extends CompilerTask { + final ConstantSystem constantSystem; + final bool isMetadata; + + /** + * Contains the initial value of fields. Must contain all static and global + * initializations of const fields. May contain eagerly compiled values for + * statics and instance fields. + * + * Invariant: The keys in this map are declarations. + */ + final Map initialVariableValues; + + /** Set of all registered compiled constants. */ + final Set compiledConstants; + + /** The set of variable elements that are in the process of being computed. */ + final Set pendingVariables; + + /** Caches the statics where the initial value cannot be eagerly compiled. */ + final Set lazyStatics; + + ConstantHandler(Compiler compiler, this.constantSystem, + { bool this.isMetadata: false }) + : initialVariableValues = new Map(), + compiledConstants = new Set(), + pendingVariables = new Set(), + lazyStatics = new Set(), + super(compiler); + + String get name => 'ConstantHandler'; + + void addCompileTimeConstantForEmission(Constant constant) { + compiledConstants.add(constant); + } + + Constant getConstantForVariable(VariableElement element) { + return initialVariableValues[element.declaration]; + } + + /** + * Returns a compile-time constant, or reports an error if the element is not + * a compile-time constant. + */ + Constant compileConstant(VariableElement element) { + return compileVariable(element, isConst: true); + } + + /** + * Returns the a compile-time constant if the variable could be compiled + * eagerly. Otherwise returns `null`. + */ + Constant compileVariable(VariableElement element, {bool isConst: false}) { + return measure(() { + if (initialVariableValues.containsKey(element.declaration)) { + Constant result = initialVariableValues[element.declaration]; + return result; + } + Element currentElement = element; + if (element.isParameter() + || element.isFieldParameter() + || element.isVariable()) { + currentElement = element.enclosingElement; + } + return compiler.withCurrentElement(currentElement, () { + TreeElements definitions = + compiler.analyzeElement(currentElement.declaration); + Constant constant = compileVariableWithDefinitions( + element, definitions, isConst: isConst); + return constant; + }); + }); + } + + /** + * Returns the a compile-time constant if the variable could be compiled + * eagerly. If the variable needs to be initialized lazily returns `null`. + * If the variable is `const` but cannot be compiled eagerly reports an + * error. + */ + Constant compileVariableWithDefinitions(VariableElement element, + TreeElements definitions, + {bool isConst: false}) { + return measure(() { + if (!isConst && lazyStatics.contains(element)) return null; + + Node node = element.parseNode(compiler); + if (pendingVariables.contains(element)) { + if (isConst) { + compiler.reportFatalError( + node, MessageKind.CYCLIC_COMPILE_TIME_CONSTANTS); + } else { + lazyStatics.add(element); + return null; + } + } + pendingVariables.add(element); + + Expression initializer = element.initializer; + Constant value; + if (initializer == null) { + // No initial value. + value = new NullConstant(); + } else { + value = compileNodeWithDefinitions( + initializer, definitions, isConst: isConst); + if (compiler.enableTypeAssertions && + value != null && + element.isField()) { + DartType elementType = element.type; + if (elementType.kind == TypeKind.MALFORMED_TYPE && !value.isNull) { + if (isConst) { + ErroneousElement element = elementType.element; + compiler.reportFatalError( + node, element.messageKind, element.messageArguments); + } else { + // We need to throw an exception at runtime. + value = null; + } + } else { + DartType constantType = value.computeType(compiler); + if (!constantSystem.isSubtype(compiler, + constantType, elementType)) { + if (isConst) { + compiler.reportFatalError( + node, MessageKind.NOT_ASSIGNABLE, + {'fromType': constantType, 'toType': elementType}); + } else { + // If the field cannot be lazily initialized, we will throw + // the exception at runtime. + value = null; + } + } + } + } + } + if (value != null) { + initialVariableValues[element.declaration] = value; + } else { + assert(!isConst); + lazyStatics.add(element); + } + pendingVariables.remove(element); + return value; + }); + } + + Constant compileNodeWithDefinitions(Node node, + TreeElements definitions, + {bool isConst: false}) { + return measure(() { + assert(node != null); + Constant constant = definitions.getConstant(node); + if (constant != null) { + return constant; + } + CompileTimeConstantEvaluator evaluator = new CompileTimeConstantEvaluator( + this, definitions, compiler, isConst: isConst); + constant = evaluator.evaluate(node); + if (constant != null) { + definitions.setConstant(node, constant); + } + return constant; + }); + } + + /** + * Returns an [Iterable] of static non final fields that need to be + * initialized. The fields list must be evaluated in order since they might + * depend on each other. + */ + Iterable getStaticNonFinalFieldsForEmission() { + return initialVariableValues.keys.where((element) { + return element.kind == ElementKind.FIELD + && !element.isInstanceMember() + && !element.modifiers.isFinal() + // The const fields are all either emitted elsewhere or inlined. + && !element.modifiers.isConst(); + }); + } + + List getLazilyInitializedFieldsForEmission() { + return new List.from(lazyStatics); + } + + /** + * Returns a list of constants topologically sorted so that dependencies + * appear before the dependent constant. [preSortCompare] is a comparator + * function that gives the constants a consistent order prior to the + * topological sort which gives the constants an ordering that is less + * sensitive to perturbations in the source code. + */ + List getConstantsForEmission([preSortCompare]) { + // We must emit dependencies before their uses. + Set seenConstants = new Set(); + List result = new List(); + + void addConstant(Constant constant) { + if (!seenConstants.contains(constant)) { + constant.getDependencies().forEach(addConstant); + assert(!seenConstants.contains(constant)); + result.add(constant); + seenConstants.add(constant); + } + } + + List sorted = compiledConstants.toList(); + if (preSortCompare != null) { + sorted.sort(preSortCompare); + } + sorted.forEach(addConstant); + return result; + } + + Constant getInitialValueFor(VariableElement element) { + Constant initialValue = initialVariableValues[element.declaration]; + if (initialValue == null) { + compiler.internalError(element, "No initial value for given element."); + } + return initialValue; + } +} + +class CompileTimeConstantEvaluator extends Visitor { + bool isEvaluatingConstant; + final ConstantHandler handler; + final TreeElements elements; + final Compiler compiler; + + CompileTimeConstantEvaluator(this.handler, + this.elements, + this.compiler, + {bool isConst: false}) + : this.isEvaluatingConstant = isConst; + + ConstantSystem get constantSystem => handler.constantSystem; + + Constant evaluate(Node node) { + return node.accept(this); + } + + Constant evaluateConstant(Node node) { + bool oldIsEvaluatingConstant = isEvaluatingConstant; + isEvaluatingConstant = true; + Constant result = node.accept(this); + isEvaluatingConstant = oldIsEvaluatingConstant; + assert(result != null); + return result; + } + + Constant visitNode(Node node) { + return signalNotCompileTimeConstant(node); + } + + Constant visitLiteralBool(LiteralBool node) { + return constantSystem.createBool(node.value); + } + + Constant visitLiteralDouble(LiteralDouble node) { + return constantSystem.createDouble(node.value); + } + + Constant visitLiteralInt(LiteralInt node) { + return constantSystem.createInt(node.value); + } + + Constant visitLiteralList(LiteralList node) { + if (!node.isConst()) { + return signalNotCompileTimeConstant(node); + } + List arguments = []; + for (Link link = node.elements.nodes; + !link.isEmpty; + link = link.tail) { + arguments.add(evaluateConstant(link.head)); + } + DartType type = elements.getType(node); + return new ListConstant(type, arguments); + } + + Constant visitLiteralMap(LiteralMap node) { + if (!node.isConst()) { + return signalNotCompileTimeConstant(node); + } + List keys = []; + Map map = new Map(); + for (Link link = node.entries.nodes; + !link.isEmpty; + link = link.tail) { + LiteralMapEntry entry = link.head; + Constant key = evaluateConstant(entry.key); + if (!map.containsKey(key)) { + keys.add(key); + } else { + compiler.reportWarning(entry.key, MessageKind.EQUAL_MAP_ENTRY_KEY); + } + map[key] = evaluateConstant(entry.value); + } + + bool onlyStringKeys = true; + Constant protoValue = null; + for (var key in keys) { + if (key.isString) { + if (key.value == MapConstant.PROTO_PROPERTY) { + protoValue = map[key]; + } + } else { + onlyStringKeys = false; + // Don't handle __proto__ values specially in the general map case. + protoValue = null; + break; + } + } + + bool hasProtoKey = (protoValue != null); + List values = map.values.toList(); + InterfaceType sourceType = elements.getType(node); + DartType keysType; + if (sourceType.treatAsRaw) { + keysType = compiler.listClass.rawType; + } else { + Link arguments = + new Link.fromList([sourceType.typeArguments.head]); + keysType = new InterfaceType(compiler.listClass, arguments); + } + ListConstant keysList = new ListConstant(keysType, keys); + String className = onlyStringKeys + ? (hasProtoKey ? MapConstant.DART_PROTO_CLASS + : MapConstant.DART_STRING_CLASS) + : MapConstant.DART_GENERAL_CLASS; + ClassElement classElement = compiler.jsHelperLibrary.find(className); + classElement.ensureResolved(compiler); + Link typeArgument = sourceType.typeArguments; + InterfaceType type; + if (sourceType.treatAsRaw) { + type = classElement.rawType; + } else { + type = new InterfaceType(classElement, typeArgument); + } + return new MapConstant(type, keysList, values, protoValue, onlyStringKeys); + } + + Constant visitLiteralNull(LiteralNull node) { + return constantSystem.createNull(); + } + + Constant visitLiteralString(LiteralString node) { + return constantSystem.createString(node.dartString); + } + + Constant visitStringJuxtaposition(StringJuxtaposition node) { + StringConstant left = evaluate(node.first); + StringConstant right = evaluate(node.second); + if (left == null || right == null) return null; + return constantSystem.createString( + new DartString.concat(left.value, right.value)); + } + + Constant visitStringInterpolation(StringInterpolation node) { + StringConstant initialString = evaluate(node.string); + if (initialString == null) return null; + DartString accumulator = initialString.value; + for (StringInterpolationPart part in node.parts) { + Constant expression = evaluate(part.expression); + DartString expressionString; + if (expression == null) { + return signalNotCompileTimeConstant(part.expression); + } else if (expression.isNum || expression.isBool) { + PrimitiveConstant primitive = expression; + expressionString = new DartString.literal(primitive.value.toString()); + } else if (expression.isString) { + PrimitiveConstant primitive = expression; + expressionString = primitive.value; + } else { + return signalNotCompileTimeConstant(part.expression); + } + accumulator = new DartString.concat(accumulator, expressionString); + StringConstant partString = evaluate(part.string); + if (partString == null) return null; + accumulator = new DartString.concat(accumulator, partString.value); + }; + return constantSystem.createString(accumulator); + } + + Constant visitLiteralSymbol(LiteralSymbol node) { + InterfaceType type = compiler.symbolClass.rawType; + List createArguments(_) { + return [constantSystem.createString( + new DartString.literal(node.slowNameString))]; + } + return makeConstructedConstant( + node, type, compiler.symbolConstructor, createArguments); + } + + Constant makeTypeConstant(TypeDeclarationElement element) { + DartType elementType = element.rawType; + DartType constantType = + compiler.backend.typeImplementation.computeType(compiler); + return new TypeConstant(elementType, constantType); + } + + /// Returns true if the prefix of the send resolves to a deferred import + /// prefix. + bool isDeferredUse(Send send) { + // We handle: + // 1. Send(Send(Send(null, Prefix), className), staticName) + // 2. Send(Send(Prefix, Classname), staticName) + // 3. Send(Send(null, Prefix), staticName | className) + // 4. Send(Send(Prefix), staticName | className) + // Nodes of the forms 2,4 occur in metadata. + if (send == null) return false; + while (send.receiver is Send) { + send = send.receiver; + } + Identifier prefixNode; + if (send.receiver is Identifier) { + prefixNode = send.receiver; + } else if (send.receiver == null && + send.selector is Identifier) { + prefixNode = send.selector; + } + if (prefixNode != null) { + Element maybePrefix = elements[prefixNode.asIdentifier()]; + if (maybePrefix != null && maybePrefix.isPrefix() && + (maybePrefix as PrefixElement).isDeferred) { + return true; + } + } + return false; + } + + // TODO(floitsch): provide better error-messages. + Constant visitSend(Send send) { + Element element = elements[send]; + if (send.isPropertyAccess) { + if (isDeferredUse(send)) { + return signalNotCompileTimeConstant(send, + message: MessageKind.DEFERRED_COMPILE_TIME_CONSTANT); + } + if (Elements.isStaticOrTopLevelFunction(element)) { + return new FunctionConstant(element); + } else if (Elements.isStaticOrTopLevelField(element)) { + Constant result; + if (element.modifiers.isConst()) { + result = handler.compileConstant(element); + } else if (element.modifiers.isFinal() && !isEvaluatingConstant) { + result = handler.compileVariable(element); + } + if (result != null) return result; + } else if (Elements.isClass(element) || Elements.isTypedef(element)) { + assert(elements.isTypeLiteral(send)); + return makeTypeConstant(element); + } else if (send.receiver != null) { + // Fall through to error handling. + } else if (!Elements.isUnresolved(element) + && element.isVariable() + && element.modifiers.isConst()) { + Constant result = handler.compileConstant(element); + if (result != null) return result; + } + return signalNotCompileTimeConstant(send); + } else if (send.isCall) { + if (identical(element, compiler.identicalFunction) + && send.argumentCount() == 2) { + Constant left = evaluate(send.argumentsNode.nodes.head); + Constant right = evaluate(send.argumentsNode.nodes.tail.head); + Constant result = constantSystem.identity.fold(left, right); + if (result != null) return result; + } else if (Elements.isClass(element) || Elements.isTypedef(element)) { + // The node itself is not a constant but we register the selector (the + // identifier that refers to the class/typedef) as a constant. + Constant typeConstant = makeTypeConstant(element); + elements.setConstant(send.selector, typeConstant); + } + return signalNotCompileTimeConstant(send); + } else if (send.isPrefix) { + assert(send.isOperator); + Constant receiverConstant = evaluate(send.receiver); + if (receiverConstant == null) return null; + Operator op = send.selector; + Constant folded; + switch (op.source) { + case "!": + folded = constantSystem.not.fold(receiverConstant); + break; + case "-": + folded = constantSystem.negate.fold(receiverConstant); + break; + case "~": + folded = constantSystem.bitNot.fold(receiverConstant); + break; + default: + compiler.internalError(op, "Unexpected operator."); + break; + } + if (folded == null) return signalNotCompileTimeConstant(send); + return folded; + } else if (send.isOperator && !send.isPostfix) { + assert(send.argumentCount() == 1); + Constant left = evaluate(send.receiver); + Constant right = evaluate(send.argumentsNode.nodes.head); + if (left == null || right == null) return null; + Operator op = send.selector.asOperator(); + Constant folded = null; + switch (op.source) { + case "+": + folded = constantSystem.add.fold(left, right); + break; + case "-": + folded = constantSystem.subtract.fold(left, right); + break; + case "*": + folded = constantSystem.multiply.fold(left, right); + break; + case "/": + folded = constantSystem.divide.fold(left, right); + break; + case "%": + folded = constantSystem.modulo.fold(left, right); + break; + case "~/": + folded = constantSystem.truncatingDivide.fold(left, right); + break; + case "|": + folded = constantSystem.bitOr.fold(left, right); + break; + case "&": + folded = constantSystem.bitAnd.fold(left, right); + break; + case "^": + folded = constantSystem.bitXor.fold(left, right); + break; + case "||": + folded = constantSystem.booleanOr.fold(left, right); + break; + case "&&": + folded = constantSystem.booleanAnd.fold(left, right); + break; + case "<<": + folded = constantSystem.shiftLeft.fold(left, right); + break; + case ">>": + folded = constantSystem.shiftRight.fold(left, right); + break; + case "<": + folded = constantSystem.less.fold(left, right); + break; + case "<=": + folded = constantSystem.lessEqual.fold(left, right); + break; + case ">": + folded = constantSystem.greater.fold(left, right); + break; + case ">=": + folded = constantSystem.greaterEqual.fold(left, right); + break; + case "==": + if (left.isPrimitive && right.isPrimitive) { + folded = constantSystem.equal.fold(left, right); + } + break; + case "===": + folded = constantSystem.identity.fold(left, right); + break; + case "!=": + if (left.isPrimitive && right.isPrimitive) { + BoolConstant areEquals = constantSystem.equal.fold(left, right); + if (areEquals == null) { + folded = null; + } else { + folded = areEquals.negate(); + } + } + break; + case "!==": + BoolConstant areIdentical = + constantSystem.identity.fold(left, right); + if (areIdentical == null) { + folded = null; + } else { + folded = areIdentical.negate(); + } + break; + } + if (folded == null) return signalNotCompileTimeConstant(send); + return folded; + } + return signalNotCompileTimeConstant(send); + } + + Constant visitConditional(Conditional node) { + Constant condition = evaluate(node.condition); + if (condition == null) { + return null; + } else if (!condition.isBool) { + DartType conditionType = condition.computeType(compiler); + if (isEvaluatingConstant) { + compiler.reportFatalError( + node.condition, MessageKind.NOT_ASSIGNABLE, + {'fromType': conditionType, 'toType': compiler.boolClass.rawType}); + } + return null; + } + Constant thenExpression = evaluate(node.thenExpression); + Constant elseExpression = evaluate(node.elseExpression); + BoolConstant boolCondition = condition; + return boolCondition.value ? thenExpression : elseExpression; + } + + Constant visitSendSet(SendSet node) { + return signalNotCompileTimeConstant(node); + } + + /** + * Returns the list of constants that are passed to the static function. + * + * Invariant: [target] must be an implementation element. + */ + List evaluateArgumentsToConstructor(Node node, + Selector selector, + Link arguments, + FunctionElement target) { + assert(invariant(node, target.isImplementation)); + List compiledArguments = []; + + Function compileArgument = evaluateConstant; + Function compileConstant = handler.compileConstant; + bool succeeded = selector.addArgumentsToList(arguments, + compiledArguments, + target, + compileArgument, + compileConstant, + compiler); + if (!succeeded) { + compiler.reportFatalError( + node, + MessageKind.INVALID_ARGUMENTS, {'methodName': target.name}); + } + return compiledArguments; + } + + Constant visitNewExpression(NewExpression node) { + if (!node.isConst()) { + return signalNotCompileTimeConstant(node); + } + + Send send = node.send; + FunctionElement constructor = elements[send]; + if (Elements.isUnresolved(constructor)) { + return signalNotCompileTimeConstant(node); + } + + // Deferred types can not be used in const instance creation expressions. + // Check if the constructor comes from a deferred library. + if (isDeferredUse(node.send.selector.asSend())) { + return signalNotCompileTimeConstant(node, + message: MessageKind.DEFERRED_COMPILE_TIME_CONSTANT_CONSTRUCTION); + } + + // TODO(ahe): This is nasty: we must eagerly analyze the + // constructor to ensure the redirectionTarget has been computed + // correctly. Find a way to avoid this. + compiler.analyzeElement(constructor.declaration); + + InterfaceType type = elements.getType(node); + List evaluateArguments(FunctionElement constructor) { + Selector selector = elements.getSelector(send); + return evaluateArgumentsToConstructor( + node, selector, send.arguments, constructor); + } + + if (constructor == compiler.intEnvironment + || constructor == compiler.boolEnvironment + || constructor == compiler.stringEnvironment) { + List arguments = evaluateArguments(constructor.implementation); + var firstArgument = arguments[0]; + Constant defaultValue = arguments[1]; + + if (firstArgument is NullConstant) { + compiler.reportFatalError( + send.arguments.head, MessageKind.NULL_NOT_ALLOWED); + } + + if (firstArgument is! StringConstant) { + DartType type = defaultValue.computeType(compiler); + compiler.reportFatalError( + send.arguments.head, MessageKind.NOT_ASSIGNABLE, + {'fromType': type, 'toType': compiler.stringClass.rawType}); + } + + if (constructor == compiler.intEnvironment + && !(defaultValue is NullConstant || defaultValue is IntConstant)) { + DartType type = defaultValue.computeType(compiler); + compiler.reportFatalError( + send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, + {'fromType': type, 'toType': compiler.intClass.rawType}); + } + + if (constructor == compiler.boolEnvironment + && !(defaultValue is NullConstant || defaultValue is BoolConstant)) { + DartType type = defaultValue.computeType(compiler); + compiler.reportFatalError( + send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, + {'fromType': type, 'toType': compiler.boolClass.rawType}); + } + + if (constructor == compiler.stringEnvironment + && !(defaultValue is NullConstant + || defaultValue is StringConstant)) { + DartType type = defaultValue.computeType(compiler); + compiler.reportFatalError( + send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, + {'fromType': type, 'toType': compiler.stringClass.rawType}); + } + + String value = + compiler.fromEnvironment(firstArgument.value.slowToString()); + + if (value == null) { + return defaultValue; + } else if (constructor == compiler.intEnvironment) { + int number = int.parse(value, onError: (_) => null); + return (number == null) + ? defaultValue + : constantSystem.createInt(number); + } else if (constructor == compiler.boolEnvironment) { + if (value == 'true') { + return constantSystem.createBool(true); + } else if (value == 'false') { + return constantSystem.createBool(false); + } else { + return defaultValue; + } + } else { + assert(constructor == compiler.stringEnvironment); + return constantSystem.createString(new DartString.literal(value)); + } + } else { + return makeConstructedConstant( + node, type, constructor, evaluateArguments); + } + } + + Constant makeConstructedConstant( + Spannable node, InterfaceType type, FunctionElement constructor, + List getArguments(FunctionElement constructor)) { + // The redirection chain of this element may not have been resolved through + // a post-process action, so we have to make sure it is done here. + compiler.resolver.resolveRedirectionChain(constructor, node); + InterfaceType constructedType = constructor.computeTargetType(type); + constructor = constructor.redirectionTarget; + ClassElement classElement = constructor.getEnclosingClass(); + // The constructor must be an implementation to ensure that field + // initializers are handled correctly. + constructor = constructor.implementation; + assert(invariant(node, constructor.isImplementation)); + + List arguments = getArguments(constructor); + ConstructorEvaluator evaluator = + new ConstructorEvaluator(constructor, handler, compiler); + evaluator.evaluateConstructorFieldValues(arguments); + List jsNewArguments = evaluator.buildJsNewArguments(classElement); + + return new ConstructedConstant(constructedType, jsNewArguments); + } + + Constant visitParenthesizedExpression(ParenthesizedExpression node) { + return node.expression.accept(this); + } + + error(Node node, MessageKind message) { + // TODO(floitsch): get the list of constants that are currently compiled + // and present some kind of stack-trace. + compiler.reportFatalError(node, message); + } + + Constant signalNotCompileTimeConstant(Node node, + {MessageKind message: MessageKind.NOT_A_COMPILE_TIME_CONSTANT}) { + if (isEvaluatingConstant) { + error(node, message); + } + // Else we don't need to do anything. The final handler is only + // optimistically trying to compile constants. So it is normal that we + // sometimes see non-compile time constants. + // Simply return [:null:] which is used to propagate a failing + // compile-time compilation. + return null; + } +} + +class TryCompileTimeConstantEvaluator extends CompileTimeConstantEvaluator { + TryCompileTimeConstantEvaluator(ConstantHandler handler, + TreeElements elements, + Compiler compiler) + : super(handler, elements, compiler, isConst: true); + + error(Node node, MessageKind message) { + // Just fail without reporting it anywhere. + throw new CompileTimeConstantError( + message, const {}, compiler.terseDiagnostics); + } +} + +class CompileTimeConstantError { + final Message message; + CompileTimeConstantError(MessageKind kind, Map arguments, bool terse) + : message = new Message(kind, arguments, terse); + String toString() => message.toString(); +} + +class ConstructorEvaluator extends CompileTimeConstantEvaluator { + final FunctionElement constructor; + final Map definitions; + final Map fieldValues; + + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [constructor] must be an implementation element. + */ + ConstructorEvaluator(FunctionElement constructor, + ConstantHandler handler, + Compiler compiler) + : this.constructor = constructor, + this.definitions = new Map(), + this.fieldValues = new Map(), + super(handler, + compiler.resolver.resolveMethodElement(constructor.declaration), + compiler, + isConst: true) { + assert(invariant(constructor, constructor.isImplementation)); + } + + Constant visitSend(Send send) { + Element element = elements[send]; + if (Elements.isLocal(element)) { + Constant constant = definitions[element]; + if (constant == null) { + compiler.internalError(send, "Local variable without value."); + } + return constant; + } + return super.visitSend(send); + } + + void potentiallyCheckType(Node node, + TypedElement element, + Constant constant) { + if (compiler.enableTypeAssertions) { + DartType elementType = element.type; + DartType constantType = constant.computeType(compiler); + // TODO(ngeoffray): Handle type parameters. + if (elementType.element.isTypeVariable()) return; + if (!constantSystem.isSubtype(compiler, constantType, elementType)) { + compiler.reportFatalError( + node, MessageKind.NOT_ASSIGNABLE, + {'fromType': elementType, 'toType': constantType}); + } + } + } + + void updateFieldValue(Node node, TypedElement element, Constant constant) { + potentiallyCheckType(node, element, constant); + fieldValues[element] = constant; + } + + /** + * Given the arguments (a list of constants) assigns them to the parameters, + * updating the definitions map. If the constructor has field-initializer + * parameters (like [:this.x:]), also updates the [fieldValues] map. + */ + void assignArgumentsToParameters(List arguments) { + // Assign arguments to parameters. + FunctionSignature parameters = constructor.functionSignature; + int index = 0; + parameters.orderedForEachParameter((Element parameter) { + Constant argument = arguments[index++]; + Node node = parameter.parseNode(compiler); + potentiallyCheckType(node, parameter, argument); + definitions[parameter] = argument; + if (parameter.kind == ElementKind.FIELD_PARAMETER) { + FieldParameterElement fieldParameterElement = parameter; + updateFieldValue(node, fieldParameterElement.fieldElement, argument); + } + }); + } + + void evaluateSuperOrRedirectSend(List compiledArguments, + FunctionElement targetConstructor) { + ConstructorEvaluator evaluator = new ConstructorEvaluator( + targetConstructor, handler, compiler); + evaluator.evaluateConstructorFieldValues(compiledArguments); + // Copy over the fieldValues from the super/redirect-constructor. + // No need to go through [updateFieldValue] because the + // assignments have already been checked in checked mode. + evaluator.fieldValues.forEach((key, value) => fieldValues[key] = value); + } + + /** + * Runs through the initializers of the given [constructor] and updates + * the [fieldValues] map. + */ + void evaluateConstructorInitializers() { + if (constructor.isSynthesized) { + List compiledArguments = []; + + Function compileArgument = (element) => definitions[element]; + Function compileConstant = handler.compileConstant; + FunctionElement target = constructor.targetConstructor.implementation; + Selector.addForwardingElementArgumentsToList(constructor, + compiledArguments, + target, + compileArgument, + compileConstant, + compiler); + evaluateSuperOrRedirectSend(compiledArguments, target); + return; + } + FunctionExpression functionNode = constructor.parseNode(compiler); + NodeList initializerList = functionNode.initializers; + + bool foundSuperOrRedirect = false; + + if (initializerList != null) { + for (Link link = initializerList.nodes; + !link.isEmpty; + link = link.tail) { + assert(link.head is Send); + if (link.head is !SendSet) { + // A super initializer or constructor redirection. + Send call = link.head; + FunctionElement target = elements[call]; + List compiledArguments = evaluateArgumentsToConstructor( + call, elements.getSelector(call), call.arguments, target); + evaluateSuperOrRedirectSend(compiledArguments, target); + foundSuperOrRedirect = true; + } else { + // A field initializer. + SendSet init = link.head; + Link initArguments = init.arguments; + assert(!initArguments.isEmpty && initArguments.tail.isEmpty); + Constant fieldValue = evaluate(initArguments.head); + updateFieldValue(init, elements[init], fieldValue); + } + } + } + + if (!foundSuperOrRedirect) { + // No super initializer found. Try to find the default constructor if + // the class is not Object. + ClassElement enclosingClass = constructor.getEnclosingClass(); + ClassElement superClass = enclosingClass.superclass; + if (enclosingClass != compiler.objectClass) { + assert(superClass != null); + assert(superClass.resolutionState == STATE_DONE); + + Selector selector = + new Selector.callDefaultConstructor(enclosingClass.getLibrary()); + + FunctionElement targetConstructor = + superClass.lookupConstructor(selector); + if (targetConstructor == null) { + compiler.internalError(functionNode, + "No default constructor available."); + } + List compiledArguments = evaluateArgumentsToConstructor( + functionNode, selector, const Link(), targetConstructor); + evaluateSuperOrRedirectSend(compiledArguments, targetConstructor); + } + } + } + + /** + * Simulates the execution of the [constructor] with the given + * [arguments] to obtain the field values that need to be passed to the + * native JavaScript constructor. + */ + void evaluateConstructorFieldValues(List arguments) { + compiler.withCurrentElement(constructor, () { + assignArgumentsToParameters(arguments); + evaluateConstructorInitializers(); + }); + } + + List buildJsNewArguments(ClassElement classElement) { + List jsNewArguments = []; + classElement.implementation.forEachInstanceField( + (ClassElement enclosing, Element field) { + Constant fieldValue = fieldValues[field]; + if (fieldValue == null) { + // Use the default value. + fieldValue = handler.compileConstant(field); + } + jsNewArguments.add(fieldValue); + }, + includeSuperAndInjectedMembers: true); + return jsNewArguments; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/compiler.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/compiler.dart new file mode 100644 index 0000000..64c6f71 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/compiler.dart @@ -0,0 +1,1790 @@ +// Copyright (c) 2012, 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. + +part of dart2js; + +/** + * If true, print a warning for each method that was resolved, but not + * compiled. + */ +const bool REPORT_EXCESS_RESOLUTION = false; + +/** + * Contains backend-specific data that is used throughout the compilation of + * one work item. + */ +class ItemCompilationContext { +} + +abstract class WorkItem { + final ItemCompilationContext compilationContext; + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [element] must be a declaration element. + */ + final Element element; + TreeElements resolutionTree; + + WorkItem(this.element, this.compilationContext) { + assert(invariant(element, element.isDeclaration)); + } + + void run(Compiler compiler, Enqueuer world); +} + +/// [WorkItem] used exclusively by the [ResolutionEnqueuer]. +class ResolutionWorkItem extends WorkItem { + ResolutionWorkItem(Element element, + ItemCompilationContext compilationContext) + : super(element, compilationContext); + + void run(Compiler compiler, ResolutionEnqueuer world) { + resolutionTree = compiler.analyze(this, world); + } + + bool isAnalyzed() => resolutionTree != null; +} + +/// [WorkItem] used exclusively by the [CodegenEnqueuer]. +class CodegenWorkItem extends WorkItem { + CodegenWorkItem(Element element, + ItemCompilationContext compilationContext) + : super(element, compilationContext); + + void run(Compiler compiler, CodegenEnqueuer world) { + if (world.isProcessed(element)) return; + resolutionTree = + compiler.enqueuer.resolution.getCachedElements(element); + assert(invariant(element, resolutionTree != null, + message: 'Resolution tree is null for $element in codegen work item')); + compiler.codegen(this, world); + } +} + +typedef void DeferredAction(); + +class DeferredTask { + final Element element; + final DeferredAction action; + + DeferredTask(this.element, this.action); +} + +abstract class Backend { + final Compiler compiler; + final ConstantSystem constantSystem; + + Backend(this.compiler, + [ConstantSystem constantSystem = DART_CONSTANT_SYSTEM]) + : this.constantSystem = constantSystem; + + // Given a [FunctionElement], return a buffer with the code generated for it + // or null if no code was generated. + CodeBuffer codeOf(Element element) => null; + + void initializeHelperClasses() {} + + void enqueueHelpers(ResolutionEnqueuer world, TreeElements elements); + void codegen(CodegenWorkItem work); + + // The backend determines the native resolution enqueuer, with a no-op + // default, so tools like dart2dart can ignore the native classes. + native.NativeEnqueuer nativeResolutionEnqueuer(world) { + return new native.NativeEnqueuer(); + } + native.NativeEnqueuer nativeCodegenEnqueuer(world) { + return new native.NativeEnqueuer(); + } + + void assembleProgram(); + List get tasks; + + void onResolutionComplete() {} + + ItemCompilationContext createItemCompilationContext() { + return new ItemCompilationContext(); + } + + bool classNeedsRti(ClassElement cls); + bool methodNeedsRti(FunctionElement function); + + + /// Called during codegen when [constant] has been used. + void registerCompileTimeConstant(Constant constant, TreeElements elements) {} + + /// Called during post-processing when [constant] has been evaluated. + void registerMetadataConstant(Constant constant, TreeElements elements) {} + + /// Called during resolution to notify to the backend that a class is + /// being instantiated. + void registerInstantiatedClass(ClassElement cls, + Enqueuer enqueuer, + TreeElements elements) {} + + /// Called during resolution to notify to the backend that the + /// program uses string interpolation. + void registerStringInterpolation(TreeElements elements) {} + + /// Called during resolution to notify to the backend that the + /// program has a catch statement. + void registerCatchStatement(Enqueuer enqueuer, + TreeElements elements) {} + + /// Called during resolution to notify to the backend that the + /// program explicitly throws an exception. + void registerThrowExpression(TreeElements elements) {} + + /// Called during resolution to notify to the backend that the + /// program has a global variable with a lazy initializer. + void registerLazyField(TreeElements elements) {} + + /// Called during resolution to notify to the backend that the + /// program uses a type variable as an expression. + void registerTypeVariableExpression(TreeElements elements) {} + + /// Called during resolution to notify to the backend that the + /// program uses a type literal. + void registerTypeLiteral(Element element, + Enqueuer enqueuer, + TreeElements elements) {} + + /// Called during resolution to notify to the backend that the + /// program has a catch statement with a stack trace. + void registerStackTraceInCatch(TreeElements elements) {} + + /// Register an is check to the backend. + void registerIsCheck(DartType type, + Enqueuer enqueuer, + TreeElements elements) {} + + /// Register an as check to the backend. + void registerAsCheck(DartType type, + Enqueuer enqueuer, + TreeElements elements) {} + + /// Register a runtime type variable bound tests between [typeArgument] and + /// [bound]. + void registerTypeVariableBoundsSubtypeCheck(DartType typeArgument, + DartType bound) {} + + /// Registers that a type variable bounds check might occur at runtime. + void registerTypeVariableBoundCheck(TreeElements elements) {} + + /// Register that the application may throw a [NoSuchMethodError]. + void registerThrowNoSuchMethod(TreeElements elements) {} + + /// Register that the application may throw a [RuntimeError]. + void registerThrowRuntimeError(TreeElements elements) {} + + /// Register that the application may throw an + /// [AbstractClassInstantiationError]. + void registerAbstractClassInstantiation(TreeElements elements) {} + + /// Register that the application may throw a [FallThroughError]. + void registerFallThroughError(TreeElements elements) {} + + /// Register that a super call will end up calling + /// [: super.noSuchMethod :]. + void registerSuperNoSuchMethod(TreeElements elements) {} + + /// Register that the application creates a constant map. + void registerConstantMap(TreeElements elements) {} + + /** + * Call this to register that an instantiated generic class has a call + * method. + */ + void registerGenericCallMethod(Element callMethod, + Enqueuer enqueuer, + TreeElements elements) {} + /** + * Call this to register that a getter exists for a function on an + * instantiated generic class. + */ + void registerGenericClosure(Element closure, + Enqueuer enqueuer, + TreeElements elements) {} + /** + * Call this to register that the [:runtimeType:] property has been accessed. + */ + void registerRuntimeType(Enqueuer enqueuer, TreeElements elements) {} + + /** + * Call this method to enable [noSuchMethod] handling in the + * backend. + */ + void enableNoSuchMethod(Enqueuer enqueuer) { + enqueuer.registerInvocation(compiler.noSuchMethodSelector); + } + + void registerRequiredType(DartType type, Element enclosingElement) {} + void registerClassUsingVariableExpression(ClassElement cls) {} + + void registerConstSymbol(String name, TreeElements elements) {} + void registerNewSymbol(TreeElements elements) {} + /// Called when resolving the `Symbol` constructor. + void registerSymbolConstructor(TreeElements elements) {} + + bool isNullImplementation(ClassElement cls) { + return cls == compiler.nullClass; + } + ClassElement get intImplementation => compiler.intClass; + ClassElement get doubleImplementation => compiler.doubleClass; + ClassElement get numImplementation => compiler.numClass; + ClassElement get stringImplementation => compiler.stringClass; + ClassElement get listImplementation => compiler.listClass; + ClassElement get growableListImplementation => compiler.listClass; + ClassElement get fixedListImplementation => compiler.listClass; + ClassElement get constListImplementation => compiler.listClass; + ClassElement get mapImplementation => compiler.mapClass; + ClassElement get constMapImplementation => compiler.mapClass; + ClassElement get functionImplementation => compiler.functionClass; + ClassElement get typeImplementation => compiler.typeClass; + ClassElement get boolImplementation => compiler.boolClass; + ClassElement get nullImplementation => compiler.nullClass; + ClassElement get uint32Implementation => compiler.intClass; + ClassElement get uint31Implementation => compiler.intClass; + ClassElement get positiveIntImplementation => compiler.intClass; + + ClassElement defaultSuperclass(ClassElement element) => compiler.objectClass; + + bool isDefaultNoSuchMethodImplementation(Element element) { + assert(element.name == Compiler.NO_SUCH_METHOD); + ClassElement classElement = element.getEnclosingClass(); + return classElement == compiler.objectClass; + } + + bool isInterceptorClass(ClassElement element) => false; + + void registerStaticUse(Element element, Enqueuer enqueuer) {} + + Future onLibraryLoaded(LibraryElement library, Uri uri) { + return new Future.value(); + } + + /// Called by [MirrorUsageAnalyzerTask] after it has merged all @MirrorsUsed + /// annotations. The arguments corresponds to the unions of the corresponding + /// fields of the annotations. + void registerMirrorUsage(Set symbols, + Set targets, + Set metaTargets) {} + + /// Returns true if this element should be retained for reflection even if it + /// would normally be tree-shaken away. + bool isNeededForReflection(Element element) => false; + + /// Returns true if global optimizations such as type inferencing + /// can apply to this element. One category of elements that do not + /// apply is runtime helpers that the backend calls, but the + /// optimizations don't see those calls. + bool canBeUsedForGlobalOptimizations(Element element) => true; + + /// Called when [enqueuer]'s queue is empty, but before it is closed. + /// This is used, for example, by the JS backend to enqueue additional + /// elements needed for reflection. + void onQueueEmpty(Enqueuer enqueuer) {} + + /// Called after [element] has been resolved. + void onElementResolved(Element element, TreeElements elements) {} +} + +/** + * Key class used in [TokenMap] in which the hash code for a token is based + * on the [charOffset]. + */ +class TokenKey { + final Token token; + TokenKey(this.token); + int get hashCode => token.charOffset; + operator==(other) => other is TokenKey && token == other.token; +} + +/// Map of tokens and the first associated comment. +/* + * This implementation was chosen among several candidates for its space/time + * efficiency by empirical tests of running dartdoc on dartdoc itself. Time + * measurements for the use of [Compiler.commentMap]: + * + * 1) Using [TokenKey] as key (this class): ~80 msec + * 2) Using [TokenKey] as key + storing a separate map in each script: ~120 msec + * 3) Using [Token] as key in a [Map]: ~38000 msec + * 4) Storing comments is new field in [Token]: ~20 msec + * (Abandoned due to the increased memory usage) + * 5) Storing comments in an [Expando]: ~14000 msec + * 6) Storing token/comments pairs in a linked list: ~5400 msec + */ +class TokenMap { + Map comments = new Map(); + + Token operator[] (Token key) { + if (key == null) return null; + return comments[new TokenKey(key)]; + } + + void operator[]= (Token key, Token value) { + if (key == null) return; + comments[new TokenKey(key)] = value; + } +} + +abstract class Compiler implements DiagnosticListener { + final Map libraries = + new Map(); + final Stopwatch totalCompileTime = new Stopwatch(); + int nextFreeClassId = 0; + World world; + String assembledCode; + Types types; + + /** + * Map from token to the first preceeding comment token. + */ + final TokenMap commentMap = new TokenMap(); + + /** + * Records global dependencies, that is, dependencies that don't + * correspond to a particular element. + * + * We should get rid of this and ensure that all dependencies are + * associated with a particular element. + */ + final TreeElements globalDependencies = new TreeElementMapping(null); + + final bool enableMinification; + final bool enableTypeAssertions; + final bool enableUserAssertions; + final bool trustTypeAnnotations; + final bool enableConcreteTypeInference; + final bool disableTypeInferenceFlag; + final bool dumpInfo; + + /** + * The maximum size of a concrete type before it widens to dynamic during + * concrete type inference. + */ + final int maxConcreteTypeSize; + final bool analyzeAllFlag; + final bool analyzeOnly; + /** + * If true, skip analysis of method bodies and field initializers. Implies + * [analyzeOnly]. + */ + final bool analyzeSignaturesOnly; + final bool enableNativeLiveTypeAnalysis; + /** + * If true, stop compilation after type inference is complete. Used for + * debugging and testing purposes only. + */ + bool stopAfterTypeInference = false; + /** + * If [:true:], comment tokens are collected in [commentMap] during scanning. + */ + final bool preserveComments; + + /** + * Is the compiler in verbose mode. + */ + final bool verbose; + + /** + * URI of the main source map if the compiler is generating source + * maps. + */ + final Uri sourceMapUri; + + /** + * URI of the main output if the compiler is generating source maps. + */ + final Uri outputUri; + + /// Emit terse diagnostics without howToFix. + final bool terseDiagnostics; + + /// If `true`, warnings and hints not from user code are reported. + final bool showPackageWarnings; + + /// `true` if the last diagnostic was filtered, in which case the + /// accompanying info message should be filtered as well. + bool lastDiagnosticWasFiltered = false; + + /// Map containing information about the warnings and hints that have been + /// suppressed for each library. + Map suppressedWarnings = {}; + + final api.CompilerOutputProvider outputProvider; + + bool disableInlining = false; + + List librariesToAnalyzeWhenRun; + + final Tracer tracer; + + CompilerTask measuredTask; + Element _currentElement; + LibraryElement coreLibrary; + LibraryElement isolateLibrary; + LibraryElement isolateHelperLibrary; + LibraryElement jsHelperLibrary; + LibraryElement interceptorsLibrary; + LibraryElement foreignLibrary; + + LibraryElement mainApp; + FunctionElement mainFunction; + + /// Initialized when dart:mirrors is loaded. + LibraryElement mirrorsLibrary; + + /// Initialized when dart:typed_data is loaded. + LibraryElement typedDataLibrary; + + ClassElement objectClass; + ClassElement closureClass; + ClassElement boundClosureClass; + ClassElement dynamicClass; + ClassElement boolClass; + ClassElement numClass; + ClassElement intClass; + ClassElement doubleClass; + ClassElement stringClass; + ClassElement functionClass; + ClassElement nullClass; + ClassElement listClass; + ClassElement typeClass; + ClassElement mapClass; + ClassElement symbolClass; + ClassElement stackTraceClass; + ClassElement typedDataClass; + + /// The constant for the [proxy] variable defined in dart:core. + Constant proxyConstant; + + // Initialized after symbolClass has been resolved. + FunctionElement symbolConstructor; + + // Initialized when dart:mirrors is loaded. + ClassElement mirrorSystemClass; + + // Initialized when dart:mirrors is loaded. + ClassElement mirrorsUsedClass; + + // Initialized after mirrorSystemClass has been resolved. + FunctionElement mirrorSystemGetNameFunction; + + // Initialized when dart:_internal is loaded. + ClassElement symbolImplementationClass; + + // Initialized when symbolImplementationClass has been resolved. + FunctionElement symbolValidatedConstructor; + + // Initialized when mirrorsUsedClass has been resolved. + FunctionElement mirrorsUsedConstructor; + + // Initialized when dart:mirrors is loaded. + ClassElement deferredLibraryClass; + + ClassElement jsInvocationMirrorClass; + /// Document class from dart:mirrors. + ClassElement documentClass; + Element assertMethod; + Element identicalFunction; + Element loadLibraryFunction; + Element functionApplyMethod; + Element invokeOnMethod; + Element intEnvironment; + Element boolEnvironment; + Element stringEnvironment; + + fromEnvironment(String name) => null; + + Element get currentElement => _currentElement; + + String tryToString(object) { + try { + return object.toString(); + } catch (_) { + return ''; + } + } + + /** + * Perform an operation, [f], returning the return value from [f]. If an + * error occurs then report it as having occurred during compilation of + * [element]. Can be nested. + */ + withCurrentElement(Element element, f()) { + Element old = currentElement; + _currentElement = element; + try { + return f(); + } on SpannableAssertionFailure catch (ex) { + if (!hasCrashed) { + reportAssertionFailure(ex); + pleaseReportCrash(); + } + hasCrashed = true; + rethrow; + } on CompilerCancelledException catch (ex) { + rethrow; + } on StackOverflowError catch (ex) { + // We cannot report anything useful in this case, because we + // do not have enough stack space. + rethrow; + } catch (ex) { + if (hasCrashed) rethrow; + try { + unhandledExceptionOnElement(element); + } catch (doubleFault) { + // Ignoring exceptions in exception handling. + } + rethrow; + } finally { + _currentElement = old; + } + } + + List tasks; + ScannerTask scanner; + DietParserTask dietParser; + ParserTask parser; + PatchParserTask patchParser; + LibraryLoader libraryLoader; + TreeValidatorTask validator; + ResolverTask resolver; + closureMapping.ClosureTask closureToClassMapper; + TypeCheckerTask checker; + IrBuilderTask irBuilder; + ti.TypesTask typesTask; + Backend backend; + ConstantHandler constantHandler; + EnqueueTask enqueuer; + DeferredLoadTask deferredLoadTask; + MirrorUsageAnalyzerTask mirrorUsageAnalyzerTask; + DumpInfoTask dumpInfoTask; + String buildId; + + static const String MAIN = 'main'; + static const String CALL_OPERATOR_NAME = 'call'; + static const String NO_SUCH_METHOD = 'noSuchMethod'; + static const int NO_SUCH_METHOD_ARG_COUNT = 1; + static const String CREATE_INVOCATION_MIRROR = + 'createInvocationMirror'; + + // TODO(ahe): Rename this field and move this logic to backend, similar to how + // we disable tree-shaking when seeing disableTreeShaking in js_mirrors.dart. + static const String INVOKE_ON = + '_getCachedInvocation'; + + static const String RUNTIME_TYPE = 'runtimeType'; + static const String START_ROOT_ISOLATE = + 'startRootIsolate'; + + static const String UNDETERMINED_BUILD_ID = + "build number could not be determined"; + + final Selector iteratorSelector = + new Selector.getter('iterator', null); + final Selector currentSelector = + new Selector.getter('current', null); + final Selector moveNextSelector = + new Selector.call('moveNext', null, 0); + final Selector noSuchMethodSelector = new Selector.call( + Compiler.NO_SUCH_METHOD, null, Compiler.NO_SUCH_METHOD_ARG_COUNT); + final Selector symbolValidatedConstructorSelector = new Selector.call( + 'validated', null, 1); + final Selector fromEnvironmentSelector = new Selector.callConstructor( + 'fromEnvironment', null, 2); + + bool enabledNoSuchMethod = false; + bool enabledRuntimeType = false; + bool enabledFunctionApply = false; + bool enabledInvokeOn = false; + + Stopwatch progress = new Stopwatch()..start(); + + static const int PHASE_SCANNING = 0; + static const int PHASE_RESOLVING = 1; + static const int PHASE_DONE_RESOLVING = 2; + static const int PHASE_COMPILING = 3; + int phase; + + bool compilationFailed = false; + + bool hasCrashed = false; + + /// Set by the backend if real reflection is detected in use of dart:mirrors. + bool disableTypeInferenceForMirrors = false; + + Compiler({this.tracer: const Tracer(), + this.enableTypeAssertions: false, + this.enableUserAssertions: false, + this.trustTypeAnnotations: false, + this.enableConcreteTypeInference: false, + this.disableTypeInferenceFlag: false, + this.maxConcreteTypeSize: 5, + this.enableMinification: false, + this.enableNativeLiveTypeAnalysis: false, + bool emitJavaScript: true, + bool generateSourceMap: true, + bool analyzeAllFlag: false, + bool analyzeOnly: false, + bool analyzeSignaturesOnly: false, + this.preserveComments: false, + this.verbose: false, + this.sourceMapUri: null, + this.outputUri: null, + this.buildId: UNDETERMINED_BUILD_ID, + this.terseDiagnostics: false, + this.dumpInfo: false, + this.showPackageWarnings: false, + outputProvider, + List strips: const []}) + : this.analyzeOnly = + analyzeOnly || analyzeSignaturesOnly || analyzeAllFlag, + this.analyzeSignaturesOnly = analyzeSignaturesOnly, + this.analyzeAllFlag = analyzeAllFlag, + this.outputProvider = (outputProvider == null) + ? NullSink.outputProvider + : outputProvider { + world = new World(this); + + closureMapping.ClosureNamer closureNamer; + if (emitJavaScript) { + js_backend.JavaScriptBackend jsBackend = + new js_backend.JavaScriptBackend(this, generateSourceMap); + closureNamer = jsBackend.namer; + backend = jsBackend; + } else { + closureNamer = new closureMapping.ClosureNamer(); + backend = new dart_backend.DartBackend(this, strips); + } + + // No-op in production mode. + validator = new TreeValidatorTask(this); + + tasks = [ + libraryLoader = new LibraryLoaderTask(this), + scanner = new ScannerTask(this), + dietParser = new DietParserTask(this), + parser = new ParserTask(this), + patchParser = new PatchParserTask(this), + resolver = new ResolverTask(this), + closureToClassMapper = new closureMapping.ClosureTask(this, closureNamer), + checker = new TypeCheckerTask(this), + irBuilder = new IrBuilderTask(this), + typesTask = new ti.TypesTask(this), + constantHandler = new ConstantHandler(this, backend.constantSystem), + deferredLoadTask = new DeferredLoadTask(this), + mirrorUsageAnalyzerTask = new MirrorUsageAnalyzerTask(this), + enqueuer = new EnqueueTask(this), + dumpInfoTask = new DumpInfoTask(this)]; + + tasks.addAll(backend.tasks); + } + + Universe get resolverWorld => enqueuer.resolution.universe; + Universe get codegenWorld => enqueuer.codegen.universe; + + bool get hasBuildId => buildId != UNDETERMINED_BUILD_ID; + + bool get analyzeAll => analyzeAllFlag || compileAll; + + bool get compileAll => false; + + bool get disableTypeInference => disableTypeInferenceFlag; + + int getNextFreeClassId() => nextFreeClassId++; + + void unimplemented(Spannable spannable, String methodName) { + internalError(spannable, "$methodName not implemented."); + } + + void internalError(Spannable node, reason) { + assembledCode = null; // Compilation failed. Make sure that we + // don't return a bogus result. + String message = tryToString(reason); + reportDiagnosticInternal( + node, MessageKind.GENERIC, {'text': message}, api.Diagnostic.CRASH); + throw 'Internal Error: $message'; + } + + void unhandledExceptionOnElement(Element element) { + if (hasCrashed) return; + hasCrashed = true; + reportDiagnostic(element, + MessageKind.COMPILER_CRASHED.message(), + api.Diagnostic.CRASH); + pleaseReportCrash(); + } + + void pleaseReportCrash() { + print(MessageKind.PLEASE_REPORT_THE_CRASH.message({'buildId': buildId})); + } + + SourceSpan spanFromSpannable(Spannable node) { + // TODO(johnniwinther): Disallow `node == null` ? + if (node == null) return null; + if (node == CURRENT_ELEMENT_SPANNABLE) { + node = currentElement; + } else if (node == NO_LOCATION_SPANNABLE) { + if (currentElement == null) return null; + node = currentElement; + } + if (node is SourceSpan) { + return node; + } else if (node is Node) { + return spanFromNode(node); + } else if (node is Token) { + return spanFromTokens(node, node); + } else if (node is HInstruction) { + return spanFromHInstruction(node); + } else if (node is Element) { + return spanFromElement(node); + } else if (node is MetadataAnnotation) { + Uri uri = node.annotatedElement.getCompilationUnit().script.readableUri; + return spanFromTokens(node.beginToken, node.endToken, uri); + } else { + throw 'No error location.'; + } + } + + /// Finds the approximate [Element] for [node]. [currentElement] is used as + /// the default value. + Element elementFromSpannable(Spannable node) { + Element element; + if (node is Element) { + element = node; + } else if (node is HInstruction) { + element = node.sourceElement; + } else if (node is MetadataAnnotation) { + element = node.annotatedElement; + } + return element != null ? element : currentElement; + } + + void log(message) { + reportDiagnostic(null, + MessageKind.GENERIC.message({'text': '$message'}), + api.Diagnostic.VERBOSE_INFO); + } + + Future run(Uri uri) { + totalCompileTime.start(); + + return new Future.sync(() => runCompiler(uri)).catchError((error) { + if (error is CompilerCancelledException) { + log('Error: $error'); + return false; + } + + try { + if (!hasCrashed) { + hasCrashed = true; + if (error is SpannableAssertionFailure) { + reportAssertionFailure(error); + } else { + reportDiagnostic(new SourceSpan(uri, 0, 0), + MessageKind.COMPILER_CRASHED.message(), + api.Diagnostic.CRASH); + } + pleaseReportCrash(); + } + } catch (doubleFault) { + // Ignoring exceptions in exception handling. + } + throw error; + }).whenComplete(() { + tracer.close(); + totalCompileTime.stop(); + }).then((_) { + return !compilationFailed; + }); + } + + bool hasIsolateSupport() => isolateLibrary != null; + + /** + * This method is called before [library] import and export scopes have been + * set up. + */ + Future onLibraryLoaded(LibraryElement library, Uri uri) { + if (dynamicClass != null) { + // When loading the built-in libraries, dynamicClass is null. We + // take advantage of this as core imports js_helper and sees [dynamic] + // this way. + withCurrentElement(dynamicClass, () { + library.addToScope(dynamicClass, this); + }); + } + if (uri == new Uri(scheme: 'dart', path: 'mirrors')) { + mirrorsLibrary = library; + mirrorSystemClass = + findRequiredElement(library, 'MirrorSystem'); + mirrorsUsedClass = + findRequiredElement(library, 'MirrorsUsed'); + } else if (uri == new Uri(scheme: 'dart', path: '_native_typed_data')) { + typedDataLibrary = library; + typedDataClass = + findRequiredElement(library, 'NativeTypedData'); + } else if (uri == new Uri(scheme: 'dart', path: '_internal')) { + symbolImplementationClass = + findRequiredElement(library, 'Symbol'); + } else if (uri == new Uri(scheme: 'dart', path: 'async')) { + deferredLibraryClass = + findRequiredElement(library, 'DeferredLibrary'); + } else if (isolateHelperLibrary == null + && (uri == new Uri(scheme: 'dart', path: '_isolate_helper'))) { + isolateHelperLibrary = library; + } else if (foreignLibrary == null + && (uri == new Uri(scheme: 'dart', path: '_foreign_helper'))) { + foreignLibrary = library; + } + return backend.onLibraryLoaded(library, uri); + } + + Element findRequiredElement(LibraryElement library, String name) { + var element = library.find(name); + if (element == null) { + internalError(library, + "The library '${library.canonicalUri}' does not contain required " + "element: '$name'."); + } + return element; + } + + void onClassResolved(ClassElement cls) { + if (mirrorSystemClass == cls) { + mirrorSystemGetNameFunction = + cls.lookupLocalMember('getName'); + } else if (symbolClass == cls) { + symbolConstructor = cls.constructors.head; + } else if (symbolImplementationClass == cls) { + symbolValidatedConstructor = symbolImplementationClass.lookupConstructor( + symbolValidatedConstructorSelector); + } else if (mirrorsUsedClass == cls) { + mirrorsUsedConstructor = cls.constructors.head; + } else if (intClass == cls) { + intEnvironment = intClass.lookupConstructor(fromEnvironmentSelector); + } else if (stringClass == cls) { + stringEnvironment = + stringClass.lookupConstructor(fromEnvironmentSelector); + } else if (boolClass == cls) { + boolEnvironment = boolClass.lookupConstructor(fromEnvironmentSelector); + } + } + + Future scanBuiltinLibrary(String filename); + + void initializeSpecialClasses() { + final List missingCoreClasses = []; + ClassElement lookupCoreClass(String name) { + ClassElement result = coreLibrary.find(name); + if (result == null) { + missingCoreClasses.add(name); + } + return result; + } + objectClass = lookupCoreClass('Object'); + boolClass = lookupCoreClass('bool'); + numClass = lookupCoreClass('num'); + intClass = lookupCoreClass('int'); + doubleClass = lookupCoreClass('double'); + stringClass = lookupCoreClass('String'); + functionClass = lookupCoreClass('Function'); + listClass = lookupCoreClass('List'); + typeClass = lookupCoreClass('Type'); + mapClass = lookupCoreClass('Map'); + nullClass = lookupCoreClass('Null'); + stackTraceClass = lookupCoreClass('StackTrace'); + if (!missingCoreClasses.isEmpty) { + internalError(coreLibrary, + 'dart:core library does not contain required classes: ' + '$missingCoreClasses'); + } + + // The Symbol class may not exist during unit testing. + // TODO(ahe): It is possible that we have to require the presence + // of Symbol as we change how we implement noSuchMethod. + symbolClass = lookupCoreClass('Symbol'); + + final List missingHelperClasses = []; + ClassElement lookupHelperClass(String name) { + ClassElement result = jsHelperLibrary.find(name); + if (result == null) { + missingHelperClasses.add(name); + } + return result; + } + jsInvocationMirrorClass = lookupHelperClass('JSInvocationMirror'); + boundClosureClass = lookupHelperClass('BoundClosure'); + closureClass = lookupHelperClass('Closure'); + dynamicClass = lookupHelperClass('Dynamic_'); + if (!missingHelperClasses.isEmpty) { + internalError(jsHelperLibrary, + 'dart:_js_helper library does not contain required classes: ' + '$missingHelperClasses'); + } + + if (types == null) { + types = new Types(this, dynamicClass); + } + backend.initializeHelperClasses(); + + dynamicClass.ensureResolved(this); + + proxyConstant = constantHandler.compileVariable( + coreLibrary.find('proxy'), isConst: true); + } + + Element _unnamedListConstructor; + Element get unnamedListConstructor { + if (_unnamedListConstructor != null) return _unnamedListConstructor; + Selector callConstructor = new Selector.callConstructor( + "", listClass.getLibrary()); + return _unnamedListConstructor = + listClass.lookupConstructor(callConstructor); + } + + Element _filledListConstructor; + Element get filledListConstructor { + if (_filledListConstructor != null) return _filledListConstructor; + Selector callConstructor = new Selector.callConstructor( + "filled", listClass.getLibrary()); + return _filledListConstructor = + listClass.lookupConstructor(callConstructor); + } + + Future scanBuiltinLibraries() { + return scanBuiltinLibrary('_js_helper').then((LibraryElement library) { + jsHelperLibrary = library; + return scanBuiltinLibrary('_interceptors'); + }).then((LibraryElement library) { + interceptorsLibrary = library; + + assertMethod = jsHelperLibrary.find('assertHelper'); + identicalFunction = coreLibrary.find('identical'); + + initializeSpecialClasses(); + + functionClass.ensureResolved(this); + functionApplyMethod = + functionClass.lookupLocalMember('apply'); + jsInvocationMirrorClass.ensureResolved(this); + invokeOnMethod = jsInvocationMirrorClass.lookupLocalMember(INVOKE_ON); + + if (preserveComments) { + var uri = new Uri(scheme: 'dart', path: 'mirrors'); + return libraryLoader.loadLibrary(uri, null, uri).then( + (LibraryElement libraryElement) { + documentClass = libraryElement.find('Comment'); + }); + } + }); + } + + void importHelperLibrary(LibraryElement library) { + if (jsHelperLibrary != null) { + libraryLoader.importLibrary(library, jsHelperLibrary, null); + } + } + + /** + * Get an [Uri] pointing to a patch for the dart: library with + * the given path. Returns null if there is no patch. + */ + Uri resolvePatchUri(String dartLibraryPath); + + Future runCompiler(Uri uri) { + // TODO(ahe): This prevents memory leaks when invoking the compiler + // multiple times. Implement a better mechanism where we can store + // such caches in the compiler and get access to them through a + // suitably maintained static reference to the current compiler. + StringToken.canonicalizedSubstrings.clear(); + Selector.canonicalizedValues.clear(); + TypedSelector.canonicalizedValues.clear(); + + assert(uri != null || analyzeOnly); + return scanBuiltinLibraries().then((_) { + if (librariesToAnalyzeWhenRun != null) { + return Future.forEach(librariesToAnalyzeWhenRun, (libraryUri) { + log('Analyzing $libraryUri ($buildId)'); + return libraryLoader.loadLibrary(libraryUri, null, libraryUri); + }); + } + }).then((_) { + if (uri != null) { + if (analyzeOnly) { + log('Analyzing $uri ($buildId)'); + } else { + log('Compiling $uri ($buildId)'); + } + return libraryLoader.loadLibrary(uri, null, uri) + .then((LibraryElement library) { + mainApp = library; + }); + } + }).then((_) { + compileLoadedLibraries(); + }); + } + + /// Performs the compilation when all libraries have been loaded. + void compileLoadedLibraries() { + Element main = null; + if (mainApp != null) { + main = mainApp.findExported(MAIN); + if (main == null) { + if (!analyzeOnly) { + // Allow analyze only of libraries with no main. + reportFatalError( + mainApp, + MessageKind.GENERIC, + {'text': "Could not find '$MAIN'."}); + } else if (!analyzeAll) { + reportFatalError(mainApp, MessageKind.GENERIC, + {'text': "Could not find '$MAIN'. " + "No source will be analyzed. " + "Use '--analyze-all' to analyze all code in the " + "library."}); + } + } else { + if (main.isErroneous()) { + reportFatalError(main, MessageKind.GENERIC, + {'text': "Cannot determine which '$MAIN' to use."}); + } else if (!main.isFunction()) { + reportFatalError(main, MessageKind.GENERIC, + {'text': "'$MAIN' is not a function."}); + } + mainFunction = main; + FunctionSignature parameters = mainFunction.computeSignature(this); + if (parameters.parameterCount > 2) { + int index = 0; + parameters.forEachParameter((Element parameter) { + if (index++ < 2) return; + reportError(parameter, MessageKind.GENERIC, + {'text': "'$MAIN' cannot have more than two parameters."}); + }); + } + } + + mirrorUsageAnalyzerTask.analyzeUsage(mainApp); + + // In order to see if a library is deferred, we must compute the + // compile-time constants that are metadata. This means adding + // something to the resolution queue. So we cannot wait with + // this until after the resolution queue is processed. + deferredLoadTask.ensureMetadataResolved(this); + } + + log('Resolving...'); + phase = PHASE_RESOLVING; + if (analyzeAll) { + libraries.forEach( + (_, lib) => fullyEnqueueLibrary(lib, enqueuer.resolution)); + } + // Elements required by enqueueHelpers are global dependencies + // that are not pulled in by a particular element. + backend.enqueueHelpers(enqueuer.resolution, globalDependencies); + resolveLibraryMetadata(); + processQueue(enqueuer.resolution, main); + enqueuer.resolution.logSummary(log); + + if (compilationFailed) return; + if (!showPackageWarnings) { + suppressedWarnings.forEach((Uri uri, SuppressionInfo info) { + MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS; + if (info.warnings == 0) { + kind = MessageKind.HIDDEN_HINTS; + } else if (info.hints == 0) { + kind = MessageKind.HIDDEN_WARNINGS; + } + reportDiagnostic(null, + kind.message({'warnings': info.warnings, + 'hints': info.hints, + 'uri': uri}, + terseDiagnostics), + api.Diagnostic.HINT); + }); + } + if (analyzeOnly) { + if (!analyzeAll) { + // No point in reporting unused code when [analyzeAll] is true: all + // code is artificially used. + reportUnusedCode(); + } + return; + } + assert(main != null); + phase = PHASE_DONE_RESOLVING; + + // TODO(ahe): Remove this line. Eventually, enqueuer.resolution + // should know this. + world.populate(); + // Compute whole-program-knowledge that the backend needs. (This might + // require the information computed in [world.populate].) + backend.onResolutionComplete(); + + deferredLoadTask.onResolutionComplete(main); + + log('Building IR...'); + irBuilder.buildNodes(); + + log('Inferring types...'); + typesTask.onResolutionComplete(main); + + if(stopAfterTypeInference) return; + + log('Compiling...'); + phase = PHASE_COMPILING; + // TODO(johnniwinther): Move these to [CodegenEnqueuer]. + if (hasIsolateSupport()) { + enqueuer.codegen.addToWorkList( + isolateHelperLibrary.find(Compiler.START_ROOT_ISOLATE)); + enqueuer.codegen.registerGetOfStaticFunction(main); + } + if (enabledNoSuchMethod) { + backend.enableNoSuchMethod(enqueuer.codegen); + } + if (compileAll) { + libraries.forEach((_, lib) => fullyEnqueueLibrary(lib, + enqueuer.codegen)); + } + processQueue(enqueuer.codegen, main); + enqueuer.codegen.logSummary(log); + + if (compilationFailed) return; + + backend.assembleProgram(); + + if (dumpInfo) { + dumpInfoTask.dumpInfo(); + } + + checkQueues(); + + if (compilationFailed) { + assembledCode = null; // Signals failure. + } + } + + void fullyEnqueueLibrary(LibraryElement library, Enqueuer world) { + void enqueueAll(Element element) { + fullyEnqueueTopLevelElement(element, world); + } + library.implementation.forEachLocalMember(enqueueAll); + } + + void fullyEnqueueTopLevelElement(Element element, Enqueuer world) { + if (element.isClass()) { + ClassElement cls = element; + cls.ensureResolved(this); + cls.forEachLocalMember(enqueuer.resolution.addToWorkList); + world.registerInstantiatedClass(element, globalDependencies); + } else { + world.addToWorkList(element); + } + } + + // Resolves metadata on library elements. This is necessary in order to + // resolve metadata classes referenced only from metadata on library tags. + // TODO(ahe): Figure out how to do this lazily. + void resolveLibraryMetadata() { + for (LibraryElement library in libraries.values) { + if (library.metadata != null) { + for (MetadataAnnotation metadata in library.metadata) { + metadata.ensureResolved(this); + } + } + } + } + + void processQueue(Enqueuer world, Element main) { + world.nativeEnqueuer.processNativeClasses(libraries.values); + if (main != null) { + FunctionElement mainMethod = main; + if (mainMethod.computeSignature(this).parameterCount != 0) { + // TODO(ngeoffray, floitsch): we should also ensure that the + // class IsolateMessage is instantiated. Currently, just enabling + // isolate support works. + world.enableIsolateSupport(main.getLibrary()); + world.registerInstantiatedClass(listClass, globalDependencies); + world.registerInstantiatedClass(stringClass, globalDependencies); + } + world.addToWorkList(main); + } + progress.reset(); + world.forEach((WorkItem work) { + withCurrentElement(work.element, () => work.run(this, world)); + }); + world.queueIsClosed = true; + if (compilationFailed) return; + assert(world.checkNoEnqueuedInvokedInstanceMethods()); + } + + /** + * Perform various checks of the queues. This includes checking that + * the queues are empty (nothing was added after we stopped + * processing the queues). Also compute the number of methods that + * were resolved, but not compiled (aka excess resolution). + */ + checkQueues() { + for (Enqueuer world in [enqueuer.resolution, enqueuer.codegen]) { + world.forEach((WorkItem work) { + internalError(work.element, "Work list is not empty."); + }); + } + if (!REPORT_EXCESS_RESOLUTION) return; + var resolved = new Set.from(enqueuer.resolution.resolvedElements.keys); + for (Element e in enqueuer.codegen.generatedCode.keys) { + resolved.remove(e); + } + for (Element e in new Set.from(resolved)) { + if (e.isClass() || + e.isField() || + e.isTypeVariable() || + e.isTypedef() || + identical(e.kind, ElementKind.ABSTRACT_FIELD)) { + resolved.remove(e); + } + if (identical(e.kind, ElementKind.GENERATIVE_CONSTRUCTOR)) { + ClassElement enclosingClass = e.getEnclosingClass(); + resolved.remove(e); + + } + if (identical(e.getLibrary(), jsHelperLibrary)) { + resolved.remove(e); + } + if (identical(e.getLibrary(), interceptorsLibrary)) { + resolved.remove(e); + } + } + log('Excess resolution work: ${resolved.length}.'); + for (Element e in resolved) { + reportWarning(e, + MessageKind.GENERIC, + {'text': 'Warning: $e resolved but not compiled.'}); + } + } + + TreeElements analyzeElement(Element element) { + assert(invariant(element, + element.impliesType() || + element.isField() || + element.isFunction() || + element.isGenerativeConstructor() || + element.isGetter() || + element.isSetter(), + message: 'Unexpected element kind: ${element.kind}')); + assert(invariant(element, element is AnalyzableElement, + message: 'Element $element is not analyzable.')); + assert(invariant(element, element.isDeclaration)); + ResolutionEnqueuer world = enqueuer.resolution; + TreeElements elements = world.getCachedElements(element); + if (elements != null) return elements; + assert(parser != null); + Node tree = parser.parse(element); + assert(invariant(element, !element.isSynthesized || tree == null)); + if (tree != null) validator.validate(tree); + elements = resolver.resolve(element); + if (tree != null && elements != null && !analyzeSignaturesOnly) { + // Only analyze nodes with a corresponding [TreeElements]. + checker.check(elements); + } + world.resolvedElements[element] = elements; + return elements; + } + + TreeElements analyze(ResolutionWorkItem work, ResolutionEnqueuer world) { + assert(invariant(work.element, identical(world, enqueuer.resolution))); + assert(invariant(work.element, !work.isAnalyzed(), + message: 'Element ${work.element} has already been analyzed')); + if (progress.elapsedMilliseconds > 500) { + // TODO(ahe): Add structured diagnostics to the compiler API and + // use it to separate this from the --verbose option. + if (phase == PHASE_RESOLVING) { + log('Resolved ${enqueuer.resolution.resolvedElements.length} ' + 'elements.'); + progress.reset(); + } + } + Element element = work.element; + TreeElements result = world.getCachedElements(element); + if (result != null) return result; + result = analyzeElement(element); + backend.onElementResolved(element, result); + return result; + } + + void codegen(CodegenWorkItem work, CodegenEnqueuer world) { + assert(invariant(work.element, identical(world, enqueuer.codegen))); + if (progress.elapsedMilliseconds > 500) { + // TODO(ahe): Add structured diagnostics to the compiler API and + // use it to separate this from the --verbose option. + log('Compiled ${enqueuer.codegen.generatedCode.length} methods.'); + progress.reset(); + } + backend.codegen(work); + } + + FunctionSignature resolveSignature(FunctionElement element) { + return withCurrentElement(element, + () => resolver.resolveSignature(element)); + } + + void resolveTypedef(TypedefElement element) { + withCurrentElement(element, + () => resolver.resolve(element)); + } + + void reportError(Spannable node, + MessageKind messageKind, + [Map arguments = const {}]) { + reportDiagnosticInternal( + node, messageKind, arguments, api.Diagnostic.ERROR); + } + + void reportFatalError(Spannable node, MessageKind messageKind, + [Map arguments = const {}]) { + reportError(node, messageKind, arguments); + // TODO(ahe): Make this only abort the current method. + throw new CompilerCancelledException( + 'Error: Cannot continue due to previous error.'); + } + + void reportWarning(Spannable node, MessageKind messageKind, + [Map arguments = const {}]) { + // TODO(ahe): Don't suppress these warning when the type checker + // is more complete. + if (messageKind == MessageKind.MISSING_RETURN) return; + if (messageKind == MessageKind.MAYBE_MISSING_RETURN) return; + reportDiagnosticInternal( + node, messageKind, arguments, api.Diagnostic.WARNING); + } + + void reportInfo(Spannable node, MessageKind messageKind, + [Map arguments = const {}]) { + reportDiagnosticInternal(node, messageKind, arguments, api.Diagnostic.INFO); + } + + void reportHint(Spannable node, MessageKind messageKind, + [Map arguments = const {}]) { + reportDiagnosticInternal(node, messageKind, arguments, api.Diagnostic.HINT); + } + + /// For debugging only, print a message with a source location. + void reportHere(Spannable node, String debugMessage) { + reportInfo(node, MessageKind.GENERIC, {'text': 'HERE: $debugMessage'}); + } + + void reportDiagnosticInternal(Spannable node, + MessageKind messageKind, + Map arguments, + api.Diagnostic kind) { + if (!showPackageWarnings) { + switch (kind) { + case api.Diagnostic.WARNING: + case api.Diagnostic.HINT: + Element element = elementFromSpannable(node); + if (!inUserCode(element)) { + Uri uri = getCanonicalUri(element); + SuppressionInfo info = + suppressedWarnings.putIfAbsent(uri, () => new SuppressionInfo()); + if (kind == api.Diagnostic.WARNING) { + info.warnings++; + } else { + info.hints++; + } + lastDiagnosticWasFiltered = true; + return; + } + break; + case api.Diagnostic.INFO: + if (lastDiagnosticWasFiltered) { + return; + } + break; + } + } + lastDiagnosticWasFiltered = false; + reportDiagnostic( + node, messageKind.message(arguments, terseDiagnostics), kind); + } + + void reportDiagnostic(Spannable span, + Message message, + api.Diagnostic kind); + + void reportAssertionFailure(SpannableAssertionFailure ex) { + String message = (ex.message != null) ? tryToString(ex.message) + : tryToString(ex); + SourceSpan span = spanFromSpannable(ex.node); + reportDiagnosticInternal( + ex.node, MessageKind.GENERIC, {'text': message}, api.Diagnostic.CRASH); + } + + SourceSpan spanFromTokens(Token begin, Token end, [Uri uri]) { + if (begin == null || end == null) { + // TODO(ahe): We can almost always do better. Often it is only + // end that is null. Otherwise, we probably know the current + // URI. + throw 'Cannot find tokens to produce error message.'; + } + if (uri == null && currentElement != null) { + uri = currentElement.getCompilationUnit().script.readableUri; + } + return SourceSpan.withCharacterOffsets(begin, end, + (beginOffset, endOffset) => new SourceSpan(uri, beginOffset, endOffset)); + } + + SourceSpan spanFromNode(Node node) { + return spanFromTokens(node.getBeginToken(), node.getEndToken()); + } + + SourceSpan spanFromElement(Element element) { + if (Elements.isErroneousElement(element)) { + element = element.enclosingElement; + } + if (element.position() == null && + !element.isLibrary() && + !element.isCompilationUnit()) { + // Sometimes, the backend fakes up elements that have no + // position. So we use the enclosing element instead. It is + // not a good error location, but cancel really is "internal + // error" or "not implemented yet", so the vicinity is good + // enough for now. + element = element.enclosingElement; + // TODO(ahe): I plan to overhaul this infrastructure anyways. + } + if (element == null) { + element = currentElement; + } + Token position = element.position(); + Uri uri = element.getCompilationUnit().script.readableUri; + return (position == null) + ? new SourceSpan(uri, 0, 0) + : spanFromTokens(position, position, uri); + } + + SourceSpan spanFromHInstruction(HInstruction instruction) { + Element element = instruction.sourceElement; + if (element == null) element = currentElement; + var position = instruction.sourcePosition; + if (position == null) return spanFromElement(element); + Token token = position.token; + if (token == null) return spanFromElement(element); + Uri uri = element.getCompilationUnit().script.readableUri; + return spanFromTokens(token, token, uri); + } + + /** + * Translates the [resolvedUri] into a readable URI. + * + * The [importingLibrary] holds the library importing [resolvedUri] or + * [:null:] if [resolvedUri] is loaded as the main library. The + * [importingLibrary] is used to grant access to internal libraries from + * platform libraries and patch libraries. + * + * If the [resolvedUri] is not accessible from [importingLibrary], this method + * is responsible for reporting errors. + * + * See [LibraryLoader] for terminology on URIs. + */ + Uri translateResolvedUri(LibraryElement importingLibrary, + Uri resolvedUri, Node node) { + unimplemented(importingLibrary, 'Compiler.translateResolvedUri'); + return null; + } + + /** + * Reads the script specified by the [readableUri]. + * + * See [LibraryLoader] for terminology on URIs. + */ + Future + +"""); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/elements.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/elements.dart new file mode 100644 index 0000000..d9bc5d6 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/elements.dart @@ -0,0 +1,1136 @@ +// Copyright (c) 2012, 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. + +library elements; + + +import '../tree/tree.dart'; +import '../util/util.dart'; +import '../resolution/resolution.dart'; + +import '../dart2jslib.dart' show InterfaceType, + DartType, + TypeVariableType, + TypedefType, + DualKind, + MessageKind, + DiagnosticListener, + Script, + FunctionType, + Selector, + Constant, + Compiler, + isPrivateName; + +import '../dart_types.dart'; + +import '../scanner/scannerlib.dart' show Token, + isUserDefinableOperator, + isMinusOperator; + +import '../ordered_typeset.dart' show OrderedTypeSet; + +import 'visitor.dart' show ElementVisitor; + +part 'names.dart'; + +const int STATE_NOT_STARTED = 0; +const int STATE_STARTED = 1; +const int STATE_DONE = 2; + +class ElementCategory { + /** + * Represents things that we don't expect to find when looking in a + * scope. + */ + static const int NONE = 0; + + /** Field, parameter, or variable. */ + static const int VARIABLE = 1; + + /** Function, method, or foreign function. */ + static const int FUNCTION = 2; + + static const int CLASS = 4; + + static const int PREFIX = 8; + + /** Constructor or factory. */ + static const int FACTORY = 16; + + static const int ALIAS = 32; + + static const int SUPER = 64; + + /** Type variable */ + static const int TYPE_VARIABLE = 128; + + static const int IMPLIES_TYPE = CLASS | ALIAS | TYPE_VARIABLE; +} + +class ElementKind { + final String id; + final int category; + + const ElementKind(String this.id, this.category); + + static const ElementKind VARIABLE = + const ElementKind('variable', ElementCategory.VARIABLE); + static const ElementKind PARAMETER = + const ElementKind('parameter', ElementCategory.VARIABLE); + // Parameters in constructors that directly initialize fields. For example: + // [:A(this.field):]. + static const ElementKind FIELD_PARAMETER = + const ElementKind('field_parameter', ElementCategory.VARIABLE); + static const ElementKind FUNCTION = + const ElementKind('function', ElementCategory.FUNCTION); + static const ElementKind CLASS = + const ElementKind('class', ElementCategory.CLASS); + static const ElementKind GENERATIVE_CONSTRUCTOR = + const ElementKind('generative_constructor', ElementCategory.FACTORY); + static const ElementKind FIELD = + const ElementKind('field', ElementCategory.VARIABLE); + static const ElementKind VARIABLE_LIST = + const ElementKind('variable_list', ElementCategory.NONE); + static const ElementKind FIELD_LIST = + const ElementKind('field_list', ElementCategory.NONE); + static const ElementKind GENERATIVE_CONSTRUCTOR_BODY = + const ElementKind('generative_constructor_body', ElementCategory.NONE); + static const ElementKind COMPILATION_UNIT = + const ElementKind('compilation_unit', ElementCategory.NONE); + static const ElementKind GETTER = + const ElementKind('getter', ElementCategory.NONE); + static const ElementKind SETTER = + const ElementKind('setter', ElementCategory.NONE); + static const ElementKind TYPE_VARIABLE = + const ElementKind('type_variable', ElementCategory.TYPE_VARIABLE); + static const ElementKind ABSTRACT_FIELD = + const ElementKind('abstract_field', ElementCategory.VARIABLE); + static const ElementKind LIBRARY = + const ElementKind('library', ElementCategory.NONE); + static const ElementKind PREFIX = + const ElementKind('prefix', ElementCategory.PREFIX); + static const ElementKind TYPEDEF = + const ElementKind('typedef', ElementCategory.ALIAS); + + static const ElementKind STATEMENT = + const ElementKind('statement', ElementCategory.NONE); + static const ElementKind LABEL = + const ElementKind('label', ElementCategory.NONE); + static const ElementKind VOID = + const ElementKind('void', ElementCategory.NONE); + + static const ElementKind AMBIGUOUS = + const ElementKind('ambiguous', ElementCategory.NONE); + static const ElementKind WARN_ON_USE = + const ElementKind('warn_on_use', ElementCategory.NONE); + static const ElementKind ERROR = + const ElementKind('error', ElementCategory.NONE); + + toString() => id; +} + +/** + * A declared element of a program. + * + * The declared elements of a program include classes, methods, + * fields, variables, parameters, etc. + * + * Sometimes it makes sense to construct "synthetic" elements that + * have not been declared anywhere in a program, for example, there + * are elements corresponding to "dynamic", "null", and unresolved + * references. + * + * Elements are distinct from types ([DartType]). For example, there + * is one declaration of the class List, but several related types, + * for example, List, List, List, etc. + * + * Elements are distinct from AST nodes ([Node]), and there normally is a + * one-to-one correspondence between an AST node and an element + * (except that not all kinds of AST nodes have an associated + * element). + * + * AST nodes represent precisely what is written in source code, for + * example, when a user writes "class MyClass {}", the corresponding + * AST node does not have a superclass. On the other hand, the + * corresponding element (once fully resolved) will record the + * information about the implicit superclass as defined by the + * language semantics. + * + * Generally, the contents of a method are represented as AST nodes + * without additional elements, but things like local functions, local + * variables, and labels have a corresponding element. + * + * We generally say that scanning, parsing, resolution, and type + * checking comprise the "front-end" of the compiler. The "back-end" + * includes things like SSA graph construction, optimizations, and + * code generation. + * + * The front-end data structures are designed to be reusable by + * several back-ends. For example, we may want to support emitting + * minified Dart and JavaScript code in one go. Also, we're planning + * on adding an incremental compilation server that should be able to + * reuse elements between compilations. So to keep things simple, it + * is best if the backends avoid setting state directly in elements. + * It is better to keep such state in a table on the side. + */ +abstract class Element implements Spannable { + String get name; + ElementKind get kind; + Modifiers get modifiers; + Element get enclosingElement; + Link get metadata; + + Node parseNode(DiagnosticListener listener); + + /// Do not use [computeType] outside of the resolver; instead retrieve the + /// type from the corresponding field: + /// - `type` for fields, variables, type variable, and function elements. + /// - `thisType` or `rawType` for [TypeDeclarationElement]s (classes and + /// typedefs), depending on the use case. + /// Trying to access a type that has not been computed in resolution is an + /// error and calling [computeType] covers that error. + /// This method will go away! + @deprecated DartType computeType(Compiler compiler); + + bool isFunction(); + bool isConstructor(); + bool isClosure(); + bool isMember(); + bool isInstanceMember(); + + bool isFactoryConstructor(); + bool isGenerativeConstructor(); + bool isGenerativeConstructorBody(); + bool isCompilationUnit(); + bool isClass(); + bool isPrefix(); + bool isVariable(); + bool isParameter(); + bool isStatement(); + bool isTypedef(); + bool isTypeVariable(); + bool isField(); + bool isFieldParameter(); + bool isAbstractField(); + bool isGetter(); + bool isSetter(); + bool isAccessor(); + bool isLibrary(); + bool isErroneous(); + bool isAmbiguous(); + bool isWarnOnUse(); + + /// Returns true if this [Element] is a top level element. + /// That is, if it is not defined within the scope of a class. + /// + /// This means whether the enclosing element is a compilation unit. + /// With the exception of [ClosureClassElement] that is considered top level + /// as all other classes. + bool isTopLevel(); + bool isAssignable(); + bool isNative(); + bool isDeferredLoaderGetter(); + + bool impliesType(); + + Token position(); + + CompilationUnitElement getCompilationUnit(); + LibraryElement getLibrary(); + LibraryElement getImplementationLibrary(); + ClassElement getEnclosingClass(); + Element getEnclosingClassOrCompilationUnit(); + Element getEnclosingMember(); + Element getOutermostEnclosingMemberOrTopLevel(); + + FunctionElement asFunctionElement(); + + bool get isPatched; + bool get isPatch; + bool get isImplementation; + bool get isDeclaration; + bool get isSynthesized; + bool get isForwardingConstructor; + bool get isMixinApplication; + + Element get implementation; + Element get declaration; + Element get patch; + Element get origin; + + bool hasFixedBackendName(); + String fixedBackendName(); + + bool get isAbstract; + bool isForeign(Compiler compiler); + + void addMetadata(MetadataAnnotation annotation); + void setNative(String name); + void setFixedBackendName(String name); + + Scope buildScope(); + + /// If the element is a forwarding constructor, [targetConstructor] holds + /// the generative constructor that the forwarding constructor points to + /// (possibly via other forwarding constructors). + FunctionElement get targetConstructor; + + void diagnose(Element context, DiagnosticListener listener); + + TreeElements get treeElements; + + accept(ElementVisitor visitor); +} + +class Elements { + static bool isUnresolved(Element e) { + return e == null || e.isErroneous(); + } + static bool isErroneousElement(Element e) => e != null && e.isErroneous(); + + /// Unwraps [element] reporting any warnings attached to it, if any. + static Element unwrap(Element element, + DiagnosticListener listener, + Spannable spannable) { + if (element != null && element.isWarnOnUse()) { + WarnOnUseElement wrappedElement = element; + element = wrappedElement.unwrap(listener, spannable); + } + return element; + } + + static bool isClass(Element e) => e != null && e.kind == ElementKind.CLASS; + static bool isTypedef(Element e) { + return e != null && e.kind == ElementKind.TYPEDEF; + } + + static bool isLocal(Element element) { + return !Elements.isUnresolved(element) + && !element.isInstanceMember() + && !isStaticOrTopLevelField(element) + && !isStaticOrTopLevelFunction(element) + && (identical(element.kind, ElementKind.VARIABLE) || + identical(element.kind, ElementKind.PARAMETER) || + identical(element.kind, ElementKind.FUNCTION)); + } + + static bool isInstanceField(Element element) { + return !Elements.isUnresolved(element) + && element.isInstanceMember() + && (identical(element.kind, ElementKind.FIELD) + || identical(element.kind, ElementKind.GETTER) + || identical(element.kind, ElementKind.SETTER)); + } + + static bool isStaticOrTopLevel(Element element) { + // TODO(ager): This should not be necessary when patch support has + // been reworked. + if (!Elements.isUnresolved(element) + && element.modifiers.isStatic()) { + return true; + } + return !Elements.isUnresolved(element) + && !element.isAmbiguous() + && !element.isInstanceMember() + && !element.isPrefix() + && element.enclosingElement != null + && (element.enclosingElement.kind == ElementKind.CLASS || + element.enclosingElement.kind == ElementKind.COMPILATION_UNIT || + element.enclosingElement.kind == ElementKind.LIBRARY || + element.enclosingElement.kind == ElementKind.PREFIX); + } + + static bool isInStaticContext(Element element) { + if (isUnresolved(element)) return true; + if (element.enclosingElement.isClosure()) { + var closureClass = element.enclosingElement; + element = closureClass.methodElement; + } + Element outer = element.getOutermostEnclosingMemberOrTopLevel(); + if (isUnresolved(outer)) return true; + if (outer.isTopLevel()) return true; + if (outer.isGenerativeConstructor()) return false; + if (outer.isInstanceMember()) return false; + return true; + } + + static bool isStaticOrTopLevelField(Element element) { + return isStaticOrTopLevel(element) + && (identical(element.kind, ElementKind.FIELD) + || identical(element.kind, ElementKind.GETTER) + || identical(element.kind, ElementKind.SETTER)); + } + + static bool isStaticOrTopLevelFunction(Element element) { + return isStaticOrTopLevel(element) + && (identical(element.kind, ElementKind.FUNCTION)); + } + + static bool isInstanceMethod(Element element) { + return !Elements.isUnresolved(element) + && element.isInstanceMember() + && (identical(element.kind, ElementKind.FUNCTION)); + } + + static bool isNativeOrExtendsNative(ClassElement element) { + if (element == null) return false; + if (element.isNative()) return true; + assert(element.resolutionState == STATE_DONE); + return isNativeOrExtendsNative(element.superclass); + } + + static bool isInstanceSend(Send send, TreeElements elements) { + Element element = elements[send]; + if (element == null) return !isClosureSend(send, element); + return isInstanceMethod(element) || isInstanceField(element); + } + + static bool isClosureSend(Send send, Element element) { + if (send.isPropertyAccess) return false; + if (send.receiver != null) return false; + Node selector = send.selector; + // this(). + if (selector.isThis()) return true; + // (o)() or foo()(). + if (element == null && selector.asIdentifier() == null) return true; + if (element == null) return false; + // foo() with foo a local or a parameter. + return isLocal(element); + } + + static String reconstructConstructorNameSourceString(Element element) { + if (element.name == '') { + return element.getEnclosingClass().name; + } else { + return reconstructConstructorName(element); + } + } + + // TODO(johnniwinther): Remove this method. + static String reconstructConstructorName(Element element) { + String className = element.getEnclosingClass().name; + if (element.name == '') { + return className; + } else { + return '$className\$${element.name}'; + } + } + + /** + * Map an operator-name to a valid Dart identifier. + * + * For non-operator names, this metod just returns its input. + * + * The results returned from this method are guaranteed to be valid + * JavaScript identifers, except it may include reserved words for + * non-operator names. + */ + static String operatorNameToIdentifier(String name) { + if (name == null) { + return name; + } else if (identical(name, '==')) { + return r'operator$eq'; + } else if (identical(name, '~')) { + return r'operator$not'; + } else if (identical(name, '[]')) { + return r'operator$index'; + } else if (identical(name, '[]=')) { + return r'operator$indexSet'; + } else if (identical(name, '*')) { + return r'operator$mul'; + } else if (identical(name, '/')) { + return r'operator$div'; + } else if (identical(name, '%')) { + return r'operator$mod'; + } else if (identical(name, '~/')) { + return r'operator$tdiv'; + } else if (identical(name, '+')) { + return r'operator$add'; + } else if (identical(name, '<<')) { + return r'operator$shl'; + } else if (identical(name, '>>')) { + return r'operator$shr'; + } else if (identical(name, '>=')) { + return r'operator$ge'; + } else if (identical(name, '>')) { + return r'operator$gt'; + } else if (identical(name, '<=')) { + return r'operator$le'; + } else if (identical(name, '<')) { + return r'operator$lt'; + } else if (identical(name, '&')) { + return r'operator$and'; + } else if (identical(name, '^')) { + return r'operator$xor'; + } else if (identical(name, '|')) { + return r'operator$or'; + } else if (identical(name, '-')) { + return r'operator$sub'; + } else if (identical(name, 'unary-')) { + return r'operator$negate'; + } else { + return name; + } + } + + static String constructOperatorNameOrNull(String op, bool isUnary) { + if (isMinusOperator(op)) { + return isUnary ? 'unary-' : op; + } else if (isUserDefinableOperator(op)) { + return op; + } else { + return null; + } + } + + static String constructOperatorName(String op, bool isUnary) { + String operatorName = constructOperatorNameOrNull(op, isUnary); + if (operatorName == null) throw 'Unhandled operator: $op'; + else return operatorName; + } + + static String mapToUserOperatorOrNull(String op) { + if (identical(op, '!=')) return '=='; + if (identical(op, '*=')) return '*'; + if (identical(op, '/=')) return '/'; + if (identical(op, '%=')) return '%'; + if (identical(op, '~/=')) return '~/'; + if (identical(op, '+=')) return '+'; + if (identical(op, '-=')) return '-'; + if (identical(op, '<<=')) return '<<'; + if (identical(op, '>>=')) return '>>'; + if (identical(op, '&=')) return '&'; + if (identical(op, '^=')) return '^'; + if (identical(op, '|=')) return '|'; + + return null; + } + + static String mapToUserOperator(String op) { + String userOperator = mapToUserOperatorOrNull(op); + if (userOperator == null) throw 'Unhandled operator: $op'; + else return userOperator; + } + + static bool isNumberOrStringSupertype(Element element, Compiler compiler) { + LibraryElement coreLibrary = compiler.coreLibrary; + return (element == coreLibrary.find('Comparable')); + } + + static bool isStringOnlySupertype(Element element, Compiler compiler) { + LibraryElement coreLibrary = compiler.coreLibrary; + return element == coreLibrary.find('Pattern'); + } + + static bool isListSupertype(Element element, Compiler compiler) { + LibraryElement coreLibrary = compiler.coreLibrary; + return element == coreLibrary.find('Iterable'); + } + + /// A `compareTo` function that places [Element]s in a consistent order based + /// on the source code order. + static int compareByPosition(Element a, Element b) { + if (identical(a, b)) return 0; + int r = a.getLibrary().compareTo(b.getLibrary()); + if (r != 0) return r; + r = a.getCompilationUnit().compareTo(b.getCompilationUnit()); + if (r != 0) return r; + Token positionA = a.position(); + Token positionB = b.position(); + int offsetA = positionA == null ? -1 : positionA.charOffset; + int offsetB = positionB == null ? -1 : positionB.charOffset; + r = offsetA.compareTo(offsetB); + if (r != 0) return r; + r = a.name.compareTo(b.name); + if (r != 0) return r; + // Same file, position and name. If this happens, we should find out why + // and make the order total and independent of hashCode. + return a.hashCode.compareTo(b.hashCode); + } + + static List sortedByPosition(Iterable elements) { + return elements.toList()..sort(compareByPosition); + } + + static bool isFixedListConstructorCall(Element element, + Send node, + Compiler compiler) { + return element == compiler.unnamedListConstructor + && node.isCall + && !node.arguments.isEmpty + && node.arguments.tail.isEmpty; + } + + static bool isGrowableListConstructorCall(Element element, + Send node, + Compiler compiler) { + return element == compiler.unnamedListConstructor + && node.isCall + && node.arguments.isEmpty; + } + + static bool isFilledListConstructorCall(Element element, + Send node, + Compiler compiler) { + return element == compiler.filledListConstructor + && node.isCall + && !node.arguments.isEmpty + && !node.arguments.tail.isEmpty + && node.arguments.tail.tail.isEmpty; + } + + static bool isConstructorOfTypedArraySubclass(Element element, + Compiler compiler) { + if (compiler.typedDataLibrary == null) return false; + if (!element.isConstructor()) return false; + FunctionElement constructor = element; + constructor = constructor.redirectionTarget; + ClassElement cls = constructor.getEnclosingClass(); + return cls.getLibrary() == compiler.typedDataLibrary + && cls.isNative() + && compiler.world.isSubtype(compiler.typedDataClass, cls) + && compiler.world.isSubtype(compiler.listClass, cls) + && constructor.name == ''; + } + + static bool switchStatementHasContinue(SwitchStatement node, + TreeElements elements) { + for (SwitchCase switchCase in node.cases) { + for (Node labelOrCase in switchCase.labelsAndCases) { + Node label = labelOrCase.asLabel(); + if (label != null) { + LabelElement labelElement = elements[label]; + if (labelElement != null && labelElement.isContinueTarget) { + return true; + } + } + } + } + return false; + } + + static bool isUnusedLabel(LabeledStatement node, TreeElements elements) { + Node body = node.statement; + TargetElement element = elements[body]; + // Labeled statements with no element on the body have no breaks. + // A different target statement only happens if the body is itself + // a break or continue for a different target. In that case, this + // label is also always unused. + return element == null || element.statement != body; + } +} + +abstract class ErroneousElement extends Element implements FunctionElement { + MessageKind get messageKind; + Map get messageArguments; + String get message; +} + +/// An [Element] whose usage should cause a warning. +abstract class WarnOnUseElement extends Element { + /// The element whose usage cause a warning. + Element get wrappedElement; + + /// Reports the attached warning and returns the wrapped element. + /// [usageSpannable] is used to report messages on the reference of + /// [wrappedElement]. + Element unwrap(DiagnosticListener listener, Spannable usageSpannable); +} + +abstract class AmbiguousElement extends Element { + MessageKind get messageKind; + Map get messageArguments; + Element get existingElement; + Element get newElement; +} + +// TODO(kasperl): This probably shouldn't be called an element. It's +// just an interface shared by classes and libraries. +abstract class ScopeContainerElement implements Element { + Element localLookup(String elementName); + + void forEachLocalMember(f(Element element)); +} + +abstract class ClosureContainer implements Element { + List get nestedClosures; +} + +abstract class CompilationUnitElement extends Element { + Script get script; + PartOf get partTag; + + void forEachLocalMember(f(Element element)); + void addMember(Element element, DiagnosticListener listener); + void setPartOf(PartOf tag, DiagnosticListener listener); + bool get hasMembers; + + int compareTo(CompilationUnitElement other); +} + +abstract class LibraryElement extends Element implements ScopeContainerElement { + /** + * The canonical uri for this library. + * + * For user libraries the canonical uri is the script uri. For platform + * libraries the canonical uri is of the form [:dart:x:]. + */ + Uri get canonicalUri; + CompilationUnitElement get entryCompilationUnit; + Link get compilationUnits; + Link get tags; + LibraryName get libraryTag; + Link get exports; + + /** + * [:true:] if this library is part of the platform, that is, its canonical + * uri has the scheme 'dart'. + */ + bool get isPlatformLibrary; + + /** + * [:true:] if this library is from a package, that is, its canonical uri has + * the scheme 'package'. + */ + bool get isPackageLibrary; + + /** + * [:true:] if this library is a platform library whose path starts with + * an underscore. + */ + bool get isInternalLibrary; + bool get canUseNative; + bool get exportsHandled; + + // TODO(kasperl): We should try to get rid of these. + void set canUseNative(bool value); + void set libraryTag(LibraryName value); + + LibraryElement get implementation; + + void addCompilationUnit(CompilationUnitElement element); + void addTag(LibraryTag tag, DiagnosticListener listener); + void addImport(Element element, Import import, DiagnosticListener listener); + + /// Record which element an import or export tag resolved to. + /// (Belongs on builder object). + void recordResolvedTag(LibraryDependency tag, LibraryElement library); + + /// Return the library element corresponding to an import or export. + LibraryElement getLibraryFromTag(LibraryDependency tag); + + void addMember(Element element, DiagnosticListener listener); + void addToScope(Element element, DiagnosticListener listener); + + // TODO(kasperl): Get rid of this method. + Iterable getNonPrivateElementsInScope(); + + void setExports(Iterable exportedElements); + + Element find(String elementName); + Element findLocal(String elementName); + Element findExported(String elementName); + void forEachExport(f(Element element)); + + /// Returns the imports that import element into this library. + Link getImportsFor(Element element); + + bool hasLibraryName(); + String getLibraryName(); + String getLibraryOrScriptName(); + + int compareTo(LibraryElement other); +} + +abstract class PrefixElement extends Element { + void addImport(Element element, Import import, DiagnosticListener listener); + Element lookupLocalMember(String memberName); + /// Is true if this prefix belongs to a deferred import. + bool get isDeferred; + void markAsDeferred(Import import); + Import get deferredImport; +} + +abstract class TypedefElement extends Element + implements TypeDeclarationElement { + TypedefType get thisType; + TypedefType get rawType; + DartType get alias; + FunctionSignature get functionSignature; + Link get typeVariables; + + bool get isResolved; + + // TODO(kasperl): Try to get rid of these setters. + void set alias(DartType value); + void set functionSignature(FunctionSignature value); + + void checkCyclicReference(Compiler compiler); +} + +abstract class VariableElement extends Element implements TypedElement { + Expression get initializer; +} + +abstract class FieldElement extends VariableElement + implements ClosureContainer {} + +abstract class ParameterElement extends VariableElement { + VariableDefinitions get node; + FunctionSignature get functionSignature; +} + +abstract class FieldParameterElement extends ParameterElement { + VariableElement get fieldElement; +} + +/** + * A synthetic element which holds a getter and/or a setter. + * + * This element unifies handling of fields and getters/setters. When + * looking at code like "foo.x", we don't have to look for both a + * field named "x", a getter named "x", and a setter named "x=". + */ +abstract class AbstractFieldElement extends Element { + FunctionElement get getter; + FunctionElement get setter; +} + +abstract class FunctionSignature { + FunctionType get type; + Link get requiredParameters; + Link get optionalParameters; + + int get requiredParameterCount; + int get optionalParameterCount; + bool get optionalParametersAreNamed; + Element get firstOptionalParameter; + + int get parameterCount; + List get orderedOptionalParameters; + + void forEachParameter(void function(Element parameter)); + void forEachRequiredParameter(void function(Element parameter)); + void forEachOptionalParameter(void function(Element parameter)); + + void orderedForEachParameter(void function(Element parameter)); + + bool isCompatibleWith(FunctionSignature constructorSignature); +} + +abstract class FunctionElement extends Element + implements TypedElement, ClosureContainer { + FunctionExpression get node; + DartType get type; + FunctionSignature get functionSignature; + FunctionElement get redirectionTarget; + FunctionElement get defaultImplementation; + + FunctionElement get patch; + FunctionElement get origin; + + bool get isRedirectingFactory; + + /** + * Compute the type of the target of a constructor for an instantiation site + * with type [:newType:]. + */ + InterfaceType computeTargetType(InterfaceType newType); + + // TODO(kasperl): These are bit fishy. Do we really need them? + void set patch(FunctionElement value); + void set origin(FunctionElement value); + void set defaultImplementation(FunctionElement value); + + /// Do not use [computeSignature] outside of the resolver; instead retrieve + /// the signature through the [functionSignature] field. + /// Trying to access a function signature that has not been computed in + /// resolution is an error and calling [computeSignature] covers that error. + /// This method will go away! + @deprecated FunctionSignature computeSignature(Compiler compiler); + + FunctionExpression parseNode(DiagnosticListener listener); +} + +abstract class ConstructorBodyElement extends FunctionElement { + FunctionElement get constructor; +} + +/** + * [TypeDeclarationElement] defines the common interface for class/interface + * declarations and typedefs. + */ +abstract class TypeDeclarationElement extends Element { + GenericType get thisType; + GenericType get rawType; + + /** + * The type variables declared on this declaration. The type variables are not + * available until the type of the element has been computed through + * [computeType]. + */ + Link get typeVariables; +} + +abstract class ClassElement extends TypeDeclarationElement + implements ScopeContainerElement { + int get id; + + /// The length of the longest inheritance path from [:Object:]. + int get hierarchyDepth; + + InterfaceType get rawType; + InterfaceType get thisType; + + ClassElement get superclass; + + DartType get supertype; + OrderedTypeSet get allSupertypesAndSelf; + Link get allSupertypes; + Link get interfaces; + + bool get hasConstructor; + Link get constructors; + + ClassElement get patch; + ClassElement get origin; + ClassElement get declaration; + ClassElement get implementation; + + int get supertypeLoadState; + int get resolutionState; + bool get isResolved; + String get nativeTagInfo; + + bool get isMixinApplication; + bool get isUnnamedMixinApplication; + bool get hasBackendMembers; + bool get hasLocalScopeMembers; + + // TODO(kasperl): These are bit fishy. Do we really need them? + void set supertype(DartType value); + void set interfaces(Link value); + void set patch(ClassElement value); + void set origin(ClassElement value); + void set supertypeLoadState(int value); + void set resolutionState(int value); + void set nativeTagInfo(String value); + + bool isObject(Compiler compiler); + bool isSubclassOf(ClassElement cls); + /// Returns true if `this` explicitly/nominally implements [intrface]. + /// + /// Note that, if [intrface] is the `Function` class, this method returns + /// falso for a class that has a `call` method but does not explicitly + /// implement `Function`. + bool implementsInterface(ClassElement intrface); + bool hasFieldShadowedBy(Element fieldMember); + + /// Returns `true` if this class has a @proxy annotation. + bool get isProxy; + + /// Returns `true` if the class hierarchy for this class contains errors. + bool get hasIncompleteHierarchy; + + void ensureResolved(Compiler compiler); + + void addMember(Element element, DiagnosticListener listener); + void addToScope(Element element, DiagnosticListener listener); + + void setDefaultConstructor(FunctionElement constructor, Compiler compiler); + + void addBackendMember(Element element); + void reverseBackendMembers(); + + Element lookupMember(String memberName); + Element lookupSelector(Selector selector, Compiler compiler); + Element lookupSuperSelector(Selector selector, Compiler compiler); + + Element lookupLocalMember(String memberName); + Element lookupBackendMember(String memberName); + Element lookupSuperMember(String memberName); + + Element lookupSuperMemberInLibrary(String memberName, + LibraryElement library); + + Element validateConstructorLookupResults(Selector selector, + Element result, + Element noMatch(Element)); + + Element lookupConstructor(Selector selector, [Element noMatch(Element)]); + + void forEachMember(void f(ClassElement enclosingClass, Element member), + {bool includeBackendMembers: false, + bool includeSuperAndInjectedMembers: false}); + + void forEachInstanceField(void f(ClassElement enclosingClass, + FieldElement field), + {bool includeSuperAndInjectedMembers: false}); + + /// Similar to [forEachInstanceField] but visits static fields. + void forEachStaticField(void f(ClassElement enclosingClass, Element field)); + + void forEachBackendMember(void f(Element member)); + + Link computeTypeParameters(Compiler compiler); + + /// Looks up the member [name] in this class. + Member lookupClassMember(Name name); + + /// Calls [f] with each member of this class. + void forEachClassMember(f(Member member)); + + /// Looks up the member [name] in the interface of this class. + MemberSignature lookupInterfaceMember(Name name); + + /// Calls [f] with each member of the interface of this class. + void forEachInterfaceMember(f(MemberSignature member)); + + /// Returns the type of the 'call' method in the interface of this class, or + /// `null` if the interface has no 'call' method. + FunctionType get callType; +} + +abstract class MixinApplicationElement extends ClassElement { + ClassElement get mixin; + InterfaceType get mixinType; + void set mixinType(InterfaceType value); + void addConstructor(FunctionElement constructor); +} + +abstract class LabelElement extends Element { + Label get label; + String get labelName; + TargetElement get target; + + bool get isTarget; + bool get isBreakTarget; + bool get isContinueTarget; + + void setBreakTarget(); + void setContinueTarget(); +} + +abstract class TargetElement extends Element { + Node get statement; + int get nestingLevel; + Link get labels; + + bool get isTarget; + bool get isBreakTarget; + bool get isContinueTarget; + bool get isSwitch; + + // TODO(kasperl): Try to get rid of these. + void set isBreakTarget(bool value); + void set isContinueTarget(bool value); + + LabelElement addLabel(Label label, String labelName); +} + +/// The [Element] for a type variable declaration on a generic class or typedef. +abstract class TypeVariableElement extends Element implements TypedElement { + /// The [type] defined by the type variable. + TypeVariableType get type; + + /// The upper bound on the type variable. If not explicitly declared, this is + /// `Object`. + DartType get bound; +} + +abstract class MetadataAnnotation implements Spannable { + Constant get value; + Element get annotatedElement; + int get resolutionState; + Token get beginToken; + Token get endToken; + + // TODO(kasperl): Try to get rid of these. + void set annotatedElement(Element value); + void set resolutionState(int value); + + MetadataAnnotation ensureResolved(Compiler compiler); +} + +abstract class VoidElement extends Element {} + +/// An [Element] that has a type. +abstract class TypedElement extends Element { + DartType get type; +} + +/// A [MemberSignature] is a member of an interface. +/// +/// A signature is either a method or a getter or setter, possibly implicitly +/// defined by a field declarations. Fields themselves are not members of an +/// interface. +/// +/// A [MemberSignature] may be defined by a member declaration or may be +/// synthetized from a set of declarations. +abstract class MemberSignature { + /// The name of this member. + Name get name; + + /// The type of the member when accessed. For getters and setters this is the + /// return type and argument type, respectively. For methods the type is the + /// [functionType] defined by the return type and parameters. + DartType get type; + + /// The function type of the member. For a getter `Foo get foo` this is + /// `() -> Foo`, for a setter `void set foo(Foo _)` this is `(Foo) -> void`. + /// For methods the function type is defined by the return type and + /// parameters. + FunctionType get functionType; + + /// Returns `true` if this member is a getter, possibly implictly defined by a + /// field declaration. + bool get isGetter; + + /// Returns `true` if this member is a setter, possibly implictly defined by a + /// field declaration. + bool get isSetter; + + /// Returns `true` if this member is a method, that is neither a getter nor + /// setter. + bool get isMethod; + + /// Returns an iterable of the declarations that define this member. + Iterable get declarations; +} + +/// A [Member] is a member of a class, that is either a method or a getter or +/// setter, possibly implicitly defined by a field declarations. Fields +/// themselves are not members of a class. +/// +/// A [Member] of a class also defines a signature which is a member of the +/// corresponding interface type. +/// +/// A [Member] is implicitly concrete. An abstract declaration only declares +/// a signature in the interface of its class. +/// +/// A [Member] is always declared by an [Element] which is accessibly through +/// the [element] getter. +abstract class Member extends MemberSignature { + /// The [Element] that declared this member, possibly implicitly in case of + /// a getter or setter defined by a field. + Element get element; + + /// The instance of the class that declared this member. + /// + /// For instance: + /// class A { T m() {} } + /// class B extends A {} + /// The declarer of `m` in `A` is `A` whereas the declarer of `m` in `B` is + /// `A`. + InterfaceType get declarer; + + /// Returns `true` if this member is static. + bool get isStatic; + + /// Returns `true` if this member is a getter or setter implicitly declared + /// by a field. + bool get isDeclaredByField; +} + diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/modelx.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/modelx.dart new file mode 100644 index 0000000..87828ea --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/modelx.dart @@ -0,0 +1,2627 @@ +// Copyright (c) 2013, 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. + +library elements.modelx; + +import 'elements.dart'; +import '../tree/tree.dart'; +import '../util/util.dart'; +import '../resolution/resolution.dart'; + +import '../dart2jslib.dart' show invariant, + InterfaceType, + DartType, + TypeVariableType, + TypedefType, + DualKind, + MessageKind, + DiagnosticListener, + Script, + FunctionType, + Selector, + Constant, + Compiler, + isPrivateName; + +import '../dart_types.dart'; + +import '../scanner/scannerlib.dart' show Token, EOF_TOKEN; + +import '../ordered_typeset.dart' show OrderedTypeSet; + +import 'visitor.dart' show ElementVisitor; + +abstract class ElementX implements Element { + static int elementHashCode = 0; + + final String name; + final ElementKind kind; + final Element enclosingElement; + final int hashCode = ++elementHashCode; + Link metadata = const Link(); + + ElementX(this.name, this.kind, this.enclosingElement) { + assert(isErroneous() || getImplementationLibrary() != null); + } + + Modifiers get modifiers => Modifiers.EMPTY; + + Node parseNode(DiagnosticListener listener) { + listener.internalError(this, 'Not implemented.'); + return null; + } + + DartType computeType(Compiler compiler) { + compiler.internalError(this, "$this.computeType."); + return null; + } + + void addMetadata(MetadataAnnotation annotation) { + assert(annotation.annotatedElement == null); + annotation.annotatedElement = this; + addMetadataInternal(annotation); + } + + void addMetadataInternal(MetadataAnnotation annotation) { + metadata = metadata.prepend(annotation); + } + + + bool isFunction() => identical(kind, ElementKind.FUNCTION); + bool isConstructor() => isFactoryConstructor() || isGenerativeConstructor(); + bool isClosure() => false; + bool isMember() { + // Check that this element is defined in the scope of a Class. + return enclosingElement != null && enclosingElement.isClass(); + } + bool isInstanceMember() => false; + bool isDeferredLoaderGetter() => false; + + bool isFactoryConstructor() => modifiers.isFactory(); + bool isGenerativeConstructor() => + identical(kind, ElementKind.GENERATIVE_CONSTRUCTOR); + bool isGenerativeConstructorBody() => + identical(kind, ElementKind.GENERATIVE_CONSTRUCTOR_BODY); + bool isCompilationUnit() => identical(kind, ElementKind.COMPILATION_UNIT); + bool isClass() => identical(kind, ElementKind.CLASS); + bool isPrefix() => identical(kind, ElementKind.PREFIX); + bool isVariable() => identical(kind, ElementKind.VARIABLE); + bool isParameter() => identical(kind, ElementKind.PARAMETER); + bool isStatement() => identical(kind, ElementKind.STATEMENT); + bool isTypedef() => identical(kind, ElementKind.TYPEDEF); + bool isTypeVariable() => identical(kind, ElementKind.TYPE_VARIABLE); + bool isField() => identical(kind, ElementKind.FIELD); + bool isFieldParameter() => identical(kind, ElementKind.FIELD_PARAMETER); + bool isAbstractField() => identical(kind, ElementKind.ABSTRACT_FIELD); + bool isGetter() => identical(kind, ElementKind.GETTER); + bool isSetter() => identical(kind, ElementKind.SETTER); + bool isAccessor() => isGetter() || isSetter(); + bool isLibrary() => identical(kind, ElementKind.LIBRARY); + bool impliesType() => (kind.category & ElementCategory.IMPLIES_TYPE) != 0; + + /** See [ErroneousElement] for documentation. */ + bool isErroneous() => false; + + /** See [AmbiguousElement] for documentation. */ + bool isAmbiguous() => false; + + /** See [WarnOnUseElement] for documentation. */ + bool isWarnOnUse() => false; + + /** + * Is [:true:] if this element has a corresponding patch. + * + * If [:true:] this element has a non-null [patch] field. + * + * See [:patch_parser.dart:] for a description of the terminology. + */ + bool get isPatched => false; + + /** + * Is [:true:] if this element is a patch. + * + * If [:true:] this element has a non-null [origin] field. + * + * See [:patch_parser.dart:] for a description of the terminology. + */ + bool get isPatch => false; + + /** + * Is [:true:] if this element defines the implementation for the entity of + * this element. + * + * See [:patch_parser.dart:] for a description of the terminology. + */ + bool get isImplementation => !isPatched; + + /** + * Is [:true:] if this element introduces the entity of this element. + * + * See [:patch_parser.dart:] for a description of the terminology. + */ + bool get isDeclaration => !isPatch; + + bool get isSynthesized => false; + + bool get isForwardingConstructor => false; + + bool get isMixinApplication => false; + + /** + * Returns the element which defines the implementation for the entity of this + * element. + * + * See [:patch_parser.dart:] for a description of the terminology. + */ + Element get implementation => isPatched ? patch : this; + + /** + * Returns the element which introduces the entity of this element. + * + * See [:patch_parser.dart:] for a description of the terminology. + */ + Element get declaration => isPatch ? origin : this; + + Element get patch { + throw new UnsupportedError('patch is not supported on $this'); + } + + Element get origin { + throw new UnsupportedError('origin is not supported on $this'); + } + + // TODO(johnniwinther): This breaks for libraries (for which enclosing + // elements are null) and is invalid for top level variable declarations for + // which the enclosing element is a VariableDeclarations and not a compilation + // unit. + bool isTopLevel() { + return enclosingElement != null && enclosingElement.isCompilationUnit(); + } + + bool isAssignable() { + if (modifiers.isFinalOrConst()) return false; + if (isFunction() || isGenerativeConstructor()) return false; + return true; + } + + Token position() => null; + + Token findMyName(Token token) { + return findNameToken(token, isConstructor(), name, enclosingElement.name); + } + + static Token findNameToken(Token token, bool isConstructor, String name, + String enclosingClassName) { + // We search for the token that has the name of this element. + // For constructors, that doesn't work because they may have + // named formed out of multiple tokens (named constructors) so + // for those we search for the class name instead. + String needle = isConstructor ? enclosingClassName : name; + // The unary '-' operator has a special element name (specified). + if (needle == 'unary-') needle = '-'; + for (Token t = token; EOF_TOKEN != t.kind; t = t.next) { + if (needle == t.value) return t; + } + return token; + } + + CompilationUnitElement getCompilationUnit() { + Element element = this; + while (!element.isCompilationUnit()) { + element = element.enclosingElement; + } + return element; + } + + LibraryElement getLibrary() => enclosingElement.getLibrary(); + + LibraryElement getImplementationLibrary() { + Element element = this; + while (!identical(element.kind, ElementKind.LIBRARY)) { + element = element.enclosingElement; + } + return element; + } + + ClassElement getEnclosingClass() { + for (Element e = this; e != null; e = e.enclosingElement) { + if (e.isClass()) return e; + } + return null; + } + + Element getEnclosingClassOrCompilationUnit() { + for (Element e = this; e != null; e = e.enclosingElement) { + if (e.isClass() || e.isCompilationUnit()) return e; + } + return null; + } + + /** + * Returns the member enclosing this element or the element itself if it is a + * member. If no enclosing element is found, [:null:] is returned. + */ + Element getEnclosingMember() { + for (Element e = this; e != null; e = e.enclosingElement) { + if (e.isMember()) return e; + } + return null; + } + + Element getOutermostEnclosingMemberOrTopLevel() { + // TODO(lrn): Why is this called "Outermost"? + for (Element e = this; e != null; e = e.enclosingElement) { + if (e.isMember() || e.isTopLevel()) { + return e; + } + } + return null; + } + + /** + * Creates the scope for this element. + */ + Scope buildScope() => enclosingElement.buildScope(); + + String toString() { + // TODO(johnniwinther): Test for nullness of name, or make non-nullness an + // invariant for all element types? + var nameText = name != null ? name : '?'; + if (enclosingElement != null && !isTopLevel()) { + String holderName = enclosingElement.name != null + ? enclosingElement.name + : '${enclosingElement.kind}?'; + return '$kind($holderName#${nameText})'; + } else { + return '$kind(${nameText})'; + } + } + + String _fixedBackendName = null; + bool _isNative = false; + bool isNative() => _isNative; + bool hasFixedBackendName() => _fixedBackendName != null; + String fixedBackendName() => _fixedBackendName; + // Marks this element as a native element. + void setNative(String name) { + _isNative = true; + _fixedBackendName = name; + } + void setFixedBackendName(String name) { + _fixedBackendName = name; + } + + FunctionElement asFunctionElement() => null; + + bool get isAbstract => modifiers.isAbstract(); + bool isForeign(Compiler compiler) => getLibrary() == compiler.foreignLibrary; + + FunctionElement get targetConstructor => null; + + void diagnose(Element context, DiagnosticListener listener) {} + + TreeElements get treeElements => enclosingElement.treeElements; +} + +/** + * Represents an unresolvable or duplicated element. + * + * An [ErroneousElement] is used instead of [:null:] to provide additional + * information about the error that caused the element to be unresolvable + * or otherwise invalid. + * + * Accessing any field or calling any method defined on [ErroneousElement] + * except [isErroneous] will currently throw an exception. (This might + * change when we actually want more information on the erroneous element, + * e.g., the name of the element we were trying to resolve.) + * + * Code that cannot not handle an [ErroneousElement] should use + * [: Element.isInvalid(element) :] + * to check for unresolvable elements instead of + * [: element == null :]. + */ +class ErroneousElementX extends ElementX implements ErroneousElement { + final MessageKind messageKind; + final Map messageArguments; + + ErroneousElementX(this.messageKind, this.messageArguments, + String name, Element enclosing) + : super(name, ElementKind.ERROR, enclosing); + + isErroneous() => true; + + unsupported() { + throw 'unsupported operation on erroneous element'; + } + + Link get metadata => unsupported(); + get node => unsupported(); + get type => unsupported(); + get cachedNode => unsupported(); + get functionSignature => unsupported(); + get patch => null; + get origin => this; + get defaultImplementation => unsupported(); + get nestedClosures => unsupported(); + + bool get isRedirectingFactory => unsupported(); + + setPatch(patch) => unsupported(); + computeSignature(compiler) => unsupported(); + requiredParameterCount(compiler) => unsupported(); + optionalParameterCount(compiler) => unsupported(); + parameterCount(compiler) => unsupported(); + + // TODO(kasperl): These seem unnecessary. + set patch(value) => unsupported(); + set origin(value) => unsupported(); + set defaultImplementation(value) => unsupported(); + + get redirectionTarget => this; + + getLibrary() => enclosingElement.getLibrary(); + + computeTargetType(InterfaceType newType) => unsupported(); + + String get message => '${messageKind.message(messageArguments)}'; + + String toString() => '<$name: $message>'; + + accept(ElementVisitor visitor) => visitor.visitErroneousElement(this); +} + +/// A message attached to a [WarnOnUseElementX]. +class WrappedMessage { + /// The message position. If [:null:] the position of the reference to the + /// [WarnOnUseElementX] is used. + final Spannable spannable; + + /** + * The message to report on resolving a wrapped element. + */ + final MessageKind messageKind; + + /** + * The message arguments to report on resolving a wrapped element. + */ + final Map messageArguments; + + WrappedMessage(this.spannable, this.messageKind, this.messageArguments); +} + +/** + * An [Element] whose reference should cause one or more warnings. + */ +class WarnOnUseElementX extends ElementX implements WarnOnUseElement { + /// Warning to report on resolving this element. + final WrappedMessage warning; + + /// Info to report on resolving this element. + final WrappedMessage info; + + /// The element whose usage cause a warning. + final Element wrappedElement; + + WarnOnUseElementX(WrappedMessage this.warning, WrappedMessage this.info, + Element enclosingElement, Element wrappedElement) + : this.wrappedElement = wrappedElement, + super(wrappedElement.name, ElementKind.WARN_ON_USE, enclosingElement); + + bool isWarnOnUse() => true; + + Element unwrap(DiagnosticListener listener, Spannable usageSpannable) { + var unwrapped = wrappedElement; + if (warning != null) { + Spannable spannable = warning.spannable; + if (spannable == null) spannable = usageSpannable; + listener.reportWarning( + spannable, warning.messageKind, warning.messageArguments); + } + if (info != null) { + Spannable spannable = info.spannable; + if (spannable == null) spannable = usageSpannable; + listener.reportInfo( + spannable, info.messageKind, info.messageArguments); + } + if (unwrapped.isWarnOnUse()) { + unwrapped = unwrapped.unwrap(listener, usageSpannable); + } + return unwrapped; + } + + accept(ElementVisitor visitor) => visitor.visitWarnOnUseElement(this); +} + +/** + * An ambiguous element represents multiple elements accessible by the same name. + * + * Ambiguous elements are created during handling of import/export scopes. If an + * ambiguous element is encountered during resolution a warning/error should be + * reported. + */ +class AmbiguousElementX extends ElementX implements AmbiguousElement { + /** + * The message to report on resolving this element. + */ + final MessageKind messageKind; + + /** + * The message arguments to report on resolving this element. + */ + final Map messageArguments; + + /** + * The first element that this ambiguous element might refer to. + */ + final Element existingElement; + + /** + * The second element that this ambiguous element might refer to. + */ + final Element newElement; + + AmbiguousElementX(this.messageKind, this.messageArguments, + Element enclosingElement, Element existingElement, Element newElement) + : this.existingElement = existingElement, + this.newElement = newElement, + super(existingElement.name, ElementKind.AMBIGUOUS, enclosingElement); + + bool isAmbiguous() => true; + + Setlet flatten() { + Element element = this; + var set = new Setlet(); + while (element.isAmbiguous()) { + AmbiguousElement ambiguous = element; + set.add(ambiguous.newElement); + element = ambiguous.existingElement; + } + set.add(element); + return set; + } + + void diagnose(Element context, DiagnosticListener listener) { + Setlet ambiguousElements = flatten(); + MessageKind code = (ambiguousElements.length == 1) + ? MessageKind.AMBIGUOUS_REEXPORT : MessageKind.AMBIGUOUS_LOCATION; + LibraryElementX importer = context.getLibrary(); + for (Element element in ambiguousElements) { + var arguments = {'name': element.name}; + listener.reportInfo(element, code, arguments); + Link importers = importer.importers.getImports(element); + listener.withCurrentElement(importer, () { + for (; !importers.isEmpty; importers = importers.tail) { + listener.reportInfo( + importers.head, MessageKind.IMPORTED_HERE, arguments); + } + }); + } + } + + accept(ElementVisitor visitor) => visitor.visitAmbiguousElement(this); +} + +class ScopeX { + final Map contents = new Map(); + + bool get isEmpty => contents.isEmpty; + Iterable get values => contents.values; + + Element lookup(String name) { + return contents[name]; + } + + void add(Element element, DiagnosticListener listener) { + String name = element.name; + if (element.isAccessor()) { + addAccessor(element, contents[name], listener); + } else { + Element existing = contents.putIfAbsent(name, () => element); + if (!identical(existing, element)) { + listener.reportError( + element, MessageKind.DUPLICATE_DEFINITION, {'name': name}); + listener.reportInfo(existing, + MessageKind.EXISTING_DEFINITION, {'name': name}); + } + } + } + + /** + * Adds a definition for an [accessor] (getter or setter) to a scope. + * The definition binds to an abstract field that can hold both a getter + * and a setter. + * + * The abstract field is added once, for the first getter or setter, and + * reused if the other one is also added. + * The abstract field should not be treated as a proper member of the + * container, it's simply a way to return two results for one lookup. + * That is, the getter or setter does not have the abstract field as enclosing + * element, they are enclosed by the class or compilation unit, as is the + * abstract field. + */ + void addAccessor(Element accessor, + Element existing, + DiagnosticListener listener) { + void reportError(Element other) { + listener.reportError(accessor, + MessageKind.DUPLICATE_DEFINITION, + {'name': accessor.name}); + // TODO(johnniwinther): Make this an info instead of a fatal error. + listener.reportFatalError(other, + MessageKind.EXISTING_DEFINITION, + {'name': accessor.name}); + } + + if (existing != null) { + if (!identical(existing.kind, ElementKind.ABSTRACT_FIELD)) { + reportError(existing); + } else { + AbstractFieldElementX field = existing; + if (accessor.isGetter()) { + if (field.getter != null && field.getter != accessor) { + reportError(field.getter); + } + field.getter = accessor; + } else { + assert(accessor.isSetter()); + if (field.setter != null && field.setter != accessor) { + reportError(field.setter); + } + field.setter = accessor; + } + } + } else { + Element container = accessor.getEnclosingClassOrCompilationUnit(); + AbstractFieldElementX field = + new AbstractFieldElementX(accessor.name, container); + if (accessor.isGetter()) { + field.getter = accessor; + } else { + field.setter = accessor; + } + add(field, listener); + } + } +} + +class CompilationUnitElementX extends ElementX with AnalyzableElement + implements CompilationUnitElement { + final Script script; + PartOf partTag; + Link localMembers = const Link(); + + CompilationUnitElementX(Script script, LibraryElement library) + : this.script = script, + super(script.name, + ElementKind.COMPILATION_UNIT, + library) { + library.addCompilationUnit(this); + } + + void forEachLocalMember(f(Element element)) { + localMembers.forEach(f); + } + + void addMember(Element element, DiagnosticListener listener) { + // Keep a list of top level members. + localMembers = localMembers.prepend(element); + // Provide the member to the library to build scope. + if (enclosingElement.isPatch) { + getImplementationLibrary().addMember(element, listener); + } else { + getLibrary().addMember(element, listener); + } + } + + void setPartOf(PartOf tag, DiagnosticListener listener) { + LibraryElementX library = enclosingElement; + if (library.entryCompilationUnit == this) { + listener.reportError(tag, MessageKind.ILLEGAL_DIRECTIVE); + return; + } + if (!localMembers.isEmpty) { + listener.reportError(tag, MessageKind.BEFORE_TOP_LEVEL); + return; + } + if (partTag != null) { + listener.reportWarning(tag, MessageKind.DUPLICATED_PART_OF); + return; + } + partTag = tag; + LibraryName libraryTag = getLibrary().libraryTag; + String actualName = tag.name.toString(); + if (libraryTag != null) { + String expectedName = libraryTag.name.toString(); + if (expectedName != actualName) { + listener.reportWarning(tag.name, + MessageKind.LIBRARY_NAME_MISMATCH, + {'libraryName': expectedName}); + } + } else { + listener.reportWarning(getLibrary(), + MessageKind.MISSING_LIBRARY_NAME, + {'libraryName': actualName}); + listener.reportInfo(tag.name, + MessageKind.THIS_IS_THE_PART_OF_TAG); + } + } + + bool get hasMembers => !localMembers.isEmpty; + + int compareTo(CompilationUnitElement other) { + if (this == other) return 0; + return '${script.readableUri}'.compareTo('${other.script.readableUri}'); + } + + accept(ElementVisitor visitor) => visitor.visitCompilationUnitElement(this); +} + +class Importers { + Map> importers = new Map>(); + + Link getImports(Element element) { + Link imports = importers[element]; + return imports != null ? imports : const Link(); + } + + Import getImport(Element element) => getImports(element).head; + + void registerImport(Element element, Import import) { + if (import == null) return; + + importers[element] = + importers.putIfAbsent(element, () => const Link()) + .prepend(import); + } +} + +class ImportScope { + /** + * Map for elements imported through import declarations. + * + * Addition to the map is performed by [addImport]. Lookup is done trough + * [find]. + */ + final Map importScope = + new Map(); + + /** + * Adds [element] to the import scope of this library. + * + * If an element by the same name is already in the imported scope, an + * [ErroneousElement] will be put in the imported scope, allowing for + * detection of ambiguous uses of imported names. + */ + void addImport(Element enclosingElement, + Element element, + Import import, + DiagnosticListener listener) { + LibraryElementX library = enclosingElement.getLibrary(); + Importers importers = library.importers; + + String name = element.name; + + // The loadLibrary function always shadows existing bindings to that name. + if (element.isDeferredLoaderGetter()) { + importScope.remove(name); + // TODO(sigurdm): Print a hint. + } + Element existing = importScope.putIfAbsent(name, () => element); + importers.registerImport(element, import); + + void registerWarnOnUseElement(Import import, + MessageKind messageKind, + Element hidingElement, + Element hiddenElement) { + Uri hiddenUri = hiddenElement.getLibrary().canonicalUri; + Uri hidingUri = hidingElement.getLibrary().canonicalUri; + Element element = new WarnOnUseElementX( + new WrappedMessage( + null, // Report on reference to [hidingElement]. + messageKind, + {'name': name, 'hiddenUri': hiddenUri, 'hidingUri': hidingUri}), + new WrappedMessage( + listener.spanFromSpannable(import), + MessageKind.IMPORTED_HERE, + {'name': name}), + enclosingElement, hidingElement); + importScope[name] = element; + importers.registerImport(element, import); + } + + if (existing != element) { + Import existingImport = importers.getImport(existing); + Element newElement; + if (existing.getLibrary().isPlatformLibrary && + !element.getLibrary().isPlatformLibrary) { + // [existing] is implicitly hidden. + registerWarnOnUseElement( + import, MessageKind.HIDDEN_IMPORT, element, existing); + } else if (!existing.getLibrary().isPlatformLibrary && + element.getLibrary().isPlatformLibrary) { + // [element] is implicitly hidden. + if (import == null) { + // [element] is imported implicitly (probably through dart:core). + registerWarnOnUseElement( + existingImport, MessageKind.HIDDEN_IMPLICIT_IMPORT, + existing, element); + } else { + registerWarnOnUseElement( + import, MessageKind.HIDDEN_IMPORT, existing, element); + } + } else { + Element ambiguousElement = new AmbiguousElementX( + MessageKind.DUPLICATE_IMPORT, {'name': name}, + enclosingElement, existing, element); + importScope[name] = ambiguousElement; + importers.registerImport(ambiguousElement, import); + importers.registerImport(ambiguousElement, existingImport); + } + } + } + + Element operator [](String name) => importScope[name]; +} + +class LibraryElementX extends ElementX with AnalyzableElement + implements LibraryElement { + final Uri canonicalUri; + CompilationUnitElement entryCompilationUnit; + Link compilationUnits = + const Link(); + Link tags = const Link(); + LibraryName libraryTag; + bool canUseNative = false; + Link localMembers = const Link(); + final ScopeX localScope = new ScopeX(); + final ImportScope importScope = new ImportScope(); + + /** + * If this library is patched, [patch] points to the patch library. + * + * See [:patch_parser.dart:] for a description of the terminology. + */ + LibraryElementX patch = null; + + /** + * If this is a patch library, [origin] points to the origin library. + * + * See [:patch_parser.dart:] for a description of the terminology. + */ + final LibraryElementX origin; + + /// A mapping from an imported element to the "import" tag. + final Importers importers = new Importers(); + + /** + * Link for elements exported either through export declarations or through + * declaration. This field should not be accessed directly but instead through + * the [exports] getter. + * + * [LibraryDependencyHandler] sets this field through [setExports] when the + * library is loaded. + */ + Link slotForExports; + + final Map tagMapping = + new Map(); + + LibraryElementX(Script script, [Uri canonicalUri, LibraryElement this.origin]) + : this.canonicalUri = + ((canonicalUri == null) ? script.readableUri : canonicalUri), + super(script.name, ElementKind.LIBRARY, null) { + entryCompilationUnit = new CompilationUnitElementX(script, this); + if (isPatch) { + origin.patch = this; + } + } + + bool get isPatched => patch != null; + bool get isPatch => origin != null; + + LibraryElement get declaration => super.declaration; + LibraryElement get implementation => super.implementation; + + Link get metadata { + return (libraryTag == null) ? super.metadata : libraryTag.metadata; + } + + set metadata(value) { + // The metadata is stored on [libraryTag]. + throw new SpannableAssertionFailure(this, 'Cannot set metadata on Library'); + } + + CompilationUnitElement getCompilationUnit() => entryCompilationUnit; + + void addCompilationUnit(CompilationUnitElement element) { + compilationUnits = compilationUnits.prepend(element); + } + + void addTag(LibraryTag tag, DiagnosticListener listener) { + tags = tags.prepend(tag); + } + + void recordResolvedTag(LibraryDependency tag, LibraryElement library) { + assert(tagMapping[tag] == null); + tagMapping[tag] = library; + } + + LibraryElement getLibraryFromTag(LibraryDependency tag) => tagMapping[tag]; + + /** + * Adds [element] to the import scope of this library. + * + * If an element by the same name is already in the imported scope, an + * [ErroneousElement] will be put in the imported scope, allowing for detection of ambiguous uses of imported names. + */ + void addImport(Element element, Import import, DiagnosticListener listener) { + importScope.addImport(this, element, import, listener); + } + + void addMember(Element element, DiagnosticListener listener) { + localMembers = localMembers.prepend(element); + addToScope(element, listener); + } + + void addToScope(Element element, DiagnosticListener listener) { + localScope.add(element, listener); + } + + Element localLookup(String elementName) { + Element result = localScope.lookup(elementName); + if (result == null && isPatch) { + result = origin.localLookup(elementName); + } + return result; + } + + /** + * Returns [:true:] if the export scope has already been computed for this + * library. + */ + bool get exportsHandled => slotForExports != null; + + Link get exports { + assert(invariant(this, exportsHandled, + message: 'Exports not handled on $this')); + return slotForExports; + } + + /** + * Sets the export scope of this library. This method can only be called once. + */ + void setExports(Iterable exportedElements) { + assert(invariant(this, !exportsHandled, + message: 'Exports already set to $slotForExports on $this')); + assert(invariant(this, exportedElements != null)); + var builder = new LinkBuilder(); + for (Element export in exportedElements) { + builder.addLast(export); + } + slotForExports = builder.toLink(); + } + + LibraryElement getLibrary() => isPatch ? origin : this; + + /** + * Look up a top-level element in this library. The element could + * potentially have been imported from another library. Returns + * null if no such element exist and an [ErroneousElement] if multiple + * elements have been imported. + */ + Element find(String elementName) { + Element result = localScope.lookup(elementName); + if (result != null) return result; + if (origin != null) { + result = origin.localScope.lookup(elementName); + if (result != null) return result; + } + result = importScope[elementName]; + if (result != null) return result; + if (origin != null) { + result = origin.importScope[elementName]; + if (result != null) return result; + } + return null; + } + + /** Look up a top-level element in this library, but only look for + * non-imported elements. Returns null if no such element exist. */ + Element findLocal(String elementName) { + // TODO(johnniwinther): How to handle injected elements in the patch + // library? + Element result = localScope.lookup(elementName); + if (result == null || result.getLibrary() != this) return null; + return result; + } + + Element findExported(String elementName) { + for (Link link = exports; !link.isEmpty; link = link.tail) { + Element element = link.head; + if (element.name == elementName) return element; + } + return null; + } + + void forEachExport(f(Element element)) { + exports.forEach((Element e) => f(e)); + } + + Link getImportsFor(Element element) => importers.getImports(element); + + void forEachLocalMember(f(Element element)) { + if (isPatch) { + // Patch libraries traverse both origin and injected members. + origin.localMembers.forEach(f); + + void filterPatch(Element element) { + if (!element.isPatch) { + // Do not traverse the patch members. + f(element); + } + } + localMembers.forEach(filterPatch); + } else { + localMembers.forEach(f); + } + } + + Iterable getNonPrivateElementsInScope() { + return localScope.values.where((Element element) { + // At this point [localScope] only contains members so we don't need + // to check for foreign or prefix elements. + return !isPrivateName(element.name); + }); + } + + bool hasLibraryName() => libraryTag != null; + + /** + * Returns the library name, which is either the name given in the library tag + * or the empty string if there is no library tag. + */ + String getLibraryName() { + if (libraryTag == null) return ''; + return libraryTag.name.toString(); + } + + /** + * Returns the library name (as defined by the library tag) or for script + * (which have no library tag) the script file name. The latter case is used + * to private 'library name' for scripts to use for instance in dartdoc. + * + * Note: the returned filename will still be escaped ("a%20b.dart" instead of + * "a b.dart"). + */ + String getLibraryOrScriptName() { + if (libraryTag != null) { + return libraryTag.name.toString(); + } else { + // Use the file name as script name. + String path = canonicalUri.path; + return path.substring(path.lastIndexOf('/') + 1); + } + } + + Scope buildScope() => new LibraryScope(this); + + bool get isPlatformLibrary => canonicalUri.scheme == 'dart'; + + bool get isPackageLibrary => canonicalUri.scheme == 'package'; + + bool get isInternalLibrary => + isPlatformLibrary && canonicalUri.path.startsWith('_'); + + String toString() { + if (origin != null) { + return 'patch library(${getLibraryOrScriptName()})'; + } else if (patch != null) { + return 'origin library(${getLibraryOrScriptName()})'; + } else { + return 'library(${getLibraryOrScriptName()})'; + } + } + + int compareTo(LibraryElement other) { + if (this == other) return 0; + return getLibraryOrScriptName().compareTo(other.getLibraryOrScriptName()); + } + + accept(ElementVisitor visitor) => visitor.visitLibraryElement(this); +} + +class PrefixElementX extends ElementX implements PrefixElement { + Token firstPosition; + + final ImportScope importScope = new ImportScope(); + + bool get isDeferred => _deferredImport != null; + + // Only needed for deferred imports. + Import _deferredImport; + Import get deferredImport => _deferredImport; + + PrefixElementX(String prefix, Element enclosing, this.firstPosition) + : super(prefix, ElementKind.PREFIX, enclosing); + + Element lookupLocalMember(String memberName) => importScope[memberName]; + + DartType computeType(Compiler compiler) => compiler.types.dynamicType; + + Token position() => firstPosition; + + void addImport(Element element, Import import, DiagnosticListener listener) { + importScope.addImport(this, element, import, listener); + } + + accept(ElementVisitor visitor) => visitor.visitPrefixElement(this); + + void markAsDeferred(Import deferredImport) { + _deferredImport = deferredImport; + } +} + +class TypedefElementX extends ElementX + with AnalyzableElement, TypeDeclarationElementX + implements TypedefElement { + Typedef cachedNode; + + /** + * The type annotation which defines this typedef. + */ + DartType alias; + + /// [:true:] if the typedef has been checked for cyclic reference. + bool hasBeenCheckedForCycles = false; + + bool get isResolved => hasTreeElements; + + TypedefElementX(String name, Element enclosing) + : super(name, ElementKind.TYPEDEF, enclosing); + + /** + * Function signature for a typedef of a function type. The signature is + * kept to provide full information about parameter names through the mirror + * system. + * + * The [functionSignature] is not available until the typedef element has been + * resolved. + */ + FunctionSignature functionSignature; + + TypedefType computeType(Compiler compiler) { + if (thisTypeCache != null) return thisTypeCache; + Typedef node = parseNode(compiler); + setThisAndRawTypes(compiler, createTypeVariables(node.typeParameters)); + compiler.resolveTypedef(this); + return thisTypeCache; + } + + TypedefType createType(Link typeArguments) { + return new TypedefType(this, typeArguments); + } + + Scope buildScope() { + return new TypeDeclarationScope(enclosingElement.buildScope(), this); + } + + void checkCyclicReference(Compiler compiler) { + if (hasBeenCheckedForCycles) return; + var visitor = new TypedefCyclicVisitor(compiler, this); + computeType(compiler).accept(visitor, null); + hasBeenCheckedForCycles = true; + } + + accept(ElementVisitor visitor) => visitor.visitTypedefElement(this); +} + +// This class holds common information for a list of variable or field +// declarations. It contains the node, and the type. A [VariableElementX] +// forwards its [computeType] and [parseNode] methods to this class. +class VariableList { + VariableDefinitions definitions; + DartType type; + final Modifiers modifiers; + Link metadata = const Link(); + + VariableList(Modifiers this.modifiers); + + VariableList.node(VariableDefinitions node, this.type) + : this.definitions = node, + this.modifiers = node.modifiers { + assert(modifiers != null); + } + + VariableDefinitions parseNode(Element element, DiagnosticListener listener) { + return definitions; + } + + DartType computeType(Element element, Compiler compiler) => type; +} + +class VariableElementX extends ElementX with AnalyzableElement + implements VariableElement { + final Token token; + final VariableList variables; + VariableDefinitions definitionsCache; + Expression initializerCache; + + Modifiers get modifiers => variables.modifiers; + + VariableElementX(String name, + ElementKind kind, + Element enclosingElement, + VariableList variables, + this.token) + : this.variables = variables, + super(name, kind, enclosingElement); + + VariableElementX.synthetic(String name, + ElementKind kind, + Element enclosing) + : token = null, + variables = null, + super(name, kind, enclosing); + + // TODO(johnniwinther): Ensure that the [TreeElements] for this variable hold + // the mappings for all its metadata. + Link get metadata => variables.metadata; + + void addMetadataInternal(MetadataAnnotation annotation) { + variables.metadata = variables.metadata.prepend(annotation); + } + + Expression get initializer { + assert(invariant(this, definitionsCache != null, + message: "Initializer has not been computed for $this.")); + return initializerCache; + } + + Node parseNode(DiagnosticListener listener) { + if (definitionsCache != null) return definitionsCache; + + VariableDefinitions definitions = variables.parseNode(this, listener); + Expression node; + int count = 0; + for (Link link = definitions.definitions.nodes; + !link.isEmpty; link = link.tail) { + Expression initializedIdentifier = link.head; + Identifier identifier = initializedIdentifier.asIdentifier(); + if (identifier == null) { + SendSet sendSet = initializedIdentifier.asSendSet(); + identifier = sendSet.selector.asIdentifier(); + if (identical(name, identifier.source)) { + node = initializedIdentifier; + initializerCache = sendSet.arguments.first; + } + } else if (identical(name, identifier.source)) { + node = initializedIdentifier; + } + count++; + } + if (node == null) { + listener.internalError(definitions, + "Could not find '$name'."); + } + if (count == 1) { + definitionsCache = definitions; + } else { + // Create a [VariableDefinitions] node for the single definition of + // [node]. + definitionsCache = new VariableDefinitions(definitions.type, + definitions.modifiers, new NodeList( + definitions.definitions.beginToken, + const Link().prepend(node), + definitions.definitions.endToken)); + } + return definitionsCache; + } + + DartType computeType(Compiler compiler) { + // Call [parseNode] to ensure that [definitionsCache] and [initializerCache] + // are set as a consequence of calling [computeType]. + parseNode(compiler); + return variables.computeType(this, compiler); + } + + DartType get type { + assert(invariant(this, variables.type != null, + message: "Type has not been computed for $this.")); + return variables.type; + } + + bool isInstanceMember() => isMember() && !modifiers.isStatic(); + + // Note: cachedNode.getBeginToken() will not be correct in all + // cases, for example, for function typed parameters. + Token position() => token; + + accept(ElementVisitor visitor) => visitor.visitVariableElement(this); +} + +class FieldElementX extends VariableElementX implements FieldElement { + List nestedClosures = new List(); + + FieldElementX(Identifier name, + Element enclosingElement, + VariableList variables) + : super(name.source, ElementKind.FIELD, enclosingElement, + variables, name.token); + + accept(ElementVisitor visitor) => visitor.visitFieldElement(this); +} + +/** + * Parameters in constructors that directly initialize fields. For example: + * [:A(this.field):]. + */ +class FieldParameterElementX extends ParameterElementX + implements FieldParameterElement { + VariableElement fieldElement; + + FieldParameterElementX(Element enclosingElement, + VariableDefinitions variables, + Identifier identifier, + Expression initializer, + this.fieldElement) + : super(ElementKind.FIELD_PARAMETER, enclosingElement, + variables, identifier, initializer); + + accept(ElementVisitor visitor) => visitor.visitFieldParameterElement(this); +} + +/// [Element] for a formal parameter. +/// +/// A [ParameterElementX] can be patched. A parameter of an external method is +/// patched with the corresponding parameter of the patch method. This is done +/// to ensure that default values on parameters are computed once (on the +/// origin parameter) but can be found through both the origin and the patch. +class ParameterElementX extends ElementX implements ParameterElement { + final VariableDefinitions definitions; + final Identifier identifier; + final Expression initializer; + DartType typeCache; + + /** + * Function signature for a variable with a function type. The signature is + * kept to provide full information about parameter names through the mirror + * system. + */ + FunctionSignature functionSignatureCache; + + ParameterElementX(ElementKind elementKind, + Element enclosingElement, + this.definitions, + Identifier identifier, + this.initializer) + : this.identifier = identifier, + super(identifier.source, elementKind, enclosingElement); + + Modifiers get modifiers => definitions.modifiers; + + Token position() => identifier.getBeginToken(); + + Node parseNode(DiagnosticListener listener) => definitions; + + DartType computeType(Compiler compiler) { + assert(invariant(this, type != null, + message: "Parameter type has not been set for $this.")); + return type; + } + + DartType get type { + assert(invariant(this, typeCache != null, + message: "Parameter type has not been set for $this.")); + return typeCache; + } + + FunctionSignature get functionSignature { + assert(invariant(this, typeCache != null, + message: "Parameter signature has not been set for $this.")); + return functionSignatureCache; + } + + VariableDefinitions get node => definitions; + + FunctionType get functionType => type; + + accept(ElementVisitor visitor) => visitor.visitVariableElement(this); + + ParameterElementX patch = null; + ParameterElementX origin = null; + + bool get isPatch => origin != null; + bool get isPatched => patch != null; +} + +class AbstractFieldElementX extends ElementX implements AbstractFieldElement { + FunctionElement getter; + FunctionElement setter; + + AbstractFieldElementX(String name, Element enclosing) + : super(name, ElementKind.ABSTRACT_FIELD, enclosing); + + DartType computeType(Compiler compiler) { + throw "internal error: AbstractFieldElement has no type"; + } + + Node parseNode(DiagnosticListener listener) { + throw "internal error: AbstractFieldElement has no node"; + } + + Token position() { + // The getter and setter may be defined in two different + // compilation units. However, we know that one of them is + // non-null and defined in the same compilation unit as the + // abstract element. + // TODO(lrn): No we don't know that if the element from the same + // compilation unit is patched. + // + // We need to make sure that the position returned is relative to + // the compilation unit of the abstract element. + if (getter != null + && identical(getter.getCompilationUnit(), getCompilationUnit())) { + return getter.position(); + } else { + return setter.position(); + } + } + + Modifiers get modifiers { + // The resolver ensures that the flags match (ignoring abstract). + if (getter != null) { + return new Modifiers.withFlags( + getter.modifiers.nodes, + getter.modifiers.flags | Modifiers.FLAG_ABSTRACT); + } else { + return new Modifiers.withFlags( + setter.modifiers.nodes, + setter.modifiers.flags | Modifiers.FLAG_ABSTRACT); + } + } + + bool isInstanceMember() { + return isMember() && !modifiers.isStatic(); + } + + accept(ElementVisitor visitor) => visitor.visitAbstractFieldElement(this); + + bool get isAbstract { + return getter != null && getter.isAbstract + || setter != null && setter.isAbstract; + } +} + +// TODO(johnniwinther): [FunctionSignature] should be merged with +// [FunctionType]. +class FunctionSignatureX implements FunctionSignature { + final Link requiredParameters; + final Link optionalParameters; + final int requiredParameterCount; + final int optionalParameterCount; + final bool optionalParametersAreNamed; + final List orderedOptionalParameters; + final FunctionType type; + + FunctionSignatureX(this.requiredParameters, + this.optionalParameters, + this.requiredParameterCount, + this.optionalParameterCount, + this.optionalParametersAreNamed, + this.orderedOptionalParameters, + this.type); + + void forEachRequiredParameter(void function(Element parameter)) { + for (Link link = requiredParameters; + !link.isEmpty; + link = link.tail) { + function(link.head); + } + } + + void forEachOptionalParameter(void function(Element parameter)) { + for (Link link = optionalParameters; + !link.isEmpty; + link = link.tail) { + function(link.head); + } + } + + Element get firstOptionalParameter => optionalParameters.head; + + void forEachParameter(void function(Element parameter)) { + forEachRequiredParameter(function); + forEachOptionalParameter(function); + } + + void orderedForEachParameter(void function(Element parameter)) { + forEachRequiredParameter(function); + orderedOptionalParameters.forEach(function); + } + + int get parameterCount => requiredParameterCount + optionalParameterCount; + + /** + * Check whether a function with this signature can be used instead of a + * function with signature [signature] without causing a `noSuchMethod` + * exception/call. + */ + bool isCompatibleWith(FunctionSignature signature) { + if (optionalParametersAreNamed) { + if (!signature.optionalParametersAreNamed) { + return requiredParameterCount == signature.parameterCount; + } + // If both signatures have named parameters, then they must have + // the same number of required parameters, and the names in + // [signature] must all be in [:this:]. + if (requiredParameterCount != signature.requiredParameterCount) { + return false; + } + Set names = optionalParameters.toList().map( + (Element element) => element.name).toSet(); + for (Element namedParameter in signature.optionalParameters) { + if (!names.contains(namedParameter.name)) { + return false; + } + } + } else { + if (signature.optionalParametersAreNamed) return false; + // There must be at least as many arguments as in the other signature, but + // this signature must not have more required parameters. Having more + // optional parameters is not a problem, they simply are never provided + // by call sites of a call to a method with the other signature. + int otherTotalCount = signature.parameterCount; + return requiredParameterCount <= otherTotalCount + && parameterCount >= otherTotalCount; + } + return true; + } +} + +class FunctionElementX extends ElementX with AnalyzableElement + implements FunctionElement { + FunctionExpression cachedNode; + DartType typeCache; + final Modifiers modifiers; + + List nestedClosures = new List(); + + FunctionSignature functionSignatureCache; + + /** + * A function declaration that should be parsed instead of the current one. + * The patch should be parsed as if it was in the current scope. Its + * signature must match this function's signature. + */ + FunctionElement patch = null; + FunctionElement origin = null; + + final bool _hasNoBody; + + /** + * If this is a redirecting factory, [defaultImplementation] will be + * changed by the resolver to point to the redirection target. + * Otherwise, [:identical(defaultImplementation, this):]. + */ + // TODO(ahe): Rename this field to redirectionTarget. + FunctionElement defaultImplementation; + + FunctionElementX(String name, + ElementKind kind, + Modifiers modifiers, + Element enclosing, + bool hasNoBody) + : this.tooMuchOverloading(name, null, kind, modifiers, enclosing, null, + hasNoBody); + + FunctionElementX.fromNode(String name, + FunctionExpression node, + ElementKind kind, + Modifiers modifiers, + Element enclosing) + : this.tooMuchOverloading(name, node, kind, modifiers, enclosing, null, + false); + + FunctionElementX.from(String name, + FunctionElement other, + Element enclosing) + : this.tooMuchOverloading(name, other.node, other.kind, + other.modifiers, enclosing, + other.functionSignature, + false); + + FunctionElementX.tooMuchOverloading(String name, + FunctionExpression this.cachedNode, + ElementKind kind, + this.modifiers, + Element enclosing, + this.functionSignatureCache, + bool hasNoBody) + : super(name, kind, enclosing), + _hasNoBody = hasNoBody { + assert(modifiers != null); + defaultImplementation = this; + } + + bool get isPatched => patch != null; + bool get isPatch => origin != null; + + bool get isRedirectingFactory => defaultImplementation != this; + + /// This field is set by the post process queue when checking for cycles. + FunctionElement internalRedirectionTarget; + DartType redirectionTargetType; + + set redirectionTarget(FunctionElement constructor) { + assert(constructor != null && internalRedirectionTarget == null); + internalRedirectionTarget = constructor; + } + + FunctionElement get redirectionTarget { + if (Elements.isErroneousElement(defaultImplementation)) { + return defaultImplementation; + } + assert(!isRedirectingFactory || internalRedirectionTarget != null); + return isRedirectingFactory ? internalRedirectionTarget : this; + } + + InterfaceType computeTargetType(InterfaceType newType) { + if (!isRedirectingFactory) return newType; + assert(invariant(this, redirectionTargetType != null, + message: 'Redirection target type has not yet been computed for ' + '$this.')); + return redirectionTargetType.substByContext(newType); + } + + /** + * Applies a patch function to this function. The patch function's body + * is used as replacement when parsing this function's body. + * This method must not be called after the function has been parsed, + * and it must be called at most once. + */ + void setPatch(FunctionElement patchElement) { + // Sanity checks. The caller must check these things before calling. + assert(patch == null); + this.patch = patchElement; + } + + bool isInstanceMember() { + return isMember() + && !isConstructor() + && !modifiers.isStatic(); + } + + FunctionSignature computeSignature(Compiler compiler) { + if (functionSignatureCache != null) return functionSignatureCache; + compiler.withCurrentElement(this, () { + functionSignatureCache = compiler.resolveSignature(this); + }); + return functionSignatureCache; + } + + FunctionSignature get functionSignature { + assert(invariant(this, functionSignatureCache != null, + message: "Function signature has not been computed for $this.")); + return functionSignatureCache; + } + + FunctionType computeType(Compiler compiler) { + if (typeCache != null) return typeCache; + typeCache = computeSignature(compiler).type; + return typeCache; + } + + FunctionType get type { + assert(invariant(this, typeCache != null, + message: "Type has not been computed for $this.")); + return typeCache; + } + + FunctionExpression parseNode(DiagnosticListener listener) { + if (patch == null) { + if (modifiers.isExternal()) { + listener.internalError(this, + "Compiling external function with no implementation."); + } + } + return cachedNode; + } + + FunctionExpression get node { + assert(invariant(this, cachedNode != null, + message: "Node has not been computed for $this.")); + return cachedNode; + } + + Token position() { + // Use the name as position if this is not an unnamed closure. + if (cachedNode.name != null) { + return cachedNode.name.getBeginToken(); + } else { + return cachedNode.getBeginToken(); + } + } + + FunctionElement asFunctionElement() => this; + + String toString() { + if (isPatch) { + return 'patch ${super.toString()}'; + } else if (isPatched) { + return 'origin ${super.toString()}'; + } else { + return super.toString(); + } + } + + bool get isAbstract { + return !modifiers.isExternal() && + (isFunction() || isAccessor()) && + _hasNoBody; + } + + accept(ElementVisitor visitor) => visitor.visitFunctionElement(this); +} + +class DeferredLoaderGetterElementX extends FunctionElementX { + + final PrefixElement prefix; + + DeferredLoaderGetterElementX(PrefixElement prefix) + : this.prefix = prefix, + super("loadLibrary", + ElementKind.FUNCTION, + Modifiers.EMPTY, + prefix, true); + + FunctionSignature computeSignature(Compiler compiler) { + if (functionSignatureCache != null) return functionSignature; + compiler.withCurrentElement(this, () { + DartType inner = new FunctionType(this, compiler.types.dynamicType); + functionSignatureCache = new FunctionSignatureX(const Link(), + const Link(), 0, 0, false, [], inner); + }); + return functionSignatureCache; + } + + bool isMember() => false; + + bool isForeign(Compiler compiler) => true; + + bool get isSynthesized => true; + + bool isFunction() => false; + + bool isDeferredLoaderGetter() => true; + + bool isGetter() => true; + + // By having position null, the enclosing elements location is printed in + // error messages. + Token position() => null; +} + +class ConstructorBodyElementX extends FunctionElementX + implements ConstructorBodyElement { + FunctionElement constructor; + + ConstructorBodyElementX(FunctionElement constructor) + : this.constructor = constructor, + super(constructor.name, + ElementKind.GENERATIVE_CONSTRUCTOR_BODY, + Modifiers.EMPTY, + constructor.enclosingElement, false) { + functionSignatureCache = constructor.functionSignature; + } + + bool isInstanceMember() => true; + + FunctionType computeType(Compiler compiler) { + compiler.internalError(this, '$this.computeType.'); + return null; + } + + Node parseNode(DiagnosticListener listener) { + if (cachedNode != null) return cachedNode; + cachedNode = constructor.parseNode(listener); + assert(cachedNode != null); + return cachedNode; + } + + Token position() => constructor.position(); + + Element getOutermostEnclosingMemberOrTopLevel() => constructor; + + accept(ElementVisitor visitor) => visitor.visitConstructorBodyElement(this); +} + +/** + * A constructor that is not defined in the source code but rather implied by + * the language semantics. + * + * This class is used to represent default constructors and forwarding + * constructors for mixin applications. + */ +class SynthesizedConstructorElementX extends FunctionElementX { + final FunctionElement superMember; + final bool isDefaultConstructor; + + SynthesizedConstructorElementX(String name, + this.superMember, + Element enclosing, + this.isDefaultConstructor) + : super(name, + ElementKind.GENERATIVE_CONSTRUCTOR, + Modifiers.EMPTY, + enclosing, false); + + SynthesizedConstructorElementX.forDefault(superMember, Element enclosing) + : this('', superMember, enclosing, true); + + Token position() => enclosingElement.position(); + + bool get isSynthesized => true; + + FunctionElement get targetConstructor => superMember; + + FunctionSignature computeSignature(compiler) { + if (functionSignatureCache != null) return functionSignatureCache; + if (isDefaultConstructor) { + return functionSignatureCache = new FunctionSignatureX( + const Link(), const Link(), 0, 0, false, + const [], + new FunctionType(this, getEnclosingClass().thisType)); + } + if (superMember.isErroneous()) { + return functionSignatureCache = + compiler.objectClass.localLookup('').computeSignature(compiler); + } + // TODO(johnniwinther): Ensure that the function signature (and with it the + // function type) substitutes type variables correctly. + return functionSignatureCache = superMember.computeSignature(compiler); + } + + get declaration => this; + get implementation => this; + get defaultImplementation => this; + + accept(ElementVisitor visitor) { + return visitor.visitFunctionElement(this); + } +} + +class VoidElementX extends ElementX implements VoidElement { + VoidElementX(Element enclosing) : super('void', ElementKind.VOID, enclosing); + DartType computeType(compiler) => compiler.types.voidType; + Node parseNode(_) { + throw 'internal error: parseNode on void'; + } + bool impliesType() => true; + + accept(ElementVisitor visitor) => visitor.visitVoidElement(this); +} + +abstract class TypeDeclarationElementX + implements TypeDeclarationElement { + /** + * The `this type` for this type declaration. + * + * The type of [:this:] is the generic type based on this element in which + * the type arguments are the declared type variables. For instance, + * [:List:] for [:List:] and [:Map:] for [:Map:]. + * + * For a class declaration this is the type of [:this:]. + * + * This type is computed in [computeType]. + */ + T thisTypeCache; + + /** + * The raw type for this type declaration. + * + * The raw type is the generic type base on this element in which the type + * arguments are all [dynamic]. For instance [:List:] for [:List:] + * and [:Map:] for [:Map:]. For non-generic classes [rawType] + * is the same as [thisType]. + * + * The [rawType] field is a canonicalization of the raw type and should be + * used to distinguish explicit and implicit uses of the [dynamic] + * type arguments. For instance should [:List:] be the [rawType] of the + * [:List:] class element whereas [:List:] should be its own + * instantiation of [InterfaceType] with [:dynamic:] as type argument. Using + * this distinction, we can print the raw type with type arguments only when + * the input source has used explicit type arguments. + * + * This type is computed together with [thisType] in [computeType]. + */ + T rawTypeCache; + + T get thisType { + assert(invariant(this, thisTypeCache != null, + message: 'This type has not been computed for $this')); + return thisTypeCache; + } + + T get rawType { + assert(invariant(this, rawTypeCache != null, + message: 'Raw type has not been computed for $this')); + return rawTypeCache; + } + + T createType(Link typeArguments); + + void setThisAndRawTypes(Compiler compiler, Link typeParameters) { + assert(invariant(this, thisTypeCache == null, + message: "This type has already been set on $this.")); + assert(invariant(this, rawTypeCache == null, + message: "Raw type has already been set on $this.")); + thisTypeCache = createType(typeParameters); + if (typeParameters.isEmpty) { + rawTypeCache = thisTypeCache; + } else { + Link dynamicParameters = const Link(); + typeParameters.forEach((_) { + dynamicParameters = + dynamicParameters.prepend(compiler.types.dynamicType); + }); + rawTypeCache = createType(dynamicParameters); + } + } + + Link get typeVariables => thisType.typeArguments; + + /** + * Creates the type variables, their type and corresponding element, for the + * type variables declared in [parameter] on [element]. The bounds of the type + * variables are not set until [element] has been resolved. + */ + Link createTypeVariables(NodeList parameters) { + if (parameters == null) return const Link(); + + // Create types and elements for type variable. + LinkBuilder arguments = new LinkBuilder(); + for (Link link = parameters.nodes; !link.isEmpty; link = link.tail) { + TypeVariable node = link.head; + String variableName = node.name.source; + TypeVariableElementX variableElement = + new TypeVariableElementX(variableName, this, node); + TypeVariableType variableType = new TypeVariableType(variableElement); + variableElement.typeCache = variableType; + arguments.addLast(variableType); + } + return arguments.toLink(); + } +} + +abstract class BaseClassElementX extends ElementX + with AnalyzableElement, TypeDeclarationElementX + implements ClassElement { + final int id; + + DartType supertype; + Link interfaces; + String nativeTagInfo; + int supertypeLoadState; + int resolutionState; + bool get isResolved => resolutionState == STATE_DONE; + bool isProxy = false; + bool hasIncompleteHierarchy = false; + + // backendMembers are members that have been added by the backend to simplify + // compilation. They don't have any user-side counter-part. + Link backendMembers = const Link(); + + OrderedTypeSet allSupertypesAndSelf; + + Link get allSupertypes => allSupertypesAndSelf.supertypes; + + int get hierarchyDepth => allSupertypesAndSelf.maxDepth; + + Map classMembers; + Map interfaceMembers; + + BaseClassElementX(String name, + Element enclosing, + this.id, + int initialState) + : supertypeLoadState = initialState, + resolutionState = initialState, + super(name, ElementKind.CLASS, enclosing); + + int get hashCode => id; + ClassElement get patch => super.patch; + ClassElement get origin => super.origin; + ClassElement get declaration => super.declaration; + ClassElement get implementation => super.implementation; + + bool get hasBackendMembers => !backendMembers.isEmpty; + + bool get isUnnamedMixinApplication => false; + + InterfaceType computeType(Compiler compiler) { + if (thisTypeCache == null) { + computeThisAndRawType(compiler, computeTypeParameters(compiler)); + } + return thisTypeCache; + } + + void computeThisAndRawType(Compiler compiler, Link typeVariables) { + if (thisTypeCache == null) { + if (origin == null) { + setThisAndRawTypes(compiler, typeVariables); + } else { + thisTypeCache = origin.computeType(compiler); + rawTypeCache = origin.rawType; + } + } + } + + InterfaceType createType(Link typeArguments) { + return new InterfaceType(this, typeArguments); + } + + Link computeTypeParameters(Compiler compiler); + + /** + * Return [:true:] if this element is the [:Object:] class for the [compiler]. + */ + bool isObject(Compiler compiler) => + identical(declaration, compiler.objectClass); + + void ensureResolved(Compiler compiler) { + if (resolutionState == STATE_NOT_STARTED) { + compiler.resolver.resolveClass(this); + } + } + + void setDefaultConstructor(FunctionElement constructor, Compiler compiler); + + void addBackendMember(Element member) { + // TODO(ngeoffray): Deprecate this method. + assert(member.isGenerativeConstructorBody()); + backendMembers = backendMembers.prepend(member); + } + + void reverseBackendMembers() { + backendMembers = backendMembers.reverse(); + } + + /** + * Lookup local members in the class. This will ignore constructors. + */ + Element lookupLocalMember(String memberName) { + var result = localLookup(memberName); + if (result != null && result.isConstructor()) return null; + return result; + } + + /// Lookup a synthetic element created by the backend. + Element lookupBackendMember(String memberName) { + for (Element element in backendMembers) { + if (element.name == memberName) { + return element; + } + } + return null; + } + /** + * Lookup super members for the class. This will ignore constructors. + */ + Element lookupSuperMember(String memberName) { + return lookupSuperMemberInLibrary(memberName, getLibrary()); + } + + /** + * Lookup super members for the class that is accessible in [library]. + * This will ignore constructors. + */ + Element lookupSuperMemberInLibrary(String memberName, + LibraryElement library) { + bool isPrivate = isPrivateName(memberName); + for (ClassElement s = superclass; s != null; s = s.superclass) { + // Private members from a different library are not visible. + if (isPrivate && !identical(library, s.getLibrary())) continue; + Element e = s.lookupLocalMember(memberName); + if (e == null) continue; + // Static members are not inherited. + if (e.modifiers.isStatic()) continue; + return e; + } + return null; + } + + /** + * Find the first member in the class chain with the given [selector]. + * + * This method is NOT to be used for resolving + * unqualified sends because it does not implement the scoping + * rules, where library scope comes before superclass scope. + * + * When called on the implementation element both members declared in the + * origin and the patch class are returned. + */ + Element lookupSelector(Selector selector, Compiler compiler) { + return internalLookupSelector(selector, compiler, false); + } + + Element lookupSuperSelector(Selector selector, Compiler compiler) { + return internalLookupSelector(selector, compiler, true); + } + + Element internalLookupSelector(Selector selector, + Compiler compiler, + bool isSuperLookup) { + String name = selector.name; + bool isPrivate = isPrivateName(name); + LibraryElement library = selector.library; + for (ClassElement current = isSuperLookup ? superclass : this; + current != null; + current = current.superclass) { + Element member = current.lookupLocalMember(name); + if (member == null && current.isPatched) { + // Doing lookups on selectors is done after resolution, so it + // is safe to look in the patch class. + member = current.patch.lookupLocalMember(name); + } + if (member == null) continue; + // Private members from a different library are not visible. + if (isPrivate && !identical(library, member.getLibrary())) continue; + // Static members are not inherited. + if (member.modifiers.isStatic() && !identical(this, current)) continue; + // If we find an abstract field we have to make sure that it has + // the getter or setter part we're actually looking + // for. Otherwise, we continue up the superclass chain. + if (member.isAbstractField()) { + AbstractFieldElement field = member; + FunctionElement getter = field.getter; + FunctionElement setter = field.setter; + if (selector.isSetter()) { + // Abstract members can be defined in a super class. + if (setter != null && !setter.isAbstract) return setter; + } else { + assert(selector.isGetter() || selector.isCall()); + if (getter != null && !getter.isAbstract) return getter; + } + // Abstract members can be defined in a super class. + } else if (!member.isAbstract) { + return member; + } + } + return null; + } + + /** + * Find the first member in the class chain with the given + * [memberName]. This method is NOT to be used for resolving + * unqualified sends because it does not implement the scoping + * rules, where library scope comes before superclass scope. + */ + Element lookupMember(String memberName) { + Element localMember = lookupLocalMember(memberName); + return localMember == null ? lookupSuperMember(memberName) : localMember; + } + + /** + * Returns true if the [fieldMember] shadows another field. The given + * [fieldMember] must be a member of this class, i.e. if there is a field of + * the same name in the superclass chain. + * + * This method also works if the [fieldMember] is private. + */ + bool hasFieldShadowedBy(Element fieldMember) { + assert(fieldMember.isField()); + String fieldName = fieldMember.name; + bool isPrivate = isPrivateName(fieldName); + LibraryElement memberLibrary = fieldMember.getLibrary(); + ClassElement lookupClass = this.superclass; + while (lookupClass != null) { + Element foundMember = lookupClass.lookupLocalMember(fieldName); + if (foundMember != null) { + if (foundMember.isField()) { + if (!isPrivate || memberLibrary == foundMember.getLibrary()) { + // Private fields can only be shadowed by a field declared in the + // same library. + return true; + } + } + } + lookupClass = lookupClass.superclass; + } + return false; + } + + Element validateConstructorLookupResults(Selector selector, + Element result, + Element noMatch(Element)) { + if (result == null + || !result.isConstructor() + || (isPrivateName(selector.name) + && result.getLibrary() != selector.library)) { + result = noMatch != null ? noMatch(result) : null; + } + return result; + } + + // TODO(aprelev@gmail.com): Peter believes that it would be great to + // make noMatch a required argument. Peter's suspicion is that most + // callers of this method would benefit from using the noMatch method. + Element lookupConstructor(Selector selector, [Element noMatch(Element)]) { + Element result = localLookup(selector.name); + return validateConstructorLookupResults(selector, result, noMatch); + } + + Link get constructors { + // TODO(ajohnsen): See if we can avoid this method at some point. + Link result = const Link(); + // TODO(johnniwinther): Should we include injected constructors? + forEachMember((_, Element member) { + if (member.isConstructor()) result = result.prepend(member); + }); + return result; + } + + /** + * Returns the super class, if any. + * + * The returned element may not be resolved yet. + */ + ClassElement get superclass { + assert(supertypeLoadState == STATE_DONE); + return supertype == null ? null : supertype.element; + } + + /** + * Runs through all members of this class. + * + * The enclosing class is passed to the callback. This is useful when + * [includeSuperAndInjectedMembers] is [:true:]. + * + * When called on an implementation element both the members in the origin + * and patch class are included. + */ + // TODO(johnniwinther): Clean up lookup to get rid of the include predicates. + void forEachMember(void f(ClassElement enclosingClass, Element member), + {includeBackendMembers: false, + includeSuperAndInjectedMembers: false}) { + bool includeInjectedMembers = includeSuperAndInjectedMembers || isPatch; + ClassElement classElement = declaration; + do { + // Iterate through the members in textual order, which requires + // to reverse the data structure [localMembers] we created. + // Textual order may be important for certain operations, for + // example when emitting the initializers of fields. + classElement.forEachLocalMember((e) => f(classElement, e)); + if (includeBackendMembers) { + classElement.forEachBackendMember((e) => f(classElement, e)); + } + if (includeInjectedMembers) { + if (classElement.patch != null) { + classElement.patch.forEachLocalMember((e) { + if (!e.isPatch) f(classElement, e); + }); + } + } + classElement = includeSuperAndInjectedMembers + ? classElement.superclass + : null; + } while (classElement != null); + } + + /** + * Runs through all instance-field members of this class. + * + * The enclosing class is passed to the callback. This is useful when + * [includeSuperAndInjectedMembers] is [:true:]. + * + * When called on the implementation element both the fields declared in the + * origin and in the patch are included. + */ + void forEachInstanceField(void f(ClassElement enclosingClass, + FieldElement field), + {bool includeSuperAndInjectedMembers: false}) { + // Filters so that [f] is only invoked with instance fields. + void fieldFilter(ClassElement enclosingClass, Element member) { + if (member.isInstanceMember() && member.kind == ElementKind.FIELD) { + f(enclosingClass, member); + } + } + + forEachMember(fieldFilter, + includeSuperAndInjectedMembers: includeSuperAndInjectedMembers); + } + + /// Similar to [forEachInstanceField] but visits static fields. + void forEachStaticField(void f(ClassElement enclosingClass, Element field)) { + // Filters so that [f] is only invoked with static fields. + void fieldFilter(ClassElement enclosingClass, Element member) { + if (!member.isInstanceMember() && member.kind == ElementKind.FIELD) { + f(enclosingClass, member); + } + } + + forEachMember(fieldFilter); + } + + void forEachBackendMember(void f(Element member)) { + backendMembers.forEach(f); + } + + bool implementsInterface(ClassElement intrface) { + for (DartType implementedInterfaceType in allSupertypes) { + ClassElement implementedInterface = implementedInterfaceType.element; + if (identical(implementedInterface, intrface)) { + return true; + } + } + return false; + } + + /** + * Returns true if [this] is a subclass of [cls]. + * + * This method is not to be used for checking type hierarchy and + * assignments, because it does not take parameterized types into + * account. + */ + bool isSubclassOf(ClassElement cls) { + // Use [declaration] for both [this] and [cls], because + // declaration classes hold the superclass hierarchy. + cls = cls.declaration; + for (ClassElement s = declaration; s != null; s = s.superclass) { + if (identical(s, cls)) return true; + } + return false; + } + + bool isNative() => nativeTagInfo != null; + void setNative(String name) { + nativeTagInfo = name; + } + + Member lookupClassMember(Name name) => classMembers[name]; + + void forEachClassMember(f(Member member)) { + classMembers.forEach((_, member) => f(member)); + } + + MemberSignature lookupInterfaceMember(Name name) => interfaceMembers[name]; + + void forEachInterfaceMember(f(MemberSignature member)) { + interfaceMembers.forEach((_, member) => f(member)); + } + + FunctionType get callType { + MemberSignature member = + lookupInterfaceMember(const PublicName(Compiler.CALL_OPERATOR_NAME)); + return member != null && member.isMethod ? member.type : null; + } +} + +abstract class ClassElementX extends BaseClassElementX { + // Lazily applied patch of class members. + ClassElement patch = null; + ClassElement origin = null; + + Link localMembers = const Link(); + final ScopeX localScope = new ScopeX(); + + ClassElementX(String name, Element enclosing, int id, int initialState) + : super(name, enclosing, id, initialState); + + ClassNode parseNode(Compiler compiler); + + bool get isMixinApplication => false; + bool get isPatched => patch != null; + bool get isPatch => origin != null; + bool get hasLocalScopeMembers => !localScope.isEmpty; + + void addMember(Element element, DiagnosticListener listener) { + localMembers = localMembers.prepend(element); + addToScope(element, listener); + } + + void addToScope(Element element, DiagnosticListener listener) { + if (element.isField() && element.name == name) { + listener.reportError(element, MessageKind.MEMBER_USES_CLASS_NAME); + } + localScope.add(element, listener); + } + + Element localLookup(String elementName) { + Element result = localScope.lookup(elementName); + if (result == null && isPatch) { + result = origin.localLookup(elementName); + } + return result; + } + + void forEachLocalMember(void f(Element member)) { + localMembers.reverse().forEach(f); + } + + bool get hasConstructor { + // Search in scope to be sure we search patched constructors. + for (var element in localScope.values) { + if (element.isConstructor()) return true; + } + return false; + } + + void setDefaultConstructor(FunctionElement constructor, Compiler compiler) { + addToScope(constructor, compiler); + // The default constructor, although synthetic, is part of a class' API. + localMembers = localMembers.prepend(constructor); + } + + Link computeTypeParameters(Compiler compiler) { + ClassNode node = parseNode(compiler); + return createTypeVariables(node.typeParameters); + } + + Scope buildScope() => new ClassScope(enclosingElement.buildScope(), this); + + String toString() { + if (origin != null) { + return 'patch ${super.toString()}'; + } else if (patch != null) { + return 'origin ${super.toString()}'; + } else { + return super.toString(); + } + } +} + +class MixinApplicationElementX extends BaseClassElementX + implements MixinApplicationElement { + final Node node; + final Modifiers modifiers; + + Link constructors = new Link(); + + InterfaceType mixinType; + + MixinApplicationElementX(String name, Element enclosing, int id, + this.node, this.modifiers) + : super(name, enclosing, id, STATE_NOT_STARTED); + + ClassElement get mixin => mixinType != null ? mixinType.element : null; + + bool get isMixinApplication => true; + bool get isUnnamedMixinApplication => node is! NamedMixinApplication; + bool get hasConstructor => !constructors.isEmpty; + bool get hasLocalScopeMembers => !constructors.isEmpty; + + unsupported(message) { + throw new UnsupportedError('$message is not supported on $this'); + } + + get patch => null; + get origin => null; + + set patch(value) => unsupported('set patch'); + set origin(value) => unsupported('set origin'); + + Token position() => node.getBeginToken(); + + Node parseNode(DiagnosticListener listener) => node; + + FunctionElement lookupLocalConstructor(String name) { + for (Link link = constructors; + !link.isEmpty; + link = link.tail) { + if (link.head.name == name) return link.head; + } + return null; + } + + Element localLookup(String name) { + Element constructor = lookupLocalConstructor(name); + if (constructor != null) return constructor; + if (mixin == null) return null; + Element mixedInElement = mixin.localLookup(name); + if (mixedInElement == null) return null; + return mixedInElement.isInstanceMember() ? mixedInElement : null; + } + + void forEachLocalMember(void f(Element member)) { + constructors.forEach(f); + if (mixin != null) mixin.forEachLocalMember((Element mixedInElement) { + if (mixedInElement.isInstanceMember()) f(mixedInElement); + }); + } + + void addMember(Element element, DiagnosticListener listener) { + throw new UnsupportedError("Cannot add member to $this."); + } + + void addToScope(Element element, DiagnosticListener listener) { + listener.internalError(this, 'Cannot add to scope of $this.'); + } + + void addConstructor(FunctionElement constructor) { + constructors = constructors.prepend(constructor); + } + + void setDefaultConstructor(FunctionElement constructor, Compiler compiler) { + assert(!hasConstructor); + addConstructor(constructor); + } + + Link computeTypeParameters(Compiler compiler) { + NamedMixinApplication named = node.asNamedMixinApplication(); + if (named == null) { + throw new SpannableAssertionFailure(node, + "Type variables on unnamed mixin applications must be set on " + "creation."); + } + return createTypeVariables(named.typeParameters); + } + + accept(ElementVisitor visitor) => visitor.visitMixinApplicationElement(this); +} + +class LabelElementX extends ElementX implements LabelElement { + + // We store the original label here so it can be returned by [parseNode]. + final Label label; + final String labelName; + final TargetElement target; + bool isBreakTarget = false; + bool isContinueTarget = false; + LabelElementX(Label label, String labelName, this.target, + Element enclosingElement) + : this.label = label, + this.labelName = labelName, + // In case of a synthetic label, just use [labelName] for + // identifying the element. + super(label == null + ? labelName + : label.identifier.source, + ElementKind.LABEL, + enclosingElement); + + void setBreakTarget() { + isBreakTarget = true; + target.isBreakTarget = true; + } + void setContinueTarget() { + isContinueTarget = true; + target.isContinueTarget = true; + } + + bool get isTarget => isBreakTarget || isContinueTarget; + Node parseNode(DiagnosticListener l) => label; + + Token position() => label.getBeginToken(); + String toString() => "${labelName}:"; + + accept(ElementVisitor visitor) => visitor.visitLabelElement(this); +} + +// Represents a reference to a statement or switch-case, either by label or the +// default target of a break or continue. +class TargetElementX extends ElementX implements TargetElement { + final Node statement; + final int nestingLevel; + Link labels = const Link(); + bool isBreakTarget = false; + bool isContinueTarget = false; + + TargetElementX(this.statement, this.nestingLevel, Element enclosingElement) + : super("target", ElementKind.STATEMENT, enclosingElement); + bool get isTarget => isBreakTarget || isContinueTarget; + + LabelElement addLabel(Label label, String labelName) { + LabelElement result = new LabelElementX(label, labelName, this, + enclosingElement); + labels = labels.prepend(result); + return result; + } + + Node parseNode(DiagnosticListener l) => statement; + + bool get isSwitch => statement is SwitchStatement; + + Token position() => statement.getBeginToken(); + String toString() => statement.toString(); + + accept(ElementVisitor visitor) => visitor.visitTargetElement(this); +} + +class TypeVariableElementX extends ElementX implements TypeVariableElement { + final Node cachedNode; + TypeVariableType typeCache; + DartType boundCache; + + TypeVariableElementX(String name, Element enclosing, this.cachedNode) + : super(name, ElementKind.TYPE_VARIABLE, enclosing); + + TypeVariableType computeType(compiler) => type; + + TypeVariableType get type { + assert(invariant(this, typeCache != null, + message: "Type has not been set on $this.")); + return typeCache; + } + + DartType get bound { + assert(invariant(this, boundCache != null, + message: "Bound has not been set on $this.")); + return boundCache; + } + + Node parseNode(compiler) => cachedNode; + + String toString() => "${enclosingElement.toString()}.${name}"; + + Token position() => cachedNode.getBeginToken(); + + accept(ElementVisitor visitor) => visitor.visitTypeVariableElement(this); +} + +/** + * A single metadata annotation. + * + * For example, consider: + * + * class Data { + * const Data(); + * } + * + * const data = const Data(); + * + * @data + * class Foo {} + * + * @data @data + * class Bar {} + * + * In this example, there are three instances of [MetadataAnnotation] + * and they correspond each to a location in the source code where + * there is an at-sign, '@'. The [value] of each of these instances + * are the same compile-time constant, [: const Data() :]. + * + * The mirror system does not have a concept matching this class. + */ +abstract class MetadataAnnotationX implements MetadataAnnotation { + /** + * The compile-time constant which this annotation resolves to. + * In the mirror system, this would be an object mirror. + */ + Constant value; + Element annotatedElement; + int resolutionState; + + /** + * The beginning token of this annotation, or [:null:] if it is synthetic. + */ + Token get beginToken; + + MetadataAnnotationX([this.resolutionState = STATE_NOT_STARTED]); + + MetadataAnnotation ensureResolved(Compiler compiler) { + if (resolutionState == STATE_NOT_STARTED) { + compiler.resolver.resolveMetadataAnnotation(this); + } + return this; + } + + Node parseNode(DiagnosticListener listener); + + String toString() => 'MetadataAnnotation($value, $resolutionState)'; +} + +/// Metadata annotation on a parameter. +class ParameterMetadataAnnotation extends MetadataAnnotationX { + final Metadata metadata; + + ParameterMetadataAnnotation(Metadata this.metadata); + + Node parseNode(DiagnosticListener listener) => metadata.expression; + + Token get beginToken => metadata.getBeginToken(); + + Token get endToken => metadata.getEndToken(); +} + diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/names.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/names.dart new file mode 100644 index 0000000..7dfdf3a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/names.dart @@ -0,0 +1,101 @@ +// Copyright (c) 2014, 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. + +part of elements; + +/// A [Name] represents the abstraction of a Dart identifier which takes privacy +/// and setter into account. +// TODO(johnniwinther): Try to share logic with [Selector]. +abstract class Name { + /// Create a [Name] for an identifier [text]. If [text] begins with '_' a + /// private name with respect to [library] is created. If [isSetter] is `true` + /// the created name represents the setter name 'text='. + factory Name(String text, LibraryElement library, {bool isSetter: false}) { + if (isPrivateName(text)) { + return new PrivateName(text, library, isSetter: isSetter); + } + return new PublicName(text, isSetter: isSetter); + } + + /// The text of the name without prefixed library name or suffixed '=' if + /// applicable. + String get text; + + /// Is `true` if this name represents the name of a setter. + bool get isSetter; + + /// Returns the getter name corresponding to this name. If this name is a + /// setter name 'v=' then the name 'v' is returned, otherwise the name itself + /// is returned. + Name get getter; + + /// Returns the seeter name corresponding to this name. If this name is a + /// getter name 'v' then the name 'v=' is returned, otherwsie the name itself + /// is returned. + Name get setter; + + /// Returns `true` if an entity of this name is accessible from library + /// [element]. + bool isAccessibleFrom(LibraryElement element); + + /// Returns `true` if this name is private. + bool get isPrivate; + + /// Returns `true` if this name is the same as [other] not taking the library + /// privacy into account. + bool isSimilarTo(Name other); +} + +class PublicName implements Name { + final String text; + final bool isSetter; + + const PublicName(this.text, {this.isSetter: false}); + + Name get getter => isSetter ? new PublicName(text) : this; + + Name get setter => isSetter ? this : new PublicName(text, isSetter: true); + + bool isAccessibleFrom(LibraryElement element) => true; + + bool get isPrivate => false; + + int get hashCode => text.hashCode + 11 * isSetter.hashCode; + + bool operator ==(other) { + if (other is! PublicName) return false; + return isSimilarTo(other); + } + + bool isSimilarTo(Name other) => + text == other.text && isSetter == other.isSetter; + + String toString() => isSetter ? '$text=' : text; +} + +class PrivateName extends PublicName { + final LibraryElement library; + + PrivateName(String text, this.library, {bool isSetter: false}) + : super(text, isSetter: isSetter); + + Name get getter => isSetter ? new PrivateName(text, library) : this; + + Name get setter { + return isSetter ? this : new PrivateName(text, library, isSetter: true); + } + + bool isAccessibleFrom(LibraryElement element) => library == element; + + bool get isPrivate => true; + + int get hashCode => super.hashCode + 13 * library.hashCode; + + bool operator ==(other) { + if (other is! PrivateName) return false; + return super==(other) && library == other.library; + } + + String toString() => '${library.getLibraryName()}#${super.toString()}'; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/visitor.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/visitor.dart new file mode 100644 index 0000000..b234f16 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/elements/visitor.dart @@ -0,0 +1,50 @@ +// Copyright (c) 2012, 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. + +library visitor; + +import 'elements.dart'; +import '../ssa/ssa.dart' + show InterceptedElement; +import '../closure.dart' + show ThisElement, + BoxElement, + BoxFieldElement, + ClosureClassElement, + ClosureFieldElement; + +abstract class ElementVisitor { + R visit(Element e) => e.accept(this); + + R visitElement(Element e); + R visitErroneousElement(ErroneousElement e) => visitFunctionElement(e); + R visitWarnOnUseElement(WarnOnUseElement e) => visitElement(e); + R visitAmbiguousElement(AmbiguousElement e) => visitElement(e); + R visitScopeContainerElement(ScopeContainerElement e) => visitElement(e); + R visitCompilationUnitElement(CompilationUnitElement e) => visitElement(e); + R visitLibraryElement(LibraryElement e) => visitScopeContainerElement(e); + R visitPrefixElement(PrefixElement e) => visitElement(e); + R visitTypedefElement(TypedefElement e) => visitElement(e); + R visitVariableElement(VariableElement e) => visitElement(e); + R visitFieldElement(FieldElement e) => visitVariableElement(e); + R visitFieldParameterElement(FieldParameterElement e) => visitElement(e); + R visitAbstractFieldElement(AbstractFieldElement e) => visitElement(e); + R visitFunctionElement(FunctionElement e) => visitElement(e); + R visitConstructorBodyElement(ConstructorBodyElement e) => visitElement(e); + R visitClassElement(ClassElement e) => visitScopeContainerElement(e); + R visitTypeDeclarationElement(TypeDeclarationElement e) => visitElement(e); + R visitMixinApplicationElement(MixinApplicationElement e) { + return visitClassElement(e); + } + R visitVoidElement(VoidElement e) => visitElement(e); + R visitLabelElement(LabelElement e) => visitElement(e); + R visitTargetElement(TargetElement e) => visitElement(e); + R visitTypeVariableElement(TypeVariableElement e) => visitElement(e); + R visitInterceptedElement(InterceptedElement e) => visitElement(e); + R visitThisElement(ThisElement e) => visitElement(e); + R visitBoxElement(BoxElement e) => visitElement(e); + R visitBoxFieldElement(BoxFieldElement e) => visitElement(e); + R visitClosureClassElement(ClosureClassElement e) => visitClassElement(e); + R visitClosureFieldElement(ClosureFieldElement e) => visitVariableElement(e); +} \ No newline at end of file diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/enqueue.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/enqueue.dart new file mode 100644 index 0000000..47150a0 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/enqueue.dart @@ -0,0 +1,781 @@ +// Copyright (c) 2012, 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. + +part of dart2js; + +typedef ItemCompilationContext ItemCompilationContextCreator(); + +class EnqueueTask extends CompilerTask { + final ResolutionEnqueuer resolution; + final CodegenEnqueuer codegen; + + /// A reverse map from name to *all* elements with that name, not + /// just instance members of instantiated classes. + final Map> allElementsByName + = new Map>(); + + void ensureAllElementsByName() { + if (!allElementsByName.isEmpty) return; + + void addMemberByName(Element element) { + element = element.declaration; + String name = element.name; + ScopeContainerElement container = null; + if (element.isLibrary()) { + LibraryElement library = element; + // Don't include private implementation libraries. These + // libraries contain special classes that cause problems + // in other parts of the resolver (in particular Null and Void). + // TODO(ahe): Consider lifting this restriction. + if (!library.isInternalLibrary) { + container = library; + // TODO(ahe): Is this right? Is this necessary? + name = library.getLibraryOrScriptName(); + } + } else if (element.isClass()) { + ClassElement cls = element; + cls.ensureResolved(compiler); + container = cls; + for (var link = cls.computeTypeParameters(compiler); + !link.isEmpty; + link = link.tail) { + addMemberByName(link.head.element); + } + } + allElementsByName[name] = allElementsByName.putIfAbsent( + name, () => const Link()).prepend(element); + if (container != null) { + container.forEachLocalMember(addMemberByName); + } + } + + compiler.libraries.values.forEach(addMemberByName); + } + + String get name => 'Enqueue'; + + EnqueueTask(Compiler compiler) + : resolution = new ResolutionEnqueuer( + compiler, compiler.backend.createItemCompilationContext), + codegen = new CodegenEnqueuer( + compiler, compiler.backend.createItemCompilationContext), + super(compiler) { + codegen.task = this; + resolution.task = this; + + codegen.nativeEnqueuer = compiler.backend.nativeCodegenEnqueuer(codegen); + resolution.nativeEnqueuer = + compiler.backend.nativeResolutionEnqueuer(resolution); + } +} + +abstract class Enqueuer { + final String name; + final Compiler compiler; // TODO(ahe): Remove this dependency. + final ItemCompilationContextCreator itemCompilationContextCreator; + final Map> instanceMembersByName + = new Map>(); + final Map> instanceFunctionsByName + = new Map>(); + final Set seenClasses = new Set(); + final Universe universe = new Universe(); + + bool queueIsClosed = false; + EnqueueTask task; + native.NativeEnqueuer nativeEnqueuer; // Set by EnqueueTask + + bool hasEnqueuedEverything = false; + bool hasEnqueuedReflectiveStaticFields = false; + + Enqueuer(this.name, this.compiler, this.itemCompilationContextCreator); + + Queue get queue; + + /// Returns [:true:] if this enqueuer is the resolution enqueuer. + bool get isResolutionQueue => false; + + /// Returns [:true:] if [member] has been processed by this enqueuer. + bool isProcessed(Element member); + + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [element] must be a declaration element. + */ + void addToWorkList(Element element) { + assert(invariant(element, element.isDeclaration)); + internalAddToWorkList(element); + } + + /** + * Adds [element] to the work list if it has not already been processed. + */ + void internalAddToWorkList(Element element); + + void registerInstantiatedType(InterfaceType type, TreeElements elements) { + task.measure(() { + ClassElement cls = type.element; + elements.registerDependency(cls); + cls.ensureResolved(compiler); + universe.instantiatedTypes.add(type); + if (!cls.isAbstract + // We can't use the closed-world assumption with native abstract + // classes; a native abstract class may have non-abstract subclasses + // not declared to the program. Instances of these classes are + // indistinguishable from the abstract class. + || cls.isNative()) { + universe.instantiatedClasses.add(cls); + } + onRegisterInstantiatedClass(cls); + }); + } + + void registerInstantiatedClass(ClassElement cls, TreeElements elements) { + cls.ensureResolved(compiler); + registerInstantiatedType(cls.rawType, elements); + } + + void registerTypeLiteral(Element element, TreeElements elements) { + registerInstantiatedClass(compiler.typeClass, elements); + compiler.backend.registerTypeLiteral(element, this, elements); + } + + bool checkNoEnqueuedInvokedInstanceMethods() { + task.measure(() { + // Run through the classes and see if we need to compile methods. + for (ClassElement classElement in universe.instantiatedClasses) { + for (ClassElement currentClass = classElement; + currentClass != null; + currentClass = currentClass.superclass) { + processInstantiatedClass(currentClass); + } + } + }); + return true; + } + + void processInstantiatedClass(ClassElement cls) { + cls.implementation.forEachMember(processInstantiatedClassMember); + } + + void processInstantiatedClassMember(ClassElement cls, Element member) { + assert(invariant(member, member.isDeclaration)); + if (isProcessed(member)) return; + if (!member.isInstanceMember()) return; + + String memberName = member.name; + + if (member.kind == ElementKind.FIELD) { + // The obvious thing to test here would be "member.isNative()", + // however, that only works after metadata has been parsed/analyzed, + // and that may not have happened yet. + // So instead we use the enclosing class, which we know have had + // its metadata parsed and analyzed. + // Note: this assumes that there are no non-native fields on native + // classes, which may not be the case when a native class is subclassed. + if (cls.isNative()) { + compiler.world.registerUsedElement(member); + nativeEnqueuer.handleFieldAnnotations(member); + if (universe.hasInvokedGetter(member, compiler) || + universe.hasInvocation(member, compiler)) { + nativeEnqueuer.registerFieldLoad(member); + // In handleUnseenSelector we can't tell if the field is loaded or + // stored. We need the basic algorithm to be Church-Rosser, since the + // resolution 'reduction' order is different to the codegen order. So + // register that the field is also stored. In other words: if we + // don't register the store here during resolution, the store could be + // registered during codegen on the handleUnseenSelector path, and + // cause the set of codegen elements to include unresolved elements. + nativeEnqueuer.registerFieldStore(member); + addToWorkList(member); + return; + } + if (universe.hasInvokedSetter(member, compiler)) { + nativeEnqueuer.registerFieldStore(member); + // See comment after registerFieldLoad above. + nativeEnqueuer.registerFieldLoad(member); + addToWorkList(member); + return; + } + // Native fields need to go into instanceMembersByName as they + // are virtual instantiation points and escape points. + } else { + // All field initializers must be resolved as they could + // have an observable side-effect (and cannot be tree-shaken + // away). + addToWorkList(member); + return; + } + } else if (member.kind == ElementKind.FUNCTION) { + if (member.name == Compiler.NO_SUCH_METHOD) { + enableNoSuchMethod(member); + } + if (member.name == Compiler.CALL_OPERATOR_NAME && + !cls.typeVariables.isEmpty) { + registerGenericCallMethod(member, compiler.globalDependencies); + } + // If there is a property access with the same name as a method we + // need to emit the method. + if (universe.hasInvokedGetter(member, compiler)) { + registerClosurizedMember(member, compiler.globalDependencies); + addToWorkList(member); + return; + } + // Store the member in [instanceFunctionsByName] to catch + // getters on the function. + Link members = instanceFunctionsByName.putIfAbsent( + memberName, () => const Link()); + instanceFunctionsByName[memberName] = members.prepend(member); + if (universe.hasInvocation(member, compiler)) { + addToWorkList(member); + return; + } + } else if (member.kind == ElementKind.GETTER) { + if (universe.hasInvokedGetter(member, compiler)) { + addToWorkList(member); + return; + } + // We don't know what selectors the returned closure accepts. If + // the set contains any selector we have to assume that it matches. + if (universe.hasInvocation(member, compiler)) { + addToWorkList(member); + return; + } + } else if (member.kind == ElementKind.SETTER) { + if (universe.hasInvokedSetter(member, compiler)) { + addToWorkList(member); + return; + } + } + + // The element is not yet used. Add it to the list of instance + // members to still be processed. + Link members = instanceMembersByName.putIfAbsent( + memberName, () => const Link()); + instanceMembersByName[memberName] = members.prepend(member); + } + + void enableNoSuchMethod(Element element) {} + void enableIsolateSupport(LibraryElement element) {} + + void onRegisterInstantiatedClass(ClassElement cls) { + task.measure(() { + if (seenClasses.contains(cls)) return; + // The class must be resolved to compute the set of all + // supertypes. + cls.ensureResolved(compiler); + + void processClass(ClassElement cls) { + if (seenClasses.contains(cls)) return; + + seenClasses.add(cls); + cls.ensureResolved(compiler); + cls.implementation.forEachMember(processInstantiatedClassMember); + if (isResolutionQueue) { + compiler.resolver.checkClass(cls); + } + // We only tell the backend once that [cls] was instantiated, so + // any additional dependencies must be treated as global + // dependencies. + compiler.backend.registerInstantiatedClass( + cls, this, compiler.globalDependencies); + } + processClass(cls); + for (Link supertypes = cls.allSupertypes; + !supertypes.isEmpty; supertypes = supertypes.tail) { + processClass(supertypes.head.element); + } + }); + } + + void registerNewSelector(Selector selector, + Map> selectorsMap) { + String name = selector.name; + Set selectors = + selectorsMap.putIfAbsent(name, () => new Set()); + if (!selectors.contains(selector)) { + selectors.add(selector); + handleUnseenSelector(name, selector); + } + } + + void registerInvocation(Selector selector) { + task.measure(() { + registerNewSelector(selector, universe.invokedNames); + }); + } + + void registerInvokedGetter(Selector selector) { + task.measure(() { + registerNewSelector(selector, universe.invokedGetters); + }); + } + + void registerInvokedSetter(Selector selector) { + task.measure(() { + registerNewSelector(selector, universe.invokedSetters); + }); + } + + /// Called when [:const Symbol(name):] is seen. + void registerConstSymbol(String name, TreeElements elements) { + compiler.backend.registerConstSymbol(name, elements); + } + + void pretendElementWasUsed(Element element, TreeElements elements) { + if (!compiler.backend.isNeededForReflection(element)) return; + if (Elements.isUnresolved(element)) { + // Ignore. + } else if (element.isSynthesized + && element.getLibrary().isPlatformLibrary) { + // TODO(ahe): Work-around for http://dartbug.com/11205. + } else if (element.isConstructor()) { + ClassElement cls = element.declaration.getEnclosingClass(); + registerInstantiatedType(cls.rawType, elements); + registerStaticUse(element.declaration); + } else if (element.isClass()) { + ClassElement cls = element.declaration; + registerInstantiatedClass(cls, elements); + // Make sure that even abstract classes are considered instantiated. + universe.instantiatedClasses.add(cls); + } else if (element.impliesType()) { + // Don't enqueue typedefs, and type variables. + } else if (Elements.isStaticOrTopLevel(element)) { + registerStaticUse(element.declaration); + } else if (element.isInstanceMember()) { + Selector selector = new Selector.fromElement(element, compiler); + registerSelectorUse(selector); + if (element.isField()) { + Selector selector = + new Selector.setter(element.name, element.getLibrary()); + registerInvokedSetter(selector); + } + } + } + + /// Called when [:new Symbol(...):] is seen. + void registerNewSymbol(TreeElements elements) { + compiler.backend.registerNewSymbol(elements); + } + + void enqueueEverything() { + if (hasEnqueuedEverything) return; + compiler.log('Enqueuing everything'); + task.ensureAllElementsByName(); + for (Link link in task.allElementsByName.values) { + for (Element element in link) { + pretendElementWasUsed(element, compiler.globalDependencies); + } + } + hasEnqueuedEverything = true; + hasEnqueuedReflectiveStaticFields = true; + } + + /// Enqueue the static fields that have been marked as used by reflective + /// usage through `MirrorsUsed`. + void enqueueReflectiveStaticFields(Iterable elements) { + if (hasEnqueuedReflectiveStaticFields) return; + hasEnqueuedReflectiveStaticFields = true; + for (Element element in elements) { + pretendElementWasUsed(element, compiler.globalDependencies); + } + } + + processLink(Map> map, + String memberName, + bool f(Element e)) { + Link members = map[memberName]; + if (members != null) { + // [f] might add elements to [: map[memberName] :] during the loop below + // so we create a new list for [: map[memberName] :] and prepend the + // [remaining] members after the loop. + map[memberName] = const Link(); + LinkBuilder remaining = new LinkBuilder(); + for (; !members.isEmpty; members = members.tail) { + if (!f(members.head)) remaining.addLast(members.head); + } + map[memberName] = remaining.toLink(map[memberName]); + } + } + + processInstanceMembers(String n, bool f(Element e)) { + processLink(instanceMembersByName, n, f); + } + + processInstanceFunctions(String n, bool f(Element e)) { + processLink(instanceFunctionsByName, n, f); + } + + void handleUnseenSelector(String methodName, Selector selector) { + processInstanceMembers(methodName, (Element member) { + if (selector.appliesUnnamed(member, compiler)) { + if (member.isFunction() && selector.isGetter()) { + registerClosurizedMember(member, compiler.globalDependencies); + } + if (member.isField() && member.getEnclosingClass().isNative()) { + if (selector.isGetter() || selector.isCall()) { + nativeEnqueuer.registerFieldLoad(member); + // We have to also handle storing to the field because we only get + // one look at each member and there might be a store we have not + // seen yet. + // TODO(sra): Process fields for storing separately. + nativeEnqueuer.registerFieldStore(member); + } else { + assert(selector.isSetter()); + nativeEnqueuer.registerFieldStore(member); + // We have to also handle loading from the field because we only get + // one look at each member and there might be a load we have not + // seen yet. + // TODO(sra): Process fields for storing separately. + nativeEnqueuer.registerFieldLoad(member); + } + } + addToWorkList(member); + return true; + } + return false; + }); + if (selector.isGetter()) { + processInstanceFunctions(methodName, (Element member) { + if (selector.appliesUnnamed(member, compiler)) { + registerClosurizedMember(member, compiler.globalDependencies); + return true; + } + return false; + }); + } + } + + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [element] must be a declaration element. + */ + void registerStaticUse(Element element) { + if (element == null) return; + assert(invariant(element, element.isDeclaration)); + addToWorkList(element); + compiler.backend.registerStaticUse(element, this); + } + + void registerGetOfStaticFunction(FunctionElement element) { + registerStaticUse(element); + registerInstantiatedClass(compiler.closureClass, + compiler.globalDependencies); + universe.staticFunctionsNeedingGetter.add(element); + } + + void registerDynamicInvocation(Selector selector) { + assert(selector != null); + registerInvocation(selector); + } + + void registerSelectorUse(Selector selector) { + if (selector.isGetter()) { + registerInvokedGetter(selector); + } else if (selector.isSetter()) { + registerInvokedSetter(selector); + } else { + registerInvocation(selector); + } + } + + void registerDynamicGetter(Selector selector) { + registerInvokedGetter(selector); + } + + void registerDynamicSetter(Selector selector) { + registerInvokedSetter(selector); + } + + void registerGetterForSuperMethod(Element element) { + universe.methodsNeedingSuperGetter.add(element); + } + + void registerFieldGetter(Element element) { + universe.fieldGetters.add(element); + } + + void registerFieldSetter(Element element) { + universe.fieldSetters.add(element); + } + + void registerIsCheck(DartType type, TreeElements elements) { + type = universe.registerIsCheck(type, compiler); + // Even in checked mode, type annotations for return type and argument + // types do not imply type checks, so there should never be a check + // against the type variable of a typedef. + assert(type.kind != TypeKind.TYPE_VARIABLE || + !type.element.enclosingElement.isTypedef()); + compiler.backend.registerIsCheck(type, this, elements); + } + + /** + * If a factory constructor is used with type arguments, we lose track + * which arguments could be used to create instances of classes that use their + * type variables as expressions, so we have to remember if we saw such a use. + */ + void registerFactoryWithTypeArguments(TreeElements elements) { + universe.usingFactoryWithTypeArguments = true; + } + + void registerAsCheck(DartType type, TreeElements elements) { + registerIsCheck(type, elements); + compiler.backend.registerAsCheck(type, this, elements); + } + + void registerGenericCallMethod(Element element, TreeElements elements) { + compiler.backend.registerGenericCallMethod(element, this, elements); + universe.genericCallMethods.add(element); + } + + void registerBoundClosure() { + registerInstantiatedClass(compiler.boundClosureClass, + // Precise dependency is not important here. + compiler.globalDependencies); + } + + void registerClosurizedMember(Element element, TreeElements elements) { + assert(element.isInstanceMember()); + registerIfGeneric(element, elements); + registerBoundClosure(); + universe.closurizedMembers.add(element); + } + + + void registerIfGeneric(Element element, TreeElements elements) { + if (element.computeType(compiler).containsTypeVariables) { + compiler.backend.registerGenericClosure(element, this, elements); + universe.genericClosures.add(element); + } + } + + void registerClosure(Element element, TreeElements elements) { + registerIfGeneric(element, elements); + } + + void forEach(f(WorkItem work)) { + do { + while (!queue.isEmpty) { + // TODO(johnniwinther): Find an optimal process order. + f(queue.removeLast()); + } + onQueueEmpty(); + } while (!queue.isEmpty); + } + + void onQueueEmpty() { + compiler.backend.onQueueEmpty(this); + } + + void logSummary(log(message)) { + _logSpecificSummary(log); + nativeEnqueuer.logSummary(log); + } + + /// Log summary specific to the concrete enqueuer. + void _logSpecificSummary(log(message)); + + String toString() => 'Enqueuer($name)'; +} + +/// [Enqueuer] which is specific to resolution. +class ResolutionEnqueuer extends Enqueuer { + /** + * Map from declaration elements to the [TreeElements] object holding the + * resolution mapping for the element implementation. + * + * Invariant: Key elements are declaration elements. + */ + final Map resolvedElements; + + final Queue queue; + + /** + * A deferred task queue for the resolution phase which is processed + * when the resolution queue has been emptied. + */ + final Queue deferredTaskQueue; + + ResolutionEnqueuer(Compiler compiler, + ItemCompilationContext itemCompilationContextCreator()) + : super('resolution enqueuer', compiler, itemCompilationContextCreator), + resolvedElements = new Map(), + queue = new Queue(), + deferredTaskQueue = new Queue(); + + bool get isResolutionQueue => true; + + bool isProcessed(Element member) => resolvedElements.containsKey(member); + + /// Returns [:true:] if [element] has actually been used. + bool isLive(Element element) { + if (seenClasses.contains(element)) return true; + if (getCachedElements(element) != null) return true; + return false; + } + + TreeElements getCachedElements(Element element) { + // TODO(ngeoffray): Get rid of this check. + if (element.enclosingElement.isClosure()) { + closureMapping.ClosureClassElement cls = element.enclosingElement; + element = cls.methodElement; + } else if (element.isGenerativeConstructorBody()) { + ConstructorBodyElement body = element; + element = body.constructor; + } + Element owner = element.getOutermostEnclosingMemberOrTopLevel(); + if (owner == null) { + owner = element; + } + return resolvedElements[owner.declaration]; + } + + void internalAddToWorkList(Element element) { + assert(invariant(element, element is AnalyzableElement, + message: 'Element $element is not analyzable.')); + if (getCachedElements(element) != null) return; + if (queueIsClosed) { + throw new SpannableAssertionFailure(element, + "Resolution work list is closed. Trying to add $element."); + } + + compiler.world.registerUsedElement(element); + + queue.add(new ResolutionWorkItem(element, itemCompilationContextCreator())); + + // Enable isolate support if we start using something from the isolate + // library, or timers for the async library. We exclude constant fields, + // which are ending here because their initializing expression is compiled. + LibraryElement library = element.getLibrary(); + if (!compiler.hasIsolateSupport() && + (!element.isField() || !element.modifiers.isConst())) { + String uri = library.canonicalUri.toString(); + if (uri == 'dart:isolate') { + enableIsolateSupport(library); + } else if (uri == 'dart:async') { + if (element.name == '_createTimer' || + element.name == '_createPeriodicTimer') { + // The [:Timer:] class uses the event queue of the isolate + // library, so we make sure that event queue is generated. + enableIsolateSupport(library); + } + } + } + + if (element.isGetter() && element.name == Compiler.RUNTIME_TYPE) { + // Enable runtime type support if we discover a getter called runtimeType. + // We have to enable runtime type before hitting the codegen, so + // that constructors know whether they need to generate code for + // runtime type. + compiler.enabledRuntimeType = true; + // TODO(ahe): Record precise dependency here. + compiler.backend.registerRuntimeType(this, compiler.globalDependencies); + } else if (element == compiler.functionApplyMethod) { + compiler.enabledFunctionApply = true; + } else if (element == compiler.invokeOnMethod) { + compiler.enabledInvokeOn = true; + } + + nativeEnqueuer.registerElement(element); + } + + void enableIsolateSupport(LibraryElement element) { + compiler.isolateLibrary = element.patch; + for (String name in const [Compiler.START_ROOT_ISOLATE, + '_currentIsolate', + '_callInIsolate']) { + Element element = compiler.isolateHelperLibrary.find(name); + addToWorkList(element); + compiler.globalDependencies.registerDependency(element); + } + } + + void enableNoSuchMethod(Element element) { + if (compiler.enabledNoSuchMethod) return; + if (compiler.backend.isDefaultNoSuchMethodImplementation(element)) return; + + Selector selector = compiler.noSuchMethodSelector; + compiler.enabledNoSuchMethod = true; + compiler.backend.enableNoSuchMethod(this); + } + + /** + * Adds an action to the deferred task queue. + * + * The action is performed the next time the resolution queue has been + * emptied. + * + * The queue is processed in FIFO order. + */ + void addDeferredAction(Element element, DeferredAction action) { + if (queueIsClosed) { + throw new SpannableAssertionFailure(element, + "Resolution work list is closed. " + "Trying to add deferred action for $element"); + } + deferredTaskQueue.add(new DeferredTask(element, action)); + } + + void onQueueEmpty() { + emptyDeferredTaskQueue(); + super.onQueueEmpty(); + } + + void emptyDeferredTaskQueue() { + while (!deferredTaskQueue.isEmpty) { + DeferredTask task = deferredTaskQueue.removeFirst(); + compiler.withCurrentElement(task.element, task.action); + } + } + + void registerJsCall(Send node, ResolverVisitor resolver) { + nativeEnqueuer.registerJsCall(node, resolver); + } + + void _logSpecificSummary(log(message)) { + log('Resolved ${resolvedElements.length} elements.'); + } +} + +/// [Enqueuer] which is specific to code generation. +class CodegenEnqueuer extends Enqueuer { + final Queue queue; + final Map generatedCode = + new Map(); + + CodegenEnqueuer(Compiler compiler, + ItemCompilationContext itemCompilationContextCreator()) + : super('codegen enqueuer', compiler, itemCompilationContextCreator), + queue = new Queue(); + + bool isProcessed(Element member) => + member.isAbstract || generatedCode.containsKey(member); + + void internalAddToWorkList(Element element) { + // Don't generate code for foreign elements. + if (element.isForeign(compiler)) return; + + // Codegen inlines field initializers. It only needs to generate + // code for checked setters. + if (element.isField() && element.isInstanceMember()) { + if (!compiler.enableTypeAssertions + || element.enclosingElement.isClosure()) { + return; + } + } + + if (queueIsClosed) { + throw new SpannableAssertionFailure(element, + "Codegen work list is closed. Trying to add $element"); + } + CodegenWorkItem workItem = new CodegenWorkItem( + element, itemCompilationContextCreator()); + queue.add(workItem); + } + + void _logSpecificSummary(log(message)) { + log('Compiled ${generatedCode.length} methods.'); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/filenames.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/filenames.dart new file mode 100644 index 0000000..32aca53 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/filenames.dart @@ -0,0 +1,34 @@ +// Copyright (c) 2012, 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. + +library filenames; + +import 'dart:io' show Platform; + +// For information about how to convert Windows file names to URIs: +// http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx + +String nativeToUriPath(String filename) { + // TODO(ahe): It would be nice to use a Dart library instead. + if (!Platform.isWindows) return filename; + filename = filename.replaceAll('\\', '/'); + if (filename.length > 2 && filename[1] == ':') { + filename = "/$filename"; + } + return filename; +} + +String uriPathToNative(String path) { + // TODO(ahe): It would be nice to use a Dart library instead. + if (!Platform.isWindows) return path; + if (path.length > 3 && path[0] == '/' && path[2] == ':') { + return path.substring(1).replaceAll('/', '\\'); + } else { + return path.replaceAll('/', '\\'); + } +} + +final Uri currentDirectory = Uri.base; + +String appendSlash(String path) => path.endsWith('/') ? path : '$path/'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/closure_tracer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/closure_tracer.dart new file mode 100644 index 0000000..0814f6e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/closure_tracer.dart @@ -0,0 +1,98 @@ +// Copyright (c) 2013, 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. + +part of type_graph_inferrer; + +class ClosureTracerVisitor extends TracerVisitor { + final FunctionElement tracedElement; + + ClosureTracerVisitor(this.tracedElement, tracedType, inferrer) + : super(tracedType, inferrer); + + void run() { + tracedElement.functionSignature.forEachParameter((Element parameter) { + ElementTypeInformation info = inferrer.types.getInferredTypeOf(parameter); + info.abandonInferencing = false; + }); + analyze(); + tracedElement.functionSignature.forEachParameter((Element parameter) { + ElementTypeInformation info = inferrer.types.getInferredTypeOf(parameter); + if (continueAnalyzing) { + info.disableHandleSpecialCases = true; + } else { + info.giveUp(inferrer); + } + }); + } + + visitMapTypeInformation(MapTypeInformation info) { + bailout('Stored in a map'); + } + + void analyzeCall(CallSiteTypeInformation info) { + Selector selector = info.selector; + if (!selector.signatureApplies(tracedElement, compiler)) return; + inferrer.updateParameterAssignments( + info, tracedElement, info.arguments, selector, remove: false, + addToQueue: false); + } + + visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { + super.visitClosureCallSiteTypeInformation(info); + if (info.closure == currentUser) { + analyzeCall(info); + } else { + bailout('Passed to a closure'); + } + } + + visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { + super.visitStaticCallSiteTypeInformation(info); + Element called = info.calledElement; + if (called.isForeign(compiler)) { + String name = called.name; + if (name == 'JS' || name == 'DART_CLOSURE_TO_JS') { + bailout('Used in JS ${info.call}'); + } + } + if (called.isGetter() + && info.selector != null + && info.selector.isCall() + && inferrer.types.getInferredTypeOf(called) == currentUser) { + // This node can be a closure call as well. For example, `foo()` + // where `foo` is a getter. + analyzeCall(info); + } + } + + bool checkIfCurrentUser(element) { + return inferrer.types.getInferredTypeOf(element) == currentUser; + } + + visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { + super.visitDynamicCallSiteTypeInformation(info); + if (info.selector.isCall()) { + if (info.arguments.contains(currentUser) + && !info.targets.every((element) => element.isFunction())) { + bailout('Passed to a closure'); + } else if (info.targets.any((element) => checkIfCurrentUser(element))) { + analyzeCall(info); + } + } + } +} + +class StaticTearOffClosureTracerVisitor extends ClosureTracerVisitor { + StaticTearOffClosureTracerVisitor(tracedElement, tracedType, inferrer) + : super(tracedElement, tracedType, inferrer); + + visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { + super.visitStaticCallSiteTypeInformation(info); + if (info.calledElement == tracedElement + && info.selector != null + && info.selector.isGetter()) { + addNewEscapeInformation(info); + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/concrete_types_inferrer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/concrete_types_inferrer.dart new file mode 100644 index 0000000..e926165 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/concrete_types_inferrer.dart @@ -0,0 +1,2360 @@ +// Copyright (c) 2012, 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. + +library concrete_types_inferrer; + +import 'dart:collection' show Queue, IterableBase; +import '../native_handler.dart' as native; +import '../dart2jslib.dart' hide Selector, TypedSelector; +import '../dart_types.dart' show DartType, TypeKind; +import '../elements/elements.dart'; +import '../tree/tree.dart'; +import '../universe/universe.dart'; +import '../util/util.dart'; + +import 'inferrer_visitor.dart'; +import '../types/types.dart' show TypeMask, FlatTypeMask, UnionTypeMask, + TypesInferrer; +import 'simple_types_inferrer.dart'; + +/** + * A singleton concrete type. More precisely, a [BaseType] is one of the + * following: + * + * - a non-asbtract class like [:int:] or [:Uri:] (but not [:List:]) + * - the null base type + * - the unknown base type + */ +abstract class BaseType { + bool isClass(); + bool isUnknown(); + bool isNull(); +} + +/** + * A non-asbtract class like [:int:] or [:Uri:] (but not [:List:]). + */ +class ClassBaseType implements BaseType { + final ClassElement element; + + ClassBaseType(this.element); + + bool operator ==(var other) { + if (identical(this, other)) return true; + if (other is! ClassBaseType) return false; + return element == other.element; + } + int get hashCode => element.hashCode; + String toString() { + return element == null ? 'toplevel' : element.name; + } + bool isClass() => true; + bool isUnknown() => false; + bool isNull() => false; +} + +/** + * The unknown base type. + */ +class UnknownBaseType implements BaseType { + const UnknownBaseType(); + bool operator ==(BaseType other) => other is UnknownBaseType; + int get hashCode => 0; + bool isClass() => false; + bool isUnknown() => true; + bool isNull() => false; + toString() => "unknown"; +} + +/** + * The null base type. + */ +class NullBaseType implements BaseType { + const NullBaseType(); + bool operator ==(BaseType other) => identical(other, this); + int get hashCode => 1; + bool isClass() => false; + bool isUnknown() => false; + bool isNull() => true; + toString() => "null"; +} + +/** + * An immutable set of base types like [:{int, bool}:], or the unknown concrete + * type. + */ +abstract class ConcreteType { + ConcreteType(); + + factory ConcreteType.empty(int maxConcreteTypeSize, + BaseTypes classBaseTypes) { + return new UnionType(maxConcreteTypeSize, classBaseTypes, + new Set()); + } + + /** + * The singleton constituted of the unknown base type is the unknown concrete + * type. + */ + factory ConcreteType.singleton(int maxConcreteTypeSize, + BaseTypes classBaseTypes, + BaseType baseType) { + if (baseType.isUnknown() || maxConcreteTypeSize < 1) { + return const UnknownConcreteType(); + } + Set singletonSet = new Set(); + singletonSet.add(baseType); + return new UnionType(maxConcreteTypeSize, classBaseTypes, singletonSet); + } + + factory ConcreteType.unknown() { + return const UnknownConcreteType(); + } + + ConcreteType union(ConcreteType other); + ConcreteType intersection(ConcreteType other); + ConcreteType refine(Selector selector, Compiler compiler); + bool isUnknown(); + bool isEmpty(); + Set get baseTypes; + + /** + * Returns the unique element of [:this:] if [:this:] is a singleton, null + * otherwise. + */ + ClassElement getUniqueType(); +} + +/** + * The unkown concrete type: it is absorbing for the union. + */ +class UnknownConcreteType implements ConcreteType { + const UnknownConcreteType(); + bool isUnknown() => true; + bool isEmpty() => false; + bool operator ==(ConcreteType other) => identical(this, other); + Set get baseTypes => + new Set.from([const UnknownBaseType()]); + int get hashCode => 0; + ConcreteType union(ConcreteType other) => this; + ConcreteType intersection(ConcreteType other) => other; + ConcreteType refine(Selector selector, Compiler compiler) => this; + ClassElement getUniqueType() => null; + toString() => "unknown"; +} + +/** + * An immutable set of base types, like [: {int, bool} :]. + */ +class UnionType implements ConcreteType { + final int maxConcreteTypeSize; + final BaseTypes classBaseTypes; + + final Set baseTypes; + + /** + * The argument should NOT be mutated later. Do not call directly, use + * ConcreteType.singleton instead. + */ + UnionType(this.maxConcreteTypeSize, this.classBaseTypes, this.baseTypes); + + bool isUnknown() => false; + bool isEmpty() => baseTypes.isEmpty; + + bool operator ==(ConcreteType other) { + if (other is! UnionType) return false; + if (baseTypes.length != other.baseTypes.length) return false; + return baseTypes.containsAll(other.baseTypes); + } + + int get hashCode { + int result = 1; + for (final baseType in baseTypes) { + result = 31 * result + baseType.hashCode; + } + return result; + } + + ConcreteType _simplify(Set baseTypes) { + // normalize all flavors of ints to int + // TODO(polux): handle different ints better + if (baseTypes.contains(classBaseTypes.uint31Type)) { + baseTypes.remove(classBaseTypes.uint31Type); + baseTypes.add(classBaseTypes.intBaseType); + } + if (baseTypes.contains(classBaseTypes.uint32Type)) { + baseTypes.remove(classBaseTypes.uint32Type); + baseTypes.add(classBaseTypes.intBaseType); + } + if (baseTypes.contains(classBaseTypes.positiveIntType)) { + baseTypes.remove(classBaseTypes.positiveIntType); + baseTypes.add(classBaseTypes.intBaseType); + } + // normalize {int, float}, {int, num} or {float, num} into num + // TODO(polux): generalize this to all types when we extend the concept of + // "concrete type" to other abstract classes than num + if (baseTypes.contains(classBaseTypes.numBaseType) || + (baseTypes.contains(classBaseTypes.intBaseType) + && baseTypes.contains(classBaseTypes.doubleBaseType))) { + baseTypes.remove(classBaseTypes.intBaseType); + baseTypes.remove(classBaseTypes.doubleBaseType); + baseTypes.add(classBaseTypes.numBaseType); + } + + // widen big types to dynamic + return baseTypes.length > maxConcreteTypeSize + ? const UnknownConcreteType() + : new UnionType(maxConcreteTypeSize, classBaseTypes, baseTypes); + } + + ConcreteType union(ConcreteType other) { + if (other.isUnknown()) { + return const UnknownConcreteType(); + } + UnionType otherUnion = other; // cast + Set newBaseTypes = new Set.from(baseTypes); + newBaseTypes.addAll(otherUnion.baseTypes); + return _simplify(newBaseTypes); + } + + ConcreteType intersection(ConcreteType other) { + if (other.isUnknown()) { + return this; + } + Set thisBaseTypes = new Set.from(baseTypes); + Set otherBaseTypes = new Set.from(other.baseTypes); + return _simplify(thisBaseTypes.intersection(otherBaseTypes)); + } + + ConcreteType refine(Selector selector, Compiler compiler) { + Set newBaseTypes = new Set(); + for (BaseType baseType in baseTypes) { + if (baseType.isClass()) { + ClassBaseType classBaseType = baseType; + if (classBaseType.element.lookupSelector(selector, compiler) != null) { + newBaseTypes.add(baseType); + } + } else { + newBaseTypes.add(baseType); + } + } + return _simplify(newBaseTypes); + } + + ClassElement getUniqueType() { + if (baseTypes.length == 1) { + var iterator = baseTypes.iterator; + iterator.moveNext(); + BaseType uniqueBaseType = iterator.current; + if (uniqueBaseType.isClass()) { + ClassBaseType uniqueClassType = uniqueBaseType; + return uniqueClassType.element; + } + } + return null; + } + + String toString() => baseTypes.toString(); +} + +class ConcreteTypeSystem extends TypeSystem { + final Compiler compiler; + final ConcreteTypesInferrer inferrer; + final BaseTypes baseTypes; + + final ConcreteType nullType; + final ConcreteType _intType; + final ConcreteType _uint31Type; + final ConcreteType _uint32Type; + final ConcreteType _positiveIntType; + final ConcreteType _doubleType; + final ConcreteType _numType; + final ConcreteType _boolType; + final ConcreteType _functionType; + final ConcreteType _listType; + final ConcreteType _constListType; + final ConcreteType _fixedListType; + final ConcreteType _growableListType; + final ConcreteType _mapType; + final ConcreteType _constMapType; + final ConcreteType _stringType; + + final ConcreteType dynamicType; + final ConcreteType typeType; + final ConcreteType nonNullEmptyType; + + ConcreteTypeSystem.internal(ConcreteTypesInferrer inferrer, + BaseTypes baseTypes, + ConcreteType singleton(BaseType baseType)) + : this.compiler = inferrer.compiler + , this.inferrer = inferrer + , this.baseTypes = baseTypes + , this._constListType = singleton(baseTypes.constMapBaseType) + , this._constMapType = singleton(baseTypes.constMapBaseType) + , this._doubleType = singleton(baseTypes.doubleBaseType) + , this._fixedListType = singleton(baseTypes.fixedListBaseType) + , this._functionType = singleton(baseTypes.functionBaseType) + , this._growableListType = singleton(baseTypes.growableListBaseType) + , this._intType = singleton(baseTypes.intBaseType) + , this._listType = singleton(baseTypes.listBaseType) + , this._mapType = singleton(baseTypes.mapBaseType) + , this._numType = singleton(baseTypes.numBaseType) + , this._boolType = singleton(baseTypes.boolBaseType) + , this._stringType = singleton(baseTypes.stringBaseType) + , this.typeType = singleton(baseTypes.typeBaseType) + , this.dynamicType = const UnknownConcreteType() + , this.nullType = singleton(const NullBaseType()) + , this.nonNullEmptyType = new ConcreteType.empty( + inferrer.compiler.maxConcreteTypeSize, baseTypes) + // TODO(polux): have better types here + , this._uint31Type = singleton(baseTypes.intBaseType) + , this._uint32Type = singleton(baseTypes.intBaseType) + , this._positiveIntType = singleton(baseTypes.intBaseType); + + factory ConcreteTypeSystem(ConcreteTypesInferrer inferrer) { + Compiler compiler = inferrer.compiler; + BaseTypes baseTypes = new BaseTypes(compiler); + return new ConcreteTypeSystem.internal( + inferrer, + baseTypes, + (BaseType baseType) => new ConcreteType.singleton( + compiler.maxConcreteTypeSize, baseTypes, baseType)); + } + + @override + ConcreteType get intType { + inferrer.augmentSeenClasses(compiler.backend.intImplementation); + return _intType; + } + + @override + ConcreteType get uint31Type { + inferrer.augmentSeenClasses(compiler.backend.uint31Implementation); + return _uint31Type; + } + + @override + ConcreteType get uint32Type { + inferrer.augmentSeenClasses(compiler.backend.uint32Implementation); + return _uint32Type; + } + + @override + ConcreteType get positiveIntType { + inferrer.augmentSeenClasses(compiler.backend.positiveIntImplementation); + return _positiveIntType; + } + + @override + ConcreteType get doubleType { + inferrer.augmentSeenClasses(compiler.backend.doubleImplementation); + return _doubleType; + } + + @override + ConcreteType get numType { + inferrer.augmentSeenClasses(compiler.backend.numImplementation); + return _numType; + } + + @override + ConcreteType get boolType { + inferrer.augmentSeenClasses(compiler.backend.boolImplementation); + return _boolType; + } + + @override + ConcreteType get functionType { + inferrer.augmentSeenClasses(compiler.backend.functionImplementation); + return _functionType; + } + + @override + ConcreteType get listType { + inferrer.augmentSeenClasses(compiler.backend.listImplementation); + return _listType; + } + + @override + ConcreteType get constListType { + inferrer.augmentSeenClasses(compiler.backend.constListImplementation); + return _constListType; + } + + @override + ConcreteType get fixedListType { + inferrer.augmentSeenClasses(compiler.backend.fixedListImplementation); + return _fixedListType; + } + + @override + ConcreteType get growableListType { + inferrer.augmentSeenClasses(compiler.backend.growableListImplementation); + return _growableListType; + } + + @override + ConcreteType get mapType { + inferrer.augmentSeenClasses(compiler.backend.mapImplementation); + return _mapType; + } + + @override + ConcreteType get constMapType { + inferrer.augmentSeenClasses(compiler.backend.constMapImplementation); + return _constMapType; + } + + @override + ConcreteType get stringType { + inferrer.augmentSeenClasses(compiler.backend.stringImplementation); + return _stringType; + } + + @override + ConcreteType stringLiteralType(_) { + inferrer.augmentSeenClasses(compiler.backend.stringImplementation); + return _stringType; + } + + /** + * Returns the [TypeMask] representation of [baseType]. + */ + TypeMask baseTypeToTypeMask(BaseType baseType) { + if (baseType.isUnknown()) { + return const DynamicTypeMask(); + } else if (baseType.isNull()) { + return new TypeMask.empty(); + } else { + ClassBaseType classBaseType = baseType; + final element = classBaseType.element; + assert(element != null); + if (element == compiler.backend.numImplementation) { + return new TypeMask.nonNullSubclass(compiler.backend.numImplementation); + } else if (element == compiler.backend.intImplementation) { + return new TypeMask.nonNullSubclass(compiler.backend.intImplementation); + } else if (element == compiler.dynamicClass) { + return new TypeMask.nonNullSubclass(compiler.objectClass); + } else { + return new TypeMask.nonNullExact(element.declaration); + } + } + } + + /** + * Returns the [TypeMask] representation of [concreteType]. + */ + TypeMask concreteTypeToTypeMask(ConcreteType concreteType) { + if (concreteType == null) return null; + TypeMask typeMask = new TypeMask.nonNullEmpty(); + for (BaseType baseType in concreteType.baseTypes) { + TypeMask baseMask = baseTypeToTypeMask(baseType); + if (baseMask == const DynamicTypeMask()) return baseMask; + typeMask = typeMask.union(baseMask, compiler); + } + return typeMask; + } + + @override + ConcreteType addPhiInput(Element element, + ConcreteType phiType, + ConcreteType newType) { + return computeLUB(phiType, newType); + } + + @override + ConcreteType allocateDiamondPhi(ConcreteType firstInput, + ConcreteType secondInput) { + return computeLUB(firstInput, secondInput); + } + + @override + ConcreteType allocatePhi(Node node, Element element, ConcreteType inputType) { + return inputType; + } + + @override + ConcreteType computeLUB(ConcreteType firstType, ConcreteType secondType) { + if (firstType == null) { + return secondType; + } else if (secondType == null) { + return firstType; + } else { + return firstType.union(secondType); + } + } + + // Implementation Inspired by + // type_graph_inferrer.TypeInformationSystem.narrowType + @override + ConcreteType narrowType(ConcreteType type, + DartType annotation, + {bool isNullable: true}) { + if (annotation.treatAsDynamic) return type; + if (annotation.isVoid) return nullType; + if (annotation.element == compiler.objectClass) return type; + ConcreteType otherType; + if (annotation.kind == TypeKind.TYPEDEF + || annotation.kind == TypeKind.FUNCTION) { + otherType = functionType; + } else if (annotation.kind == TypeKind.TYPE_VARIABLE) { + // TODO(polux): Narrow to bound. + return type; + } else { + assert(annotation.kind == TypeKind.INTERFACE); + otherType = nonNullSubtype(annotation.element); + } + if (isNullable) otherType = otherType.union(nullType); + if (type == null) return otherType; + return type.intersection(otherType); + } + + @override + Selector newTypedSelector(ConcreteType receiver, Selector selector) { + return new TypedSelector(concreteTypeToTypeMask(receiver), selector); + } + + @override + ConcreteType nonNullEmpty() { + return nonNullEmptyType; + } + + @override + ConcreteType nonNullExact(ClassElement cls) { + return nonNullSubtype(cls); + } + + /** + * Helper method for [nonNullSubtype] and [nonNullSubclass]. + */ + ConcreteType nonNullSubX(ClassElement cls, + Set extractor(ClassElement cls)) { + if (cls == compiler.objectClass) { + return dynamicType; + } + ConcreteType result = nonNullEmptyType; + void registerClass(ClassElement element) { + if (!element.isAbstract) { + result = result.union( + new ConcreteType.singleton(compiler.maxConcreteTypeSize, + baseTypes, + new ClassBaseType(element))); + inferrer.augmentSeenClasses(element); + } + } + registerClass(cls); + Set subtypes = extractor(cls); + if (subtypes != null) { + subtypes.forEach(registerClass); + } + return result; + } + + @override + ConcreteType nonNullSubclass(ClassElement cls) { + return nonNullSubX(cls, compiler.world.subclassesOf); + } + + @override + ConcreteType nonNullSubtype(ClassElement cls) { + return nonNullSubX(cls, compiler.world.subtypesOf); + } + + @override + ConcreteType simplifyPhi(Node node, Element element, ConcreteType phiType) { + return phiType; + } + + @override + ConcreteType refineReceiver(Selector selector, ConcreteType receiverType) { + return receiverType.refine(selector, compiler); + } + + @override + bool isNull(ConcreteType type) { + return (type.baseTypes.length == 1) && (type.baseTypes.first.isNull()); + } + + @override + ConcreteType allocateClosure(Node node, Element element) { + // TODO(polux): register closure here instead of in visitor? + return functionType; + } + + @override + ConcreteType allocateList(ConcreteType type, + Node node, + Element enclosing, + [ConcreteType elementType, int length]) { + if (elementType != null) { + inferrer.augmentListElementType(elementType); + } + return type; + } + + @override + ConcreteType allocateMap(ConcreteType type, + Node node, + Element element, + [List keyTypes, + List valueTypes]) { + // TODO(polux): treat maps the same way we treat lists + return type; + } + + @override + ConcreteType getConcreteTypeFor(TypeMask mask) { + if (mask.isUnion) { + UnionTypeMask union = mask; + return union.disjointMasks.fold( + nonNullEmptyType, + (type1, type2) => type1.union(getConcreteTypeFor(type2))); + } else { + FlatTypeMask flat = mask; + ConcreteType result; + if (flat.isEmpty) { + result = nonNullEmptyType; + } else if (flat.isExact) { + result = nonNullExact(flat.base); + } else if (flat.isSubclass) { + result = nonNullSubclass(flat.base); + } else if (flat.isSubtype) { + result = nonNullSubtype(flat.base); + } else { + throw new ArgumentError("unexpected mask"); + } + return flat.isNullable ? result.union(nullType) : result; + } + } +} + +/** + * The cartesian product of concrete types: an iterable of + * [ConcreteTypesEnvironment]s. + */ +class ConcreteTypeCartesianProduct + extends IterableBase { + final ConcreteTypesInferrer inferrer; + final ClassElement typeOfThis; + final Map concreteTypes; + ConcreteTypeCartesianProduct(this.inferrer, this.typeOfThis, + this.concreteTypes); + Iterator get iterator => concreteTypes.isEmpty + ? [new ConcreteTypesEnvironment(typeOfThis)].iterator + : new ConcreteTypeCartesianProductIterator(inferrer, typeOfThis, + concreteTypes); + String toString() => this.toList().toString(); +} + +/** + * An helper class for [ConcreteTypeCartesianProduct]. + */ +class ConcreteTypeCartesianProductIterator + implements Iterator { + final ConcreteTypesInferrer inferrer; + final ClassElement classOfThis; + final Map concreteTypes; + final Map nextValues; + final Map state; + int size = 1; + int counter = 0; + ConcreteTypesEnvironment _current; + + ConcreteTypeCartesianProductIterator(this.inferrer, this.classOfThis, + Map concreteTypes) + : this.concreteTypes = concreteTypes, + nextValues = new Map(), + state = new Map() { + if (concreteTypes.isEmpty) { + size = 0; + return; + } + for (final e in concreteTypes.keys) { + final baseTypes = concreteTypes[e].baseTypes; + size *= baseTypes.length; + } + } + + ConcreteTypesEnvironment get current => _current; + + ConcreteTypesEnvironment takeSnapshot() { + Map result = new Map(); + nextValues.forEach((k, v) { + result[k] = inferrer.singletonConcreteType(v); + }); + return new ConcreteTypesEnvironment.of(result, classOfThis); + } + + bool moveNext() { + if (counter >= size) { + _current = null; + return false; + } + Element keyToIncrement = null; + for (final key in concreteTypes.keys) { + final iterator = state[key]; + if (iterator != null && iterator.moveNext()) { + nextValues[key] = state[key].current; + break; + } + Iterator newIterator = concreteTypes[key].baseTypes.iterator; + state[key] = newIterator; + newIterator.moveNext(); + nextValues[key] = newIterator.current; + } + counter++; + _current = takeSnapshot(); + return true; + } +} + +/** + * [BaseType] Constants. + */ +class BaseTypes { + final ClassBaseType intBaseType; + final ClassBaseType doubleBaseType; + final ClassBaseType numBaseType; + final ClassBaseType boolBaseType; + final ClassBaseType stringBaseType; + final ClassBaseType listBaseType; + final ClassBaseType growableListBaseType; + final ClassBaseType fixedListBaseType; + final ClassBaseType constListBaseType; + final ClassBaseType mapBaseType; + final ClassBaseType constMapBaseType; + final ClassBaseType objectBaseType; + final ClassBaseType typeBaseType; + final ClassBaseType functionBaseType; + final ClassBaseType uint31Type; + final ClassBaseType uint32Type; + final ClassBaseType positiveIntType; + + BaseTypes(Compiler compiler) : + intBaseType = new ClassBaseType(compiler.backend.intImplementation), + doubleBaseType = new ClassBaseType(compiler.backend.doubleImplementation), + numBaseType = new ClassBaseType(compiler.backend.numImplementation), + boolBaseType = new ClassBaseType(compiler.backend.boolImplementation), + stringBaseType = new ClassBaseType(compiler.backend.stringImplementation), + listBaseType = new ClassBaseType(compiler.backend.listImplementation), + growableListBaseType = + new ClassBaseType(compiler.backend.growableListImplementation), + fixedListBaseType = + new ClassBaseType(compiler.backend.fixedListImplementation), + constListBaseType = + new ClassBaseType(compiler.backend.constListImplementation), + mapBaseType = new ClassBaseType(compiler.backend.mapImplementation), + constMapBaseType = + new ClassBaseType(compiler.backend.constMapImplementation), + objectBaseType = new ClassBaseType(compiler.objectClass), + typeBaseType = new ClassBaseType(compiler.backend.typeImplementation), + functionBaseType = + new ClassBaseType(compiler.backend.functionImplementation), + uint31Type = new ClassBaseType(compiler.backend.uint31Implementation), + uint32Type = new ClassBaseType(compiler.backend.uint32Implementation), + positiveIntType = + new ClassBaseType(compiler.backend.positiveIntImplementation); +} + +/** + * An immutable mapping from method arguments to [ConcreteTypes]. + */ +class ConcreteTypesEnvironment { + final Map environment; + final ClassElement classOfThis; + + ConcreteTypesEnvironment([this.classOfThis]) : + environment = new Map(); + ConcreteTypesEnvironment.of(this.environment, this.classOfThis); + + ConcreteType lookupType(Element element) => environment[element]; + + bool operator ==(ConcreteTypesEnvironment other) { + if (other is! ConcreteTypesEnvironment) return false; + if (classOfThis != other.classOfThis) return false; + if (environment.length != other.environment.length) return false; + for (Element key in environment.keys) { + if (!other.environment.containsKey(key) + || (environment[key] != other.environment[key])) { + return false; + } + } + return true; + } + + int get hashCode { + int result = (classOfThis != null) ? classOfThis.hashCode : 1; + environment.forEach((element, concreteType) { + result = 31 * (31 * result + element.hashCode) + concreteType.hashCode; + }); + return result; + } + + String toString() => "{ this: $classOfThis, env: $environment }"; +} + +class ClosureEnvironment { + ConcreteType thisType; + final LocalsHandler locals; + + ClosureEnvironment(this.thisType, this.locals); + + bool mergeLocals(LocalsHandler newLocals) { + assert((locals == null) == (newLocals == null)); + return (locals != null) ? locals.mergeAll([newLocals]) : false; + } + + /// Returns true if changed. + bool merge(ConcreteType thisType, LocalsHandler locals) { + ConcreteType oldThisType = this.thisType; + if (this.thisType == null) { + this.thisType = thisType; + } else if (thisType != null) { + this.thisType = this.thisType.union(thisType); + } + return mergeLocals(locals) || (this.thisType != oldThisType); + } + + toString() => "ClosureEnvironment { thisType = $thisType, locals = ... }"; +} + +/** + * A set of encoutered closures. + */ +class Closures { + final Compiler compiler; + final Map closures = + new Map(); + + Closures(this.compiler); + + /// Returns true if the environment of the closure has changed. + bool put(FunctionElement closure, + ConcreteType typeOfThis, + LocalsHandler locals) { + ClosureEnvironment oldEnvironent = closures[closure]; + if (oldEnvironent == null) { + closures[closure] = new ClosureEnvironment(typeOfThis, locals); + return true; + } else { + return oldEnvironent.merge(typeOfThis, locals); + } + } + + ClosureEnvironment getEnvironmentOrNull(FunctionElement function) { + return closures[function]; + } + + Iterable get functionElements => closures.keys; + + bool contains(FunctionElement function) => closures.containsKey(function); + + String toString() => closures.toString(); +} + +/** + * A work item for the type inference queue. + */ +class InferenceWorkItem { + Element method; + ConcreteTypesEnvironment environment; + InferenceWorkItem(this.method, this.environment); + + toString() => "{ method = $method, environment = $environment }"; + + bool operator ==(other) { + return (other is InferenceWorkItem) + && method == other.method + && environment == other.environment; + } + + int get hashCode => 31 * method.hashCode + environment.hashCode; +} + +/** + * A sentinel type mask class representing the dynamicType. It is absorbing + * for [:ConcreteTypesEnvironment.typeMaskUnion:]. + */ +class DynamicTypeMask implements TypeMask { + const DynamicTypeMask(); + + String toString() => 'sentinel type mask'; + + TypeMask nullable() { + throw new UnsupportedError(""); + } + + TypeMask nonNullable() { + throw new UnsupportedError(""); + } + + bool get isEmpty { + throw new UnsupportedError(""); + } + + bool get isNullable { + throw new UnsupportedError(""); + } + + bool get isExact { + throw new UnsupportedError(""); + } + + bool get isUnion { + throw new UnsupportedError(""); + } + + bool get isContainer { + throw new UnsupportedError(""); + } + + bool get isMap { + throw new UnsupportedError(""); + } + + bool get isDictionary { + throw new UnsupportedError(""); + } + + bool get isForwarding { + throw new UnsupportedError(""); + } + + bool get isValue { + throw new UnsupportedError(""); + } + + bool containsOnlyInt(Compiler compiler) { + throw new UnsupportedError(""); + } + + bool containsOnlyDouble(Compiler compiler) { + throw new UnsupportedError(""); + } + + bool containsOnlyNum(Compiler compiler) { + throw new UnsupportedError(""); + } + + bool containsOnlyBool(Compiler compiler) { + throw new UnsupportedError(""); + } + + bool containsOnlyString(Compiler compiler) { + throw new UnsupportedError(""); + } + + bool containsOnly(ClassElement element) { + throw new UnsupportedError(""); + } + + bool satisfies(ClassElement cls, Compiler compiler) { + throw new UnsupportedError(""); + } + + bool contains(ClassElement type, Compiler compiler) { + throw new UnsupportedError(""); + } + + bool containsAll(Compiler compiler) { + throw new UnsupportedError(""); + } + + ClassElement singleClass(Compiler compiler) { + throw new UnsupportedError(""); + } + + TypeMask union(TypeMask other, Compiler compiler) { + throw new UnsupportedError(""); + } + + TypeMask intersection(TypeMask other, Compiler compiler) { + throw new UnsupportedError(""); + } + + bool canHit(Element element, Selector selector, Compiler compiler) { + throw new UnsupportedError(""); + } + + Element locateSingleElement(Selector selector, Compiler compiler) { + throw new UnsupportedError(""); + } + + bool needsNoSuchMethodHandling(Selector selector, Compiler compiler) { + throw new UnsupportedError(""); + } + + bool isInMask(TypeMask other, Compiler compiler) { + throw new UnsupportedError(""); + } + + bool containsMask(TypeMask other, Compiler compiler) { + throw new UnsupportedError(""); + } +} + +class WorkQueue { + final Queue queue = new Queue(); + + void add(InferenceWorkItem workItem) { + if (!queue.contains(workItem)) { + queue.addLast(workItem); + } + } + + InferenceWorkItem remove() { + return queue.removeFirst(); + } + + bool get isEmpty => queue.isEmpty; +} + +/** + * A task which conservatively infers a [ConcreteType] for each sub expression + * of the program. The entry point is [analyzeMain]. + */ +class ConcreteTypesInferrer + extends InferrerEngine + implements TypesInferrer { + + final String name = "Type inferrer"; + + /** + * When true, the string literal [:"__dynamic_for_test":] is inferred to + * have the unknown type. + */ + // TODO(polux): get rid of this hack once we have a natural way of inferring + // the unknown type. + bool testMode = false; + + // --- constants --- + + /** + * Constants representing builtin base types. Initialized by [initialize] + * and not by the constructor because the compiler elements are not yet + * populated. + */ + BaseTypes baseTypes; + + /** The associated type system */ + ConcreteTypeSystem types; + + /** + * Constant representing [:ConcreteList#[]:] where [:ConcreteList:] is the + * concrete implementation of lists for the selected backend. + */ + FunctionElement listIndex; + + /** + * Constant representing [:ConcreteList#[]=:] where [:ConcreteList:] is the + * concrete implementation of lists for the selected backend. + */ + FunctionElement listIndexSet; + + /** + * Constant representing [:ConcreteList#add:] where [:ConcreteList:] is the + * concrete implementation of lists for the selected backend. + */ + FunctionElement listAdd; + + /** + * Constant representing [:ConcreteList#removeAt:] where [:ConcreteList:] is + * the concrete implementation of lists for the selected backend. + */ + FunctionElement listRemoveAt; + + /** + * Constant representing [:ConcreteList#insert:] where [:ConcreteList:] is + * the concrete implementation of lists for the selected backend. + */ + FunctionElement listInsert; + + /** + * Constant representing [:ConcreteList#removeLast:] where [:ConcreteList:] is + * the concrete implementation of lists for the selected backend. + */ + FunctionElement listRemoveLast; + + /** Constant representing [:List():]. */ + FunctionElement listConstructor; + + /** The unknown concrete type */ + final ConcreteType unknownConcreteType; + + /** The empty concrete type */ + ConcreteType emptyConcreteType; + + /** The null concrete type */ + ConcreteType nullConcreteType; + + // --- state updated by the inference --- + + /** + * A map from (function x argument base types) to their inferred concrete + * type. Another way of seeing [methodToTemplates] is as a map from + * [FunctionElement]s to "templates" in the sense of "The Cartesian Product + * Algorithm - Simple and Precise Type Inference of Parametric Polymorphism" + * by Ole Agesen. + */ + // TODO(polux): build a better abstraction, like Closures + final Map> + methodToTemplates; + + /** The set of encountered closures. */ + final Closures closures; + + /** A map from expressions to their inferred concrete types. */ + final Map inferredTypes; + + /** A map from fields to their inferred concrete types. */ + final Map inferredFieldTypes; + + /** + * [:callers[f]:] is the list of [:f:]'s possible callers or fields + * whose initialization is a call to [:f:]. + */ + final Map> callers; + + /** + * [:readers[field]:] is the list of [:field:]'s possible readers or fields + * whose initialization is a read of [:field:]. + */ + final Map> fieldReaders; + + /** + * [:readers[local]:] is the list of [:local:]'s possible readers. + */ + final Map> capuredLocalsReaders; + + /// The set of classes encountered so far. + final Set seenClasses; + + /** + * A map from selector names to callers of methods with this name on objects + * of unknown inferred type. + */ + final Map> dynamicCallers; + + /** The inferred type of elements stored in Lists. */ + ConcreteType listElementType; + + /** + * A map from parameters to their inferred concrete types. It plays no role + * in the analysis, it is write only. + */ + final Map inferredParameterTypes; + + /** + * A map from selectors to their inferred type masks, indexed by the mask + * of the receiver. It plays no role in the analysis, it is write only. + */ + final Map> inferredSelectorTypes; + + /** The work queue consumed by [analyzeMain]. */ + final WorkQueue workQueue; + + /** The item being worked on. */ + InferenceWorkItem currentWorkItem; + + ConcreteTypesInferrer(Compiler compiler) + : methodToTemplates = new Map>(), + closures = new Closures(compiler), + inferredTypes = new Map(), + inferredFieldTypes = new Map(), + inferredParameterTypes = new Map(), + workQueue = new WorkQueue(), + callers = new Map>(), + fieldReaders = new Map>(), + capuredLocalsReaders = new Map>(), + seenClasses = new Set(), + dynamicCallers = new Map>(), + inferredSelectorTypes = new Map>(), + unknownConcreteType = new ConcreteType.unknown(), + super(compiler, null); + + /* Initialization code that cannot be run in the constructor because it + * requires the compiler's elements to be populated. + */ + void initialize() { + baseTypes = new BaseTypes(compiler); + types = new ConcreteTypeSystem(this); + ClassElement jsArrayClass = baseTypes.listBaseType.element; + listIndex = jsArrayClass.lookupMember('[]'); + listIndexSet = jsArrayClass.lookupMember('[]='); + listAdd = jsArrayClass.lookupMember('add'); + listRemoveAt = jsArrayClass.lookupMember('removeAt'); + listInsert = jsArrayClass.lookupMember('insert'); + listRemoveLast = + jsArrayClass.lookupMember('removeLast'); + List typePreservingOps = const ['+', '-', '*']; + listConstructor = + compiler.listClass.lookupConstructor( + new Selector.callConstructor( + '', + compiler.listClass.getLibrary())).implementation; + emptyConcreteType = new ConcreteType.empty(compiler.maxConcreteTypeSize, + baseTypes); + nullConcreteType = singletonConcreteType(const NullBaseType()); + listElementType = emptyConcreteType; + } + + // --- utility methods --- + + /** Creates a singleton concrete type containing [baseType]. */ + ConcreteType singletonConcreteType(BaseType baseType) { + return new ConcreteType.singleton(compiler.maxConcreteTypeSize, baseTypes, + baseType); + } + + /** + * Computes the union of [mask1] and [mask2] where [mask1] and [mask2] are + * possibly equal to [: DynamicTypeMask.instance :]. + */ + TypeMask typeMaskUnion(TypeMask mask1, TypeMask mask2) { + if (mask1 == const DynamicTypeMask() || mask2 == const DynamicTypeMask()) { + return const DynamicTypeMask(); + } + return mask1.union(mask2, compiler); + } + + /** + * Returns all the members matching [selector]. + */ + Set getMembersBySelector(Selector selector) { + // TODO(polux): memoize? + Set result = new Set(); + for (ClassElement cls in seenClasses) { + Element elem = cls.lookupSelector(selector, compiler); + if (elem != null) { + result.add(elem.implementation); + } + } + return result; + } + + /** + * Returns all the subtypes of [cls], [cls] included. + */ + Set getReflexiveSubtypesOf(ClassElement cls) { + // TODO(polux): memoize? + Set result = new Set()..add(cls); + for (ClassElement candidate in seenClasses) { + if (compiler.world.isSubtype(cls, candidate)) { + result.add(candidate); + } + } + return result; + } + + /** + * Sets the concrete type associated to [node] to the union of the inferred + * concrete type so far and of [type]. + */ + void augmentInferredType(Node node, ConcreteType type) { + ConcreteType currentType = inferredTypes[node]; + inferredTypes[node] = + (currentType == null) ? type : currentType.union(type); + } + + /** + * Sets the concrete type associated to [selector] to the union of the + * inferred concrete type so far and of [returnType]. + * + * Precondition: [:(typeOfThis != null) && (returnType != null):] + */ + void augmentInferredSelectorType(Selector selector, TypeMask typeOfThis, + TypeMask returnType) { + assert(returnType != null); + assert(typeOfThis != null); + + selector = selector.asUntyped; + Map currentMap = inferredSelectorTypes.putIfAbsent( + selector, () => new Map()); + TypeMask currentReturnType = currentMap[typeOfThis]; + currentMap[typeOfThis] = (currentReturnType == null) + ? returnType + : typeMaskUnion(currentReturnType, returnType); + } + + /** + * Returns the current inferred concrete type of [field]. + */ + ConcreteType getFieldType(Selector selector, Element field) { + ensureFieldInitialized(field); + ConcreteType result = inferredFieldTypes[field]; + result = (result == null) ? emptyConcreteType : result; + if (selector != null) { + Element enclosing = field.enclosingElement; + if (enclosing.isClass()) { + ClassElement cls = enclosing; + TypeMask receiverMask = new TypeMask.exact(cls.declaration); + TypeMask resultMask = types.concreteTypeToTypeMask(result); + augmentInferredSelectorType(selector, receiverMask, resultMask); + } + } + return result; + } + + /** + * Sets the concrete type associated to [field] to the union of the inferred + * concrete type so far and of [type]. + */ + void augmentFieldType(Element field, ConcreteType type) { + ensureFieldInitialized(field); + ConcreteType oldType = inferredFieldTypes[field]; + ConcreteType newType = (oldType != null) ? oldType.union(type) : type; + if (oldType != newType) { + inferredFieldTypes[field] = newType; + invalidateReaders(field); + } + } + + /** Augment the inferred type of elements stored in Lists. */ + void augmentListElementType(ConcreteType type) { + ConcreteType newType = listElementType.union(type); + if (newType != listElementType) { + invalidateCallers(listIndex); + listElementType = newType; + } + } + + /** + * Sets the concrete type associated to [parameter] to the union of the + * inferred concrete type so far and of [type]. + */ + void augmentParameterType(VariableElement parameter, ConcreteType type) { + ConcreteType oldType = inferredParameterTypes[parameter]; + inferredParameterTypes[parameter] = + (oldType == null) ? type : oldType.union(type); + } + + /** Augments the set of classes encountered so far. */ + void augmentSeenClasses(ClassElement cls) { + if (!seenClasses.contains(cls)) { + seenClasses.add(cls); + cls.forEachMember((_, Element member) { + Set functions = dynamicCallers[member.name]; + if (functions != null) { + functions.forEach(invalidate); + } + }, includeSuperAndInjectedMembers: true); + } + } + + /** + * Add [caller] to the set of [callee]'s callers. + */ + void addCaller(FunctionElement callee, Element caller) { + callers.putIfAbsent(callee, () => new Set()) + .add(caller); + } + + /** + * Add [caller] to the set of [callee]'s dynamic callers. + */ + void addDynamicCaller(Selector callee, FunctionElement caller) { + dynamicCallers + .putIfAbsent(callee.name, () => new Set()) + .add(caller); + } + + /** + * Add [reader] to the set of [field]'s readers. + */ + void addFieldReader(Element field, Element reader) { + fieldReaders.putIfAbsent(field, () => new Set()) + .add(reader); + } + + /** + * Add [reader] to the set of [local]'s readers. + */ + void addCapturedLocalReader(VariableElement local, FunctionElement reader) { + capuredLocalsReaders.putIfAbsent(local, () => new Set()) + .add(reader); + } + + /** + * Add a closure to the set of seen closures. Invalidate callers if + * the set of locals has changed. + */ + void addClosure(FunctionElement closure, + ConcreteType typeOfThis, + LocalsHandler locals) { + if (closures.put(closure, typeOfThis, locals)) { + invalidateCallers(closure); + } + } + + /** + * Invalidate all callers of [function]. + */ + void invalidateCallers(FunctionElement function) { + Set methodCallers = callers[function]; + if (methodCallers != null) { + methodCallers.forEach(invalidate); + } + } + + /** + * Invalidate all reader of [field]. + */ + void invalidateReaders(Element field) { + Set readers = fieldReaders[field]; + if (readers != null) { + readers.forEach(invalidate); + } + } + + /** + * Add all templates of [methodOrField] to the workqueue. + */ + void invalidate(Element methodOrField) { + if (methodOrField.isField()) { + workQueue.add(new InferenceWorkItem( + methodOrField, new ConcreteTypesEnvironment())); + } else { + Map templates = + methodToTemplates[methodOrField]; + if (templates != null) { + templates.forEach((environment, _) { + workQueue.add( + new InferenceWorkItem(methodOrField, environment)); + }); + } + } + } + + /** + * Returns the template associated to [function] or create an empty template + * for [function] return it. + */ + // TODO(polux): encapsulate this in an abstraction for templates + Map + getTemplatesOrEmpty(FunctionElement function) { + return methodToTemplates.putIfAbsent( + function, + () => new Map()); + } + + // -- methods of types.TypesInferrer (interface with the backend) -- + + /** Get the inferred concrete type of [node]. */ + @override + TypeMask getTypeOfNode(Element owner, Node node) { + TypeMask result = types.concreteTypeToTypeMask(inferredTypes[node]); + return (result == const DynamicTypeMask()) ? null : result; + } + + /** Get the inferred concrete type of [element]. */ + @override + TypeMask getTypeOfElement(Element element) { + final result = types.concreteTypeToTypeMask(typeOfElement(element)); + return (result == const DynamicTypeMask()) ? null : result; + } + + /** + * Get the inferred concrete return type of [element]. A null return value + * means "I don't know". + */ + @override + TypeMask getReturnTypeOfElement(Element element) { + assert(element is FunctionElement); + Map templates = + methodToTemplates[element]; + if (templates == null) return null; + ConcreteType returnType = emptyConcreteType; + templates.forEach((_, concreteType) { + returnType = returnType.union(concreteType); + }); + TypeMask result = types.concreteTypeToTypeMask(returnType); + return (result == const DynamicTypeMask()) ? null : result; + } + + /** + * Get the inferred concrete type of [selector]. A null return value means + * "I don't know". + */ + @override + TypeMask getTypeOfSelector(Selector selector) { + Map candidates = + inferredSelectorTypes[selector.asUntyped]; + if (candidates == null) { + return null; + } + TypeMask result = new TypeMask.nonNullEmpty(); + if (selector.mask == null) { + candidates.forEach((TypeMask receiverType, TypeMask returnType) { + result = typeMaskUnion(result, returnType); + }); + } else { + candidates.forEach((TypeMask receiverType, TypeMask returnType) { + TypeMask intersection = + receiverType.intersection(selector.mask, compiler); + if (!intersection.isEmpty || intersection.isNullable) { + result = typeMaskUnion(result, returnType); + } + }); + } + return result == const DynamicTypeMask() ? null : result; + } + + @override + void clear() { + throw new UnsupportedError("clearing is not yet implemented"); + } + + @override + bool isCalledOnce(Element element) { + // Never called by SimpleTypeInferrer. + throw new UnsupportedError(""); + } + + @override + bool isFixedArrayCheckedForGrowable(Node node) { + // Never called by SimpleTypeInferrer. + throw new UnsupportedError(""); + } + + // --- analysis --- + + /** + * Returns the concrete type returned by [function] given arguments of + * concrete types [argumentsTypes]. If [function] is static then + * [receiverType] must be null, else [function] must be a member of + * [receiverType]. + */ + ConcreteType getSendReturnType(Selector selector, + FunctionElement function, + ClassElement receiverType, + ArgumentsTypes argumentsTypes) { + assert(function != null); + + ConcreteType result = emptyConcreteType; + Map argumentMap = + associateArguments(function, argumentsTypes); + // if the association failed, this send will never occur or will fail + if (argumentMap == null) { + return emptyConcreteType; + } + + argumentMap.forEach(augmentParameterType); + ConcreteTypeCartesianProduct product = + new ConcreteTypeCartesianProduct(this, receiverType, argumentMap); + for (ConcreteTypesEnvironment environment in product) { + result = result.union( + getMonomorphicSendReturnType(function, environment)); + } + + if (selector != null && receiverType != null) { + // TODO(polux): generalize to any abstract class if we ever handle other + // abstract classes than num. + TypeMask receiverMask = + (receiverType == compiler.backend.numImplementation + || receiverType == compiler.backend.intImplementation) + ? new TypeMask.nonNullSubclass(receiverType.declaration) + : new TypeMask.nonNullExact(receiverType.declaration); + TypeMask resultMask = types.concreteTypeToTypeMask(result); + augmentInferredSelectorType(selector, receiverMask, resultMask); + } + + return result; + } + + /** + * Given a method signature and a list of concrete types, builds a map from + * formals to their corresponding concrete types. Returns null if the + * association is impossible (for instance: too many arguments). + */ + Map associateArguments( + FunctionElement function, + ArgumentsTypes argumentsTypes) { + final Map result = new Map(); + final FunctionSignature signature = function.functionSignature; + + // guard 1: too many arguments + if (argumentsTypes.length > signature.parameterCount) { + return null; + } + // guard 2: not enough arguments + if (argumentsTypes.positional.length < signature.requiredParameterCount) { + return null; + } + // guard 3: too many positional arguments + if (signature.optionalParametersAreNamed && + argumentsTypes.positional.length > signature.requiredParameterCount) { + return null; + } + + handleLeftoverOptionalParameter(ParameterElement parameter) { + Expression initializer = parameter.initializer; + result[parameter] = (initializer == null) + ? nullConcreteType + : analyzeDefaultValue(function, initializer); + } + + final Iterator remainingPositionalArguments = + argumentsTypes.positional.iterator; + // we attach each positional parameter to its corresponding positional + // argument + for (Link requiredParameters = signature.requiredParameters; + !requiredParameters.isEmpty; + requiredParameters = requiredParameters.tail) { + final Element requiredParameter = requiredParameters.head; + // we know moveNext() succeeds because of guard 2 + remainingPositionalArguments.moveNext(); + result[requiredParameter] = remainingPositionalArguments.current; + } + if (signature.optionalParametersAreNamed) { + // we build a map out of the remaining named parameters + Link remainingOptionalParameters = signature.optionalParameters; + final Map leftOverNamedParameters = + new Map(); + for (; + !remainingOptionalParameters.isEmpty; + remainingOptionalParameters = remainingOptionalParameters.tail) { + final Element namedParameter = remainingOptionalParameters.head; + leftOverNamedParameters[namedParameter.name] = namedParameter; + } + // we attach the named arguments to their corresponding optional + // parameters + for (String source in argumentsTypes.named.keys) { + final ConcreteType concreteType = argumentsTypes.named[source]; + final Element namedParameter = leftOverNamedParameters[source]; + // unexisting or already used named parameter + if (namedParameter == null) return null; + result[namedParameter] = concreteType; + leftOverNamedParameters.remove(source); + } + leftOverNamedParameters.forEach((_, Element parameter) { + handleLeftoverOptionalParameter(parameter); + }); + } else { // optional parameters are positional + // we attach the remaining positional arguments to their corresponding + // optional parameters + Link remainingOptionalParameters = signature.optionalParameters; + while (remainingPositionalArguments.moveNext()) { + final Element optionalParameter = remainingOptionalParameters.head; + result[optionalParameter] = remainingPositionalArguments.current; + // we know tail is defined because of guard 1 + remainingOptionalParameters = remainingOptionalParameters.tail; + } + for (; + !remainingOptionalParameters.isEmpty; + remainingOptionalParameters = remainingOptionalParameters.tail) { + handleLeftoverOptionalParameter(remainingOptionalParameters.head); + } + } + return result; + } + + ConcreteType getMonomorphicSendReturnType( + FunctionElement function, + ConcreteTypesEnvironment environment) { + Map template = + getTemplatesOrEmpty(function); + ConcreteType type = template[environment]; + ConcreteType specialType = getSpecialCaseReturnType(function, environment); + if (type != null) { + return specialType != null ? specialType : type; + } else { + workQueue.add(new InferenceWorkItem(function, environment)); + return specialType != null ? specialType : emptyConcreteType; + } + } + + /** + * Handles external methods that cannot be cached because they depend on some + * other state of [ConcreteTypesInferrer] like [:List#[]:] and + * [:List#[]=:]. Returns null if [function] and [environment] don't form a + * special case + */ + ConcreteType getSpecialCaseReturnType(FunctionElement function, + ConcreteTypesEnvironment environment) { + // Handles int + int, double + double, int - int, ... + // We cannot compare function to int#+, int#-, etc. because int and double + // don't override these methods. So for 1+2, getSpecialCaseReturnType will + // be invoked with function = num#+. We use environment.typeOfThis instead. + ClassElement cls = environment.classOfThis; + if (cls != null) { + String name = function.name; + if ((cls == baseTypes.intBaseType.element + || cls == baseTypes.doubleBaseType.element) + && (name == '+' || name == '-' || name == '*')) { + Link parameters = + function.functionSignature.requiredParameters; + ConcreteType argumentType = environment.lookupType(parameters.head); + if (argumentType.getUniqueType() == cls) { + return singletonConcreteType(new ClassBaseType(cls)); + } + } + } + + if (function == listIndex || function == listRemoveAt) { + Link parameters = function.functionSignature.requiredParameters; + ConcreteType indexType = environment.lookupType(parameters.head); + if (!indexType.baseTypes.contains(baseTypes.intBaseType)) { + return emptyConcreteType; + } + return listElementType; + } else if (function == listIndexSet || function == listInsert) { + Link parameters = function.functionSignature.requiredParameters; + ConcreteType indexType = environment.lookupType(parameters.head); + if (!indexType.baseTypes.contains(baseTypes.intBaseType)) { + return emptyConcreteType; + } + ConcreteType elementType = environment.lookupType(parameters.tail.head); + augmentListElementType(elementType); + return emptyConcreteType; + } else if (function == listAdd) { + Link parameters = function.functionSignature.requiredParameters; + ConcreteType elementType = environment.lookupType(parameters.head); + augmentListElementType(elementType); + return emptyConcreteType; + } else if (function == listRemoveLast) { + return listElementType; + } + return null; + } + + ConcreteType analyzeMethodOrClosure(Element element, + ConcreteTypesEnvironment environment) { + ConcreteType specialResult = handleSpecialMethod(element, environment); + if (specialResult != null) return specialResult; + ClosureEnvironment closureEnv = closures.getEnvironmentOrNull(element); + return (closureEnv == null) + ? analyzeMethod(element, environment) + : analyzeClosure(element, closureEnv, environment); + } + + ConcreteType analyzeMethod(Element element, + ConcreteTypesEnvironment environment) { + TypeInferrerVisitor visitor = new TypeInferrerVisitor( + element, + this, + singletonConcreteType(new ClassBaseType(environment.classOfThis)), + environment.environment); + visitor.run(); + return visitor.returnType; + } + + ConcreteType analyzeClosure(Element element, + ClosureEnvironment closureEnv, + ConcreteTypesEnvironment environment) { + assert(environment.classOfThis == null); + LocalsHandler locals = (closureEnv.locals != null) + ? new LocalsHandler.deepCopyOf(closureEnv.locals) + : null; + TypeInferrerVisitor visitor = new TypeInferrerVisitor(element, this, + closureEnv.thisType, environment.environment, locals); + visitor.run(); + return visitor.returnType; + } + + /** + * Analyze the initializer of a field if it has not yet been done and update + * [inferredFieldTypes] accordingly. Invalidate the readers of the field if + * needed. + */ + void ensureFieldInitialized(Element field) { + // This is test is needed for fitering out BoxFieldElements. + if (field is FieldElement && inferredFieldTypes[field] == null) { + analyzeFieldInitialization(field); + } + } + + /** + * Analyze the initializer of a field and update [inferredFieldTypes] + * accordingly. Invalidate the readers of the field if needed. + */ + ConcreteType analyzeFieldInitialization(VariableElement field) { + Visitor visitor = new TypeInferrerVisitor(field, this, null, new Map()); + ConcreteType type; + if (field.initializer != null) { + type = field.initializer.accept(visitor); + inferredFieldTypes[field] = type; + invalidateReaders(field); + } + return type; + } + + /** + * Analyze a default value. + */ + ConcreteType analyzeDefaultValue(Element function, Node expression) { + assert((function != null) && (expression != null)); + Visitor visitor = new TypeInferrerVisitor(function, this, null, {}); + return expression.accept(visitor); + } + + /** + * Hook that performs side effects on some special method calls (like + * [:List(length):]) and possibly returns a concrete type. + */ + ConcreteType handleSpecialMethod(FunctionElement element, + ConcreteTypesEnvironment environment) { + // We trust the return type of native elements + if (isNativeElement(element)) { + var elementType = element.type; + assert(elementType.kind == TypeKind.FUNCTION); + return typeOfNativeBehavior( + native.NativeBehavior.ofMethod(element, compiler)); + } + // When List([length]) is called with some length, we must augment + // listElementType with {null}. + if (element == listConstructor) { + Link parameters = + listConstructor.functionSignature.optionalParameters; + ConcreteType lengthType = environment.lookupType(parameters.head); + if (lengthType.baseTypes.contains(baseTypes.intBaseType)) { + augmentListElementType(nullConcreteType); + } + } + return null; + } + + /** + * Performs concrete type inference of the code reachable from [element]. + */ + @override + bool analyzeMain(Element element) { + initialize(); + workQueue.add( + new InferenceWorkItem(element, new ConcreteTypesEnvironment())); + while (!workQueue.isEmpty) { + currentWorkItem = workQueue.remove(); + if (currentWorkItem.method.isField()) { + analyzeFieldInitialization(currentWorkItem.method); + } else { + Map template = + getTemplatesOrEmpty(currentWorkItem.method); + template.putIfAbsent( + currentWorkItem.environment, () => emptyConcreteType); + recordReturnType( + currentWorkItem.method, + analyzeMethodOrClosure(currentWorkItem.method, + currentWorkItem.environment)); + } + } + return true; + } + + /** + * Dumps debugging information on the standard output. + */ + void debug() { + print("queue:"); + for (InferenceWorkItem workItem in workQueue.queue) { + print(" $workItem"); + } + print("seen classes:"); + for (ClassElement cls in seenClasses) { + print(" ${cls.name}"); + } + print("callers:"); + callers.forEach((k,v) { + print(" $k: $v"); + }); + print("dynamic callers:"); + dynamicCallers.forEach((k,v) { + print(" $k: $v"); + }); + print("readers:"); + fieldReaders.forEach((k,v) { + print(" $k: $v"); + }); + print("readers of captured locals:"); + capuredLocalsReaders.forEach((k,v) { + print(" $k: $v"); + }); + print("inferredFieldTypes:"); + inferredFieldTypes.forEach((k,v) { + print(" $k: $v"); + }); + print("listElementType:"); + print(" $listElementType"); + print("inferredParameterTypes:"); + inferredParameterTypes.forEach((k,v) { + print(" $k: $v"); + }); + print("inferred selector types:"); + inferredSelectorTypes.forEach((selector, map) { + print(" $selector:"); + map.forEach((k, v) { + print(" $k: $v"); + }); + }); + print("cache:"); + methodToTemplates.forEach((k,v) { + print(" $k: $v"); + }); + print("closures:"); + closures.closures.forEach((k, ClosureEnvironment v) { + print(" $k"); + print(" this: ${v.thisType}"); + if (v.locals != null) { + v.locals.locals.forEachLocal((local, type) { + print(" $local: $type"); + }); + } + }); + print("inferred expression types:"); + inferredTypes.forEach((k,v) { + print(" $k: $v"); + }); + } + + @override + ConcreteType addReturnTypeFor(Element analyzedElement, + ConcreteType currentType, + ConcreteType newType) { + return (currentType == null) ? newType : currentType.union(newType); + } + + @override + void forEachElementMatching(Selector selector, bool f(Element element)) { + getMembersBySelector(selector).forEach(f); + } + + @override + void recordReturnType(Element element, ConcreteType type) { + assert((type != null) && (element == currentWorkItem.method)); + Map template = + getTemplatesOrEmpty(element); + if (template[currentWorkItem.environment] != type) { + template[currentWorkItem.environment] = type; + invalidateCallers(element); + } + } + + @override + void recordType(Element element, ConcreteType type) { + assert(element is FieldElement); + augmentFieldType(element, type); + } + + @override + void recordTypeOfFinalField(Node node, + Element nodeHolder, + Element field, + ConcreteType type) { + augmentFieldType(field, type); + } + + @override + void recordTypeOfNonFinalField(Spannable node, Element field, + ConcreteType type) { + augmentFieldType(field, type); + } + + @override + void recordCapturedLocalRead(Element local) { + addCapturedLocalReader(local, currentWorkItem.method); + } + + @override + void recordLocalUpdate(Element local, ConcreteType type) { + Set localReaders = capuredLocalsReaders[local]; + if (localReaders != null) { + localReaders.forEach(invalidate); + } + } + + /** + * Returns the caller of the current analyzed element, given the alleged + * caller provided by SimpleTypeInferrer. + * + * SimpleTypeInferrer lies about the caller when it's a closure. + * Unfortunately we cannot always trust currentWorkItem.method either because + * it is wrong for fields initializers. + */ + Element getRealCaller(Element allegedCaller) { + Element currentMethod = currentWorkItem.method; + if ((currentMethod != allegedCaller) + && currentMethod.isFunction() + && closures.contains(currentMethod)) { + return currentMethod; + } else { + return allegedCaller; + } + } + + @override + ConcreteType registerCalledElement(Spannable node, + Selector selector, + Element caller, + Element callee, + ArgumentsTypes arguments, + SideEffects sideEffects, + bool inLoop) { + caller = getRealCaller(caller); + if ((selector == null) || (selector.kind == SelectorKind.CALL)) { + callee = callee.implementation; + if (selector != null && selector.name == 'JS') { + return null; + } + if (callee.isField()) { // toplevel closure call + getFieldType(selector, callee); // trigger toplevel field analysis + addFieldReader(callee, caller); + ConcreteType result = emptyConcreteType; + for (FunctionElement function in closures.functionElements) { + addCaller(function, caller); + result = result.union( + getSendReturnType(selector, function, null, arguments)); + } + return result; + } else { // method or constructor call + addCaller(callee, caller); + ClassElement receiverClass = null; + if (callee.isGenerativeConstructor()) { + receiverClass = callee.getEnclosingClass(); + } else if (node is Send) { + Send send = node; + if (send.receiver != null) { + if (send.receiver.isSuper()) { + receiverClass = + currentWorkItem.environment.classOfThis.superclass; + } else { + receiverClass = currentWorkItem.environment.classOfThis; + } + } + } + return getSendReturnType(selector, callee, receiverClass, arguments); + } + } else if (selector.kind == SelectorKind.GETTER) { + if (callee.isField()) { + addFieldReader(callee, caller); + return getFieldType(selector, callee); + } else if (callee.isGetter()) { + Element enclosing = callee.enclosingElement.isCompilationUnit() + ? null : callee.enclosingElement; + addCaller(callee, caller); + ArgumentsTypes noArguments = new ArgumentsTypes([], new Map()); + return getSendReturnType(selector, callee, enclosing, noArguments); + } else if (callee.isFunction()) { + addClosure(callee, null, null); + return singletonConcreteType(baseTypes.functionBaseType); + } + } else if (selector.kind == SelectorKind.SETTER) { + ConcreteType argumentType = arguments.positional.first; + if (callee.isField()) { + augmentFieldType(callee, argumentType); + } else if (callee.isSetter()) { + FunctionElement setter = callee; + // TODO(polux): A setter always returns void so there's no need to + // invalidate its callers even if it is called with new arguments. + // However, if we start to record more than returned types, like + // exceptions for instance, we need to do it by uncommenting the + // following line. + // inferrer.addCaller(setter, currentMethod); + Element enclosing = callee.enclosingElement.isCompilationUnit() + ? null : callee.enclosingElement; + return getSendReturnType(selector, setter, enclosing, + new ArgumentsTypes([argumentType], new Map())); + } + } else { + throw new ArgumentError("unexpected selector kind"); + } + return null; + } + + @override + ConcreteType registerCalledSelector(Node node, + Selector selector, + ConcreteType receiverType, + Element caller, + ArgumentsTypes arguments, + SideEffects sideEffects, + bool inLoop) { + caller = getRealCaller(caller); + switch (selector.kind) { + case SelectorKind.GETTER: + return registerDynamicGetterSend(selector, receiverType, caller); + case SelectorKind.SETTER: + return registerDynamicSetterSend( + selector, receiverType, caller, arguments); + default: + return registerDynamicSend(selector, receiverType, caller, arguments); + } + } + + ConcreteType registerDynamicGetterSend(Selector selector, + ConcreteType receiverType, + Element caller) { + caller = getRealCaller(caller); + ConcreteType result = emptyConcreteType; + + void augmentResult(ClassElement baseReceiverType, Element member) { + if (member.isField()) { + addFieldReader(member, caller); + result = result.union(getFieldType(selector, member)); + } else if (member.isGetter()) { + addCaller(member, caller); + ArgumentsTypes noArguments = new ArgumentsTypes([], new Map()); + result = result.union( + getSendReturnType(selector, member, baseReceiverType, noArguments)); + } else if (member.isFunction()) { + addClosure(member, receiverType, null); + result = result.union( + singletonConcreteType(baseTypes.functionBaseType)); + } else { + throw new ArgumentError("unexpected element type"); + } + } + + if (receiverType.isUnknown()) { + addDynamicCaller(selector, caller); + Set members = getMembersBySelector(selector); + for (Element member in members) { + if (!(member.isField() || member.isGetter())) continue; + for (ClassElement cls in + getReflexiveSubtypesOf(member.enclosingElement)) { + augmentResult(cls, member); + } + } + } else { + for (BaseType baseReceiverType in receiverType.baseTypes) { + if (!baseReceiverType.isNull()) { + ClassBaseType classBaseType = baseReceiverType; + ClassElement cls = classBaseType.element; + Element getterOrField = cls.lookupSelector(selector, compiler); + if (getterOrField != null) { + augmentResult(cls, getterOrField.implementation); + } + } + } + } + return result; + } + + ConcreteType registerDynamicSetterSend( + Selector selector, + ConcreteType receiverType, + Element caller, + ArgumentsTypes arguments) { + caller = getRealCaller(caller); + ConcreteType argumentType = arguments.positional.first; + + void augmentField(ClassElement receiverType, Element setterOrField) { + if (setterOrField.isField()) { + augmentFieldType(setterOrField, argumentType); + } else if (setterOrField.isSetter()) { + // A setter always returns void so there's no need to invalidate its + // callers even if it is called with new arguments. However, if we + // start to record more than returned types, like exceptions for + // instance, we need to do it by uncommenting the following line. + // inferrer.addCaller(setter, currentMethod); + getSendReturnType(selector, setterOrField, receiverType, + new ArgumentsTypes([argumentType], new Map())); + } else { + throw new ArgumentError("unexpected element type"); + } + } + + if (receiverType.isUnknown()) { + // Same remark as above + // addDynamicCaller(selector, caller); + for (Element member in getMembersBySelector(selector)) { + if (!(member.isField() || member.isSetter())) continue; + Element cls = member.getEnclosingClass(); + augmentField(cls, member); + } + } else { + for (BaseType baseReceiverType in receiverType.baseTypes) { + if (!baseReceiverType.isNull()) { + ClassBaseType classBaseType = baseReceiverType; + ClassElement cls = classBaseType.element; + Element setterOrField = cls.lookupSelector(selector, compiler); + if (setterOrField != null) { + augmentField(cls, setterOrField.implementation); + } + } + } + } + return argumentType; + } + + ConcreteType registerDynamicSend(Selector selector, + ConcreteType receiverType, + Element caller, + ArgumentsTypes arguments) { + caller = getRealCaller(caller); + ConcreteType result = emptyConcreteType; + if (receiverType.isUnknown()) { + addDynamicCaller(selector, caller); + Set elements = getMembersBySelector(selector); + for (Element element in elements) { + if (element.isFunction()) { + FunctionElement method = element; + addCaller(method, caller); + for (ClassElement cls in + getReflexiveSubtypesOf(method.enclosingElement)) { + result = result.union( + getSendReturnType(selector, method, cls, arguments)); + } + } else { // closure call + assert(element.isField()); + for (FunctionElement function in closures.functionElements) { + addCaller(function, caller); + result = result.union( + getSendReturnType(selector, function, null, arguments)); + } + } + } + } else { + for (BaseType baseReceiverType in receiverType.baseTypes) { + if (!baseReceiverType.isNull()) { + ClassBaseType classBaseReceiverType = baseReceiverType; + ClassElement cls = classBaseReceiverType.element; + Element method = cls.lookupSelector(selector, compiler); + if (method != null) { + if (method.isFunction()) { + assert(method is FunctionElement); + method = method.implementation; + addCaller(method, caller); + result = result.union( + getSendReturnType(selector, method, cls, arguments)); + } else { // closure call + for (FunctionElement function in closures.functionElements) { + addCaller(function, caller); + result = result.union( + getSendReturnType(selector, function, null, arguments)); + } + } + } + } + } + } + return result; + } + + @override + void setDefaultTypeOfParameter(Element parameter, ConcreteType type) { + // We handle default parameters our own way in associateArguments + } + + @override + ConcreteType registerCalledClosure(Node node, + Selector selector, + ConcreteType closure, + Element caller, + ArgumentsTypes arguments, + SideEffects sideEffects, + bool inLoop) { + caller = getRealCaller(caller); + ConcreteType result = emptyConcreteType; + for (FunctionElement function in closures.functionElements) { + addCaller(function, caller); + result = result.union( + getSendReturnType(selector, function, null, arguments)); + } + return result; + } + + @override + ConcreteType returnTypeOfElement(Element element) { + // Never called by SimpleTypeInferrer. + throw new UnsupportedError(""); + } + + @override + ConcreteType typeOfElement(Element element) { + if (currentWorkItem != null) { + final result = currentWorkItem.environment.lookupType(element); + if (result != null) return result; + } + if (element.isParameter() || element.isFieldParameter()) { + return inferredParameterTypes[element]; + } else if (element.isField()) { + return inferredFieldTypes[element]; + } + throw new ArgumentError("unexpected element type"); + } + + @override + void analyze(Element element, ArgumentsTypes arguments) { + FunctionElement function = element; + getSendReturnType( + null, function, currentWorkItem.environment.classOfThis, arguments); + } +} + +class TypeInferrerVisitor extends SimpleTypeInferrerVisitor { + final ConcreteType thisType; + ConcreteTypesInferrer get inferrer => super.inferrer; + + TypeInferrerVisitor(Element element, + ConcreteTypesInferrer inferrer, + this.thisType, + Map environment, + [LocalsHandler handler]) + : super(element, inferrer.compiler, inferrer, handler); + + @override + ConcreteType visitFunctionExpression(FunctionExpression node) { + Element element = elements[node]; + // visitFunctionExpression should be only called for closures + assert(element != analyzedElement); + inferrer.addClosure( + element, thisType, new LocalsHandler.deepCopyOf(locals)); + return types.functionType; + } + + @override + ConcreteType visitLiteralString(LiteralString node) { + // TODO(polux): get rid of this hack once we have a natural way of inferring + // the unknown type. + if (inferrer.testMode + && (node.dartString.slowToString() == "__dynamic_for_test")) { + return inferrer.unknownConcreteType; + } + return super.visitLiteralString(node); + } + + /** + * Same as super.visitLiteralList except it doesn't cache anything. + */ + @override + ConcreteType visitLiteralList(LiteralList node) { + ConcreteType elementType; + int length = 0; + for (Node element in node.elements.nodes) { + ConcreteType type = visit(element); + elementType = elementType == null + ? types.allocatePhi(null, null, type) + : types.addPhiInput(null, elementType, type); + length++; + } + elementType = elementType == null + ? types.nonNullEmpty() + : types.simplifyPhi(null, null, elementType); + ConcreteType containerType = node.isConst() + ? types.constListType + : types.growableListType; + return types.allocateList( + containerType, + node, + outermostElement, + elementType, + length); + } + + /** + * Same as super.visitGetterSend except it records the type of nodes in test + * mode. + */ + @override + ConcreteType visitGetterSend(Send node) { + if (inferrer.testMode) { + Element element = elements[node]; + ConcreteType type = locals.use(element); + if (type != null) { + inferrer.augmentInferredType(node, type); + } + } + return super.visitGetterSend(node); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/inferrer_visitor.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/inferrer_visitor.dart new file mode 100644 index 0000000..cf49e90 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/inferrer_visitor.dart @@ -0,0 +1,1223 @@ +// Copyright (c) 2013, 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. + +library inferrer_visitor; + +import '../dart2jslib.dart' hide Selector, TypedSelector; +import '../dart_types.dart'; +import '../elements/elements.dart'; +import '../tree/tree.dart'; +import '../universe/universe.dart'; +import '../util/util.dart'; +import '../types/types.dart' show TypeMask; + +/** + * The interface [InferrerVisitor] will use when working on types. + */ +abstract class TypeSystem { + T get dynamicType; + T get nullType; + T get intType; + T get uint31Type; + T get uint32Type; + T get positiveIntType; + T get doubleType; + T get numType; + T get boolType; + T get functionType; + T get listType; + T get constListType; + T get fixedListType; + T get growableListType; + T get mapType; + T get constMapType; + T get stringType; + T get typeType; + + T stringLiteralType(DartString value); + + T nonNullSubtype(ClassElement type); + T nonNullSubclass(ClassElement type); + T nonNullExact(ClassElement type); + T nonNullEmpty(); + bool isNull(T type); + Selector newTypedSelector(T receiver, Selector selector); + + T allocateList(T type, + Node node, + Element enclosing, + [T elementType, int length]); + + T allocateMap(T type, Node node, Element element, [List keyType, + List valueType]); + + T allocateClosure(Node node, Element element); + + /** + * Returns the least upper bound between [firstType] and + * [secondType]. + */ + T computeLUB(T firstType, T secondType); + + /** + * Returns the intersection between [T] and [annotation]. + * [isNullable] indicates whether the annotation implies a null + * type. + */ + T narrowType(T type, DartType annotation, {bool isNullable: true}); + + /** + * Returns a new type that unions [firstInput] and [secondInput]. + */ + T allocateDiamondPhi(T firstInput, T secondInput); + + /** + * Returns a new type for holding the potential types of [element]. + * [inputType] is the first incoming type of the phi. + */ + T allocatePhi(Node node, Element element, T inputType); + + /** + * Simplies the phi representing [element] and of the type + * [phiType]. For example, if this phi has one incoming input, an + * implementation of this method could just return that incoming + * input type. + */ + T simplifyPhi(Node node, Element element, T phiType); + + /** + * Adds [newType] as an input of [phiType]. + */ + T addPhiInput(Element element, T phiType, T newType); + + /** + * Returns a new receiver type for this [selector] applied to + * [receiverType]. + */ + T refineReceiver(Selector selector, T receiverType); + + /** + * Returns the internal inferrer representation for [mask]. + */ + T getConcreteTypeFor(TypeMask mask); +} + +/** + * A variable scope holds types for variables. It has a link to a + * parent scope, but never changes the types in that parent. Instead, + * updates to locals of a parent scope are put in the current scope. + * The inferrer makes sure updates get merged into the parent scope, + * once the control flow block has been visited. + */ +class VariableScope { + Map variables; + + /// The parent of this scope. Null for the root scope. + final VariableScope parent; + + /// The [Node] that created this scope. + final Node block; + + VariableScope(this.block, [parent]) + : this.variables = null, + this.parent = parent; + + VariableScope.deepCopyOf(VariableScope other) + : variables = other.variables == null + ? null + : new Map.from(other.variables), + block = other.block, + parent = other.parent == null + ? null + : new VariableScope.deepCopyOf(other.parent); + + T operator [](Element variable) { + T result; + if (variables == null || (result = variables[variable]) == null) { + return parent == null ? null : parent[variable]; + } + return result; + } + + void operator []=(Element variable, T mask) { + assert(mask != null); + if (variables == null) { + variables = new Map(); + } + variables[variable] = mask; + } + + void forEachOwnLocal(void f(Element element, T type)) { + if (variables == null) return; + variables.forEach(f); + } + + void forEachLocalUntilNode(Node node, + void f(Element element, T type), + [Setlet seenLocals]) { + if (seenLocals == null) seenLocals = new Setlet(); + if (variables != null) { + variables.forEach((element, type) { + if (seenLocals.contains(element)) return; + seenLocals.add(element); + f(element, type); + }); + } + if (block == node) return; + if (parent != null) parent.forEachLocalUntilNode(node, f, seenLocals); + } + + void forEachLocal(void f(Element, T type)) { + forEachLocalUntilNode(null, f); + } + + bool updates(Element element) { + if (variables == null) return false; + return variables.containsKey(element); + } + + String toString() { + String rest = parent == null ? "null" : parent.toString(); + return '$variables $rest'; + } +} + +class FieldInitializationScope { + final TypeSystem types; + Map fields; + bool isThisExposed; + + FieldInitializationScope(this.types) : isThisExposed = false; + + FieldInitializationScope.internalFrom(FieldInitializationScope other) + : types = other.types, + isThisExposed = other.isThisExposed; + + factory FieldInitializationScope.from(FieldInitializationScope other) { + if (other == null) return null; + return new FieldInitializationScope.internalFrom(other); + } + + void updateField(Element field, T type) { + if (isThisExposed) return; + if (fields == null) fields = new Map(); + fields[field] = type; + } + + T readField(Element field) { + return fields == null ? null : fields[field]; + } + + void forEach(void f(Element element, T type)) { + if (fields == null) return; + fields.forEach(f); + } + + void mergeDiamondFlow(FieldInitializationScope thenScope, + FieldInitializationScope elseScope) { + // Quick bailout check. If [isThisExposed] is true, we know the + // code following won't do anything. + if (isThisExposed) return; + if (elseScope == null || elseScope.fields == null) { + elseScope = this; + } + + thenScope.forEach((Element field, T type) { + T otherType = elseScope.readField(field); + if (otherType == null) return; + updateField(field, types.allocateDiamondPhi(type, otherType)); + }); + isThisExposed = thenScope.isThisExposed || elseScope.isThisExposed; + } +} + +/** + * Placeholder for inferred arguments types on sends. + */ +class ArgumentsTypes { + final List positional; + final Map named; + ArgumentsTypes(this.positional, named) + : this.named = (named == null || named.isEmpty) ? const {} : named { + assert(this.positional.every((T type) => type != null)); + assert(this.named.values.every((T type) => type != null)); + } + + int get length => positional.length + named.length; + + String toString() => "{ positional = $positional, named = $named }"; + + bool operator==(other) { + if (positional.length != other.positional.length) return false; + if (named.length != other.named.length) return false; + for (int i = 0; i < positional.length; i++) { + if (positional[i] != other.positional[i]) return false; + } + named.forEach((name, type) { + if (other.named[name] != type) return false; + }); + return true; + } + + int get hashCode => throw new UnsupportedError('ArgumentsTypes.hashCode'); + + bool hasNoArguments() => positional.isEmpty && named.isEmpty; + + bool hasOnePositionalArgumentThatMatches(bool f(T type)) { + return named.isEmpty && positional.length == 1 && f(positional[0]); + } + + void forEach(void f(T type)) { + positional.forEach(f); + named.values.forEach(f); + } + + bool every(bool f(T type)) { + return positional.every(f) && named.values.every(f); + } + + bool contains(T type) { + return positional.contains(type) || named.containsValue(type); + } +} + +abstract class MinimalInferrerEngine { + /** + * Returns the type of [element]. + */ + T typeOfElement(Element element); + + /** + * Records that [node] sets non-final field [element] to be of type + * [type]. + */ + void recordTypeOfNonFinalField(Node node, Element field, T type); + + /** + * Records that the captured variable [local] is read. + */ + void recordCapturedLocalRead(Element local); + + /** + * Records that the variable [local] is being updated. + */ + void recordLocalUpdate(Element local, T type); +} + +/** + * Placeholder for inferred types of local variables. + */ +class LocalsHandler { + final Compiler compiler; + final TypeSystem types; + final MinimalInferrerEngine inferrer; + final VariableScope locals; + final Map captured; + final Map capturedAndBoxed; + final FieldInitializationScope fieldScope; + LocalsHandler tryBlock; + bool seenReturnOrThrow = false; + bool seenBreakOrContinue = false; + + bool get aborts { + return seenReturnOrThrow || seenBreakOrContinue; + } + bool get inTryBlock => tryBlock != null; + + LocalsHandler(this.inferrer, + this.types, + this.compiler, + Node block, + [this.fieldScope]) + : locals = new VariableScope(block), + captured = new Map(), + capturedAndBoxed = new Map(), + tryBlock = null; + + LocalsHandler.from(LocalsHandler other, + Node block, + {bool useOtherTryBlock: true}) + : locals = new VariableScope(block, other.locals), + fieldScope = new FieldInitializationScope.from(other.fieldScope), + captured = other.captured, + capturedAndBoxed = other.capturedAndBoxed, + types = other.types, + inferrer = other.inferrer, + compiler = other.compiler { + tryBlock = useOtherTryBlock ? other.tryBlock : this; + } + + LocalsHandler.deepCopyOf(LocalsHandler other) + : locals = new VariableScope.deepCopyOf(other.locals), + fieldScope = new FieldInitializationScope.from(other.fieldScope), + captured = other.captured, + capturedAndBoxed = other.capturedAndBoxed, + tryBlock = other.tryBlock, + types = other.types, + inferrer = other.inferrer, + compiler = other.compiler; + + T use(Element local) { + if (capturedAndBoxed.containsKey(local)) { + return inferrer.typeOfElement(capturedAndBoxed[local]); + } else { + if (captured.containsKey(local)) { + inferrer.recordCapturedLocalRead(local); + } + return locals[local]; + } + } + + void update(TypedElement local, T type, Node node) { + assert(type != null); + if (compiler.trustTypeAnnotations || compiler.enableTypeAssertions) { + type = types.narrowType(type, local.type); + } + updateLocal() { + T currentType = locals[local]; + locals[local] = type; + if (currentType != type) { + inferrer.recordLocalUpdate(local, type); + } + } + if (capturedAndBoxed.containsKey(local)) { + inferrer.recordTypeOfNonFinalField( + node, capturedAndBoxed[local], type); + } else if (inTryBlock) { + // We don't know if an assignment in a try block + // will be executed, so all assigments in that block are + // potential types after we have left it. We update the parent + // of the try block so that, at exit of the try block, we get + // the right phi for it. + T existing = tryBlock.locals.parent[local]; + if (existing != null) { + T phiType = types.allocatePhi(tryBlock.locals.block, local, existing); + T inputType = types.addPhiInput(local, phiType, type); + tryBlock.locals.parent[local] = inputType; + } + // Update the current handler unconditionnally with the new + // type. + updateLocal(); + } else { + updateLocal(); + } + } + + void setCaptured(Element local, Element field) { + captured[local] = field; + } + + void setCapturedAndBoxed(Element local, Element field) { + capturedAndBoxed[local] = field; + } + + void mergeDiamondFlow(LocalsHandler thenBranch, + LocalsHandler elseBranch) { + if (fieldScope != null && elseBranch != null) { + fieldScope.mergeDiamondFlow(thenBranch.fieldScope, elseBranch.fieldScope); + } + seenReturnOrThrow = thenBranch.seenReturnOrThrow + && elseBranch != null + && elseBranch.seenReturnOrThrow; + seenBreakOrContinue = thenBranch.seenBreakOrContinue + && elseBranch != null + && elseBranch.seenBreakOrContinue; + if (aborts) return; + + void mergeOneBranch(LocalsHandler other) { + other.locals.forEachOwnLocal((Element local, T type) { + T myType = locals[local]; + if (myType == null) return; // Variable is only defined in [other]. + if (type == myType) return; + locals[local] = types.allocateDiamondPhi(myType, type); + }); + } + + void inPlaceUpdateOneBranch(LocalsHandler other) { + other.locals.forEachOwnLocal((Element local, T type) { + T myType = locals[local]; + if (myType == null) return; // Variable is only defined in [other]. + if (type == myType) return; + locals[local] = type; + }); + } + + if (thenBranch.aborts) { + if (elseBranch == null) return; + inPlaceUpdateOneBranch(elseBranch); + } else if (elseBranch == null) { + mergeOneBranch(thenBranch); + } else if (elseBranch.aborts) { + inPlaceUpdateOneBranch(thenBranch); + } else { + void mergeLocal(Element local) { + T myType = locals[local]; + if (myType == null) return; + T elseType = elseBranch.locals[local]; + T thenType = thenBranch.locals[local]; + if (thenType == elseType) { + locals[local] = thenType; + } else { + locals[local] = types.allocateDiamondPhi(thenType, elseType); + } + } + + thenBranch.locals.forEachOwnLocal((Element local, _) { + mergeLocal(local); + }); + elseBranch.locals.forEachOwnLocal((Element local, _) { + // Discard locals we already processed when iterating over + // [thenBranch]'s locals. + if (!thenBranch.locals.updates(local)) mergeLocal(local); + }); + } + } + + /** + * Merge all [LocalsHandler] in [handlers] into [:this:]. + * + * If [keepOwnLocals] is true, the types of locals in this + * [LocalsHandler] are being used in the merge. [keepOwnLocals] + * should be true if this [LocalsHandler], the dominator of + * all [handlers], also direclty flows into the join point, + * that is the code after all [handlers]. For example, consider: + * + * [: switch (...) { + * case 1: ...; break; + * } + * :] + * + * The [LocalsHandler] at entry of the switch also flows into the + * exit of the switch, because there is no default case. So the + * types of locals at entry of the switch have to take part to the + * merge. + */ + void mergeAfterBreaks(List> handlers, + {bool keepOwnLocals: true}) { + Node level = locals.block; + LocalsHandler startWith; + int index = 0; + if (keepOwnLocals && !seenReturnOrThrow) { + startWith = this; + index--; + } else { + // Find the first handler that does not abort. + while (index < handlers.length + && (startWith = handlers[index]).seenReturnOrThrow) { + index++; + } + if (index == handlers.length) { + // If we haven't found a handler that does not abort, we know + // this handler aborts. + seenReturnOrThrow = true; + return; + } else { + // Otherwise, this handler does not abort. + seenReturnOrThrow = false; + } + } + // Use [startWith] to initialize the types of locals. + locals.forEachLocal((local, myType) { + T otherType = startWith.locals[local]; + T newType = types.allocatePhi(level, local, otherType); + if (myType != newType) { + locals[local] = newType; + } + }); + // Merge all other handlers. + for (int i = index + 1; i < handlers.length; i++) { + mergeHandler(handlers[i]); + } + + locals.forEachLocal((Element element, T type) { + T newType = types.simplifyPhi(level, element, type); + if (newType != type) { + locals[element] = newType; + } + }); + } + + /** + * Merge [other] into this handler. Returns whether a local in this + * has changed. + */ + bool mergeHandler(LocalsHandler other) { + if (other.seenReturnOrThrow) return false; + bool changed = false; + other.locals.forEachLocalUntilNode(locals.block, (local, otherType) { + T myType = locals[local]; + if (myType == null) return; + T newType = types.addPhiInput(local, myType, otherType); + if (newType != myType) { + changed = true; + locals[local] = newType; + } + }); + return changed; + } + + /** + * Merge all [LocalsHandler] in [handlers] into this handler. + * Returns whether a local in this handler has changed. + */ + bool mergeAll(List> handlers) { + bool changed = false; + assert(!seenReturnOrThrow); + handlers.forEach((other) { + changed = mergeHandler(other) || changed; + }); + return changed; + } + + void startLoop(Node loop) { + locals.forEachLocal((Element element, T type) { + T newType = types.allocatePhi(loop, element, type); + if (newType != type) { + locals[element] = newType; + } + }); + } + + void endLoop(Node loop) { + locals.forEachLocal((Element element, T type) { + T newType = types.simplifyPhi(loop, element, type); + if (newType != type) { + locals[element] = newType; + } + }); + } + + void updateField(Element element, T type) { + fieldScope.updateField(element, type); + } +} + +abstract class InferrerVisitor + > extends ResolvedVisitor { + final Element analyzedElement; + final TypeSystem types; + final E inferrer; + final Map>> breaksFor = + new Map>>(); + final Map> continuesFor = + new Map>>(); + LocalsHandler locals; + final List cascadeReceiverStack = new List(); + + bool accumulateIsChecks = false; + bool conditionIsSimple = false; + List isChecks; + int loopLevel = 0; + + bool get inLoop => loopLevel > 0; + bool get isThisExposed { + return analyzedElement.isGenerativeConstructor() + ? locals.fieldScope.isThisExposed + : true; + } + void set isThisExposed(value) { + if (analyzedElement.isGenerativeConstructor()) { + locals.fieldScope.isThisExposed = value; + } + } + + InferrerVisitor(Element analyzedElement, + this.inferrer, + this.types, + Compiler compiler, + [LocalsHandler handler]) + : this.analyzedElement = analyzedElement, + this.locals = handler, + super(compiler.enqueuer.resolution.getCachedElements(analyzedElement), + compiler) { + if (handler != null) return; + Node node = analyzedElement.parseNode(compiler); + FieldInitializationScope fieldScope = + analyzedElement.isGenerativeConstructor() + ? new FieldInitializationScope(types) + : null; + locals = new LocalsHandler(inferrer, types, compiler, node, fieldScope); + } + + T visitSendSet(SendSet node); + + T visitSuperSend(Send node); + + T visitStaticSend(Send node); + + T visitGetterSend(Send node); + + T visitClosureSend(Send node); + + T visitDynamicSend(Send node); + + T visitForIn(ForIn node); + + T visitReturn(Return node); + + T visitFunctionExpression(FunctionExpression node); + + T visitAssert(Send node) { + if (!compiler.enableUserAssertions) { + return types.nullType; + } + return visitStaticSend(node); + } + + T visitNode(Node node) { + return node.visitChildren(this); + } + + T visitNewExpression(NewExpression node) { + return node.send.accept(this); + } + + T visit(Node node) { + return node == null ? null : node.accept(this); + } + + T visitFunctionDeclaration(FunctionDeclaration node) { + locals.update(elements[node], types.functionType, node); + return visit(node.function); + } + + T visitLiteralString(LiteralString node) { + return types.stringLiteralType(node.dartString); + } + + T visitStringInterpolation(StringInterpolation node) { + node.visitChildren(this); + return types.stringType; + } + + T visitStringJuxtaposition(StringJuxtaposition node) { + node.visitChildren(this); + return types.stringType; + } + + T visitLiteralBool(LiteralBool node) { + return types.boolType; + } + + T visitLiteralDouble(LiteralDouble node) { + ConstantSystem constantSystem = compiler.backend.constantSystem; + // The JavaScript backend may turn this literal into an integer at + // runtime. + return types.getConcreteTypeFor( + constantSystem.createDouble(node.value).computeMask(compiler)); + } + + T visitLiteralInt(LiteralInt node) { + ConstantSystem constantSystem = compiler.backend.constantSystem; + // The JavaScript backend may turn this literal into a double at + // runtime. + return types.getConcreteTypeFor( + constantSystem.createInt(node.value).computeMask(compiler)); + } + + T visitLiteralList(LiteralList node) { + node.visitChildren(this); + return node.isConst() ? types.constListType : types.growableListType; + } + + T visitLiteralMap(LiteralMap node) { + node.visitChildren(this); + return node.isConst() ? types.constMapType : types.mapType; + } + + T visitLiteralNull(LiteralNull node) { + return types.nullType; + } + + T visitLiteralSymbol(LiteralSymbol node) { + // TODO(kasperl): We should be able to tell that the type of a literal + // symbol is always a non-null exact symbol implementation -- not just + // any non-null subtype of the symbol interface. + return types.nonNullSubtype(compiler.symbolClass); + } + + T visitTypeReferenceSend(Send node) { + return elements.isTypeLiteral(node) ? types.typeType : types.dynamicType; + } + + bool isThisOrSuper(Node node) => node.isThis() || node.isSuper(); + + Element get outermostElement { + return + analyzedElement.getOutermostEnclosingMemberOrTopLevel().implementation; + } + + T _thisType; + T get thisType { + if (_thisType != null) return _thisType; + ClassElement cls = outermostElement.getEnclosingClass(); + if (compiler.world.isUsedAsMixin(cls)) { + return _thisType = types.nonNullSubtype(cls); + } else if (compiler.world.hasAnySubclass(cls)) { + return _thisType = types.nonNullSubclass(cls); + } else { + return _thisType = types.nonNullExact(cls); + } + } + + T _superType; + T get superType { + if (_superType != null) return _superType; + return _superType = types.nonNullExact( + outermostElement.getEnclosingClass().superclass); + } + + T visitIdentifier(Identifier node) { + if (node.isThis()) { + return thisType; + } else if (node.isSuper()) { + return superType; + } else { + Element element = elements[node]; + if (Elements.isLocal(element)) { + return locals.use(element); + } + return null; + } + } + + void potentiallyAddIsCheck(Send node) { + if (!accumulateIsChecks) return; + if (!Elements.isLocal(elements[node.receiver])) return; + isChecks.add(node); + } + + void potentiallyAddNullCheck(Send node, Node receiver) { + if (!accumulateIsChecks) return; + if (!Elements.isLocal(elements[receiver])) return; + isChecks.add(node); + } + + void updateIsChecks(List tests, {bool usePositive}) { + void narrow(Element element, DartType type, Node node) { + T existing = locals.use(element); + T newType = types.narrowType(existing, type, isNullable: false); + locals.update(element, newType, node); + } + + if (tests == null) return; + for (Send node in tests) { + if (node.isTypeTest) { + if (node.isIsNotCheck) { + if (usePositive) continue; + } else { + if (!usePositive) continue; + } + DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); + narrow(elements[node.receiver], type, node); + } else { + Element receiverElement = elements[node.receiver]; + Element argumentElement = elements[node.arguments.first]; + String operator = node.selector.asOperator().source; + if ((operator == '==' && usePositive) + || (operator == '!=' && !usePositive)) { + // Type the elements as null. + if (Elements.isLocal(receiverElement)) { + locals.update(receiverElement, types.nullType, node); + } + if (Elements.isLocal(argumentElement)) { + locals.update(argumentElement, types.nullType, node); + } + } else { + // Narrow the elements to a non-null type. + DartType objectType = compiler.objectClass.rawType; + if (Elements.isLocal(receiverElement)) { + narrow(receiverElement, objectType, node); + } + if (Elements.isLocal(argumentElement)) { + narrow(argumentElement, objectType, node); + } + } + } + } + } + + T visitOperatorSend(Send node) { + Operator op = node.selector; + if ("[]" == op.source) { + return visitDynamicSend(node); + } else if ("&&" == op.source) { + conditionIsSimple = false; + bool oldAccumulateIsChecks = accumulateIsChecks; + List oldIsChecks = isChecks; + if (!accumulateIsChecks) { + accumulateIsChecks = true; + isChecks = []; + } + visit(node.receiver); + LocalsHandler saved = locals; + locals = new LocalsHandler.from(locals, node); + updateIsChecks(isChecks, usePositive: true); + if (!oldAccumulateIsChecks) { + accumulateIsChecks = false; + isChecks = oldIsChecks; + } + visit(node.arguments.head); + saved.mergeDiamondFlow(locals, null); + locals = saved; + return types.boolType; + } else if ("||" == op.source) { + conditionIsSimple = false; + List tests = []; + bool isSimple = handleCondition(node.receiver, tests); + LocalsHandler saved = locals; + locals = new LocalsHandler.from(locals, node); + if (isSimple) updateIsChecks(tests, usePositive: false); + bool oldAccumulateIsChecks = accumulateIsChecks; + accumulateIsChecks = false; + visit(node.arguments.head); + accumulateIsChecks = oldAccumulateIsChecks; + saved.mergeDiamondFlow(locals, null); + locals = saved; + return types.boolType; + } else if ("!" == op.source) { + bool oldAccumulateIsChecks = accumulateIsChecks; + accumulateIsChecks = false; + node.visitChildren(this); + accumulateIsChecks = oldAccumulateIsChecks; + return types.boolType; + } else if ("is" == op.source) { + potentiallyAddIsCheck(node); + node.visitChildren(this); + return types.boolType; + } else if ("as" == op.source) { + T receiverType = visit(node.receiver); + DartType type = elements.getType(node.arguments.head); + return types.narrowType(receiverType, type); + } else if (node.argumentsNode is Prefix) { + // Unary operator. + return visitDynamicSend(node); + } else if ('===' == op.source + || '!==' == op.source) { + node.visitChildren(this); + return types.boolType; + } else if ('!=' == op.source) { + visitDynamicSend(node); + return types.boolType; + } else { + // Binary operator. + return visitDynamicSend(node); + } + } + + // Because some nodes just visit their children, we may end up + // visiting a type annotation, that may contain a send in case of a + // prefixed type. Therefore we explicitly visit the type annotation + // to avoid confusing the [ResolvedVisitor]. + visitTypeAnnotation(TypeAnnotation node) {} + + T visitConditional(Conditional node) { + List tests = []; + bool simpleCondition = handleCondition(node.condition, tests); + LocalsHandler saved = locals; + locals = new LocalsHandler.from(locals, node); + updateIsChecks(tests, usePositive: true); + T firstType = visit(node.thenExpression); + LocalsHandler thenLocals = locals; + locals = new LocalsHandler.from(saved, node); + if (simpleCondition) updateIsChecks(tests, usePositive: false); + T secondType = visit(node.elseExpression); + saved.mergeDiamondFlow(thenLocals, locals); + locals = saved; + T type = types.allocateDiamondPhi(firstType, secondType); + return type; + } + + T visitVariableDefinitions(VariableDefinitions node) { + for (Link link = node.definitions.nodes; + !link.isEmpty; + link = link.tail) { + Node definition = link.head; + if (definition is Identifier) { + locals.update(elements[definition], types.nullType, node); + } else { + assert(definition.asSendSet() != null); + visit(definition); + } + } + return null; + } + + bool handleCondition(Node node, List tests) { + bool oldConditionIsSimple = conditionIsSimple; + bool oldAccumulateIsChecks = accumulateIsChecks; + List oldIsChecks = isChecks; + accumulateIsChecks = true; + conditionIsSimple = true; + isChecks = tests; + visit(node); + bool simpleCondition = conditionIsSimple; + accumulateIsChecks = oldAccumulateIsChecks; + isChecks = oldIsChecks; + conditionIsSimple = oldConditionIsSimple; + return simpleCondition; + } + + T visitIf(If node) { + List tests = []; + bool simpleCondition = handleCondition(node.condition, tests); + LocalsHandler saved = locals; + locals = new LocalsHandler.from(locals, node); + updateIsChecks(tests, usePositive: true); + visit(node.thenPart); + LocalsHandler thenLocals = locals; + locals = new LocalsHandler.from(saved, node); + if (simpleCondition) updateIsChecks(tests, usePositive: false); + visit(node.elsePart); + saved.mergeDiamondFlow(thenLocals, locals); + locals = saved; + return null; + } + + void setupBreaksAndContinues(TargetElement element) { + if (element == null) return; + if (element.isContinueTarget) continuesFor[element] = []; + if (element.isBreakTarget) breaksFor[element] = []; + } + + void clearBreaksAndContinues(TargetElement element) { + continuesFor.remove(element); + breaksFor.remove(element); + } + + List> getBreaks(TargetElement element) { + List> list = >[locals]; + if (element == null) return list; + if (!element.isBreakTarget) return list; + return list..addAll(breaksFor[element]); + } + + List> getLoopBackEdges(TargetElement element) { + List> list = >[locals]; + if (element == null) return list; + if (!element.isContinueTarget) return list; + return list..addAll(continuesFor[element]); + } + + T handleLoop(Node node, void logic()) { + loopLevel++; + bool changed = false; + TargetElement target = elements[node]; + LocalsHandler saved = locals; + saved.startLoop(node); + do { + // Setup (and clear in case of multiple iterations of the loop) + // the lists of breaks and continues seen in the loop. + setupBreaksAndContinues(target); + locals = new LocalsHandler.from(saved, node); + logic(); + changed = saved.mergeAll(getLoopBackEdges(target)); + } while (changed); + loopLevel--; + saved.endLoop(node); + bool keepOwnLocals = node.asDoWhile() == null; + saved.mergeAfterBreaks( + getBreaks(target), keepOwnLocals: keepOwnLocals); + locals = saved; + clearBreaksAndContinues(target); + return null; + } + + T visitWhile(While node) { + return handleLoop(node, () { + List tests = []; + handleCondition(node.condition, tests); + updateIsChecks(tests, usePositive: true); + visit(node.body); + }); + } + + T visitDoWhile(DoWhile node) { + return handleLoop(node, () { + visit(node.body); + List tests = []; + handleCondition(node.condition, tests); + updateIsChecks(tests, usePositive: true); + }); + } + + T visitFor(For node) { + visit(node.initializer); + return handleLoop(node, () { + List tests = []; + handleCondition(node.condition, tests); + updateIsChecks(tests, usePositive: true); + visit(node.body); + visit(node.update); + }); + } + + T visitTryStatement(TryStatement node) { + LocalsHandler saved = locals; + locals = new LocalsHandler.from( + locals, node, useOtherTryBlock: false); + visit(node.tryBlock); + saved.mergeDiamondFlow(locals, null); + locals = saved; + for (Node catchBlock in node.catchBlocks) { + saved = locals; + locals = new LocalsHandler.from(locals, catchBlock); + visit(catchBlock); + saved.mergeDiamondFlow(locals, null); + locals = saved; + } + visit(node.finallyBlock); + return null; + } + + T visitThrow(Throw node) { + node.visitChildren(this); + locals.seenReturnOrThrow = true; + return types.nonNullEmpty(); + } + + T visitCatchBlock(CatchBlock node) { + Node exception = node.exception; + if (exception != null) { + DartType type = elements.getType(node.type); + T mask = type == null || type.treatAsDynamic + ? types.dynamicType + : types.nonNullSubtype(type.element); + locals.update(elements[exception], mask, node); + } + Node trace = node.trace; + if (trace != null) { + locals.update(elements[trace], types.dynamicType, node); + } + visit(node.block); + return null; + } + + T visitParenthesizedExpression(ParenthesizedExpression node) { + return visit(node.expression); + } + + T visitBlock(Block node) { + if (node.statements != null) { + for (Node statement in node.statements) { + visit(statement); + if (locals.aborts) break; + } + } + return null; + } + + T visitLabeledStatement(LabeledStatement node) { + Statement body = node.statement; + if (body is Loop + || body is SwitchStatement + || Elements.isUnusedLabel(node, elements)) { + // Loops and switches handle their own labels. + visit(body); + } else { + TargetElement targetElement = elements[body]; + setupBreaksAndContinues(targetElement); + visit(body); + locals.mergeAfterBreaks(getBreaks(targetElement)); + clearBreaksAndContinues(targetElement); + } + return null; + } + + T visitBreakStatement(BreakStatement node) { + TargetElement target = elements[node]; + locals.seenBreakOrContinue = true; + // Do a deep-copy of the locals, because the code following the + // break will change them. + breaksFor[target].add(new LocalsHandler.deepCopyOf(locals)); + return null; + } + + T visitContinueStatement(ContinueStatement node) { + TargetElement target = elements[node]; + locals.seenBreakOrContinue = true; + // Do a deep-copy of the locals, because the code following the + // continue will change them. + continuesFor[target].add(new LocalsHandler.deepCopyOf(locals)); + return null; + } + + void internalError(String reason, {Node node}) { + compiler.internalError(node, reason); + } + + T visitSwitchStatement(SwitchStatement node) { + visit(node.parenthesizedExpression); + + setupBreaksAndContinues(elements[node]); + if (Elements.switchStatementHasContinue(node, elements)) { + void forEachLabeledCase(void action(TargetElement target)) { + for (SwitchCase switchCase in node.cases) { + for (Node labelOrCase in switchCase.labelsAndCases) { + if (labelOrCase.asLabel() == null) continue; + LabelElement labelElement = elements[labelOrCase]; + if (labelElement != null) { + action(labelElement.target); + } + } + } + } + + forEachLabeledCase((TargetElement target) { + setupBreaksAndContinues(target); + }); + + // If the switch statement has a continue, we conservatively + // visit all cases and update [locals] until we have reached a + // fixed point. + bool changed; + locals.startLoop(node); + do { + changed = false; + for (Node switchCase in node.cases) { + LocalsHandler saved = locals; + locals = new LocalsHandler.from(locals, switchCase); + visit(switchCase); + changed = saved.mergeAll([locals]) || changed; + locals = saved; + } + } while (changed); + locals.endLoop(node); + + forEachLabeledCase((TargetElement target) { + clearBreaksAndContinues(target); + }); + } else { + LocalsHandler saved = locals; + List> localsToMerge = >[]; + bool hasDefaultCase = false; + + for (SwitchCase switchCase in node.cases) { + if (switchCase.isDefaultCase) { + hasDefaultCase = true; + } + locals = new LocalsHandler.from(saved, switchCase); + visit(switchCase); + localsToMerge.add(locals); + } + saved.mergeAfterBreaks(localsToMerge, keepOwnLocals: !hasDefaultCase); + locals = saved; + } + clearBreaksAndContinues(elements[node]); + return null; + } + + T visitCascadeReceiver(CascadeReceiver node) { + var type = visit(node.expression); + cascadeReceiverStack.add(type); + return type; + } + + T visitCascade(Cascade node) { + // Ignore the result of the cascade send and return the type of the cascade + // receiver. + visit(node.expression); + return cascadeReceiverStack.removeLast(); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/list_tracer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/list_tracer.dart new file mode 100644 index 0000000..e95f986 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/list_tracer.dart @@ -0,0 +1,209 @@ +// Copyright (c) 2013, 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. + +part of type_graph_inferrer; + +/** + * A set of selector names that [List] implements, that we know do not + * change the element type of the list, or let the list escape to code + * that might change the element type. + */ +Set okListSelectorsSet = new Set.from( + const [ + // From Object. + '==', + 'hashCode', + 'toString', + 'noSuchMethod', + 'runtimeType', + + // From Iterable. + 'iterator', + 'map', + 'where', + 'expand', + 'contains', + 'forEach', + 'reduce', + 'fold', + 'every', + 'join', + 'any', + 'toList', + 'toSet', + 'length', + 'isEmpty', + 'isNotEmpty', + 'take', + 'takeWhile', + 'skip', + 'skipWhile', + 'first', + 'last', + 'single', + 'firstWhere', + 'lastWhere', + 'singleWhere', + 'elementAt', + + // From List. + '[]', + 'length', + 'reversed', + 'sort', + 'indexOf', + 'lastIndexOf', + 'clear', + 'remove', + 'removeAt', + 'removeLast', + 'removeWhere', + 'retainWhere', + 'sublist', + 'getRange', + 'removeRange', + 'asMap', + + // From JSArray. + 'checkMutable', + 'checkGrowable', + ]); + +Set doNotChangeLengthSelectorsSet = new Set.from( + const [ + // From Object. + '==', + 'hashCode', + 'toString', + 'noSuchMethod', + 'runtimeType', + + // From Iterable. + 'iterator', + 'map', + 'where', + 'expand', + 'contains', + 'forEach', + 'reduce', + 'fold', + 'every', + 'join', + 'any', + 'toList', + 'toSet', + 'length', + 'isEmpty', + 'isNotEmpty', + 'take', + 'takeWhile', + 'skip', + 'skipWhile', + 'first', + 'last', + 'single', + 'firstWhere', + 'lastWhere', + 'singleWhere', + 'elementAt', + + // From List. + '[]', + '[]=', + 'length', + 'reversed', + 'sort', + 'indexOf', + 'lastIndexOf', + 'sublist', + 'getRange', + 'asMap', + + // From JSArray. + 'checkMutable', + 'checkGrowable', + ]); + + +class ListTracerVisitor extends TracerVisitor { + // The [List] of found assignments to the list. + List assignments = []; + bool callsGrowableMethod = false; + + ListTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer); + + /** + * Returns [true] if the analysis completed successfully, [false] if it + * bailed out. In the former case, [assignments] holds a list of + * [TypeInformation] nodes that flow into the element type of this list. + */ + bool run() { + analyze(); + ListTypeInformation list = tracedType; + if (continueAnalyzing) { + if (!callsGrowableMethod && list.inferredLength == null) { + list.inferredLength = list.originalLength; + } + list.flowsInto.addAll(flowsInto); + return true; + } else { + callsGrowableMethod = true; + assignments = null; + return false; + } + } + + visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { + bailout('Passed to a closure'); + } + + visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { + super.visitStaticCallSiteTypeInformation(info); + Element called = info.calledElement; + if (called.isForeign(compiler) && called.name == 'JS') { + bailout('Used in JS ${info.call}'); + } + } + + visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { + super.visitDynamicCallSiteTypeInformation(info); + Selector selector = info.selector; + String selectorName = selector.name; + if (currentUser == info.receiver) { + if (!okListSelectorsSet.contains(selectorName)) { + if (selector.isCall()) { + int positionalLength = info.arguments.positional.length; + if (selectorName == 'add') { + if (positionalLength == 1) { + assignments.add(info.arguments.positional[0]); + } + } else if (selectorName == 'insert') { + if (positionalLength == 2) { + assignments.add(info.arguments.positional[1]); + } + } else { + bailout('Used in a not-ok selector'); + return; + } + } else if (selector.isIndexSet()) { + assignments.add(info.arguments.positional[1]); + } else if (!selector.isIndex()) { + bailout('Used in a not-ok selector'); + return; + } + } + if (!doNotChangeLengthSelectorsSet.contains(selectorName)) { + callsGrowableMethod = true; + } + if (selectorName == 'length' && selector.isSetter()) { + callsGrowableMethod = true; + assignments.add(inferrer.types.nullType); + } + } else if (selector.isCall() && + !info.targets.every((element) => element.isFunction())) { + bailout('Passed to a closure'); + return; + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/map_tracer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/map_tracer.dart new file mode 100644 index 0000000..76cc9e5 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/map_tracer.dart @@ -0,0 +1,123 @@ +// Copyright (c) 2014, 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. + +part of type_graph_inferrer; + +Set okMapSelectorsSet = new Set.from( + const [ + // From Object. + "==", + "hashCode", + "toString", + "noSuchMethod", + "runtimeType", + // From Map + "[]", + "isEmpty", + "isNotEmpty", + "keys", + "length", + "values", + "clear", + "containsKey", + "containsValue", + "forEach", + "remove"]); + +class MapTracerVisitor extends TracerVisitor { + // These lists are used to keep track of newly discovered assignments to + // the map. Note that elements at corresponding indices are expected to + // belong to the same assignment operation. + List keyAssignments = []; + List valueAssignments = []; + // This list is used to keep track of assignments of entire maps to + // this map. + List mapAssignments = []; + + MapTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer); + + /** + * Returns [true] if the analysis completed successfully, [false] + * if it bailed out. In the former case, [keyAssignments] and + * [valueAssignments] hold a list of [TypeInformation] nodes that + * flow into the key and value types of this map. + */ + bool run() { + analyze(); + MapTypeInformation map = tracedType; + if (continueAnalyzing) { + map.flowsInto.addAll(flowsInto); + return true; + } + keyAssignments = valueAssignments = mapAssignments = null; + return false; + } + + visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { + bailout('Passed to a closure'); + } + + visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { + super.visitStaticCallSiteTypeInformation(info); + Element called = info.calledElement; + if (called.isForeign(compiler) && called.name == 'JS') { + bailout('Used in JS ${info.call}'); + } + } + + visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { + super.visitDynamicCallSiteTypeInformation(info); + Selector selector = info.selector; + String selectorName = selector.name; + if (currentUser == info.receiver) { + if (!okMapSelectorsSet.contains(selectorName)) { + if (selector.isCall()) { + int positionalLength = info.arguments.positional.length; + if (selectorName == 'addAll') { + // All keys and values from the argument flow into + // the map. + TypeInformation map = info.arguments.positional[0]; + if (map is MapTypeInformation) { + mapAssignments.add(map); + } else { + // If we could select a component from a [TypeInformation], + // like the keytype or valuetype in this case, we could + // propagate more here. + // TODO(herhut): implement selection on [TypeInformation]. + bailout('Adding map with unknown typeinfo to current map'); + } + } else if (selectorName == 'putIfAbsent') { + // The first argument is a new key, the result type of + // the second argument becomes a new value. + // Unfortunately, the type information does not + // explicitly track the return type, yet, so we have + // to go to dynamic. + // TODO(herhut,16507): Use return type of closure in + // Map.putIfAbsent. + keyAssignments.add(info.arguments.positional[0]); + valueAssignments.add(inferrer.types.dynamicType); + } else { + // It would be nice to handle [Map.keys] and [Map.values], too. + // However, currently those calls do not trigger the creation + // of a [ListTypeInformation], so I have nowhere to propagate + // that information. + // TODO(herhut): add support for Map.keys and Map.values. + bailout('Map used in a not-ok selector [$selectorName]'); + return; + } + } else if (selector.isIndexSet()) { + keyAssignments.add(info.arguments.positional[0]); + valueAssignments.add(info.arguments.positional[1]); + } else if (!selector.isIndex()) { + bailout('Map used in a not-ok selector [$selectorName]'); + return; + } + } + } else if (selector.isCall() && + !info.targets.every((element) => element.isFunction())) { + bailout('Passed to a closure'); + return; + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/node_tracer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/node_tracer.dart new file mode 100644 index 0000000..73a81dc --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/node_tracer.dart @@ -0,0 +1,311 @@ +// Copyright (c) 2013, 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. + +part of type_graph_inferrer; + +// A set of selectors we know do not escape the elements inside the +// list. +Set doesNotEscapeListSet = new Set.from( + const [ + // From Object. + '==', + 'hashCode', + 'toString', + 'noSuchMethod', + 'runtimeType', + + // From Iterable. + 'isEmpty', + 'isNotEmpty', + 'length', + 'any', + 'contains', + 'every', + 'join', + + // From List. + 'add', + 'addAll', + 'clear', + 'fillRange', + 'indexOf', + 'insert', + 'insertAll', + 'lastIndexOf', + 'remove', + 'removeRange', + 'replaceRange', + 'setAll', + 'setRange', + 'shuffle', + '[]=', + + // From JSArray. + 'checkMutable', + 'checkGrowable', + ]); + +Set doesNotEscapeMapSet = new Set.from( + const [ + // From Object. + '==', + 'hashCode', + 'toString', + 'noSuchMethod', + 'runtimeType', + // from Map. + 'isEmpty', + 'isNotEmpty', + 'length', + 'clear', + 'containsKey', + 'containsValue' + ]); + +abstract class TracerVisitor implements TypeInformationVisitor { + final TypeInformation tracedType; + final TypeGraphInferrerEngine inferrer; + final Compiler compiler; + + static const int MAX_ANALYSIS_COUNT = 16; + final Setlet analyzedElements = new Setlet(); + + TracerVisitor(this.tracedType, inferrer) + : this.inferrer = inferrer, this.compiler = inferrer.compiler; + + // Work list that gets populated with [TypeInformation] that could + // contain the container. + final List workList = []; + + // Work list of lists to analyze after analyzing the users of a + // [TypeInformation]. We know the [tracedType] has been stored in these + // lists and we must check how it escapes from these lists. + final List listsToAnalyze = + []; + // Work list of maps to analyze after analyzing the users of a + // [TypeInformation]. We know the [tracedType] has been stored in these + // maps and we must check how it escapes from these maps. + final List mapsToAnalyze = []; + + final Setlet flowsInto = new Setlet(); + + // The current [TypeInformation] in the analysis. + TypeInformation currentUser; + bool continueAnalyzing = true; + + void addNewEscapeInformation(TypeInformation info) { + if (flowsInto.contains(info)) return; + flowsInto.add(info); + workList.add(info); + } + + void analyze() { + // Collect the [TypeInformation] where the list can flow in, + // as well as the operations done on all these [TypeInformation]s. + addNewEscapeInformation(tracedType); + while (!workList.isEmpty) { + currentUser = workList.removeLast(); + currentUser.users.forEach((TypeInformation info) { + analyzedElements.add(info.owner); + info.accept(this); + }); + while (!listsToAnalyze.isEmpty) { + analyzeStoredIntoList(listsToAnalyze.removeLast()); + } + while (!mapsToAnalyze.isEmpty) { + analyzeStoredIntoMap(mapsToAnalyze.removeLast()); + } + if (!continueAnalyzing) break; + if (analyzedElements.length > MAX_ANALYSIS_COUNT) { + bailout('Too many users'); + break; + } + } + } + + void bailout(String reason) { + if (_VERBOSE) { + print('Bailing out on $tracedType because: $reason'); + } + continueAnalyzing = false; + } + + void visitNarrowTypeInformation(NarrowTypeInformation info) { + addNewEscapeInformation(info); + } + + void visitPhiElementTypeInformation(PhiElementTypeInformation info) { + addNewEscapeInformation(info); + } + + void visitElementInContainerTypeInformation( + ElementInContainerTypeInformation info) { + addNewEscapeInformation(info); + } + + void visitKeyInMapTypeInformation(KeyInMapTypeInformation info) { + addNewEscapeInformation(info); + } + + void visitValueInMapTypeInformation(ValueInMapTypeInformation info) { + addNewEscapeInformation(info); + } + + void visitListTypeInformation(ListTypeInformation info) { + listsToAnalyze.add(info); + } + + void visitMapTypeInformation(MapTypeInformation info) { + mapsToAnalyze.add(info); + } + void visitConcreteTypeInformation(ConcreteTypeInformation info) {} + + void visitStringLiteralTypeInformation(StringLiteralTypeInformation info) {} + + void visitClosureTypeInformation(ClosureTypeInformation info) {} + + void visitClosureCallSiteTypeInformation( + ClosureCallSiteTypeInformation info) {} + + visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { + Element called = info.calledElement; + if (inferrer.types.getInferredTypeOf(called) == currentUser) { + addNewEscapeInformation(info); + } + } + + void analyzeStoredIntoList(ListTypeInformation list) { + inferrer.analyzeListAndEnqueue(list); + if (list.bailedOut) { + bailout('Stored in a list that bailed out'); + } else { + list.flowsInto.forEach((flow) { + flow.users.forEach((user) { + if (user is !DynamicCallSiteTypeInformation) return; + if (user.receiver != flow) return; + if (returnsListElementTypeSet.contains(user.selector)) { + addNewEscapeInformation(user); + } else if (!doesNotEscapeListSet.contains(user.selector.name)) { + bailout('Escape from a list via [${user.selector.name}]'); + } + }); + }); + } + } + + void analyzeStoredIntoMap(MapTypeInformation map) { + inferrer.analyzeMapAndEnqueue(map); + if (map.bailedOut) { + bailout('Stored in a map that bailed out'); + } else { + map.flowsInto.forEach((flow) { + flow.users.forEach((user) { + if (user is !DynamicCallSiteTypeInformation) return; + if (user.receiver != flow) return; + if (user.selector.isIndex()) { + addNewEscapeInformation(user); + } else if (!doesNotEscapeMapSet.contains(user.selector.name)) { + bailout('Escape from a map via [${user.selector.name}]'); + } + }); + }); + } + } + + bool isAddedToContainer(DynamicCallSiteTypeInformation info) { + if (info.arguments == null) return false; + var receiverType = info.receiver.type; + if (!receiverType.isContainer) return false; + String selectorName = info.selector.name; + List arguments = info.arguments.positional; + return (selectorName == '[]=' && currentUser == arguments[1]) + || (selectorName == 'insert' && currentUser == arguments[0]) + || (selectorName == 'add' && currentUser == arguments[0]); + } + + bool isValueAddedToMap(DynamicCallSiteTypeInformation info) { + if (info.arguments == null) return false; + var receiverType = info.receiver.type; + if (!receiverType.isMap) return false; + String selectorName = info.selector.name; + List arguments = info.arguments.positional; + return selectorName == '[]=' && currentUser == arguments[1]; + } + + void visitDynamicCallSiteTypeInformation( + DynamicCallSiteTypeInformation info) { + if (isAddedToContainer(info)) { + ContainerTypeMask mask = info.receiver.type; + + if (mask.allocationNode != null) { + ListTypeInformation list = + inferrer.types.allocatedLists[mask.allocationNode]; + listsToAnalyze.add(list); + } else { + // The [ContainerTypeMask] is a union of two containers, and + // we lose track of where these containers have been allocated + // at this point. + bailout('Stored in too many containers'); + } + } else if (isValueAddedToMap(info)) { + MapTypeMask mask = info.receiver.type; + if (mask.allocationNode != null) { + MapTypeInformation map = + inferrer.types.allocatedMaps[mask.allocationNode]; + mapsToAnalyze.add(map); + } else { + // The [MapTypeMask] is a union. See comment for + // [ContainerTypeMask] above. + bailout('Stored in too many maps'); + } + } + + Iterable inferredTargetTypes = info.targets.map((element) { + return inferrer.types.getInferredTypeOf(element); + }); + if (inferredTargetTypes.any((user) => user == currentUser)) { + addNewEscapeInformation(info); + } + } + + bool isParameterOfListAddingMethod(Element element) { + if (!element.isParameter()) return false; + if (element.getEnclosingClass() != compiler.backend.listImplementation) { + return false; + } + Element method = element.enclosingElement; + return (method.name == '[]=') + || (method.name == 'add') + || (method.name == 'insert'); + } + + bool isClosure(Element element) { + if (!element.isFunction()) return false; + Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); + return outermost.declaration != element.declaration; + } + + void visitElementTypeInformation(ElementTypeInformation info) { + Element element = info.element; + if (element.isParameter() + && inferrer.isNativeElement(element.enclosingElement)) { + bailout('Passed to a native method'); + } + if (info.isClosurized()) { + bailout('Returned from a closurized method'); + } + if (isClosure(info.element)) { + bailout('Returned from a closure'); + } + if (compiler.backend.isNeededForReflection(info.element)) { + bailout('Escape in reflection'); + } + if (isParameterOfListAddingMethod(info.element)) { + // These elements are being handled in + // [visitDynamicCallSiteTypeInformation]. + return; + } + addNewEscapeInformation(info); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/simple_types_inferrer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/simple_types_inferrer.dart new file mode 100644 index 0000000..0cae963 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/simple_types_inferrer.dart @@ -0,0 +1,1277 @@ +// Copyright (c) 2013, 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. + +library simple_types_inferrer; + +import '../closure.dart' show ClosureClassMap, ClosureScope; +import '../dart_types.dart' + show DartType, InterfaceType, FunctionType, TypeKind; +import '../elements/elements.dart'; +import '../native_handler.dart' as native; +import '../tree/tree.dart' as ast; +import '../ir/ir_nodes.dart' as ir show Node; +import '../util/util.dart' show Link, Spannable, Setlet; +import '../types/types.dart' + show TypesInferrer, FlatTypeMask, TypeMask, ContainerTypeMask, + ElementTypeMask, ValueTypeMask, TypeSystem, MinimalInferrerEngine; +import 'inferrer_visitor.dart'; + +// BUG(8802): There's a bug in the analyzer that makes the re-export +// of Selector from dart2jslib.dart fail. For now, we work around that +// by importing universe.dart explicitly and disabling the re-export. +import '../dart2jslib.dart' hide Selector, TypedSelector; +import '../universe/universe.dart' show Selector, SideEffects, TypedSelector; + +/** + * An implementation of [TypeSystem] for [TypeMask]. + */ +class TypeMaskSystem implements TypeSystem { + final Compiler compiler; + TypeMaskSystem(this.compiler); + + TypeMask narrowType(TypeMask type, + DartType annotation, + {bool isNullable: true}) { + if (annotation.treatAsDynamic) return type; + if (annotation.element == compiler.objectClass) return type; + TypeMask otherType; + if (annotation.kind == TypeKind.TYPEDEF + || annotation.kind == TypeKind.FUNCTION) { + otherType = functionType; + } else if (annotation.kind == TypeKind.TYPE_VARIABLE) { + // TODO(ngeoffray): Narrow to bound. + return type; + } else if (annotation.isVoid) { + otherType = nullType; + } else { + assert(annotation.kind == TypeKind.INTERFACE); + otherType = new TypeMask.nonNullSubtype(annotation.element); + } + if (isNullable) otherType = otherType.nullable(); + if (type == null) return otherType; + return type.intersection(otherType, compiler); + } + + TypeMask computeLUB(TypeMask firstType, TypeMask secondType) { + if (firstType == null) { + return secondType; + } else if (secondType == dynamicType || firstType == dynamicType) { + return dynamicType; + } else if (firstType == secondType) { + return firstType; + } else { + TypeMask union = firstType.union(secondType, compiler); + // TODO(kasperl): If the union isn't nullable it seems wasteful + // to use dynamic. Fix that. + return union.containsAll(compiler) ? dynamicType : union; + } + } + + TypeMask allocateDiamondPhi(TypeMask firstType, TypeMask secondType) { + return computeLUB(firstType, secondType); + } + + TypeMask get dynamicType => compiler.typesTask.dynamicType; + TypeMask get nullType => compiler.typesTask.nullType; + TypeMask get intType => compiler.typesTask.intType; + TypeMask get uint32Type => compiler.typesTask.uint32Type; + TypeMask get uint31Type => compiler.typesTask.uint31Type; + TypeMask get positiveIntType => compiler.typesTask.positiveIntType; + TypeMask get doubleType => compiler.typesTask.doubleType; + TypeMask get numType => compiler.typesTask.numType; + TypeMask get boolType => compiler.typesTask.boolType; + TypeMask get functionType => compiler.typesTask.functionType; + TypeMask get listType => compiler.typesTask.listType; + TypeMask get constListType => compiler.typesTask.constListType; + TypeMask get fixedListType => compiler.typesTask.fixedListType; + TypeMask get growableListType => compiler.typesTask.growableListType; + TypeMask get mapType => compiler.typesTask.mapType; + TypeMask get constMapType => compiler.typesTask.constMapType; + TypeMask get stringType => compiler.typesTask.stringType; + TypeMask get typeType => compiler.typesTask.typeType; + bool isNull(TypeMask mask) => mask.isEmpty && mask.isNullable; + + TypeMask stringLiteralType(ast.DartString value) => stringType; + + TypeMask nonNullSubtype(ClassElement type) + => new TypeMask.nonNullSubtype(type.declaration); + TypeMask nonNullSubclass(ClassElement type) + => new TypeMask.nonNullSubclass(type.declaration); + TypeMask nonNullExact(ClassElement type) + => new TypeMask.nonNullExact(type.declaration); + TypeMask nonNullEmpty() => new TypeMask.nonNullEmpty(); + + TypeMask allocateList(TypeMask type, + ast.Node node, + Element enclosing, + [TypeMask elementType, int length]) { + return new ContainerTypeMask(type, node, enclosing, elementType, length); + } + + TypeMask allocateMap(TypeMask type, ast.Node node, Element element, + [List keys, List values]) { + return type; + } + + TypeMask allocateClosure(ast.Node node, Element element) { + return functionType; + } + + Selector newTypedSelector(TypeMask receiver, Selector selector) { + return new TypedSelector(receiver, selector); + } + + TypeMask addPhiInput(Element element, TypeMask phiType, TypeMask newType) { + return computeLUB(phiType, newType); + } + + TypeMask allocatePhi(ast.Node node, Element element, TypeMask inputType) { + return inputType; + } + + TypeMask simplifyPhi(ast.Node node, Element element, TypeMask phiType) { + return phiType; + } + + TypeMask refineReceiver(Selector selector, TypeMask receiverType) { + TypeMask newType = compiler.world.allFunctions.receiverType(selector); + return receiverType.intersection(newType, compiler); + } + + TypeMask getConcreteTypeFor(TypeMask mask) => mask; +} + +/** + * Common super class used by [SimpleTypeInferrerVisitor] to propagate + * type information about visited nodes, as well as to request type + * information of elements. + */ +abstract class InferrerEngine + implements MinimalInferrerEngine { + final Compiler compiler; + final V types; + final Map concreteTypes = new Map(); + final Set generativeConstructorsExposingThis = new Set(); + + InferrerEngine(this.compiler, this.types); + + /** + * Records the default type of parameter [parameter]. + */ + void setDefaultTypeOfParameter(Element parameter, T type); + + /** + * Returns the type of [element]. + */ + T typeOfElement(Element element); + + /** + * Returns the return type of [element]. + */ + T returnTypeOfElement(Element element); + + /** + * Records that [node] sets final field [element] to be of type [type]. + * + * [nodeHolder] is the element holder of [node]. + */ + void recordTypeOfFinalField(ast.Node node, + Element nodeHolder, + Element field, + T type); + + /** + * Records that [node] sets non-final field [element] to be of type + * [type]. + */ + void recordTypeOfNonFinalField(Spannable node, Element field, T type); + + /** + * Records that [element] is of type [type]. + */ + void recordType(Element element, T type); + + /** + * Records that the return type [element] is of type [type]. + */ + void recordReturnType(Element element, T type); + + /** + * Registers that [caller] calls [callee] at location [node], with + * [selector], and [arguments]. Note that [selector] is null for + * forwarding constructors. + * + * [sideEffects] will be updated to incorporate [callee]'s side + * effects. + * + * [inLoop] tells whether the call happens in a loop. + */ + T registerCalledElement(Spannable node, + Selector selector, + Element caller, + Element callee, + ArgumentsTypes arguments, + SideEffects sideEffects, + bool inLoop); + + /** + * Registers that [caller] calls [selector] with [receiverType] as + * receiver, and [arguments]. + * + * [sideEffects] will be updated to incorporate the potential + * callees' side effects. + * + * [inLoop] tells whether the call happens in a loop. + */ + T registerCalledSelector(ast.Node node, + Selector selector, + T receiverType, + Element caller, + ArgumentsTypes arguments, + SideEffects sideEffects, + bool inLoop); + + /** + * Registers that [caller] calls [closure] with [arguments]. + * + * [sideEffects] will be updated to incorporate the potential + * callees' side effects. + * + * [inLoop] tells whether the call happens in a loop. + */ + T registerCalledClosure(ast.Node node, + Selector selector, + T closure, + Element caller, + ArgumentsTypes arguments, + SideEffects sideEffects, + bool inLoop); + + /** + * Notifies to the inferrer that [analyzedElement] can have return + * type [newType]. [currentType] is the type the [InferrerVisitor] + * currently found. + * + * Returns the new type for [analyzedElement]. + */ + T addReturnTypeFor(Element analyzedElement, T currentType, T newType); + + /** + * Applies [f] to all elements in the universe that match + * [selector]. If [f] returns false, aborts the iteration. + */ + void forEachElementMatching(Selector selector, bool f(Element element)) { + Iterable elements = compiler.world.allFunctions.filter(selector); + for (Element e in elements) { + if (!f(e.implementation)) return; + } + } + + /** + * Update [sideEffects] with the side effects of [callee] being + * called with [selector]. + */ + void updateSideEffects(SideEffects sideEffects, + Selector selector, + Element callee) { + if (callee.isField()) { + if (callee.isInstanceMember()) { + if (selector.isSetter()) { + sideEffects.setChangesInstanceProperty(); + } else if (selector.isGetter()) { + sideEffects.setDependsOnInstancePropertyStore(); + } else { + sideEffects.setAllSideEffects(); + sideEffects.setDependsOnSomething(); + } + } else { + if (selector.isSetter()) { + sideEffects.setChangesStaticProperty(); + } else if (selector.isGetter()) { + sideEffects.setDependsOnStaticPropertyStore(); + } else { + sideEffects.setAllSideEffects(); + sideEffects.setDependsOnSomething(); + } + } + } else if (callee.isGetter() && !selector.isGetter()) { + sideEffects.setAllSideEffects(); + sideEffects.setDependsOnSomething(); + } else { + sideEffects.add(compiler.world.getSideEffectsOfElement(callee)); + } + } + + /** + * Returns the type for [nativeBehavior]. See documentation on + * [native.NativeBehavior]. + */ + T typeOfNativeBehavior(native.NativeBehavior nativeBehavior) { + if (nativeBehavior == null) return types.dynamicType; + List typesReturned = nativeBehavior.typesReturned; + if (typesReturned.isEmpty) return types.dynamicType; + T returnType; + for (var type in typesReturned) { + T mappedType; + if (type == native.SpecialType.JsObject) { + mappedType = types.nonNullExact(compiler.objectClass); + } else if (type.element == compiler.stringClass) { + mappedType = types.stringType; + } else if (type.element == compiler.intClass) { + mappedType = types.intType; + } else if (type.element == compiler.doubleClass) { + mappedType = types.doubleType; + } else if (type.element == compiler.numClass) { + mappedType = types.numType; + } else if (type.element == compiler.boolClass) { + mappedType = types.boolType; + } else if (type.element == compiler.nullClass) { + mappedType = types.nullType; + } else if (type.isVoid) { + mappedType = types.nullType; + } else if (type.isDynamic) { + return types.dynamicType; + } else if (!compiler.world.hasAnySubtype(type.element)) { + mappedType = types.nonNullExact(type.element); + } else { + ClassElement element = type.element; + Set subtypes = compiler.world.subtypesOf(element); + Set subclasses = compiler.world.subclassesOf(element); + if (subclasses != null && subtypes.length == subclasses.length) { + mappedType = types.nonNullSubclass(element); + } else { + mappedType = types.nonNullSubtype(element); + } + } + returnType = types.computeLUB(returnType, mappedType); + if (returnType == types.dynamicType) { + break; + } + } + return returnType; + } + + void updateSelectorInTree( + Element owner, Spannable node, Selector selector) { + if (node is ir.Node) { + // TODO(lry): update selector for IrInvokeDynamic. + throw "updateSelector for IR node $node"; + } + ast.Node astNode = node; + var elements = compiler.enqueuer.resolution.getCachedElements(owner); + if (astNode.asSendSet() != null) { + if (selector.isSetter() || selector.isIndexSet()) { + elements.setSelector(node, selector); + } else if (selector.isGetter() || selector.isIndex()) { + elements.setGetterSelectorInComplexSendSet(node, selector); + } else { + assert(selector.isOperator()); + elements.setOperatorSelectorInComplexSendSet(node, selector); + } + } else if (astNode.asSend() != null) { + elements.setSelector(node, selector); + } else { + assert(astNode.asForIn() != null); + if (selector.asUntyped == compiler.iteratorSelector) { + elements.setIteratorSelector(node, selector); + } else if (selector.asUntyped == compiler.currentSelector) { + elements.setCurrentSelector(node, selector); + } else { + assert(selector.asUntyped == compiler.moveNextSelector); + elements.setMoveNextSelector(node, selector); + } + } + } + + bool isNativeElement(Element element) { + if (element.isNative()) return true; + return element.isMember() + && element.getEnclosingClass().isNative() + && element.isField(); + } + + void analyze(Element element, ArgumentsTypes arguments); + + bool checkIfExposesThis(Element element) { + element = element.implementation; + return generativeConstructorsExposingThis.contains(element); + } + + void recordExposesThis(Element element, bool exposesThis) { + element = element.implementation; + if (exposesThis) { + generativeConstructorsExposingThis.add(element); + } + } +} + +class SimpleTypeInferrerVisitor + extends InferrerVisitor>> { + T returnType; + bool visitingInitializers = false; + bool isConstructorRedirect = false; + bool seenSuperConstructorCall = false; + SideEffects sideEffects = new SideEffects.empty(); + final Element outermostElement; + final InferrerEngine> inferrer; + final Setlet capturedVariables = new Setlet(); + + SimpleTypeInferrerVisitor.internal(analyzedElement, + this.outermostElement, + inferrer, + compiler, + locals) + : super(analyzedElement, inferrer, inferrer.types, compiler, locals), + this.inferrer = inferrer { + assert(outermostElement != null); + } + + SimpleTypeInferrerVisitor(Element element, + Compiler compiler, + InferrerEngine> inferrer, + [LocalsHandler handler]) + : this.internal(element, + element.getOutermostEnclosingMemberOrTopLevel().implementation, + inferrer, compiler, handler); + + void analyzeSuperConstructorCall(Element target, ArgumentsTypes arguments) { + inferrer.analyze(target, arguments); + isThisExposed = isThisExposed || inferrer.checkIfExposesThis(target); + } + + T run() { + var node = analyzedElement.parseNode(compiler); + ast.Expression initializer; + if (analyzedElement.isField()) { + VariableElement fieldElement = analyzedElement; + initializer = fieldElement.initializer; + if (initializer == null) { + // Eagerly bailout, because computing the closure data only + // works for functions and field assignments. + return types.nullType; + } + } + // Update the locals that are boxed in [locals]. These locals will + // be handled specially, in that we are computing their LUB at + // each update, and reading them yields the type that was found in a + // previous analysis of [outermostElement]. + ClosureClassMap closureData = + compiler.closureToClassMapper.computeClosureToClassMapping( + analyzedElement, node, elements); + closureData.forEachCapturedVariable((variable, field) { + locals.setCaptured(variable, field); + }); + closureData.forEachBoxedVariable((variable, field) { + locals.setCapturedAndBoxed(variable, field); + }); + if (analyzedElement.isField()) { + return visit(initializer); + } + + FunctionElement function = analyzedElement; + FunctionSignature signature = function.functionSignature; + signature.forEachOptionalParameter((element) { + ast.Expression defaultValue = element.initializer; + T type = (defaultValue == null) ? types.nullType : visit(defaultValue); + inferrer.setDefaultTypeOfParameter(element, type); + }); + + if (analyzedElement.isNative()) { + // Native methods do not have a body, and we currently just say + // they return dynamic. + return types.dynamicType; + } + + if (analyzedElement.isGenerativeConstructor()) { + isThisExposed = false; + signature.forEachParameter((element) { + T parameterType = inferrer.typeOfElement(element); + if (element.kind == ElementKind.FIELD_PARAMETER) { + if (element.fieldElement.modifiers.isFinal()) { + inferrer.recordTypeOfFinalField( + node, + analyzedElement, + element.fieldElement, + parameterType); + } else { + locals.updateField(element.fieldElement, parameterType); + inferrer.recordTypeOfNonFinalField( + element.parseNode(compiler), + element.fieldElement, + parameterType); + } + } + locals.update(element, parameterType, node); + }); + ClassElement cls = analyzedElement.getEnclosingClass(); + if (analyzedElement.isSynthesized) { + node = analyzedElement; + synthesizeForwardingCall(node, analyzedElement.targetConstructor); + } else { + visitingInitializers = true; + visit(node.initializers); + visitingInitializers = false; + // For a generative constructor like: `Foo();`, we synthesize + // a call to the default super constructor (the one that takes + // no argument). Resolution ensures that such a constructor + // exists. + if (!isConstructorRedirect + && !seenSuperConstructorCall + && !cls.isObject(compiler)) { + Selector selector = + new Selector.callDefaultConstructor(analyzedElement.getLibrary()); + FunctionElement target = cls.superclass.lookupConstructor(selector); + analyzeSuperConstructorCall(target, new ArgumentsTypes([], {})); + synthesizeForwardingCall(analyzedElement, target); + } + visit(node.body); + inferrer.recordExposesThis(analyzedElement, isThisExposed); + } + if (!isConstructorRedirect) { + // Iterate over all instance fields, and give a null type to + // fields that we haven't initialized for sure. + cls.forEachInstanceField((_, field) { + if (field.modifiers.isFinal()) return; + T type = locals.fieldScope.readField(field); + if (type == null && field.initializer == null) { + inferrer.recordTypeOfNonFinalField(node, field, types.nullType); + } + }); + } + returnType = types.nonNullExact(cls); + } else { + signature.forEachParameter((element) { + locals.update(element, inferrer.typeOfElement(element), node); + }); + visit(node.body); + if (returnType == null) { + // No return in the body. + returnType = locals.seenReturnOrThrow + ? types.nonNullEmpty() // Body always throws. + : types.nullType; + } else if (!locals.seenReturnOrThrow) { + // We haven't seen returns on all branches. So the method may + // also return null. + returnType = inferrer.addReturnTypeFor( + analyzedElement, returnType, types.nullType); + } + } + + compiler.world.registerSideEffects(analyzedElement, sideEffects); + assert(breaksFor.isEmpty); + assert(continuesFor.isEmpty); + return returnType; + } + + T visitFunctionExpression(ast.FunctionExpression node) { + Element element = elements[node]; + // We don't put the closure in the work queue of the + // inferrer, because it will share information with its enclosing + // method, like for example the types of local variables. + LocalsHandler closureLocals = new LocalsHandler.from( + locals, node, useOtherTryBlock: false); + SimpleTypeInferrerVisitor visitor = new SimpleTypeInferrerVisitor( + element, compiler, inferrer, closureLocals); + visitor.run(); + inferrer.recordReturnType(element, visitor.returnType); + + // Record the types of captured non-boxed variables. Types of + // these variables may already be there, because of an analysis of + // a previous closure. + ClosureClassMap nestedClosureData = + compiler.closureToClassMapper.getMappingForNestedFunction(node); + nestedClosureData.forEachCapturedVariable((variable, field) { + if (!nestedClosureData.isVariableBoxed(variable)) { + if (variable == nestedClosureData.thisElement) { + inferrer.recordType(field, thisType); + } + // The type is null for type parameters. + if (locals.locals[variable] == null) return; + inferrer.recordType(field, locals.locals[variable]); + } + capturedVariables.add(variable); + }); + + return inferrer.concreteTypes.putIfAbsent(node, () { + return types.allocateClosure(node, element); + }); + } + + T visitFunctionDeclaration(ast.FunctionDeclaration node) { + Element element = elements[node]; + T type = inferrer.concreteTypes.putIfAbsent(node.function, () { + return types.allocateClosure(node.function, element); + }); + locals.update(element, type, node); + visit(node.function); + return type; + } + + T visitStringInterpolation(ast.StringInterpolation node) { + // Interpolation could have any effects since it could call any toString() + // method. + // TODO(sra): This could be modelled by a call to toString() but with a + // guaranteed String return type. Interpolation of known types would get + // specialized effects. This would not currently be effective since the JS + // code in the toString methods for intercepted primitive types is assumed + // to have all effects. Effect annotations on JS code would be needed to + // get the benefit. + sideEffects.setAllSideEffects(); + return super.visitStringInterpolation(node); + } + + T visitLiteralList(ast.LiteralList node) { + // We only set the type once. We don't need to re-visit the children + // when re-analyzing the node. + return inferrer.concreteTypes.putIfAbsent(node, () { + T elementType; + int length = 0; + for (ast.Node element in node.elements.nodes) { + T type = visit(element); + elementType = elementType == null + ? types.allocatePhi(null, null, type) + : types.addPhiInput(null, elementType, type); + length++; + } + elementType = elementType == null + ? types.nonNullEmpty() + : types.simplifyPhi(null, null, elementType); + T containerType = node.isConst() + ? types.constListType + : types.growableListType; + return types.allocateList( + containerType, + node, + outermostElement, + elementType, + length); + }); + } + + T visitLiteralMap(ast.LiteralMap node) { + return inferrer.concreteTypes.putIfAbsent(node, () { + ast.NodeList entries = node.entries; + List keyTypes = []; + List valueTypes = []; + + for (ast.LiteralMapEntry entry in entries) { + keyTypes.add(visit(entry.key)); + valueTypes.add(visit(entry.value)); + } + + T type = node.isConst() ? types.constMapType : types.mapType; + return types.allocateMap(type, + node, + outermostElement, + keyTypes, + valueTypes); + }); + } + + bool isThisOrSuper(ast.Node node) => node.isThis() || node.isSuper(); + + bool isInClassOrSubclass(Element element) { + ClassElement cls = outermostElement.getEnclosingClass(); + ClassElement enclosing = element.getEnclosingClass(); + return (enclosing == cls) || compiler.world.isSubclass(cls, enclosing); + } + + void checkIfExposesThis(Selector selector) { + if (isThisExposed) return; + inferrer.forEachElementMatching(selector, (element) { + if (element.isField()) { + if (!selector.isSetter() + && isInClassOrSubclass(element) + && !element.modifiers.isFinal() + && locals.fieldScope.readField(element) == null + && element.initializer == null) { + // If the field is being used before this constructor + // actually had a chance to initialize it, say it can be + // null. + inferrer.recordTypeOfNonFinalField( + analyzedElement.parseNode(compiler), element, + types.nullType); + } + // Accessing a field does not expose [:this:]. + return true; + } + // TODO(ngeoffray): We could do better here if we knew what we + // are calling does not expose this. + isThisExposed = true; + return false; + }); + } + + bool get inInstanceContext { + return (outermostElement.isInstanceMember() && !outermostElement.isField()) + || outermostElement.isGenerativeConstructor(); + } + + bool treatAsInstanceMember(Element element) { + return (Elements.isUnresolved(element) && inInstanceContext) + || (element != null && element.isInstanceMember()); + } + + T visitSendSet(ast.SendSet node) { + Element element = elements[node]; + if (!Elements.isUnresolved(element) && element.impliesType()) { + node.visitChildren(this); + return types.dynamicType; + } + + Selector getterSelector = + elements.getGetterSelectorInComplexSendSet(node); + Selector operatorSelector = + elements.getOperatorSelectorInComplexSendSet(node); + Selector setterSelector = elements.getSelector(node); + + String op = node.assignmentOperator.source; + bool isIncrementOrDecrement = op == '++' || op == '--'; + + T receiverType; + bool isCallOnThis = false; + if (node.receiver == null) { + if (treatAsInstanceMember(element)) { + receiverType = thisType; + isCallOnThis = true; + } + } else { + receiverType = visit(node.receiver); + isCallOnThis = isThisOrSuper(node.receiver); + } + + T rhsType; + T indexType; + + if (isIncrementOrDecrement) { + rhsType = types.uint31Type; + if (node.isIndex) indexType = visit(node.arguments.head); + } else if (node.isIndex) { + indexType = visit(node.arguments.head); + rhsType = visit(node.arguments.tail.head); + } else { + rhsType = visit(node.arguments.head); + } + + if (!visitingInitializers && !isThisExposed) { + for (ast.Node node in node.arguments) { + if (isThisOrSuper(node)) { + isThisExposed = true; + break; + } + } + if (!isThisExposed && isCallOnThis) { + checkIfExposesThis( + types.newTypedSelector(receiverType, setterSelector)); + if (getterSelector != null) { + checkIfExposesThis( + types.newTypedSelector(receiverType, getterSelector)); + } + } + } + + if (node.isIndex) { + if (op == '=') { + // [: foo[0] = 42 :] + handleDynamicSend( + node, + setterSelector, + receiverType, + new ArgumentsTypes([indexType, rhsType], null)); + return rhsType; + } else { + // [: foo[0] += 42 :] or [: foo[0]++ :]. + T getterType = handleDynamicSend( + node, + getterSelector, + receiverType, + new ArgumentsTypes([indexType], null)); + T returnType = handleDynamicSend( + node, + operatorSelector, + getterType, + new ArgumentsTypes([rhsType], null)); + handleDynamicSend( + node, + setterSelector, + receiverType, + new ArgumentsTypes([indexType, returnType], null)); + + if (node.isPostfix) { + return getterType; + } else { + return returnType; + } + } + } else if (op == '=') { + return handlePlainAssignment( + node, element, setterSelector, receiverType, rhsType, + node.arguments.head); + } else { + // [: foo++ :] or [: foo += 1 :]. + ArgumentsTypes operatorArguments = new ArgumentsTypes([rhsType], null); + T getterType; + T newType; + if (Elements.isErroneousElement(element)) { + getterType = types.dynamicType; + newType = types.dynamicType; + } else if (Elements.isStaticOrTopLevelField(element)) { + Element getterElement = elements[node.selector]; + getterType = + handleStaticSend(node, getterSelector, getterElement, null); + newType = handleDynamicSend( + node, operatorSelector, getterType, operatorArguments); + handleStaticSend( + node, setterSelector, element, + new ArgumentsTypes([newType], null)); + } else if (Elements.isUnresolved(element) + || element.isSetter() + || element.isField()) { + getterType = handleDynamicSend( + node, getterSelector, receiverType, null); + newType = handleDynamicSend( + node, operatorSelector, getterType, operatorArguments); + handleDynamicSend(node, setterSelector, receiverType, + new ArgumentsTypes([newType], null)); + } else if (Elements.isLocal(element)) { + getterType = locals.use(element); + newType = handleDynamicSend( + node, operatorSelector, getterType, operatorArguments); + locals.update(element, newType, node); + } else { + // Bogus SendSet, for example [: myMethod += 42 :]. + getterType = types.dynamicType; + newType = handleDynamicSend( + node, operatorSelector, getterType, operatorArguments); + } + + if (node.isPostfix) { + return getterType; + } else { + return newType; + } + } + } + + T handlePlainAssignment(ast.Node node, + Element element, + Selector setterSelector, + T receiverType, + T rhsType, + ast.Node rhs) { + ArgumentsTypes arguments = new ArgumentsTypes([rhsType], null); + if (Elements.isErroneousElement(element)) { + // Code will always throw. + } else if (Elements.isStaticOrTopLevelField(element)) { + handleStaticSend(node, setterSelector, element, arguments); + } else if (Elements.isUnresolved(element) || element.isSetter()) { + if (analyzedElement.isGenerativeConstructor() + && (node.asSendSet() != null) + && (node.asSendSet().receiver != null) + && node.asSendSet().receiver.isThis()) { + Iterable targets = compiler.world.allFunctions.filter( + types.newTypedSelector(thisType, setterSelector)); + // We just recognized a field initialization of the form: + // `this.foo = 42`. If there is only one target, we can update + // its type. + if (targets.length == 1) { + Element single = targets.first; + if (single.isField()) { + locals.updateField(single, rhsType); + } + } + } + handleDynamicSend( + node, setterSelector, receiverType, arguments); + } else if (element.isField()) { + if (element.modifiers.isFinal()) { + inferrer.recordTypeOfFinalField( + node, outermostElement, element, rhsType); + } else { + if (analyzedElement.isGenerativeConstructor()) { + locals.updateField(element, rhsType); + } + if (visitingInitializers) { + inferrer.recordTypeOfNonFinalField(node, element, rhsType); + } else { + handleDynamicSend( + node, setterSelector, receiverType, arguments); + } + } + } else if (Elements.isLocal(element)) { + locals.update(element, rhsType, node); + } + return rhsType; + } + + T visitSuperSend(ast.Send node) { + Element element = elements[node]; + ArgumentsTypes arguments = node.isPropertyAccess + ? null + : analyzeArguments(node.arguments); + if (visitingInitializers) { + seenSuperConstructorCall = true; + analyzeSuperConstructorCall(element, arguments); + } + Selector selector = elements.getSelector(node); + // TODO(ngeoffray): We could do better here if we knew what we + // are calling does not expose this. + isThisExposed = true; + if (Elements.isUnresolved(element) + || !selector.applies(element, compiler)) { + // Ensure we create a node, to make explicit the call to the + // `noSuchMethod` handler. + return handleDynamicSend(node, selector, superType, arguments); + } else if (node.isPropertyAccess + || element.isFunction() + || element.isGenerativeConstructor()) { + return handleStaticSend(node, selector, element, arguments); + } else { + return inferrer.registerCalledClosure( + node, selector, inferrer.typeOfElement(element), + outermostElement, arguments, sideEffects, inLoop); + } + } + + // Try to find the length given to a fixed array constructor call. + int findLength(ast.Send node) { + ast.Node firstArgument = node.arguments.head; + Element element = elements[firstArgument]; + ast.LiteralInt length = firstArgument.asLiteralInt(); + if (length != null) { + return length.value; + } else if (element != null + && element.isField() + && Elements.isStaticOrTopLevelField(element) + && compiler.world.fieldNeverChanges(element)) { + var constant = + compiler.constantHandler.getConstantForVariable(element); + if (constant != null && constant.isInt) { + return constant.value; + } + } + return null; + } + + T visitStaticSend(ast.Send node) { + Element element = elements[node]; + ArgumentsTypes arguments = analyzeArguments(node.arguments); + if (visitingInitializers) { + if (ast.Initializers.isConstructorRedirect(node)) { + isConstructorRedirect = true; + } else if (ast.Initializers.isSuperConstructorCall(node)) { + seenSuperConstructorCall = true; + analyzeSuperConstructorCall(element, arguments); + } + } + if (element.isForeign(compiler)) { + return handleForeignSend(node); + } + Selector selector = elements.getSelector(node); + // In erroneous code the number of arguments in the selector might not + // match the function element. + // TODO(polux): return nonNullEmpty and check it doesn't break anything + if (!selector.applies(element, compiler)) return types.dynamicType; + + T returnType = handleStaticSend(node, selector, element, arguments); + if (Elements.isGrowableListConstructorCall(element, node, compiler)) { + return inferrer.concreteTypes.putIfAbsent( + node, () => types.allocateList( + types.growableListType, node, outermostElement, + types.nonNullEmpty(), 0)); + } else if (Elements.isFixedListConstructorCall(element, node, compiler) + || Elements.isFilledListConstructorCall(element, node, compiler)) { + + int length = findLength(node); + T elementType = + Elements.isFixedListConstructorCall(element, node, compiler) + ? types.nullType + : arguments.positional[1]; + + return inferrer.concreteTypes.putIfAbsent( + node, () => types.allocateList( + types.fixedListType, node, outermostElement, + elementType, length)); + } else if (Elements.isConstructorOfTypedArraySubclass(element, compiler)) { + int length = findLength(node); + FunctionElement constructor = element; + constructor = constructor.redirectionTarget; + T elementType = inferrer.returnTypeOfElement( + constructor.getEnclosingClass().lookupMember('[]')); + return inferrer.concreteTypes.putIfAbsent( + node, () => types.allocateList( + types.nonNullExact(constructor.getEnclosingClass()), node, + outermostElement, elementType, length)); + } else if (element.isFunction() || element.isConstructor()) { + return returnType; + } else { + assert(element.isField() || element.isGetter()); + return inferrer.registerCalledClosure( + node, selector, inferrer.typeOfElement(element), + outermostElement, arguments, sideEffects, inLoop); + } + } + + T handleForeignSend(ast.Send node) { + ArgumentsTypes arguments = analyzeArguments(node.arguments); + Selector selector = elements.getSelector(node); + String name = selector.name; + handleStaticSend(node, selector, elements[node], arguments); + if (name == 'JS') { + native.NativeBehavior nativeBehavior = + compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node); + sideEffects.add(nativeBehavior.sideEffects); + return inferrer.typeOfNativeBehavior(nativeBehavior); + } else if (name == 'JS_GET_NAME' + || name == 'JS_NULL_CLASS_NAME' + || name == 'JS_OBJECT_CLASS_NAME' + || name == 'JS_OPERATOR_IS_PREFIX' + || name == 'JS_OPERATOR_AS_PREFIX') { + return types.stringType; + } else { + sideEffects.setAllSideEffects(); + return types.dynamicType; + } + } + + ArgumentsTypes analyzeArguments(Link arguments) { + List positional = []; + Map named; + for (var argument in arguments) { + ast.NamedArgument namedArgument = argument.asNamedArgument(); + if (namedArgument != null) { + argument = namedArgument.expression; + if (named == null) named = new Map(); + named[namedArgument.name.source] = argument.accept(this); + } else { + positional.add(argument.accept(this)); + } + // TODO(ngeoffray): We could do better here if we knew what we + // are calling does not expose this. + isThisExposed = isThisExposed || argument.isThis(); + } + return new ArgumentsTypes(positional, named); + } + + T visitGetterSend(ast.Send node) { + Element element = elements[node]; + Selector selector = elements.getSelector(node); + if (Elements.isStaticOrTopLevelField(element)) { + return handleStaticSend(node, selector, element, null); + } else if (Elements.isInstanceSend(node, elements)) { + return visitDynamicSend(node); + } else if (Elements.isStaticOrTopLevelFunction(element)) { + return handleStaticSend(node, selector, element, null); + } else if (Elements.isErroneousElement(element)) { + return types.dynamicType; + } else if (Elements.isLocal(element)) { + assert(locals.use(element) != null); + return locals.use(element); + } else { + assert(element is PrefixElement); + return null; + } + } + + T visitClosureSend(ast.Send node) { + assert(node.receiver == null); + T closure = node.selector.accept(this); + ArgumentsTypes arguments = analyzeArguments(node.arguments); + Element element = elements[node]; + Selector selector = elements.getSelector(node); + if (element != null && element.isFunction()) { + assert(Elements.isLocal(element)); + // This only works for function statements. We need a + // more sophisticated type system with function types to support + // more. + return inferrer.registerCalledElement( + node, selector, outermostElement, element, arguments, + sideEffects, inLoop); + } else { + return inferrer.registerCalledClosure( + node, selector, closure, outermostElement, arguments, + sideEffects, inLoop); + } + } + + T handleStaticSend(ast.Node node, + Selector selector, + Element element, + ArgumentsTypes arguments) { + // Erroneous elements may be unresolved, for example missing getters. + if (Elements.isUnresolved(element)) return types.dynamicType; + return inferrer.registerCalledElement( + node, selector, outermostElement, element, arguments, + sideEffects, inLoop); + } + + T handleDynamicSend(ast.Node node, + Selector selector, + T receiverType, + ArgumentsTypes arguments) { + assert(receiverType != null); + if (selector.mask != receiverType) { + selector = (receiverType == types.dynamicType) + ? selector.asUntyped + : types.newTypedSelector(receiverType, selector); + inferrer.updateSelectorInTree(analyzedElement, node, selector); + } + + // If the receiver of the call is a local, we may know more about + // its type by refining it with the potential targets of the + // calls. + if (node.asSend() != null) { + ast.Node receiver = node.asSend().receiver; + if (receiver != null) { + Element element = elements[receiver]; + if (Elements.isLocal(element) && !capturedVariables.contains(element)) { + T refinedType = types.refineReceiver(selector, receiverType); + locals.update(element, refinedType, node); + } + } + } + + return inferrer.registerCalledSelector( + node, selector, receiverType, outermostElement, arguments, + sideEffects, inLoop); + } + + T visitDynamicSend(ast.Send node) { + Element element = elements[node]; + T receiverType; + bool isCallOnThis = false; + if (node.receiver == null) { + if (treatAsInstanceMember(element)) { + isCallOnThis = true; + receiverType = thisType; + } + } else { + ast.Node receiver = node.receiver; + isCallOnThis = isThisOrSuper(receiver); + receiverType = visit(receiver); + } + + Selector selector = elements.getSelector(node); + if (!isThisExposed && isCallOnThis) { + checkIfExposesThis(types.newTypedSelector(receiverType, selector)); + } + + ArgumentsTypes arguments = node.isPropertyAccess + ? null + : analyzeArguments(node.arguments); + if (selector.name == '==' + || selector.name == '!=') { + if (types.isNull(receiverType)) { + potentiallyAddNullCheck(node, node.arguments.head); + return types.boolType; + } else if (types.isNull(arguments.positional[0])) { + potentiallyAddNullCheck(node, node.receiver); + return types.boolType; + } + } + return handleDynamicSend(node, selector, receiverType, arguments); + } + + void recordReturnType(T type) { + returnType = inferrer.addReturnTypeFor(analyzedElement, returnType, type); + } + + T synthesizeForwardingCall(Spannable node, FunctionElement element) { + element = element.implementation; + FunctionElement function = analyzedElement; + FunctionSignature signature = function.functionSignature; + FunctionSignature calleeSignature = element.functionSignature; + if (!calleeSignature.isCompatibleWith(signature)) { + return types.nonNullEmpty(); + } + + List unnamed = []; + signature.forEachRequiredParameter((Element element) { + assert(locals.use(element) != null); + unnamed.add(locals.use(element)); + }); + + Map named; + if (signature.optionalParametersAreNamed) { + named = new Map(); + signature.forEachOptionalParameter((Element element) { + named[element.name] = locals.use(element); + }); + } else { + signature.forEachOptionalParameter((Element element) { + unnamed.add(locals.use(element)); + }); + } + + ArgumentsTypes arguments = new ArgumentsTypes(unnamed, named); + return inferrer.registerCalledElement(node, + null, + outermostElement, + element, + arguments, + sideEffects, + inLoop); + } + + T visitReturn(ast.Return node) { + if (node.isRedirectingFactoryBody) { + Element element = elements[node.expression]; + if (Elements.isErroneousElement(element)) { + recordReturnType(types.dynamicType); + } else { + // We don't create a selector for redirecting factories, and + // the send is just a property access. Therefore we must + // manually create the [ArgumentsTypes] of the call, and + // manually register [analyzedElement] as a caller of [element]. + T mask = synthesizeForwardingCall(node.expression, element); + recordReturnType(mask); + } + } else { + ast.Node expression = node.expression; + recordReturnType(expression == null + ? types.nullType + : expression.accept(this)); + } + locals.seenReturnOrThrow = true; + return null; + } + + T visitForIn(ast.ForIn node) { + T expressionType = visit(node.expression); + Selector iteratorSelector = elements.getIteratorSelector(node); + Selector currentSelector = elements.getCurrentSelector(node); + Selector moveNextSelector = elements.getMoveNextSelector(node); + + T iteratorType = + handleDynamicSend(node, iteratorSelector, expressionType, null); + handleDynamicSend(node, moveNextSelector, + iteratorType, new ArgumentsTypes([], null)); + T currentType = + handleDynamicSend(node, currentSelector, iteratorType, null); + + if (node.expression.isThis()) { + // Any reasonable implementation of an iterator would expose + // this, so we play it safe and assume it will. + isThisExposed = true; + } + + ast.Node identifier = node.declaredIdentifier; + Element element = elements[identifier]; + Selector selector = elements.getSelector(identifier); + + T receiverType; + if (element != null && element.isInstanceMember()) { + receiverType = thisType; + } else { + receiverType = types.dynamicType; + } + + handlePlainAssignment(identifier, element, selector, + receiverType, currentType, + node.expression); + return handleLoop(node, () { + visit(node.body); + }); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/type_graph_inferrer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/type_graph_inferrer.dart new file mode 100644 index 0000000..cfe1661 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/type_graph_inferrer.dart @@ -0,0 +1,1116 @@ +// Copyright (c) 2013, 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. + +library type_graph_inferrer; + +import 'dart:collection' show Queue, IterableBase; +import '../dart_types.dart' show DartType, InterfaceType, TypeKind; +import '../elements/elements.dart'; +import '../tree/tree.dart' as ast show DartString, Node; +import '../ir/ir_nodes.dart' as ir show Node; +import '../types/types.dart' + show TypeMask, ContainerTypeMask, MapTypeMask, DictionaryTypeMask, + ValueTypeMask, TypesInferrer; +import '../universe/universe.dart' show Selector, TypedSelector, SideEffects; +import '../dart2jslib.dart' show Compiler, TreeElementMapping; +import 'inferrer_visitor.dart' show TypeSystem, ArgumentsTypes; +import '../native_handler.dart' as native; +import '../util/util.dart' show Spannable, Setlet; +import 'simple_types_inferrer.dart'; +import '../dart2jslib.dart' show invariant, Constant, FunctionConstant; + +part 'type_graph_nodes.dart'; +part 'closure_tracer.dart'; +part 'list_tracer.dart'; +part 'node_tracer.dart'; +part 'map_tracer.dart'; + +bool _VERBOSE = false; + +/** + * A set of selector names that [List] implements, that we know return + * their element type. + */ +Set returnsListElementTypeSet = new Set.from( + [ + new Selector.getter('first', null), + new Selector.getter('last', null), + new Selector.getter('single', null), + new Selector.call('singleWhere', null, 1), + new Selector.call('elementAt', null, 1), + new Selector.index(), + new Selector.call('removeAt', null, 1), + new Selector.call('removeLast', null, 0) + ]); + +bool returnsListElementType(Selector selector) { + return (selector.mask != null) && + selector.mask.isContainer && + returnsListElementTypeSet.contains(selector.asUntyped); +} + +bool returnsMapValueType(Selector selector) { + return (selector.mask != null) && + selector.mask.isMap && + selector.isIndex(); +} + +class TypeInformationSystem extends TypeSystem { + final Compiler compiler; + + /// [ElementTypeInformation]s for elements. + final Map typeInformations = + new Map(); + + /// [ListTypeInformation] for allocated lists. + final Map allocatedLists = + new Map(); + + /// [MapTypeInformation] for allocated Maps. + final Map allocatedMaps = + new Map(); + + /// Closures found during the analysis. + final Set allocatedClosures = new Set(); + + /// Cache of [ConcreteTypeInformation]. + final Map concreteTypes = + new Map(); + + /// List of [TypeInformation]s allocated inside method bodies (calls, + /// narrowing, phis, and containers). + final List allocatedTypes = []; + + TypeInformationSystem(this.compiler) { + nonNullEmptyType = getConcreteTypeFor(const TypeMask.nonNullEmpty()); + } + + TypeInformation nullTypeCache; + TypeInformation get nullType { + if (nullTypeCache != null) return nullTypeCache; + return nullTypeCache = getConcreteTypeFor(compiler.typesTask.nullType); + } + + TypeInformation intTypeCache; + TypeInformation get intType { + if (intTypeCache != null) return intTypeCache; + return intTypeCache = getConcreteTypeFor(compiler.typesTask.intType); + } + + TypeInformation uint32TypeCache; + TypeInformation get uint32Type { + if (uint32TypeCache != null) return uint32TypeCache; + return uint32TypeCache = getConcreteTypeFor(compiler.typesTask.uint32Type); + } + + TypeInformation uint31TypeCache; + TypeInformation get uint31Type { + if (uint31TypeCache != null) return uint31TypeCache; + return uint31TypeCache = getConcreteTypeFor(compiler.typesTask.uint31Type); + } + + TypeInformation positiveIntTypeCache; + TypeInformation get positiveIntType { + if (positiveIntTypeCache != null) return positiveIntTypeCache; + return positiveIntTypeCache = + getConcreteTypeFor(compiler.typesTask.positiveIntType); + } + + TypeInformation doubleTypeCache; + TypeInformation get doubleType { + if (doubleTypeCache != null) return doubleTypeCache; + return doubleTypeCache = getConcreteTypeFor(compiler.typesTask.doubleType); + } + + TypeInformation numTypeCache; + TypeInformation get numType { + if (numTypeCache != null) return numTypeCache; + return numTypeCache = getConcreteTypeFor(compiler.typesTask.numType); + } + + TypeInformation boolTypeCache; + TypeInformation get boolType { + if (boolTypeCache != null) return boolTypeCache; + return boolTypeCache = getConcreteTypeFor(compiler.typesTask.boolType); + } + + TypeInformation functionTypeCache; + TypeInformation get functionType { + if (functionTypeCache != null) return functionTypeCache; + return functionTypeCache = + getConcreteTypeFor(compiler.typesTask.functionType); + } + + TypeInformation listTypeCache; + TypeInformation get listType { + if (listTypeCache != null) return listTypeCache; + return listTypeCache = getConcreteTypeFor(compiler.typesTask.listType); + } + + TypeInformation constListTypeCache; + TypeInformation get constListType { + if (constListTypeCache != null) return constListTypeCache; + return constListTypeCache = + getConcreteTypeFor(compiler.typesTask.constListType); + } + + TypeInformation fixedListTypeCache; + TypeInformation get fixedListType { + if (fixedListTypeCache != null) return fixedListTypeCache; + return fixedListTypeCache = + getConcreteTypeFor(compiler.typesTask.fixedListType); + } + + TypeInformation growableListTypeCache; + TypeInformation get growableListType { + if (growableListTypeCache != null) return growableListTypeCache; + return growableListTypeCache = + getConcreteTypeFor(compiler.typesTask.growableListType); + } + + TypeInformation mapTypeCache; + TypeInformation get mapType { + if (mapTypeCache != null) return mapTypeCache; + return mapTypeCache = getConcreteTypeFor(compiler.typesTask.mapType); + } + + TypeInformation constMapTypeCache; + TypeInformation get constMapType { + if (constMapTypeCache != null) return constMapTypeCache; + return constMapTypeCache = + getConcreteTypeFor(compiler.typesTask.constMapType); + } + + TypeInformation stringTypeCache; + TypeInformation get stringType { + if (stringTypeCache != null) return stringTypeCache; + return stringTypeCache = getConcreteTypeFor(compiler.typesTask.stringType); + } + + TypeInformation typeTypeCache; + TypeInformation get typeType { + if (typeTypeCache != null) return typeTypeCache; + return typeTypeCache = getConcreteTypeFor(compiler.typesTask.typeType); + } + + TypeInformation dynamicTypeCache; + TypeInformation get dynamicType { + if (dynamicTypeCache != null) return dynamicTypeCache; + return dynamicTypeCache = + getConcreteTypeFor(compiler.typesTask.dynamicType); + } + + TypeInformation nonNullEmptyType; + + + TypeInformation stringLiteralType(ast.DartString value) { + return new StringLiteralTypeInformation( + value, compiler.typesTask.stringType); + } + + TypeInformation computeLUB(TypeInformation firstType, + TypeInformation secondType) { + if (firstType == null) return secondType; + if (firstType == secondType) return firstType; + if (firstType == nonNullEmptyType) return secondType; + if (secondType == nonNullEmptyType) return firstType; + if (firstType == dynamicType || secondType == dynamicType) { + return dynamicType; + } + return getConcreteTypeFor( + firstType.type.union(secondType.type, compiler)); + } + + TypeInformation refineReceiver(Selector selector, TypeInformation receiver) { + if (receiver.type.isExact) return receiver; + TypeMask otherType = compiler.world.allFunctions.receiverType(selector); + // If this is refining to nullable subtype of `Object` just return + // the receiver. We know the narrowing is useless. + if (otherType.isNullable && otherType.containsAll(compiler)) { + return receiver; + } + TypeInformation newType = new NarrowTypeInformation(receiver, otherType); + allocatedTypes.add(newType); + return newType; + } + + TypeInformation narrowType(TypeInformation type, + DartType annotation, + {bool isNullable: true}) { + if (annotation.treatAsDynamic) return type; + if (annotation.isVoid) return nullType; + if (annotation.element == compiler.objectClass) return type; + TypeMask otherType; + if (annotation.kind == TypeKind.TYPEDEF || + annotation.kind == TypeKind.FUNCTION) { + otherType = functionType.type; + } else if (annotation.kind == TypeKind.TYPE_VARIABLE) { + // TODO(ngeoffray): Narrow to bound. + return type; + } else { + assert(annotation.kind == TypeKind.INTERFACE); + otherType = new TypeMask.nonNullSubtype(annotation.element); + } + if (isNullable) otherType = otherType.nullable(); + if (type.type.isExact) { + return type; + } else { + TypeInformation newType = new NarrowTypeInformation(type, otherType); + allocatedTypes.add(newType); + return newType; + } + } + + ElementTypeInformation getInferredTypeOf(Element element) { + element = element.implementation; + return typeInformations.putIfAbsent(element, () { + return new ElementTypeInformation(element); + }); + } + + ConcreteTypeInformation getConcreteTypeFor(TypeMask mask) { + assert(mask != null); + return concreteTypes.putIfAbsent(mask, () { + return new ConcreteTypeInformation(mask); + }); + } + + TypeInformation nonNullSubtype(ClassElement type) { + return getConcreteTypeFor(new TypeMask.nonNullSubtype(type.declaration)); + } + + TypeInformation nonNullSubclass(ClassElement type) { + return getConcreteTypeFor(new TypeMask.nonNullSubclass(type.declaration)); + } + + TypeInformation nonNullExact(ClassElement type) { + return getConcreteTypeFor(new TypeMask.nonNullExact(type.declaration)); + } + + TypeInformation nonNullEmpty() { + return nonNullEmptyType; + } + + bool isNull(TypeInformation type) { + return type == nullType; + } + + TypeInformation allocateList(TypeInformation type, + ast.Node node, + Element enclosing, + [TypeInformation elementType, int length]) { + bool isTypedArray = (compiler.typedDataClass != null) && + type.type.satisfies(compiler.typedDataClass, compiler); + bool isConst = (type.type == compiler.typesTask.constListType); + bool isFixed = (type.type == compiler.typesTask.fixedListType) || + isConst || + isTypedArray; + bool isElementInferred = isConst || isTypedArray; + + int inferredLength = isFixed ? length : null; + TypeMask elementTypeMask = isElementInferred + ? elementType.type + : dynamicType.type; + ContainerTypeMask mask = new ContainerTypeMask( + type.type, node, enclosing, elementTypeMask, inferredLength); + ElementInContainerTypeInformation element = + new ElementInContainerTypeInformation(elementType); + element.inferred = isElementInferred; + + allocatedTypes.add(element); + return allocatedLists[node] = + new ListTypeInformation(mask, element, length); + } + + TypeInformation allocateClosure(ast.Node node, Element element) { + TypeInformation result = new ClosureTypeInformation(node, element); + allocatedClosures.add(result); + return result; + } + + TypeInformation allocateMap(ConcreteTypeInformation type, + ast.Node node, + Element element, + [List keyTypes, + List valueTypes]) { + assert(keyTypes.length == valueTypes.length); + bool isFixed = (type.type == compiler.typesTask.constMapType); + + TypeMask keyType, valueType; + if (isFixed) { + keyType = keyTypes.fold(nonNullEmptyType.type, + (type, info) => type.union(info.type, compiler)); + valueType = valueTypes.fold(nonNullEmptyType.type, + (type, info) => type.union(info.type, compiler)); + } else { + keyType = valueType = dynamicType.type; + } + MapTypeMask mask = new MapTypeMask(type.type, + node, + element, + keyType, + valueType); + + TypeInformation keyTypeInfo = new KeyInMapTypeInformation(null); + TypeInformation valueTypeInfo = new ValueInMapTypeInformation(null); + allocatedTypes.add(keyTypeInfo); + allocatedTypes.add(valueTypeInfo); + + MapTypeInformation map = + new MapTypeInformation(mask, keyTypeInfo, valueTypeInfo); + + for (int i = 0; i < keyTypes.length; ++i) { + TypeInformation newType = + map.addEntryAssignment(keyTypes[i], valueTypes[i], true); + if (newType != null) allocatedTypes.add(newType); + } + + if (isFixed) map.markAsInferred(); + + allocatedMaps[node] = map; + return map; + } + + Selector newTypedSelector(TypeInformation info, Selector selector) { + // Only type the selector if [info] is concrete, because the other + // kinds of [TypeInformation] have the empty type at this point of + // analysis. + return info.isConcrete + ? new TypedSelector(info.type, selector) + : selector; + } + + TypeInformation allocateDiamondPhi(TypeInformation firstInput, + TypeInformation secondInput) { + PhiElementTypeInformation result = + new PhiElementTypeInformation(null, false, null); + result.addAssignment(firstInput); + result.addAssignment(secondInput); + allocatedTypes.add(result); + return result; + } + + PhiElementTypeInformation allocatePhi(ast.Node node, + Element element, + inputType) { + // Check if [inputType] is a phi for a local updated in + // the try/catch block [node]. If it is, no need to allocate a new + // phi. + if (inputType is PhiElementTypeInformation && + inputType.branchNode == node) { + return inputType; + } + PhiElementTypeInformation result = + new PhiElementTypeInformation(node, true, element); + allocatedTypes.add(result); + result.addAssignment(inputType); + return result; + } + + TypeInformation simplifyPhi(ast.Node node, + Element element, + PhiElementTypeInformation phiType) { + if (phiType.assignments.length == 1) return phiType.assignments.first; + return phiType; + } + + PhiElementTypeInformation addPhiInput(Element element, + PhiElementTypeInformation phiType, + TypeInformation newType) { + phiType.addAssignment(newType); + return phiType; + } + + TypeMask computeTypeMask(Iterable assignments) { + return joinTypeMasks(assignments.map((e) => e.type)); + } + + TypeMask joinTypeMasks(Iterable masks) { + TypeMask newType = const TypeMask.nonNullEmpty(); + for (TypeMask mask in masks) { + newType = newType.union(mask, compiler); + } + return newType.containsAll(compiler) ? dynamicType.type : newType; + } +} + +/** + * A work queue for the inferrer. It filters out nodes on + * which we gave up on inferencing, as well as ensures through + * [TypeInformation.inQueue] that a node is in the queue only once at + * a time. + */ +class WorkQueue { + final Queue queue = new Queue(); + + void add(TypeInformation element) { + if (element.abandonInferencing) return; + if (element.inQueue) return; + queue.addLast(element); + element.inQueue = true; + } + + void addAll(Iterable all) { + all.forEach(add); + } + + TypeInformation remove() { + TypeInformation element = queue.removeFirst(); + element.inQueue = false; + return element; + } + + bool get isEmpty => queue.isEmpty; + + int get length => queue.length; +} + +/** + * An inferencing engine that computes a call graph of + * [TypeInformation] nodes by visiting the AST of the application, and + * then does the inferencing on the graph. + * + */ +class TypeGraphInferrerEngine + extends InferrerEngine { + final Map defaultTypeOfParameter = + new Map(); + final List allocatedCalls = + []; + final WorkQueue workQueue = new WorkQueue(); + final Element mainElement; + final Set analyzedElements = new Set(); + + /// The maximum number of times we allow a node in the graph to + /// change types. If a node reaches that limit, we give up + /// inferencing on it and give it the dynamic type. + final int MAX_CHANGE_COUNT = 6; + + int overallRefineCount = 0; + int addedInGraph = 0; + + TypeGraphInferrerEngine(Compiler compiler, this.mainElement) + : super(compiler, new TypeInformationSystem(compiler)); + + void analyzeListAndEnqueue(ListTypeInformation info) { + if (info.analyzed) return; + info.analyzed = true; + + ListTracerVisitor tracer = new ListTracerVisitor(info, this); + bool succeeded = tracer.run(); + if (!succeeded) return; + + info.bailedOut = false; + info.elementType.inferred = true; + TypeMask fixedListType = compiler.typesTask.fixedListType; + if (info.originalContainerType.forwardTo == fixedListType) { + info.checksGrowable = tracer.callsGrowableMethod; + } + tracer.assignments.forEach(info.elementType.addAssignment); + // Enqueue the list for later refinement + workQueue.add(info); + workQueue.add(info.elementType); + } + + void analyzeMapAndEnqueue(MapTypeInformation info) { + if (info.analyzed) return; + info.analyzed = true; + MapTracerVisitor tracer = new MapTracerVisitor(info, this); + + bool succeeded = tracer.run(); + if (!succeeded) return; + + info.bailedOut = false; + for (int i = 0; i < tracer.keyAssignments.length; ++i) { + TypeInformation newType = info.addEntryAssignment( + tracer.keyAssignments[i], tracer.valueAssignments[i]); + if (newType != null) workQueue.add(newType); + } + for (TypeInformation map in tracer.mapAssignments) { + workQueue.addAll(info.addMapAssignment(map)); + } + + info.markAsInferred(); + workQueue.add(info.keyType); + workQueue.add(info.valueType); + workQueue.addAll(info.typeInfoMap.values); + workQueue.add(info); + } + + void runOverAllElements() { + if (compiler.disableTypeInference) return; + compiler.progress.reset(); + + sortResolvedElements().forEach((Element element) { + if (compiler.progress.elapsedMilliseconds > 500) { + compiler.log('Added $addedInGraph elements in inferencing graph.'); + compiler.progress.reset(); + } + // Force the creation of the [ElementTypeInformation] to ensure it is + // in the graph. + types.getInferredTypeOf(element); + analyze(element, null); + }); + compiler.log('Added $addedInGraph elements in inferencing graph.'); + + buildWorkQueue(); + refine(); + + // Try to infer element types of lists. + types.allocatedLists.values.forEach((ListTypeInformation info) { + if (info.elementType.inferred) return; + analyzeListAndEnqueue(info); + }); + + // Try to infer the key and value types for maps. + types.allocatedMaps.values.forEach((MapTypeInformation info) { + if (info.keyType.inferred && info.valueType.inferred) return; + analyzeMapAndEnqueue(info); + }); + + types.allocatedClosures.forEach((info) { + ClosureTracerVisitor tracer = info is ClosureTypeInformation + ? new ClosureTracerVisitor(info.element, info, this) + : new StaticTearOffClosureTracerVisitor(info.element, info, this); + tracer.run(); + if (!tracer.continueAnalyzing) return; + FunctionElement element = info.element; + element.functionSignature.forEachParameter((parameter) { + workQueue.add(types.getInferredTypeOf(parameter)); + }); + }); + + // Reset all nodes that use lists/maps that have been inferred, as well + // as nodes that use elements fetched from these lists/maps. The + // workset for a new run of the analysis will be these nodes. + Set seenTypes = new Set(); + while (!workQueue.isEmpty) { + TypeInformation info = workQueue.remove(); + if (seenTypes.contains(info)) continue; + info.reset(this); + seenTypes.add(info); + workQueue.addAll(info.users); + } + + workQueue.addAll(seenTypes); + refine(); + + if (_VERBOSE) { + types.allocatedLists.values.forEach((ListTypeInformation info) { + print('${info.type} ' + 'for ${info.originalContainerType.allocationNode} ' + 'at ${info.originalContainerType.allocationElement} ' + 'after ${info.refineCount}'); + }); + types.allocatedMaps.values.forEach((MapTypeInformation info) { + print('${info.type} ' + 'for ${(info.type as MapTypeMask).allocationNode} ' + 'at ${(info.type as MapTypeMask).allocationElement} ' + 'after ${info.refineCount}'); + }); + analyzedElements.forEach((Element elem) { + TypeInformation type = types.getInferredTypeOf(elem); + print('${elem} :: ${type} from ${type.assignments} '); + }); + } + + compiler.log('Inferred $overallRefineCount types.'); + + processLoopInformation(); + } + + void analyze(Element element, ArgumentsTypes arguments) { + element = element.implementation; + if (analyzedElements.contains(element)) return; + analyzedElements.add(element); + + SimpleTypeInferrerVisitor visitor = + new SimpleTypeInferrerVisitor(element, compiler, this); + TypeInformation type; + compiler.withCurrentElement(element, () { + type = visitor.run(); + }); + addedInGraph++; + + if (element.isField()) { + VariableElement fieldElement = element; + ast.Node node = fieldElement.parseNode(compiler); + if (element.modifiers.isFinal() || element.modifiers.isConst()) { + // If [element] is final and has an initializer, we record + // the inferred type. + if (fieldElement.initializer != null) { + if (type is! ListTypeInformation) { + // For non-container types, the constant handler does + // constant folding that could give more precise results. + Constant value = + compiler.constantHandler.getConstantForVariable(element); + if (value != null) { + if (value.isFunction) { + FunctionConstant functionConstant = value; + type = types.allocateClosure(node, functionConstant.element); + } else { + type = types.getConcreteTypeFor(value.computeMask(compiler)); + } + } + } + recordType(element, type); + } else if (!element.isInstanceMember()) { + recordType(element, types.nullType); + } + } else if (fieldElement.initializer == null) { + // Only update types of static fields if there is no + // assignment. Instance fields are dealt with in the constructor. + if (Elements.isStaticOrTopLevelField(element)) { + recordTypeOfNonFinalField(node, element, type); + } + } else { + recordTypeOfNonFinalField(node, element, type); + } + if (Elements.isStaticOrTopLevelField(element) && + fieldElement.initializer != null && + !element.modifiers.isConst()) { + var argument = fieldElement.initializer; + // TODO(13429): We could do better here by using the + // constant handler to figure out if it's a lazy field or not. + if (argument.asSend() != null || + (argument.asNewExpression() != null && !argument.isConst())) { + recordType(element, types.nullType); + } + } + } else { + recordReturnType(element, type); + } + } + + void processLoopInformation() { + allocatedCalls.forEach((info) { + if (!info.inLoop) return; + if (info is StaticCallSiteTypeInformation) { + compiler.world.addFunctionCalledInLoop(info.calledElement); + } else if (info.selector.mask != null && + !info.selector.mask.containsAll(compiler)) { + // For instance methods, we only register a selector called in a + // loop if it is a typed selector, to avoid marking too many + // methods as being called from within a loop. This cuts down + // on the code bloat. + info.targets.forEach(compiler.world.addFunctionCalledInLoop); + } + }); + } + + void refine() { + while (!workQueue.isEmpty) { + if (compiler.progress.elapsedMilliseconds > 500) { + compiler.log('Inferred $overallRefineCount types.'); + compiler.progress.reset(); + } + TypeInformation info = workQueue.remove(); + TypeMask oldType = info.type; + TypeMask newType = info.refine(this); + if ((info.type = newType) != oldType) { + overallRefineCount++; + info.refineCount++; + workQueue.addAll(info.users); + if (info.hasStableType(this)) { + info.stabilize(this); + } else if (info.refineCount > MAX_CHANGE_COUNT) { + info.giveUp(this); + } + } + } + } + + void buildWorkQueue() { + workQueue.addAll(types.typeInformations.values); + workQueue.addAll(types.allocatedTypes); + workQueue.addAll(types.allocatedClosures); + workQueue.addAll(allocatedCalls); + } + + /** + * Update the assignments to parameters in the graph. [remove] tells + * wheter assignments must be added or removed. If [init] is false, + * parameters are added to the work queue. + */ + void updateParameterAssignments(TypeInformation caller, + Element callee, + ArgumentsTypes arguments, + Selector selector, + {bool remove, bool addToQueue: true}) { + if (callee.name == Compiler.NO_SUCH_METHOD) return; + if (callee.isField()) { + if (selector.isSetter()) { + ElementTypeInformation info = types.getInferredTypeOf(callee); + if (remove) { + info.removeAssignment(arguments.positional[0]); + } else { + info.addAssignment(arguments.positional[0]); + } + if (addToQueue) workQueue.add(info); + } + } else if (callee.isGetter()) { + return; + } else if (selector != null && selector.isGetter()) { + ElementTypeInformation info = types.getInferredTypeOf(callee); + if (remove) { + info.closurizedCount--; + } else { + info.closurizedCount++; + if (Elements.isStaticOrTopLevel(callee)) { + types.allocatedClosures.add(info); + } + FunctionElement function = callee.implementation; + FunctionSignature signature = function.functionSignature; + signature.forEachParameter((Element parameter) { + ElementTypeInformation info = types.getInferredTypeOf(parameter); + info.giveUp(this, clearAssignments: false); + if (addToQueue) workQueue.addAll(info.users); + }); + } + } else { + FunctionElement function = callee.implementation; + FunctionSignature signature = function.functionSignature; + int parameterIndex = 0; + bool visitingRequiredParameter = true; + signature.forEachParameter((Element parameter) { + if (parameter == signature.firstOptionalParameter) { + visitingRequiredParameter = false; + } + TypeInformation type = visitingRequiredParameter + ? arguments.positional[parameterIndex] + : signature.optionalParametersAreNamed + ? arguments.named[parameter.name] + : parameterIndex < arguments.positional.length + ? arguments.positional[parameterIndex] + : null; + if (type == null) type = getDefaultTypeOfParameter(parameter); + TypeInformation info = types.getInferredTypeOf(parameter); + if (remove) { + info.removeAssignment(type); + } else { + info.addAssignment(type); + } + parameterIndex++; + if (addToQueue) workQueue.add(info); + }); + } + } + + void setDefaultTypeOfParameter(Element parameter, TypeInformation type) { + assert(parameter.enclosingElement.isImplementation); + TypeInformation existing = defaultTypeOfParameter[parameter]; + defaultTypeOfParameter[parameter] = type; + TypeInformation info = types.getInferredTypeOf(parameter); + if (!info.abandonInferencing && existing != null && existing != type) { + // Replace references to [existing] to use [type] instead. + if (parameter.enclosingElement.isInstanceMember()) { + ParameterAssignments assignments = info.assignments; + int count = assignments.assignments[existing]; + if (count == null) return; + type.addUser(info); + assignments.assignments[type] = count; + assignments.assignments.remove(existing); + } else { + List assignments = info.assignments; + for (int i = 0; i < assignments.length; i++) { + if (assignments[i] == existing) { + info.assignments[i] = type; + type.addUser(info); + } + } + } + } + } + + TypeInformation getDefaultTypeOfParameter(Element parameter) { + return defaultTypeOfParameter.putIfAbsent(parameter, () { + return new ConcreteTypeInformation(types.dynamicType.type); + }); + } + + TypeInformation typeOfElement(Element element) { + if (element is FunctionElement) return types.functionType; + return types.getInferredTypeOf(element); + } + + TypeInformation returnTypeOfElement(Element element) { + if (element is !FunctionElement) return types.dynamicType; + return types.getInferredTypeOf(element); + } + + void recordTypeOfFinalField(Spannable node, + Element analyzed, + Element element, + TypeInformation type) { + types.getInferredTypeOf(element).addAssignment(type); + } + + void recordTypeOfNonFinalField(Spannable node, + Element element, + TypeInformation type) { + types.getInferredTypeOf(element).addAssignment(type); + } + + void recordType(Element element, TypeInformation type) { + types.getInferredTypeOf(element).addAssignment(type); + } + + void recordReturnType(Element element, TypeInformation type) { + TypeInformation info = types.getInferredTypeOf(element); + if (element.name == '==') { + // Even if x.== doesn't return a bool, 'x == null' evaluates to 'false'. + info.addAssignment(types.boolType); + } + // TODO(ngeoffray): Clean up. We do these checks because + // [SimpleTypesInferrer] deals with two different inferrers. + if (type == null) return; + if (info.assignments.isEmpty) info.addAssignment(type); + } + + TypeInformation addReturnTypeFor(Element element, + TypeInformation unused, + TypeInformation newType) { + TypeInformation type = types.getInferredTypeOf(element); + // TODO(ngeoffray): Clean up. We do this check because + // [SimpleTypesInferrer] deals with two different inferrers. + if (element.isGenerativeConstructor()) return type; + type.addAssignment(newType); + return type; + } + + TypeInformation registerCalledElement(Spannable node, + Selector selector, + Element caller, + Element callee, + ArgumentsTypes arguments, + SideEffects sideEffects, + bool inLoop) { + CallSiteTypeInformation info = new StaticCallSiteTypeInformation( + node, caller, callee, selector, arguments, inLoop); + info.addToGraph(this); + allocatedCalls.add(info); + updateSideEffects(sideEffects, selector, callee); + return info; + } + + TypeInformation registerCalledSelector(ast.Node node, + Selector selector, + TypeInformation receiverType, + Element caller, + ArgumentsTypes arguments, + SideEffects sideEffects, + bool inLoop) { + if (selector.isClosureCall()) { + return registerCalledClosure( + node, selector, receiverType, caller, arguments, sideEffects, inLoop); + } + + compiler.world.allFunctions.filter(selector).forEach((callee) { + updateSideEffects(sideEffects, selector, callee); + }); + + CallSiteTypeInformation info = new DynamicCallSiteTypeInformation( + node, caller, selector, receiverType, arguments, inLoop); + + info.addToGraph(this); + allocatedCalls.add(info); + return info; + } + + TypeInformation registerCalledClosure(ast.Node node, + Selector selector, + TypeInformation closure, + Element caller, + ArgumentsTypes arguments, + SideEffects sideEffects, + bool inLoop) { + sideEffects.setDependsOnSomething(); + sideEffects.setAllSideEffects(); + CallSiteTypeInformation info = new ClosureCallSiteTypeInformation( + node, caller, selector, closure, arguments, inLoop); + info.addToGraph(this); + allocatedCalls.add(info); + return info; + } + + // Sorts the resolved elements by size. We do this for this inferrer + // to get the same results for [ListTracer] compared to the + // [SimpleTypesInferrer]. + Iterable sortResolvedElements() { + int max = 0; + Map> methodSizes = new Map>(); + compiler.enqueuer.resolution.resolvedElements.forEach( + (Element element, TreeElementMapping mapping) { + element = element.implementation; + if (element.impliesType()) return; + assert(invariant(element, + element.isField() || + element.isFunction() || + element.isGenerativeConstructor() || + element.isGetter() || + element.isSetter(), + message: 'Unexpected element kind: ${element.kind}')); + // TODO(ngeoffray): Not sure why the resolver would put a null + // mapping. + if (mapping == null) return; + if (element.isAbstract) return; + // Put the other operators in buckets by length, later to be added in + // length order. + int length = mapping.selectors.length; + max = length > max ? length : max; + Setlet set = methodSizes.putIfAbsent( + length, () => new Setlet()); + set.add(element); + }); + + List result = []; + for (int i = 0; i <= max; i++) { + Setlet set = methodSizes[i]; + if (set != null) result.addAll(set); + } + return result; + } + + void clear() { + allocatedCalls.clear(); + defaultTypeOfParameter.clear(); + types.typeInformations.values.forEach((info) => info.clear()); + types.allocatedTypes.clear(); + types.concreteTypes.clear(); + types.allocatedClosures.clear(); + analyzedElements.clear(); + generativeConstructorsExposingThis.clear(); + } + + Iterable getCallersOf(Element element) { + if (compiler.disableTypeInference) { + throw new UnsupportedError( + "Cannot query the type inferrer when type inference is disabled."); + } + return types.getInferredTypeOf(element).callers; + } + + /** + * Returns the type of [element] when being called with [selector]. + */ + TypeInformation typeOfElementWithSelector(Element element, + Selector selector) { + if (element.name == Compiler.NO_SUCH_METHOD && + selector.name != element.name) { + // An invocation can resolve to a [noSuchMethod], in which case + // we get the return type of [noSuchMethod]. + return returnTypeOfElement(element); + } else if (selector.isGetter()) { + if (element.isFunction()) { + // [functionType] is null if the inferrer did not run. + return types.functionType == null + ? types.dynamicType + : types.functionType; + } else if (element.isField()) { + return typeOfElement(element); + } else if (Elements.isUnresolved(element)) { + return types.dynamicType; + } else { + assert(element.isGetter()); + return returnTypeOfElement(element); + } + } else if (element.isGetter() || element.isField()) { + assert(selector.isCall() || selector.isSetter()); + return types.dynamicType; + } else { + return returnTypeOfElement(element); + } + } + + void recordCapturedLocalRead(Element local) {} + + void recordLocalUpdate(Element local, TypeInformation type) {} +} + +class TypeGraphInferrer implements TypesInferrer { + TypeGraphInferrerEngine inferrer; + final Compiler compiler; + TypeGraphInferrer(Compiler this.compiler); + + String get name => 'Graph inferrer'; + + void analyzeMain(Element main) { + inferrer = new TypeGraphInferrerEngine(compiler, main); + inferrer.runOverAllElements(); + } + + TypeMask getReturnTypeOfElement(Element element) { + if (compiler.disableTypeInference) return compiler.typesTask.dynamicType; + // Currently, closure calls return dynamic. + if (element is! FunctionElement) return compiler.typesTask.dynamicType; + return inferrer.types.getInferredTypeOf(element).type; + } + + TypeMask getTypeOfElement(Element element) { + if (compiler.disableTypeInference) return compiler.typesTask.dynamicType; + // The inferrer stores the return type for a function, so we have to + // be careful to not return it here. + if (element is FunctionElement) return compiler.typesTask.functionType; + return inferrer.types.getInferredTypeOf(element).type; + } + + TypeMask getTypeOfNode(Element owner, ast.Node node) { + if (compiler.disableTypeInference) return compiler.typesTask.dynamicType; + return inferrer.types.allocatedLists[node].type; + } + + bool isFixedArrayCheckedForGrowable(ast.Node node) { + if (compiler.disableTypeInference) return true; + ListTypeInformation info = inferrer.types.allocatedLists[node]; + return info.checksGrowable; + } + + TypeMask getTypeOfSelector(Selector selector) { + if (compiler.disableTypeInference) return compiler.typesTask.dynamicType; + // Bailout for closure calls. We're not tracking types of + // closures. + if (selector.isClosureCall()) return compiler.typesTask.dynamicType; + if (selector.isSetter() || selector.isIndexSet()) { + return compiler.typesTask.dynamicType; + } + if (returnsListElementType(selector)) { + ContainerTypeMask mask = selector.mask; + TypeMask elementType = mask.elementType; + return elementType == null ? compiler.typesTask.dynamicType : elementType; + } + if (returnsMapValueType(selector)) { + MapTypeMask mask = selector.mask; + TypeMask valueType = mask.valueType; + return valueType == null ? compiler.typesTask.dynamicType + : valueType; + } + + TypeMask result = const TypeMask.nonNullEmpty(); + Iterable elements = compiler.world.allFunctions.filter(selector); + for (Element element in elements) { + TypeMask type = + inferrer.typeOfElementWithSelector(element, selector).type; + result = result.union(type, compiler); + } + return result; + } + + Iterable getCallersOf(Element element) { + if (compiler.disableTypeInference) { + throw new UnsupportedError( + "Cannot query the type inferrer when type inference is disabled."); + } + return inferrer.getCallersOf(element); + } + + bool isCalledOnce(Element element) { + if (compiler.disableTypeInference) return false; + ElementTypeInformation info = inferrer.types.getInferredTypeOf(element); + return info.isCalledOnce(); + } + + void clear() { + inferrer.clear(); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/type_graph_nodes.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/type_graph_nodes.dart new file mode 100644 index 0000000..2c45b4a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/inferrer/type_graph_nodes.dart @@ -0,0 +1,1273 @@ +// Copyright (c) 2013, 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. + +part of type_graph_inferrer; + +/** + * Common class for all nodes in the graph. The current nodes are: + * + * - Concrete types + * - Elements + * - Call sites + * - Narrowing instructions + * - Phi instructions + * - Containers (for lists) + * - Type of the element in a container + * + * A node has a set of assignments and users. Assignments are used to + * compute the type of the node ([TypeInformation.refine]). Users are + * added to the inferrer's work queue when the type of the node + * changes. + */ +abstract class TypeInformation { + var /* List|Set */ users; + var /* List|ParameterAssignments */ assignments; + + /// The type the inferrer has found for this [TypeInformation]. + /// Initially empty. + TypeMask type = const TypeMask.nonNullEmpty(); + + /// We give up on inferencing for special elements, as well as for + /// complicated cyclic dependencies. + bool abandonInferencing = false; + + /// Number of times this [TypeInformation] has changed type. + int refineCount = 0; + + /// Whether this [TypeInformation] is currently in the inferrer's + /// work queue. + bool inQueue = false; + + /// Whether this [TypeInformation] has a stable [type] that will not + /// change. + bool isStable = false; + + // TypeInformations are unique. + static int staticHashCode = 0; + final int hashCode = staticHashCode++; + + bool get isConcrete => false; + + TypeInformation([users, assignments]) + : users = (users == null) ? new Setlet() : users, + assignments = (assignments == null) ? [] : assignments; + + + void addUser(TypeInformation user) { + if (isStable) return; + assert(!user.isConcrete); + users.add(user); + } + + void removeUser(TypeInformation user) { + if (isStable) return; + assert(!user.isConcrete); + users.remove(user); + } + + static final STOP_TRACKING_ASSIGNMENTS_MARKER = const []; + + bool areAssignmentsTracked() { + return assignments != STOP_TRACKING_ASSIGNMENTS_MARKER; + } + + void addAssignment(TypeInformation assignment) { + // Cheap one-level cycle detection. + if (assignment == this) return; + if (areAssignmentsTracked()) { + assignments.add(assignment); + } + // Even if we abandon inferencing on this [TypeInformation] we + // need to collect the users, so that phases that track where + // elements flow in still work. + assignment.addUser(this); + } + + void removeAssignment(TypeInformation assignment) { + if (!abandonInferencing) { + assignments.remove(assignment); + } + // We can have multiple assignments of the same [TypeInformation]. + if (!assignments.contains(assignment)) { + assignment.removeUser(this); + } + } + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + return type; + } + + void giveUp(TypeGraphInferrerEngine inferrer, {bool clearAssignments: true}) { + abandonInferencing = true; + type = inferrer.types.dynamicType.type; + // Do not remove [this] as a user of nodes in [assignments], + // because our tracing analysis could be interested in tracing + // this node. + if (clearAssignments) assignments = const []; + // Do not remove users because our tracing analysis could be + // interested in tracing the users of this node. + } + + void clear() { + assignments = STOP_TRACKING_ASSIGNMENTS_MARKER; + users = const []; + } + + /// Reset the analysis of this node by making its type empty. + void reset(TypeGraphInferrerEngine inferrer) { + if (abandonInferencing) return; + type = const TypeMask.nonNullEmpty(); + refineCount = 0; + } + + accept(TypeInformationVisitor visitor); + + /// The [Element] where this [TypeInformation] was created. May be + /// for some [TypeInformation] nodes, where we do not need to store + /// the information. + Element get owner => null; + + /// Returns whether the type cannot change after it has been + /// inferred. + bool hasStableType(TypeGraphInferrerEngine inferrer) { + return !abandonInferencing && assignments.every((e) => e.isStable); + } + + void removeAndClearReferences(TypeGraphInferrerEngine inferrer) { + assignments.forEach((info) { info.removeUser(this); }); + } + + void stabilize(TypeGraphInferrerEngine inferrer) { + removeAndClearReferences(inferrer); + users = const []; + assignments = STOP_TRACKING_ASSIGNMENTS_MARKER; + abandonInferencing = true; + isStable = true; + } +} + +/** + * Parameters of instance functions behave differently than other + * elements because the inferrer may remove assignments. This happens + * when the receiver of a dynamic call site can be refined + * to a type where we know more about which instance method is being + * called. + */ +class ParameterAssignments extends IterableBase { + final Map assignments = + new Map(); + + void remove(TypeInformation info) { + int existing = assignments[info]; + if (existing == null) return; + if (existing == 1) { + assignments.remove(info); + } else { + assignments[info] = existing - 1; + } + } + + void add(TypeInformation info) { + int existing = assignments[info]; + if (existing == null) { + assignments[info] = 1; + } else { + assignments[info] = existing + 1; + } + } + + Iterator get iterator => assignments.keys.iterator; + Iterable where(Function f) => assignments.keys.where(f); + + bool contains(TypeInformation info) => assignments.containsKey(info); + + String toString() => assignments.keys.toList().toString(); +} + +/** + * A node representing a resolved element of the program. The kind of + * elements that need an [ElementTypeInformation] are: + * + * - Functions (including getters and setters) + * - Constructors (factory or generative) + * - Fields + * - Parameters + * - Local variables mutated in closures + * + * The [ElementTypeInformation] of a function and a constructor is its + * return type. + * + * Note that a few elements of these kinds must be treated specially, + * and they are dealt in [ElementTypeInformation.handleSpecialCases]: + * + * - Parameters of closures, [noSuchMethod] and [call] instance + * methods: we currently do not infer types for those. + * + * - Fields and parameters being assigned by synthesized calls done by + * the backend: we do not know what types the backend will use. + * + * - Native functions and fields: because native methods contain no Dart + * code, and native fields do not have Dart assignments, we just + * trust their type annotation. + * + */ +class ElementTypeInformation extends TypeInformation { + final Element element; + + // Marker to disable [handleSpecialCases]. For example, parameters + // of closures that are traced can be inferred. + bool disableHandleSpecialCases = false; + + /** + * If [element] is a function, [closurizedCount] is the number of + * times it is closurized. The value gets updated while infering. + */ + int closurizedCount = 0; + + /** + * This map contains the callers of [element]. It stores all unique call sites + * to enable counting the global number of call sites of [element]. + * + * A call site is either an AST [ast.Node], an [ir.Node] or in the case of + * synthesized calls, an [Element] (see uses of [synthesizeForwardingCall] + * in [SimpleTypeInferrerVisitor]). + */ + final Map> _callers = new Map(); + + ElementTypeInformation.internal(this.element, assignments) + : super(null, assignments); + + factory ElementTypeInformation(Element element) { + var assignments = null; + if (element.enclosingElement.isInstanceMember() && + (element.isParameter() || element.isFieldParameter())) { + assignments = new ParameterAssignments(); + } + return new ElementTypeInformation.internal(element, assignments); + } + + void addCall(Element caller, Spannable node) { + assert(node is ast.Node || node is ir.Node || node is Element); + _callers.putIfAbsent(caller, () => new Setlet()).add(node); + } + + void removeCall(Element caller, node) { + Setlet calls = _callers[caller]; + if (calls == null) return; + calls.remove(node); + if (calls.isEmpty) { + _callers.remove(caller); + } + } + + Iterable get callers => _callers.keys; + + bool isCalledOnce() { + int count = 0; + for (var set in _callers.values) { + count += set.length; + if (count > 1) return false; + } + return count == 1; + } + + bool isClosurized() => closurizedCount > 0; + + TypeMask handleSpecialCases(TypeGraphInferrerEngine inferrer) { + if (abandonInferencing) return type; + if (disableHandleSpecialCases) return null; + + if (element.isParameter()) { + Element enclosing = element.enclosingElement; + if (Elements.isLocal(enclosing)) { + // Do not infer types for parameters of closures. We do not + // clear the assignments in case the closure is successfully + // traced. + giveUp(inferrer, clearAssignments: false); + return type; + } else if (enclosing.isInstanceMember() && + (enclosing.name == Compiler.NO_SUCH_METHOD || + enclosing.name == Compiler.CALL_OPERATOR_NAME)) { + // Do not infer types for parameters of [noSuchMethod] and + // [call] instance methods. + giveUp(inferrer); + return type; + } else if (enclosing == inferrer.mainElement) { + // The implicit call to main is not seen by the inferrer, + // therefore we explicitly set the type of its parameters as + // dynamic. + // TODO(14566): synthesize a call instead to get the exact + // types. + giveUp(inferrer); + return type; + } + } + if (element.isField() || + element.isParameter() || + element.isFieldParameter()) { + if (!inferrer.compiler.backend.canBeUsedForGlobalOptimizations(element)) { + // Do not infer types for fields and parameters being assigned + // by synthesized calls. + giveUp(inferrer); + return type; + } + } + if (inferrer.isNativeElement(element)) { + // Use the type annotation as the type for native elements. We + // also give up on inferring to make sure this element never + // goes in the work queue. + giveUp(inferrer); + if (element.isField()) { + return inferrer.typeOfNativeBehavior( + native.NativeBehavior.ofFieldLoad(element, inferrer.compiler)).type; + } else { + assert(element.isFunction() || + element.isGetter() || + element.isSetter()); + TypedElement typedElement = element; + var elementType = typedElement.type; + if (elementType.kind != TypeKind.FUNCTION) { + return type; + } else { + return inferrer.typeOfNativeBehavior( + native.NativeBehavior.ofMethod(element, inferrer.compiler)).type; + } + } + } + + Compiler compiler = inferrer.compiler; + if (element.declaration == compiler.intEnvironment) { + giveUp(inferrer); + return compiler.typesTask.intType.nullable(); + } else if (element.declaration == compiler.boolEnvironment) { + giveUp(inferrer); + return compiler.typesTask.boolType.nullable(); + } else if (element.declaration == compiler.stringEnvironment) { + giveUp(inferrer); + return compiler.typesTask.stringType.nullable(); + } + return null; + } + + TypeMask potentiallyNarrowType(TypeMask mask, + TypeGraphInferrerEngine inferrer) { + Compiler compiler = inferrer.compiler; + // Parameters are being explicitly checked in the method. + if (element.isParameter() || element.isFieldParameter()) return mask; + if (!compiler.trustTypeAnnotations && !compiler.enableTypeAssertions) { + return mask; + } + if (element.isGenerativeConstructor() || element.isSetter()) return mask; + var type = element.computeType(compiler); + if (element.isFunction() || + element.isGetter() || + element.isFactoryConstructor()) { + type = type.returnType; + } + return new TypeMaskSystem(compiler).narrowType(mask, type); + } + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + TypeMask special = handleSpecialCases(inferrer); + if (special != null) return potentiallyNarrowType(special, inferrer); + return potentiallyNarrowType( + inferrer.types.computeTypeMask(assignments), inferrer); + } + + String toString() => 'Element $element $type'; + + accept(TypeInformationVisitor visitor) { + return visitor.visitElementTypeInformation(this); + } + + Element get owner => element.getOutermostEnclosingMemberOrTopLevel(); + + bool hasStableType(TypeGraphInferrerEngine inferrer) { + // The number of assignments of parameters of instance methods is + // not stable. Therefore such a parameter cannot be stable. + if (element.isParameter() && element.enclosingElement.isInstanceMember()) { + return false; + } + // The number of assignments of non-final fields is + // not stable. Therefore such a field cannot be stable. + if (element.isField() && + !(element.modifiers.isConst() || element.modifiers.isFinal())) { + return false; + } + // If the method is closurized, the closure tracing phase will go + // through the users. + if (closurizedCount != 0) return false; + + return super.hasStableType(inferrer); + } +} + +/** + * A [CallSiteTypeInformation] is a call found in the AST, or a + * synthesized call for implicit calls in Dart (such as forwarding + * factories). The [call] field is a [ast.Node] for the former, and an + * [Element] for the latter. + * + * In the inferrer graph, [CallSiteTypeInformation] nodes do not have + * any assignment. They rely on the [caller] field for static calls, + * and [selector] and [receiver] fields for dynamic calls. + */ +abstract class CallSiteTypeInformation extends TypeInformation { + final Spannable call; + final Element caller; + final Selector selector; + final ArgumentsTypes arguments; + final bool inLoop; + + CallSiteTypeInformation( + this.call, + this.caller, + this.selector, + this.arguments, + this.inLoop) : super(null, const []); + + String toString() => 'Call site $call $type'; + + /// Add [this] to the graph being computed by [engine]. + void addToGraph(TypeGraphInferrerEngine engine); + + /// Return an iterable over the targets of this call. + Iterable get callees; + + Element get owner => caller; +} + +class StaticCallSiteTypeInformation extends CallSiteTypeInformation { + final Element calledElement; + + StaticCallSiteTypeInformation( + Spannable call, + Element enclosing, + this.calledElement, + Selector selector, + ArgumentsTypes arguments, + bool inLoop) : super(call, enclosing, selector, arguments, inLoop); + + void addToGraph(TypeGraphInferrerEngine inferrer) { + ElementTypeInformation callee = + inferrer.types.getInferredTypeOf(calledElement); + callee.addCall(caller, call); + callee.addUser(this); + if (arguments != null) { + arguments.forEach((info) => info.addUser(this)); + } + inferrer.updateParameterAssignments( + this, calledElement, arguments, selector, remove: false, + addToQueue: false); + } + + bool get isSynthesized { + // Some calls do not have a corresponding node, for example + // fowarding factory constructors, or synthesized super + // constructor calls. We synthesize these calls but do + // not create a selector for them. + return selector == null; + } + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + if (isSynthesized) { + assert(arguments != null); + return inferrer.types.getInferredTypeOf(calledElement).type; + } else { + return inferrer.typeOfElementWithSelector(calledElement, selector).type; + } + } + + Iterable get callees => [calledElement.implementation]; + + accept(TypeInformationVisitor visitor) { + return visitor.visitStaticCallSiteTypeInformation(this); + } + + bool hasStableType(TypeGraphInferrerEngine inferrer) { + return inferrer.types.getInferredTypeOf(calledElement).isStable && + (arguments == null || arguments.every((info) => info.isStable)) && + super.hasStableType(inferrer); + } + + void removeAndClearReferences(TypeGraphInferrerEngine inferrer) { + ElementTypeInformation callee = + inferrer.types.getInferredTypeOf(calledElement); + callee.removeUser(this); + if (arguments != null) { + arguments.forEach((info) => info.removeUser(this)); + } + super.removeAndClearReferences(inferrer); + } +} + +class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { + final TypeInformation receiver; + /// Cached targets of this call. + Iterable targets; + + DynamicCallSiteTypeInformation( + Spannable call, + Element enclosing, + Selector selector, + this.receiver, + ArgumentsTypes arguments, + bool inLoop) : super(call, enclosing, selector, arguments, inLoop); + + void addToGraph(TypeGraphInferrerEngine inferrer) { + assert(receiver != null); + Selector typedSelector = computeTypedSelector(inferrer); + targets = inferrer.compiler.world.allFunctions.filter(typedSelector); + receiver.addUser(this); + if (arguments != null) { + arguments.forEach((info) => info.addUser(this)); + } + for (Element element in targets) { + ElementTypeInformation callee = inferrer.types.getInferredTypeOf(element); + callee.addCall(caller, call); + callee.addUser(this); + inferrer.updateParameterAssignments( + this, element, arguments, typedSelector, remove: false, + addToQueue: false); + } + } + + Iterable get callees => targets.map((e) => e.implementation); + + Selector computeTypedSelector(TypeGraphInferrerEngine inferrer) { + TypeMask receiverType = receiver.type; + + if (selector.mask != receiverType) { + return receiverType == inferrer.compiler.typesTask.dynamicType + ? selector.asUntyped + : new TypedSelector(receiverType, selector); + } else { + return selector; + } + } + + /** + * We optimize certain operations on the [int] class because we know + * more about their return type than the actual Dart code. For + * example, we know int + int returns an int. The Dart code for + * [int.operator+] only says it returns a [num]. + */ + TypeInformation handleIntrisifiedSelector(Selector selector, + TypeGraphInferrerEngine inferrer) { + Compiler compiler = inferrer.compiler; + if (!compiler.backend.intImplementation.isResolved) return null; + TypeMask emptyType = const TypeMask.nonNullEmpty(); + if (selector.mask == null) return null; + if (!selector.mask.containsOnlyInt(compiler)) { + return null; + } + if (!selector.isCall() && !selector.isOperator()) return null; + if (!arguments.named.isEmpty) return null; + if (arguments.positional.length > 1) return null; + + ClassElement uint31Implementation = compiler.backend.uint31Implementation; + bool isInt(info) => info.type.containsOnlyInt(compiler); + bool isEmpty(info) => info.type == emptyType; + bool isUInt31(info) { + return info.type.satisfies(uint31Implementation, compiler); + } + bool isPositiveInt(info) { + return info.type.satisfies( + compiler.backend.positiveIntImplementation, compiler); + } + + String name = selector.name; + // We are optimizing for the cases that are not expressed in the + // Dart code, for example: + // int + int -> int + // uint31 | uint31 -> uint31 + if (name == '*' || name == '+' || name == '%' || name == 'remainder' || + name == '~/') { + if (isPositiveInt(receiver) && + arguments.hasOnePositionalArgumentThatMatches(isPositiveInt)) { + return inferrer.types.positiveIntType; + } else if (arguments.hasOnePositionalArgumentThatMatches(isInt)) { + return inferrer.types.intType; + } else if (arguments.hasOnePositionalArgumentThatMatches(isEmpty)) { + return inferrer.types.nonNullEmptyType; + } else { + return null; + } + } else if (name == '|' || name == '^') { + if (isUInt31(receiver) && + arguments.hasOnePositionalArgumentThatMatches(isUInt31)) { + return inferrer.types.uint31Type; + } + } else if (name == '>>') { + if (isUInt31(receiver)) { + return inferrer.types.uint31Type; + } + } else if (name == '&') { + if (isUInt31(receiver) || + arguments.hasOnePositionalArgumentThatMatches(isUInt31)) { + return inferrer.types.uint31Type; + } + } else if (name == 'unary-') { + // The receiver being an int, the return value will also be an + // int. + return inferrer.types.intType; + } else if (name == '-') { + if (arguments.hasOnePositionalArgumentThatMatches(isInt)) { + return inferrer.types.intType; + } else if (arguments.hasOnePositionalArgumentThatMatches(isEmpty)) { + return inferrer.types.nonNullEmptyType; + } + return null; + } else if (name == 'abs') { + return arguments.hasNoArguments() ? inferrer.types.positiveIntType : null; + } + return null; + } + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + Iterable oldTargets = targets; + Selector typedSelector = computeTypedSelector(inferrer); + inferrer.updateSelectorInTree(caller, call, typedSelector); + + Compiler compiler = inferrer.compiler; + Selector selectorToUse = typedSelector.extendIfReachesAll(compiler); + + bool canReachAll = compiler.enabledInvokeOn && + (selectorToUse != typedSelector); + + // If this call could potentially reach all methods that satisfy + // the untyped selector (through noSuchMethod's `Invocation` + // and a call to `delegate`), we iterate over all these methods to + // update their parameter types. + targets = compiler.world.allFunctions.filter(selectorToUse); + Iterable typedTargets = canReachAll + ? compiler.world.allFunctions.filter(typedSelector) + : targets; + + // Walk over the found targets, and compute the joined union type mask + // for all these targets. + TypeMask newType = inferrer.types.joinTypeMasks(targets.map((element) { + if (!oldTargets.contains(element)) { + ElementTypeInformation callee = + inferrer.types.getInferredTypeOf(element); + callee.addCall(caller, call); + callee.addUser(this); + inferrer.updateParameterAssignments( + this, element, arguments, typedSelector, remove: false, + addToQueue: true); + } + + // If [canReachAll] is true, then we are iterating over all + // targets that satisfy the untyped selector. We skip the return + // type of the targets that can only be reached through + // `Invocation.delegate`. Note that the `noSuchMethod` targets + // are included in [typedTargets]. + if (canReachAll && !typedTargets.contains(element)) { + return const TypeMask.nonNullEmpty(); + } + + if (returnsListElementType(typedSelector)) { + ContainerTypeMask mask = receiver.type; + return mask.elementType; + } else if (returnsMapValueType(typedSelector)) { + if (typedSelector.mask.isDictionary && + arguments.positional[0].type.isValue) { + DictionaryTypeMask mask = typedSelector.mask; + ValueTypeMask arg = arguments.positional[0].type; + String key = arg.value; + if (mask.typeMap.containsKey(key)) { + if (_VERBOSE) { + print("Dictionary lookup for $key yields ${mask.typeMap[key]}."); + } + return mask.typeMap[key]; + } else { + // The typeMap is precise, so if we do not find the key, the lookup + // will be [null] at runtime. + if (_VERBOSE) { + print("Dictionary lookup for $key yields [null]."); + } + return inferrer.types.nullType.type; + } + } + MapTypeMask mask = typedSelector.mask; + if (_VERBOSE) { + print("Map lookup for $typedSelector yields ${mask.valueType}."); + } + return mask.valueType; + } else { + TypeInformation info = + handleIntrisifiedSelector(typedSelector, inferrer); + if (info != null) return info.type; + return inferrer.typeOfElementWithSelector(element, typedSelector).type; + } + })); + + // Walk over the old targets, and remove calls that cannot happen + // anymore. + oldTargets.forEach((element) { + if (!targets.contains(element)) { + ElementTypeInformation callee = + inferrer.types.getInferredTypeOf(element); + callee.removeCall(caller, call); + callee.removeUser(this); + inferrer.updateParameterAssignments( + this, element, arguments, typedSelector, remove: true, + addToQueue: true); + } + }); + + return newType; + } + + void giveUp(TypeGraphInferrerEngine inferrer, {bool clearAssignments: true}) { + inferrer.updateSelectorInTree(caller, call, selector); + Iterable oldTargets = targets; + targets = inferrer.compiler.world.allFunctions.filter(selector); + for (Element element in targets) { + if (!oldTargets.contains(element)) { + ElementTypeInformation callee = + inferrer.types.getInferredTypeOf(element); + callee.addCall(caller, call); + inferrer.updateParameterAssignments( + this, element, arguments, selector, remove: false, + addToQueue: true); + } + } + super.giveUp(inferrer, clearAssignments: clearAssignments); + } + + void removeAndClearReferences(TypeGraphInferrerEngine inferrer) { + for (Element element in targets) { + ElementTypeInformation callee = inferrer.types.getInferredTypeOf(element); + callee.removeUser(this); + } + if (arguments != null) { + arguments.forEach((info) => info.removeUser(this)); + } + super.removeAndClearReferences(inferrer); + } + + String toString() => 'Call site $call on ${receiver.type} $type'; + + accept(TypeInformationVisitor visitor) { + return visitor.visitDynamicCallSiteTypeInformation(this); + } + + bool hasStableType(TypeGraphInferrerEngine inferrer) { + return receiver.isStable && + targets.every( + (element) => inferrer.types.getInferredTypeOf(element).isStable) && + (arguments == null || arguments.every((info) => info.isStable)) && + super.hasStableType(inferrer); + } +} + +class ClosureCallSiteTypeInformation extends CallSiteTypeInformation { + final TypeInformation closure; + + ClosureCallSiteTypeInformation( + Spannable call, + Element enclosing, + Selector selector, + this.closure, + ArgumentsTypes arguments, + bool inLoop) : super(call, enclosing, selector, arguments, inLoop); + + void addToGraph(TypeGraphInferrerEngine inferrer) { + arguments.forEach((info) => info.addUser(this)); + closure.addUser(this); + } + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + return inferrer.types.dynamicType.type; + } + + Iterable get callees { + throw new UnsupportedError("Cannot compute callees of a closure call."); + } + + String toString() => 'Closure call $call on $closure'; + + accept(TypeInformationVisitor visitor) { + return visitor.visitClosureCallSiteTypeInformation(this); + } + + void removeAndClearReferences(TypeGraphInferrerEngine inferrer) { + // This method is a placeholder for the following comment: + // We should maintain the information that the closure is a user + // of its arguments because we do not check that the arguments + // have a stable type for a closure call to be stable; our tracing + // analysis want to know whether an (non-stable) argument is + // passed to a closure. + return super.removeAndClearReferences(inferrer); + } +} + +/** + * A [ConcreteTypeInformation] represents a type that needed + * to be materialized during the creation of the graph. For example, + * literals, [:this:] or [:super:] need a [ConcreteTypeInformation]. + * + * [ConcreteTypeInformation] nodes have no assignment. Also, to save + * on memory, we do not add users to [ConcreteTypeInformation] nodes, + * because we know such node will never be refined to a different + * type. + */ +class ConcreteTypeInformation extends TypeInformation { + ConcreteTypeInformation(TypeMask type) + : super(const [], const []) { + this.type = type; + this.isStable = true; + } + + bool get isConcrete => true; + + void addUser(TypeInformation user) { + // Nothing to do, a concrete type does not get updated so never + // needs to notify its users. + } + + void removeUser(TypeInformation user) { + } + + void addAssignment(TypeInformation assignment) { + } + + void removeAssignment(TypeInformation assignment) { + assert(false); + } + + void reset(TypeGraphInferrerEngine inferrer) { + assert(false); + } + + String toString() => 'Type $type'; + + accept(TypeInformationVisitor visitor) { + return visitor.visitConcreteTypeInformation(this); + } + + bool hasStableType(TypeGraphInferrerEngine inferrer) { + return true; + } +} + +class StringLiteralTypeInformation extends ConcreteTypeInformation { + final ast.DartString value; + + StringLiteralTypeInformation(value, TypeMask mask) + : super(new ValueTypeMask(mask, value.slowToString())), + this.value = value; + + String asString() => value.slowToString(); + String toString() => 'Type $type value ${value.slowToString()}'; + + accept(TypeInformationVisitor visitor) { + return visitor.visitStringLiteralTypeInformation(this); + } +} + +/** + * A [NarrowTypeInformation] narrows a [TypeInformation] to a type, + * represented in [typeAnnotation]. + * + * A [NarrowTypeInformation] node has only one assignment: the + * [TypeInformation] it narrows. + * + * [NarrowTypeInformation] nodes are created for: + * + * - Code after `is` and `as` checks, where we have more information + * on the type of the right hand side of the expression. + * + * - Code after a dynamic call, where we have more information on the + * type of the receiver: it can only be of a class that holds a + * potential target of this dynamic call. + * + * - In checked mode, after a type annotation, we have more + * information on the type of a local. + */ +class NarrowTypeInformation extends TypeInformation { + final TypeMask typeAnnotation; + + NarrowTypeInformation(narrowedType, this.typeAnnotation) { + addAssignment(narrowedType); + } + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + return assignments[0].type.intersection(typeAnnotation, inferrer.compiler); + } + + String toString() { + return 'Narrow to $typeAnnotation $type'; + } + + accept(TypeInformationVisitor visitor) { + return visitor.visitNarrowTypeInformation(this); + } +} + +/** + * An [InferredTypeInformation] is a [TypeInformation] that + * defaults to the dynamic type until it is marked as beeing + * inferred, at which point it computes its type based on + * its assignments. + */ +abstract class InferredTypeInformation extends TypeInformation { + /** Whether the element type in that container has been inferred. */ + bool inferred = false; + + InferredTypeInformation(parentType) { + if (parentType != null) addAssignment(parentType); + } + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + if (!inferred) { + return inferrer.types.dynamicType.type; + } + return inferrer.types.computeTypeMask(assignments); + } + + bool hasStableType(TypeGraphInferrerEngine inferrer) { + return inferred && super.hasStableType(inferrer); + } +} + +/** + * A [ListTypeInformation] is a [TypeInformation] created + * for each `List` instantiations. + */ +class ListTypeInformation extends TypeInformation { + final ElementInContainerTypeInformation elementType; + + /** The container type before it is inferred. */ + final ContainerTypeMask originalContainerType; + + /** The length at the allocation site. */ + final int originalLength; + + /** The length after the container has been traced. */ + int inferredLength; + + /** + * Whether this list goes through a growable check. + * We conservatively assume it does. + */ + bool checksGrowable = true; + + // The set of [TypeInformation] where the traced container could + // flow in. + final Setlet flowsInto = new Setlet(); + + bool bailedOut = true; + bool analyzed = false; + + ListTypeInformation(this.originalContainerType, + this.elementType, + this.originalLength) { + type = originalContainerType; + inferredLength = originalContainerType.length; + elementType.addUser(this); + } + + String toString() => 'List type $type'; + + accept(TypeInformationVisitor visitor) { + return visitor.visitListTypeInformation(this); + } + + bool hasStableType(TypeGraphInferrerEngine inferrer) { + return elementType.isStable && super.hasStableType(inferrer); + } + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + var mask = type; + if (!mask.isContainer || + mask.elementType != elementType.type || + mask.length != inferredLength) { + return new ContainerTypeMask(originalContainerType.forwardTo, + originalContainerType.allocationNode, + originalContainerType.allocationElement, + elementType.type, + inferredLength); + } + return mask; + } + + void giveUp(TypeGraphInferrerEngine inferrer, {bool clearAssignments: true}) { + super.giveUp(inferrer, clearAssignments: clearAssignments); + // We still know that this node represents a container, so we explicitly + // preserve that information here. + type = new ContainerTypeMask(originalContainerType.forwardTo, + originalContainerType.allocationNode, + originalContainerType.allocationElement, + inferrer.types.dynamicType.type, + null); + } +} + +/** + * An [ElementInContainerTypeInformation] holds the common type of the + * elements in a [ListTypeInformation]. + */ +class ElementInContainerTypeInformation extends InferredTypeInformation { + ElementInContainerTypeInformation(elementType) : super(elementType); + + String toString() => 'Element in container $type'; + + accept(TypeInformationVisitor visitor) { + return visitor.visitElementInContainerTypeInformation(this); + } +} + +/** + * A [MapTypeInformation] is a [TypeInformation] created + * for maps. + */ +class MapTypeInformation extends TypeInformation { + // When in Dictionary mode, this map tracks the type of the values that + // have been assigned to a specific [String] key. + final Map typeInfoMap = {}; + // These fields track the overall type of the keys/values in the map. + final KeyInMapTypeInformation keyType; + final ValueInMapTypeInformation valueType; + final MapTypeMask initialType; + + // The set of [TypeInformation] where values from the traced map could + // flow in. + final Setlet flowsInto = new Setlet(); + + // Set to false once analysis has succeeded. + bool bailedOut = true; + bool analyzed = false; + + // Set to false if a statically unknown key flows into this map. + bool isDictionary = true; + + MapTypeInformation(this.initialType, this.keyType, this.valueType) { + keyType.addUser(this); + valueType.addUser(this); + type = initialType; + } + + TypeInformation addEntryAssignment(TypeInformation key, + TypeInformation value, + [bool nonNull = false]) { + TypeInformation newInfo = null; + if (isDictionary && key is StringLiteralTypeInformation) { + String keyString = key.asString(); + typeInfoMap.putIfAbsent(keyString, + () => newInfo = new ValueInMapTypeInformation(null, nonNull)); + typeInfoMap[keyString].addAssignment(value); + } else { + isDictionary = false; + typeInfoMap.clear(); + } + keyType.addAssignment(key); + valueType.addAssignment(value); + if (newInfo != null) newInfo.addUser(this); + + return newInfo; + } + + List addMapAssignment(MapTypeInformation map) { + List newInfos = []; + if (map.isDictionary) { + map.typeInfoMap.forEach((keyString, value) { + typeInfoMap.putIfAbsent(keyString, () { + TypeInformation newInfo = new ValueInMapTypeInformation(null, false); + newInfos.add(newInfo); + return newInfo; + }); + typeInfoMap[keyString].addAssignment(value); + }); + } + keyType.addAssignment(map.keyType); + valueType.addAssignment(map.valueType); + + return newInfos; + } + + markAsInferred() { + keyType.inferred = valueType.inferred = true; + typeInfoMap.values.forEach((v) => v.inferred = true); + } + + addAssignment(TypeInformation other) { + throw "not supported"; + } + + accept(TypeInformationVisitor visitor) { + return visitor.visitMapTypeInformation(this); + } + + TypeMask toTypeMask(TypeGraphInferrerEngine inferrer) { + Map mappings; + if (isDictionary) { + mappings = new Map(); + for (var key in typeInfoMap.keys) { + // We strip out entries that contain no information + if (!typeInfoMap[key].type.containsAll(inferrer.compiler) || + !typeInfoMap[key].type.isNullable) { + mappings[key] = typeInfoMap[key].type; + } + } + } + if (mappings != null && !mappings.isEmpty) { + return new DictionaryTypeMask(initialType.forwardTo, + initialType.allocationNode, + initialType.allocationElement, + keyType.type, + valueType.type, + mappings); + } else { + return new MapTypeMask(initialType.forwardTo, + initialType.allocationNode, + initialType.allocationElement, + keyType.type, + valueType.type); + } + } + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + if (type.isDictionary != isDictionary) { + return toTypeMask(inferrer); + } else if (type.isDictionary) { + DictionaryTypeMask mask = type; + for (var key in typeInfoMap.keys) { + TypeInformation value = typeInfoMap[key]; + if (!mask.typeMap.containsKey(key) && + !value.type.containsAll(inferrer.compiler) && + !value.type.isNullable) { + return toTypeMask(inferrer); + } + if (mask.typeMap[key] != typeInfoMap[key].type) { + return toTypeMask(inferrer); + } + } + } else if (type.isMap) { + MapTypeMask mask = type; + if (mask.keyType != keyType.type || + mask.valueType != valueType.type) { + return toTypeMask(inferrer); + } + } else { + return toTypeMask(inferrer); + } + + return type; + } + + bool hasStableType(TypeGraphInferrerEngine inferrer) { + return keyType.isStable && + valueType.isStable && + super.hasStableType(inferrer); + } + + String toString() { + return 'Map $type (K:$keyType, V:$valueType) contents $typeInfoMap'; + } +} + +/** + * A [KeyInMapTypeInformation] holds the common type + * for the keys in a [MapTypeInformation] + */ +class KeyInMapTypeInformation extends InferredTypeInformation { + KeyInMapTypeInformation(TypeInformation keyType) : super(keyType); + + accept(TypeInformationVisitor visitor) { + return visitor.visitKeyInMapTypeInformation(this); + } + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + return super.refine(inferrer); + } + + String toString() => 'Key in Map $type'; +} + +/** + * A [ValueInMapTypeInformation] holds the common type + * for the values in a [MapTypeInformation] + */ +class ValueInMapTypeInformation extends InferredTypeInformation { + // [nonNull] is set to true if this value is known to be part of the map. + // Note that only values assigned to a specific key value in dictionary + // mode can ever be marked as [nonNull]. + final bool nonNull; + + ValueInMapTypeInformation(TypeInformation valueType, [this.nonNull = false]) + : super(valueType); + + accept(TypeInformationVisitor visitor) { + return visitor.visitValueInMapTypeInformation(this); + } + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + return nonNull ? super.refine(inferrer) : super.refine(inferrer).nullable(); + } + + String toString() => 'Value in Map $type'; +} + +/** + * A [PhiElementTypeInformation] is an union of + * [ElementTypeInformation], that is local to a method. + */ +class PhiElementTypeInformation extends TypeInformation { + final ast.Node branchNode; + final bool isLoopPhi; + final Element element; + + PhiElementTypeInformation(this.branchNode, this.isLoopPhi, this.element); + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + return inferrer.types.computeTypeMask(assignments); + } + + String toString() => 'Phi $element $type'; + + accept(TypeInformationVisitor visitor) { + return visitor.visitPhiElementTypeInformation(this); + } +} + +class ClosureTypeInformation extends TypeInformation { + final ast.Node node; + final Element element; + + ClosureTypeInformation(this.node, this.element); + + TypeMask refine(TypeGraphInferrerEngine inferrer) { + return inferrer.types.functionType.type; + } + + String toString() => 'Closure $element'; + + accept(TypeInformationVisitor visitor) { + return visitor.visitClosureTypeInformation(this); + } + + bool hasStableType(TypeGraphInferrerEngine inferrer) { + return false; + } +} + +abstract class TypeInformationVisitor { + T visitNarrowTypeInformation(NarrowTypeInformation info); + T visitPhiElementTypeInformation(PhiElementTypeInformation info); + T visitElementInContainerTypeInformation( + ElementInContainerTypeInformation info); + T visitKeyInMapTypeInformation(KeyInMapTypeInformation info); + T visitValueInMapTypeInformation(ValueInMapTypeInformation info); + T visitListTypeInformation(ListTypeInformation info); + T visitMapTypeInformation(MapTypeInformation info); + T visitConcreteTypeInformation(ConcreteTypeInformation info); + T visitStringLiteralTypeInformation(StringLiteralTypeInformation info); + T visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info); + T visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info); + T visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info); + T visitElementTypeInformation(ElementTypeInformation info); + T visitClosureTypeInformation(ClosureTypeInformation info); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart new file mode 100644 index 0000000..95c519b --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart @@ -0,0 +1,398 @@ +// Copyright (c) 2013, 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. + +library dart2js.ir_builder; + +import 'ir_nodes.dart' as ir; +import '../elements/elements.dart'; +import '../dart2jslib.dart'; +import '../source_file.dart'; +import '../tree/tree.dart' as ast; +import '../scanner/scannerlib.dart' show Token; +import '../dart_backend/dart_backend.dart' show DartBackend; +import 'ir_pickler.dart' show Unpickler, IrConstantPool; + +/** + * This task iterates through all resolved elements and builds [ir.Node]s. The + * nodes are stored in the [nodes] map and accessible through [hasIr] and + * [getIr]. + * + * The functionality of the IrNodes is added gradually, therefore elements might + * have an IR or not, depending on the language features that are used. For + * elements that do have an IR, the tree [ast.Node]s and the [Token]s are not + * used in the rest of the compilation. This is ensured by setting the element's + * cached tree to [:null:] and also breaking the token stream to crash future + * attempts to parse. + * + * The type inferrer works on either IR nodes or tree nodes. The IR nodes are + * then translated into the SSA form for optimizations and code generation. + * Long-term, once the IR supports the full language, the backend can be + * re-implemented to work directly on the IR. + */ +class IrBuilderTask extends CompilerTask { + final Map nodes = {}; + + IrBuilderTask(Compiler compiler) : super(compiler); + + String get name => 'IR builder'; + + bool hasIr(Element element) => nodes.containsKey(element.implementation); + + ir.Function getIr(Element element) => nodes[element.implementation]; + + void buildNodes() { + if (!irEnabled()) return; + measure(() { + Map resolved = + compiler.enqueuer.resolution.resolvedElements; + resolved.forEach((Element element, TreeElements elementsMapping) { + if (canBuild(element)) { + element = element.implementation; + + SourceFile sourceFile = elementSourceFile(element); + IrBuilder builder = + new IrBuilder(elementsMapping, compiler, sourceFile); + ir.Function function; + ElementKind kind = element.kind; + if (kind == ElementKind.GENERATIVE_CONSTRUCTOR) { + // TODO(lry): build ir for constructors. + } else if (element.isDeferredLoaderGetter()) { + // TODO(sigurdm): Build ir for deferred loader functions. + } else if (kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY || + kind == ElementKind.FUNCTION || + kind == ElementKind.GETTER || + kind == ElementKind.SETTER) { + function = builder.buildFunction(element); + } else if (kind == ElementKind.FIELD) { + // TODO(lry): build ir for lazy initializers of static fields. + } else { + compiler.internalError(element, 'Unexpected element kind $kind.'); + } + + if (function != null) { + assert(() { + // In host-checked mode, serialize and de-serialize the IrNode. + LibraryElement library = element.declaration.getLibrary(); + IrConstantPool constantPool = IrConstantPool.forLibrary(library); + List data = function.pickle(constantPool); + function = new Unpickler(compiler, constantPool).unpickle(data); + return true; + }); + nodes[element] = function; + } + } + }); + }); + } + + bool irEnabled() { + // TODO(lry): support checked-mode checks. + if (compiler.enableTypeAssertions || + compiler.backend is !DartBackend || + compiler.enableConcreteTypeInference) { + return false; + } + return const bool.fromEnvironment('enable_ir', defaultValue: false); + } + + bool canBuild(Element element) { + // TODO(lry): support lazy initializers. + FunctionElement function = element.asFunctionElement(); + if (function == null) return false; + + // TODO(lry): support functions with parameters. + FunctionSignature signature = function.functionSignature; + if (signature.parameterCount > 0) return false; + + // TODO(kmillikin): support return types. With the current Dart Tree + // emitter they require constructing an AST and tokens from a type element. + if (!signature.type.returnType.isDynamic) return false; + + // TODO(kmillikin): support getters and setters and static class members. + // With the current Dart Tree emitter they just require recognizing them + // and generating the correct syntax. + if (element.isGetter() || element.isSetter()) return false; + if (element.enclosingElement.isClass()) return false; + + // TODO(lry): support native functions (also in [visitReturn]). + if (function.isNative()) return false; + + return true; + } + + bool get inCheckedMode { + bool result = false; + assert((result = true)); + return result; + } + + SourceFile elementSourceFile(Element element) { + if (element is FunctionElement) { + FunctionElement functionElement = element; + if (functionElement.patch != null) element = functionElement.patch; + } + return element.getCompilationUnit().script.file; + } +} + +/** + * A tree visitor that builds [IrNodes]. The visit methods add statements using + * to the [builder] and return the last added statement for trees that represent + * an expression. + */ +class IrBuilder extends ResolvedVisitor { + final SourceFile sourceFile; + ir.Continuation returnContinuation = null; + + // The IR builder maintains a context, which is an expression with a hole in + // it. The hole represents the focus where new expressions can be added. + // The context is implemented by 'root' which is the root of the expression + // and 'current' which is the expression that immediately contains the hole. + // Not all expressions have a hole (e.g., invocations, which always occur in + // tail position, do not have a hole). Expressions with a hole have a plug + // method. + // + // Conceptually, visiting a statement takes a context as input and returns + // either a new context or else an expression without a hole if all + // control-flow paths through the statement have exited. An expression + // without a hole is represented by a (root, current) pair where root is the + // expression and current is null. + // + // Conceptually again, visiting an expression takes a context as input and + // returns either a pair of a new context and a definition denoting + // the expression's value, or else an expression without a hole if all + // control-flow paths through the expression have exited. + // + // We do not pass and return contexts, rather we use the current context + // (root, current) as the visitor state and mutate current. Visiting a + // statement returns null; visiting an expression optionally returns the + // definition denoting its value. + ir.Expression root = null; + ir.Expression current = null; + + IrBuilder(TreeElements elements, Compiler compiler, this.sourceFile) + : super(elements, compiler); + + /** + * Builds the [ir.Function] for a function element. In case the function + * uses features that cannot be expressed in the IR, this function returns + * [:null:]. + */ + ir.Function buildFunction(FunctionElement functionElement) { + return nullIfGiveup(() => buildFunctionInternal(functionElement)); + } + + ir.Function buildFunctionInternal(FunctionElement functionElement) { + assert(invariant(functionElement, functionElement.isImplementation)); + ast.FunctionExpression function = functionElement.parseNode(compiler); + assert(function != null); + assert(!function.modifiers.isExternal()); + assert(elements[function] != null); + + returnContinuation = new ir.Continuation.retrn(); + root = current = null; + function.body.accept(this); + ensureReturn(function); + int endPosition = function.getEndToken().charOffset; + int namePosition = elements[function].position().charOffset; + return + new ir.Function(endPosition, namePosition, returnContinuation, root); + } + + ConstantSystem get constantSystem => compiler.backend.constantSystem; + + bool get isOpen => root == null || current != null; + + // Plug an expression into the 'hole' in the context being accumulated. The + // empty context (just a hole) is represented by root (and current) being + // null. Since the hole in the current context is filled by this function, + // the new hole must be in the newly added expression---which becomes the + // new value of current. + void add(ir.Expression expr) { + if (root == null) { + root = current = expr; + } else if (current != null) { + current = current.plug(expr); + } + } + + /** + * Add an explicit [:return null:] for functions that don't have a return + * statement on each branch. This includes functions with an empty body, + * such as [:foo(){ }:]. + */ + void ensureReturn(ast.FunctionExpression node) { + if (!isOpen) return; + ir.Constant constant = new ir.Constant(constantSystem.createNull()); + add(new ir.LetPrim(constant)); + add(new ir.InvokeContinuation(returnContinuation, constant)); + current = null; + } + + ir.Definition visitEmptyStatement(ast.EmptyStatement node) { + assert(isOpen); + return null; + } + + ir.Definition visitBlock(ast.Block node) { + assert(isOpen); + for (var n in node.statements.nodes) { + n.accept(this); + if (!isOpen) return null; + } + return null; + } + + // Build(Return) = let val x = null in InvokeContinuation(return, x) + // Build(Return(e)) = C[InvokeContinuation(return, x)] + // where (C, x) = Build(e) + ir.Definition visitReturn(ast.Return node) { + assert(isOpen); + // TODO(lry): support native returns. + if (node.beginToken.value == 'native') return giveup(); + ir.Definition value; + if (node.expression == null) { + value = new ir.Constant(constantSystem.createNull()); + add(new ir.LetPrim(value)); + } else { + value = node.expression.accept(this); + if (!isOpen) return null; + } + add(new ir.InvokeContinuation(returnContinuation, value)); + current = null; + return null; + } + + // For all simple literals: + // Build(Literal(c)) = (let val x = Constant(c) in [], x) + ir.Definition visitLiteralBool(ast.LiteralBool node) { + assert(isOpen); + ir.Constant constant = + new ir.Constant(constantSystem.createBool(node.value)); + add(new ir.LetPrim(constant)); + return constant; + } + + ir.Definition visitLiteralDouble(ast.LiteralDouble node) { + assert(isOpen); + ir.Constant constant = + new ir.Constant(constantSystem.createDouble(node.value)); + add(new ir.LetPrim(constant)); + return constant; + } + + ir.Definition visitLiteralInt(ast.LiteralInt node) { + assert(isOpen); + ir.Constant constant = + new ir.Constant(constantSystem.createInt(node.value)); + add(new ir.LetPrim(constant)); + return constant; + } + + + ir.Definition visitLiteralNull(ast.LiteralNull node) { + assert(isOpen); + ir.Constant constant = new ir.Constant(constantSystem.createNull()); + add(new ir.LetPrim(constant)); + return constant; + } + + // TODO(kmillikin): other literals. Strings require quoting and escaping + // in the Dart backend. + // LiteralString + // LiteralList + // LiteralMap + // LiteralMapEntry + // LiteralSymbol + + ir.Definition visitAssert(ast.Send node) { + return giveup(); + } + + ir.Definition visitClosureSend(ast.Send node) { + return giveup(); + } + + ir.Definition visitDynamicSend(ast.Send node) { + return giveup(); + } + + ir.Definition visitGetterSend(ast.Send node) { + return giveup(); + } + + ir.Definition visitOperatorSend(ast.Send node) { + return giveup(); + } + + // Build(StaticSend(f, a...)) = C[InvokeStatic(f, x...)] + // where (C, x...) = BuildList(a...) + ir.Definition visitStaticSend(ast.Send node) { + assert(isOpen); + Element element = elements[node]; + // TODO(lry): support static fields. (separate IR instruction?) + if (element.isField() || element.isGetter()) return giveup(); + // TODO(lry): support constructors / factory calls. + if (element.isConstructor()) return giveup(); + // TODO(lry): support foreign functions. + if (element.isForeign(compiler)) return giveup(); + // TODO(lry): for elements that could not be resolved emit code to throw a + // [NoSuchMethodError]. + if (element.isErroneous()) return giveup(); + // TODO(lry): generate IR for object identicality. + if (element == compiler.identicalFunction) giveup(); + + Selector selector = elements.getSelector(node); + // TODO(lry): support named arguments + if (selector.namedArgumentCount != 0) return giveup(); + + // TODO(kmillikin): support a receiver: A.m(). + if (node.receiver != null) return giveup(); + + List arguments = []; + // TODO(lry): support default arguments, need support for locals. + bool succeeded = selector.addArgumentsToList( + node.arguments, arguments, element.implementation, + // Guard against visiting arguments after an argument expression throws. + (node) => isOpen ? node.accept(this) : null, + (node) => giveup(), + compiler); + if (!succeeded) { + // TODO(lry): generate code to throw a [WrongArgumentCountError]. + return giveup(); + } + if (!isOpen) return null; + ir.Parameter v = new ir.Parameter(); + ir.Continuation k = new ir.Continuation(v); + ir.Expression invoke = + new ir.InvokeStatic(element, selector, k, arguments); + add(new ir.LetCont(k, invoke)); + return v; + } + + ir.Definition visitSuperSend(ast.Send node) { + return giveup(); + } + + ir.Definition visitTypeReferenceSend(ast.Send node) { + return giveup(); + } + + static final String ABORT_IRNODE_BUILDER = "IrNode builder aborted"; + + ir.Definition giveup() => throw ABORT_IRNODE_BUILDER; + + ir.Function nullIfGiveup(ir.Function action()) { + try { + return action(); + } catch(e) { + if (e == ABORT_IRNODE_BUILDER) return null; + rethrow; + } + } + + void internalError(String reason, {ast.Node node}) { + giveup(); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart new file mode 100644 index 0000000..b0566e3 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart @@ -0,0 +1,256 @@ +// Copyright (c) 2013, 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. + +// IrNodes are kept in a separate library to have precise control over their +// dependencies on other parts of the system. +library dart2js.ir_nodes; + +import '../dart2jslib.dart' as dart2js show Constant; +import '../elements/elements.dart' show FunctionElement, LibraryElement; +import 'ir_pickler.dart' show Pickler, IrConstantPool; +import '../universe/universe.dart' show Selector, SelectorKind; +import '../util/util.dart' show Spannable; + +abstract class Node { + static int hashCount = 0; + final int hashCode = hashCount = (hashCount + 1) & 0x3fffffff; + + accept(Visitor visitor); +} + +abstract class Expression extends Node { + Expression plug(Expression expr) => throw 'impossible'; +} + +/// The base class of things that variables can refer to: primitives, +/// continuations, function and continuation parameters, etc. +abstract class Definition extends Node { + // The head of a linked-list of occurrences, in no particular order. + Reference firstRef = null; + + bool get hasAtMostOneUse => firstRef == null || firstRef.nextRef == null; + bool get hasExactlyOneUse => firstRef != null && firstRef.nextRef == null; +} + +abstract class Primitive extends Definition { +} + +/// Operands to invocations and primitives are always variables. They point to +/// their definition and are linked into a list of occurrences. +class Reference { + Definition definition; + Reference nextRef = null; + + Reference(this.definition) { + nextRef = definition.firstRef; + definition.firstRef = this; + } +} + +/// Binding a value (primitive or constant): 'let val x = V in E'. The bound +/// value is in scope in the body. +/// During one-pass construction a LetVal with an empty body is used to +/// represent one-level context 'let val x = V in []'. +class LetPrim extends Expression { + final Primitive primitive; + Expression body = null; + + LetPrim(this.primitive); + + Expression plug(Expression expr) { + assert(body == null); + return body = expr; + } + + accept(Visitor visitor) => visitor.visitLetPrim(this); +} + + +/// Binding a continuation: 'let cont k(v) = E in E'. The bound continuation +/// is in scope in the body and the continuation parameter is in scope in the +/// continuation body. +/// During one-pass construction a LetCont with an empty continuation body is +/// used to represent the one-level context 'let cont k(v) = [] in E'. +class LetCont extends Expression { + final Continuation continuation; + final Expression body; + + LetCont(this.continuation, this.body); + + Expression plug(Expression expr) { + assert(continuation.body == null); + return continuation.body = expr; + } + + accept(Visitor visitor) => visitor.visitLetCont(this); +} + +/// Invoke a static function in tail position. +class InvokeStatic extends Expression { + final FunctionElement target; + + /** + * The selector encodes how the function is invoked: number of positional + * arguments, names used in named arguments. This information is required + * to build the [StaticCallSiteTypeInformation] for the inference graph. + */ + final Selector selector; + + final Reference continuation; + final List arguments; + + InvokeStatic(this.target, this.selector, Continuation cont, + List args) + : continuation = new Reference(cont), + arguments = args.map((t) => new Reference(t)).toList(growable: false) { + assert(selector.kind == SelectorKind.CALL); + assert(selector.name == target.name); + } + + accept(Visitor visitor) => visitor.visitInvokeStatic(this); +} + +/// Invoke a continuation in tail position. +class InvokeContinuation extends Expression { + final Reference continuation; + final Reference argument; + + InvokeContinuation(Continuation cont, Definition arg) + : continuation = new Reference(cont), + argument = new Reference(arg); + + accept(Visitor visitor) => visitor.visitInvokeContinuation(this); +} + +class Constant extends Primitive { + final dart2js.Constant value; + + Constant(this.value); + + accept(Visitor visitor) => visitor.visitConstant(this); +} + +class Parameter extends Primitive { + Parameter(); + + accept(Visitor visitor) => visitor.visitParameter(this); +} + +/// Continuations are normally bound by 'let cont'. A continuation with no +/// parameter (or body) is used to represent a function's return continuation. +/// The return continuation is bound by the Function, not by 'let cont'. +class Continuation extends Definition { + final Parameter parameter; + Expression body = null; + + Continuation(this.parameter); + + Continuation.retrn() : parameter = null; + + accept(Visitor visitor) => visitor.visitContinuation(this); +} + +/// A function definition, consisting of parameters and a body. The parameters +/// include a distinguished continuation parameter. +class Function extends Node { + final int endOffset; + final int namePosition; + + final Continuation returnContinuation; + final Expression body; + + Function(this.endOffset, this.namePosition, this.returnContinuation, + this.body); + + List pickle(IrConstantPool constantPool) { + return new Pickler(constantPool).pickle(this); + } + + accept(Visitor visitor) => visitor.visitFunction(this); +} + +abstract class Visitor { + // Abstract classes. + T visitNode(Node node) => node.accept(this); + T visitExpression(Expression node) => visitNode(node); + T visitDefinition(Definition node) => visitNode(node); + T visitPrimitive(Primitive node) => visitDefinition(node); + + // Concrete classes. + T visitFunction(Function node) => visitNode(node); + + T visitLetPrim(LetPrim node) => visitExpression(node); + T visitLetCont(LetCont node) => visitExpression(node); + T visitInvokeStatic(InvokeStatic node) => visitExpression(node); + T visitInvokeContinuation(InvokeContinuation node) => visitExpression(node); + + T visitConstant(Constant node) => visitPrimitive(node); + T visitParameter(Parameter node) => visitPrimitive(node); + T visitContinuation(Continuation node) => visitDefinition(node); +} + +/// Generate a Lisp-like S-expression representation of an IR node as a string. +/// The representation is not pretty-printed, but it can easily be quoted and +/// dropped into the REPL of one's favorite Lisp or Scheme implementation to be +/// pretty-printed. +class SExpressionStringifier extends Visitor { + final Map names = {}; + + int _valueCounter = 0; + int _continuationCounter = 0; + + String newValueName() => 'v${_valueCounter++}'; + String newContinuationName() => 'k${_continuationCounter++}'; + + String visitFunction(Function node) { + names[node.returnContinuation] = 'return'; + return '(Function ${node.body.accept(this)})'; + } + + String visitLetPrim(LetPrim expr) { + String name = newValueName(); + names[expr.primitive] = name; + String value = expr.primitive.accept(this); + String body = expr.body.accept(this); + return '(LetPrim $name $value) $body'; + } + + String visitLetCont(LetCont expr) { + String cont = newContinuationName(); + String param = newValueName(); + names[expr.continuation] = cont; + names[expr.continuation.parameter] = param; + String contBody = expr.continuation.body.accept(this); + String body = expr.body == null ? 'null' : expr.body.accept(this); + return '(LetCont ($cont $param) $contBody) $body'; + } + + String visitInvokeStatic(InvokeStatic expr) { + String name = expr.target.name; + String cont = names[expr.continuation.definition]; + List args = + expr.arguments.map((v) => names[v.definition]).toList(growable: false); + return '(InvokeStatic $name $cont ${args.join(' ')})'; + } + + String visitInvokeContinuation(InvokeContinuation expr) { + String cont = names[expr.continuation.definition]; + String arg = names[expr.argument.definition]; + return '(InvokeContinuation $cont $arg)'; + } + + String visitConstant(Constant triv) { + return '(Constant ${triv.value})'; + } + + String visitParameter(Parameter triv) { + // Parameters are visited directly in visitLetCont. + return '(Unexpected Parameter)'; + } + + String visitContinuation(Continuation triv) { + // Continuations are visited directly in visitLetCont. + return '(Unexpected Continuation)'; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_pickler.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_pickler.dart new file mode 100644 index 0000000..2a6c5ed --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_pickler.dart @@ -0,0 +1,457 @@ +// Copyright (c) 2013, 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. + +library dart2js.ir_pickler; + +import 'ir_nodes.dart' as ir; +import '../dart2jslib.dart' show + Constant, FalseConstant, TrueConstant, IntConstant, DoubleConstant, + StringConstant, NullConstant, ListConstant, MapConstant, + InterceptorConstant, DummyConstant, FunctionConstant, TypeConstant, + ConstructedConstant, + ConstantVisitor, ConstantSystem, + Compiler, NO_LOCATION_SPANNABLE; +import 'dart:typed_data' show ByteData, Endianness, Uint8List; +import 'dart:convert' show UTF8; +import '../tree/tree.dart' as ast show + DartString, LiteralDartString, RawSourceDartString, EscapedSourceDartString, + ConsDartString; +import '../elements/elements.dart' show + Element, LibraryElement, FunctionElement; +import '../universe/universe.dart' show Selector, TypedSelector, SelectorKind; + +part 'ir_unpickler.dart'; + +/* The int(entries) counts expression nodes, which might potentially be + * referred to in a back reference. + * + * pickle ::= int(entries) function + * + * function ::= int(endSourceOffset) int(namePosition) node(body) + * + * int ::= see [writeInt] for number encoding + * + * string ::= byte(STRING_ASCII) int(length) {byte(ascii)} + * | byte(STRING_UTF8) int(length) {byte(utf8)} + * + * node ::= byte(NODE_CONSTANT) constant node(next) + * | byte(NODE_LET_CONT) node(next) node(body) + * | byte(NODE_INVOKE_STATIC) element selector + * reference(continuation) {reference(argument)} + * | byte(NODE_INVOKE_CONTINUATION) reference(continuation) + * reference(argument) + * + * reference ::= int(indexDelta) + * + * constant ::= byte(CONST_BOOL) byte(0 or 1) + * | byte(CONST_DOUBLE) byte{8} + * | byte(CONST_INT) int(value) + * | byte(CONST_STRING_LITERAL) string + * | byte(CONST_STRING_RAW) string int(length) + * | byte(CONST_STRING_ESCAPED) string int(length) + * | byte(CONST_STRING_CONS) constant(left) constant(right) + * | byte(CONST_NULL) + * + * selector ::= byte(BACKREFERENCE) reference + * | byte(SELECTOR_UNTYPED) int(kind) string(name) element(library) + * int(argumentsCount) int(namedArgumentsCount) + * {string(parameterName)} + * + * element ::= int(constantPoolIndex) + */ +class Pickles { + static const int BACKREFERENCE = 1; + + static const int STRING_ASCII = BACKREFERENCE + 1; + static const int STRING_UTF8 = STRING_ASCII + 1; + + static const int FIRST_NODE_TAG = STRING_UTF8 + 1; + static const int NODE_CONSTANT = FIRST_NODE_TAG; + static const int NODE_LET_CONT = NODE_CONSTANT + 1; + static const int NODE_INVOKE_STATIC = NODE_LET_CONT + 1; + static const int NODE_INVOKE_CONTINUATION = NODE_INVOKE_STATIC + 1; + static const int LAST_NODE_TAG = NODE_INVOKE_CONTINUATION; + + static const int FIRST_CONST_TAG = LAST_NODE_TAG + 1; + static const int CONST_BOOL = FIRST_CONST_TAG; + static const int CONST_INT = CONST_BOOL + 1; + static const int CONST_DOUBLE = CONST_INT + 1; + static const int CONST_STRING_LITERAL = CONST_DOUBLE + 1; + static const int CONST_STRING_RAW = CONST_STRING_LITERAL + 1; + static const int CONST_STRING_ESCAPED = CONST_STRING_RAW + 1; + static const int CONST_STRING_CONS = CONST_STRING_ESCAPED + 1; + static const int CONST_NULL = CONST_STRING_CONS + 1; + static const int LAST_CONST_TAG = CONST_NULL; + + static const int FIRST_SELECTOR_TAG = LAST_CONST_TAG + 1; + static const int SELECTOR_UNTYPED = FIRST_SELECTOR_TAG; + static const int LAST_SELECTOR_TAG = SELECTOR_UNTYPED; + + static const int LAST_TAG = LAST_SELECTOR_TAG; + + static final List selectorKindFromId = _selectorKindFromId(); + + static List _selectorKindFromId() { + List result = [ + SelectorKind.GETTER, + SelectorKind.SETTER, + SelectorKind.CALL, + SelectorKind.OPERATOR, + SelectorKind.INDEX]; + for (int i = 0; i < result.length; i++) { + assert(result[i].hashCode == i); + } + return result; + } +} + +class IrConstantPool { + static Map _constantPools = + {}; + + static IrConstantPool forLibrary(LibraryElement library) { + return _constantPools.putIfAbsent(library, () => new IrConstantPool()); + } + + /** + * The entries of the constant pool. Method [add] ensures that an object + * is only added once to this list. + */ + List entries = []; + + /** + * This map is the inverse of [entries], it stores the index of each object. + */ + Map entryIndex = {}; + + int add(Object o) { + return entryIndex.putIfAbsent(o, () { + entries.add(o); + return entries.length - 1; + }); + } + + Object get(int index) => entries[index]; +} + +/** + * The [Pickler] serializes [ir.Node]s to a byte array. + */ +class Pickler extends ir.Visitor { + ConstantPickler constantPickler; + + IrConstantPool constantPool; + + Pickler(this.constantPool) { + assert(Pickles.LAST_TAG <= 0xff); + constantPickler = new ConstantPickler(this); + } + + static final int INITIAL_SIZE = 8; + static final int MAX_GROW_RATE = 4096; + + List data; + + /** Offset of the next byte that will be written. */ + int offset; + + /** Stores the index for emitted entries that might be back-referenced. */ + Map emitted; + + /** A counter for entries in the [emitted] map. */ + int index; + + /** + * This buffer is used in [writeConstDouble] to obtain a byte representation + * for doubles. + */ + ByteData doubleData = new ByteData(8); + + List pickle(ir.Function function) { + data = new Uint8List(INITIAL_SIZE); + offset = 0; + emitted = {}; + index = 0; + function.accept(this); + + int sizeOffset = offset; + writeInt(emitted.length); + int sizeBytes = offset - sizeOffset; + + // The array is longer than necessary, create a copy with the actual size. + Uint8List result = new Uint8List(offset); + // Emit the number or entries in the beginning. + for (int i = 0, j = sizeOffset; i < sizeBytes; i++, j++) { + result[i] = data[j]; + } + for (int i = sizeBytes, j = 0; i < offset; i++, j++) { + result[i] = data[j]; + } + return result; + } + + void resize(int newSize) { + Uint8List newData = new Uint8List(newSize); + for (int i = 0; i < data.length; i++) { + newData[i] = data[i]; + } + data = newData; + } + + void ensureCapacity() { + // (offset == data.length-1) is still OK, the byte at [offset] has not yet + // been written. + int size = data.length; + if (offset < size) return; + if (size > MAX_GROW_RATE) { + size += MAX_GROW_RATE; + } else { + size *= 2; + } + resize(size); + } + + static isByte(int byte) => 0 <= byte && byte <= 0xff; + + void writeByte(int byte) { + assert(isByte(byte)); + ensureCapacity(); + data[offset++] = byte; + } + + /** + * Writes integers to the buffer. + * + * The least significant bit of the serialized data encodes the sign. Each + * byte contains 7 bits of data and one bit indicating if it is the last byte + * of the number. + */ + void writeInt(int n) { + bool isNegative = n < 0; + n = isNegative ? -n : n; + // Least significant bit is the sign. + int bits = (n << 1) | (isNegative ? 1 : 0); + do { + int next = bits & 0x7f; + bits >>= 7; + bool hasMore = bits != 0; + next = (next << 1) | (hasMore ? 1 : 0); + writeByte(next); + } while (bits != 0); + } + + void writeString(String s) { + int startOffset = offset; + writeByte(Pickles.STRING_ASCII); + writeInt(s.length); + for (int i = 0; i < s.length; i++) { + int c = s.codeUnitAt(i); + if (c < 0x80) { + writeByte(c); + } else { + // Strings with non-ascii characters are encoded using UTF-8. + writeUtf8String(s, startOffset); + return; + } + } + } + + void writeUtf8String(String s, int startOffset) { + offset = startOffset; + writeByte(Pickles.STRING_UTF8); + List bytes = UTF8.encode(s); + writeInt(bytes.length); + for (int i = 0; i < bytes.length; i++) { + writeByte(bytes[i]); + } + } + + /** + * This function records [entry] in the [emitted] table. It needs to be + * invoked when pickling an object which might later on be used in a back + * reference, for example expression nodes or selectors. + */ + void recordForBackReference(Object entry) { + assert(emitted[entry] == null); + emitted[entry] = index++; + } + + void writeBackReference(Object entry) { + int entryIndex = emitted[entry]; + writeInt(index - entryIndex); + } + + void writeBackReferenceList(List entries) { + writeInt(entries.length); + for (int i = 0; i < entries.length; i++) { + writeBackReference(entries[i]); + } + } + + void writeConstBool(bool b) { + writeByte(Pickles.CONST_BOOL); + writeByte(b ? 1 : 0); + } + + void writeConstInt(int n) { + writeByte(Pickles.CONST_INT); + writeInt(n); + } + + void writeConstDouble(double d) { + writeByte(Pickles.CONST_DOUBLE); + doubleData.setFloat64(0, d, Endianness.BIG_ENDIAN); + for (int i = 0; i < 8; i++) { + writeByte(doubleData.getUint8(i)); + } + } + + void writeDartString(ast.DartString s) { + if (s is ast.LiteralDartString) { + writeByte(Pickles.CONST_STRING_LITERAL); + writeString(s.string); + } else if (s is ast.RawSourceDartString) { + writeByte(Pickles.CONST_STRING_RAW); + writeString(s.source); + writeInt(s.length); + } else if (s is ast.EscapedSourceDartString) { + writeByte(Pickles.CONST_STRING_ESCAPED); + writeString(s.source); + writeInt(s.length); + } else if (s is ast.ConsDartString) { + writeByte(Pickles.CONST_STRING_CONS); + writeDartString(s.left); + writeDartString(s.right); + } else { + throw "Unexpected DartString: $s"; + } + } + + void writeConstNull() { + writeByte(Pickles.CONST_NULL); + } + + void writeElement(Element element) { + writeInt(constantPool.add(element)); + } + + void writeSelector(Selector selector) { + if (emitted.containsKey(selector)) { + writeByte(Pickles.BACKREFERENCE); + writeBackReference(selector); + } else { + recordForBackReference(selector); + assert(selector is !TypedSelector); + writeByte(Pickles.SELECTOR_UNTYPED); + writeInt(selector.kind.hashCode); + writeString(selector.name); + writeElement(selector.library); + writeInt(selector.argumentCount); + int namedArgumentsCount = selector.namedArguments.length; + writeInt(namedArgumentsCount); + for (int i = 0; i < namedArgumentsCount; i++) { + writeString(selector.namedArguments[i]); + } + } + } + + void visitFunction(ir.Function node) { + writeInt(node.endOffset); + writeInt(node.namePosition); + // The continuation parameter is bound in the body. + recordForBackReference(node.returnContinuation); + node.body.accept(this); + } + + void visitLetPrim(ir.LetPrim node) { + node.primitive.accept(this); + // The right-hand side is bound in the body. + recordForBackReference(node.primitive); + node.body.accept(this); + } + + void visitLetCont(ir.LetCont node) { + // There are two choices of which expression tree to write first---the + // continuation body or the LetCont body. The unpickler will unpickle the + // the first recursively and the second iteratively. Since the hole in + // LetCont contexts is in the continuation body, the continuation should be + // written second. + writeByte(Pickles.NODE_LET_CONT); + // The continuation is bound in the body. + recordForBackReference(node.continuation); + node.body.accept(this); + // The continuation parameter is bound in the continuation's body. + recordForBackReference(node.continuation.parameter); + node.continuation.body.accept(this); + } + + void visitInvokeStatic(ir.InvokeStatic node) { + writeByte(Pickles.NODE_INVOKE_STATIC); + writeElement(node.target); + writeSelector(node.selector); + // TODO(lry): compact encoding when the arity of the selector and the + // arguments list are the same + writeBackReference(node.continuation.definition); + writeBackReferenceList(node.arguments.map( + (a) => a.definition).toList(growable: false)); + } + + void visitInvokeContinuation(ir.InvokeContinuation node) { + writeByte(Pickles.NODE_INVOKE_CONTINUATION); + writeBackReference(node.continuation.definition); + writeBackReference(node.argument.definition); + } + + void visitConstant(ir.Constant node) { + writeByte(Pickles.NODE_CONSTANT); + node.value.accept(constantPickler); + } + + void visitNode(ir.Node node) { + throw "Unexpected $node in pickler."; + } +} + +/** + * A visitor for constants which writes the constant values to its [Pickler]. + */ +class ConstantPickler extends ConstantVisitor { + + final Pickler pickler; + ConstantPickler(this.pickler); + + void visitFalse(FalseConstant constant) { + pickler.writeConstBool(false); + } + + void visitTrue(TrueConstant constant) { + pickler.writeConstBool(true); + } + + void visitInt(IntConstant constant) { + pickler.writeConstInt(constant.value); + } + + void visitDouble(DoubleConstant constant) { + pickler.writeConstDouble(constant.value); + } + + void visitString(StringConstant constant) { + pickler.writeDartString(constant.value); + } + + void visitNull(NullConstant constant) { + pickler.writeConstNull(); + } + + void visitList(ListConstant constant) => abort(constant); + void visitMap(MapConstant constant) => abort(constant); + void visitInterceptor(InterceptorConstant constant) => abort(constant); + void visitDummy(DummyConstant constant) => abort(constant); + void visitFunction(FunctionConstant constant) => abort(constant); + void visitType(TypeConstant constant) => abort(constant); + void visitConstructed(ConstructedConstant constant) => abort(constant); + + void abort(Constant value) => throw "Can not pickle constant $value"; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_unpickler.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_unpickler.dart new file mode 100644 index 0000000..e560997 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ir/ir_unpickler.dart @@ -0,0 +1,259 @@ +// Copyright (c) 2013, 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. + +part of dart2js.ir_pickler; + +class Unpickler { + final Compiler compiler; + + final IrConstantPool constantPool; + + Unpickler(this.compiler, this.constantPool); + + List data; + + int offset; + + /** For each entry index, the corresponding unpickled object. */ + List unpickled; + + /** Counter for entries in [unpickled]. */ + int index; + + /** + * This buffer is used in [readConstant] to reconstruct a double value from + * a sequence of bytes. + */ + ByteData doubleData = new ByteData(8); + + ConstantSystem get constantSystem => compiler.backend.constantSystem; + + // A partially constructed expression is one that has a single 'hole' where + // there is an expression missing. Just like the IR builder, the unpickler + // represents such an expression by its root and by the 'current' expression + // that immediately contains the hole. If there is no hole (e.g., an + // expression in tail position has been seen), then current is null. + ir.Expression root; + ir.Expression current; + + ir.Function unpickle(List data) { + this.data = data; + offset = 0; + int numEntries = readInt(); + unpickled = new List(numEntries); + index = 0; + root = current = null; + return readFunctionNode(); + } + + int readByte() { + return data[offset++]; + } + + int readInt() { + int result = 0; + int next; + for (int i = 0; true; i += 7) { + next = readByte(); + result |= (next >> 1) << i; + if ((next & 1) == 0) break; + } + bool isNegative = (result & 1) == 1; + result >>= 1; + return isNegative ? -result : result; + } + + String readString() { + int tag = readByte(); + int length = readInt(); + List bytes = new Uint8List(length); + for (int i = 0; i < length; i++) { + bytes[i] = readByte(); + } + if (tag == Pickles.STRING_ASCII) { + return new String.fromCharCodes(bytes); + } else if (tag == Pickles.STRING_UTF8) { + return UTF8.decode(bytes); + } else { + compiler.internalError(NO_LOCATION_SPANNABLE, + "Unexpected string tag: $tag"); + return null; + } + } + + Element readElement() { + int elementIndex = readInt(); + return constantPool.get(elementIndex); + } + + Selector readSelector() { + int tag = readByte(); + if (tag == Pickles.BACKREFERENCE) { + return readBackReference(); + } + assert(tag == Pickles.SELECTOR_UNTYPED); + int entryIndex = index++; + SelectorKind kind = Pickles.selectorKindFromId[readInt()]; + String name = readString(); + Element library = readElement(); + int argumentsCount = readInt(); + int namedArgumentsCount = readInt(); + List namedArguments = new List(namedArgumentsCount); + for (int i = 0; i < namedArgumentsCount; i++) { + namedArguments[i] = readString(); + } + Selector result = new Selector( + kind, name, library, argumentsCount, namedArguments); + unpickled[entryIndex] = result; + return result; + } + + void addExpression(ir.Expression expr) { + if (root == null) { + root = current = expr; + } else { + current = current.plug(expr); + } + } + + // Read a single expression and plug it into the outer context. + ir.Expression readExpressionNode() { + int tag = readByte(); + switch (tag) { + case Pickles.NODE_CONSTANT: + ir.Definition constant = readConstantNode(); + unpickled[index++] = constant; + addExpression(new ir.LetPrim(constant)); + break; + case Pickles.NODE_LET_CONT: + ir.Parameter parameter = new ir.Parameter(); + ir.Continuation continuation = new ir.Continuation(parameter); + unpickled[index++] = continuation; + ir.Expression body = readDelimitedExpressionNode(); + unpickled[index++] = parameter; + addExpression(new ir.LetCont(continuation, body)); + break; + case Pickles.NODE_INVOKE_STATIC: + addExpression(readInvokeStaticNode()); + current = null; + break; + case Pickles.NODE_INVOKE_CONTINUATION: + addExpression(readInvokeContinuationNode()); + current = null; + break; + default: + compiler.internalError(NO_LOCATION_SPANNABLE, + "Unexpected expression entry tag: $tag"); + break; + } + } + + // Iteratively read expressions until an expression in a tail position + // (e.g., an invocation) is found. Do not change the outer context. + ir.Expression readDelimitedExpressionNode() { + ir.Expression previous_root = root; + ir.Expression previous_current = current; + root = current = null; + do { + readExpressionNode(); + } while (current != null); + ir.Expression result = root; + root = previous_root; + current = previous_current; + return result; + } + + Object readBackReference() { + int indexDelta = readInt(); + int entryIndex = index - indexDelta; + assert(unpickled[entryIndex] != null); + return unpickled[entryIndex]; + } + + List readBackReferenceList() { + int length = readInt(); + List result = new List(length); + for (int i = 0; i < length; i++) { + result[i] = readBackReference(); + } + return result; + } + + ir.Function readFunctionNode() { + int endOffset = readInt(); + int namePosition = readInt(); + // There is implicitly a return continuation which can be the target of + // back references. + ir.Continuation continuation = new ir.Continuation.retrn(); + unpickled[index++] = continuation; + + ir.Expression body = readDelimitedExpressionNode(); + return new ir.Function(endOffset, namePosition, continuation, body); + } + + ir.Constant readConstantNode() { + Constant constant = readConstant(); + return new ir.Constant(constant); + } + + ir.InvokeStatic readInvokeStaticNode() { + FunctionElement functionElement = readElement(); + Selector selector = readSelector(); + ir.Continuation continuation = readBackReference(); + List arguments = readBackReferenceList(); + return new ir.InvokeStatic(functionElement, selector, continuation, + arguments); + } + + ir.InvokeContinuation readInvokeContinuationNode() { + ir.Continuation continuation = readBackReference(); + ir.Definition argument = readBackReference(); + return new ir.InvokeContinuation(continuation, argument); + } + + Constant readConstant() { + int tag = readByte(); + switch(tag) { + case Pickles.CONST_BOOL: + return constantSystem.createBool(readByte() == 1); + case Pickles.CONST_INT: + return constantSystem.createInt(readInt()); + case Pickles.CONST_DOUBLE: + for (int i = 0; i < 8; i++) { + doubleData.setUint8(i, readByte()); + } + double value = doubleData.getFloat64(0, Endianness.BIG_ENDIAN); + return constantSystem.createDouble(value); + case Pickles.CONST_STRING_LITERAL: + case Pickles.CONST_STRING_RAW: + case Pickles.CONST_STRING_ESCAPED: + case Pickles.CONST_STRING_CONS: + return constantSystem.createString(readDartString(tag)); + case Pickles.CONST_NULL: + return constantSystem.createNull(); + default: + compiler.internalError(NO_LOCATION_SPANNABLE, + "Unexpected constant tag: $tag"); + return null; + } + } + + ast.DartString readDartString(int tag) { + switch(tag) { + case Pickles.CONST_STRING_LITERAL: + return new ast.LiteralDartString(readString()); + case Pickles.CONST_STRING_RAW: + return new ast.RawSourceDartString(readString(), readInt()); + case Pickles.CONST_STRING_ESCAPED: + return new ast.EscapedSourceDartString(readString(), readInt()); + case Pickles.CONST_STRING_CONS: + return new ast.ConsDartString( + readDartString(readByte()), readDartString(readByte())); + default: + compiler.internalError(NO_LOCATION_SPANNABLE, + "Unexpected dart string tag: $tag"); + return null; + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/builder.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/builder.dart new file mode 100644 index 0000000..5ae235e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/builder.dart @@ -0,0 +1,832 @@ +// Copyright (c) 2013, 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. + +// Utilities for building JS ASTs at runtime. Contains a builder class +// and a parser that parses part of the language. + +part of js; + +class JsBuilder { + const JsBuilder(); + + // Parse a bit of JavaScript, and return an expression. + // See the MiniJsParser class. + // You can provide an expression or a list of expressions, which will be + // interpolated into the source at the '#' signs. + Expression call(String source, [var expression]) { + var result = new MiniJsParser(source).expression(); + if (expression == null) return result; + + List expressions; + if (expression is List) { + expressions = expression; + } else { + expressions = [expression]; + } + if (expressions.length != result.interpolatedExpressions.length) { + throw "Unmatched number of interpolated expressions"; + } + for (int i = 0; i < expressions.length; i++) { + result.interpolatedExpressions[i].value = expressions[i]; + } + + return result.value; + } + + // Parse JavaScript written in the JS foreign instruction. + Expression parseForeignJS(String source, [var expression]) { + // We can parse simple JS with the mini parser. At the moment we can't + // handle JSON literals and function literals, both of which contain "{". + if (source.contains("{") || source.startsWith("throw ")) { + assert(expression == null); + return new LiteralExpression(source); + } + return call(source, expression); + } + + /// Creates a litteral js string from [value]. + LiteralString escapedString(String value) { + // Do not escape unicode characters and ' because they are allowed in the + // string literal anyway. + String escaped = + value.replaceAllMapped(new RegExp('\n|"|\\|\0|\b|\t|\v'), (match) { + switch (match.group(0)) { + case "\n" : return r"\n"; + case "\\" : return r"\\"; + case "\"" : return r'\"'; + case "\0" : return r"\0"; + case "\b" : return r"\b"; + case "\t" : return r"\t"; + case "\f" : return r"\f"; + case "\v" : return r"\v"; + } + }); + LiteralString result = string(escaped); + // We don't escape ' under the assumption that the string is wrapped + // into ". Verify that assumption. + assert(result.value.codeUnitAt(0) == '"'.codeUnitAt(0)); + return result; + } + + /// Creates a litteral js string from [value]. + /// + /// Note that this function only puts quotes around [value]. It does not do + /// any escaping, so use only when you can guarantee that [value] does not + /// contain newlines or backslashes. For escaping the string use + /// [escapedString]. + LiteralString string(String value) => new LiteralString('"$value"'); + + LiteralNumber number(num value) => new LiteralNumber('$value'); + + If if_(condition, thenPart, [elsePart]) { + condition = toExpression(condition); + return (elsePart == null) + ? new If.noElse(condition, toStatement(thenPart)) + : new If(condition, toStatement(thenPart), toStatement(elsePart)); + } + + Return return_([value]) { + return new Return(value == null ? null : toExpression(value)); + } + + Block block(statement) { + if (statement is Block) { + return statement; + } else if (statement is List) { + List statements = statement + .map(toStatement) + .where((s) => s is !EmptyStatement) + .toList(); + return new Block(statements); + } else { + return new Block([toStatement(statement)]); + } + } + + Fun fun(parameters, body) { + Parameter toParameter(parameter) { + if (parameter is String) { + return new Parameter(parameter); + } else if (parameter is Parameter) { + return parameter; + } else { + throw new ArgumentError('parameter should be a String or a Parameter'); + } + } + if (parameters is! List) { + parameters = [parameters]; + } + return new Fun(parameters.map(toParameter).toList(), block(body)); + } + + VariableDeclarationList defineVar(String name, [initializer]) { + if (initializer != null) { + initializer = toExpression(initializer); + } + var declaration = new VariableDeclaration(name); + var initialization = [new VariableInitialization(declaration, initializer)]; + return new VariableDeclarationList(initialization); + } + + Statement toStatement(statement) { + if (statement is List) { + return block(statement); + } else if (statement is Node) { + return statement.toStatement(); + } else { + throw new ArgumentError('statement'); + } + } + + Expression toExpression(expression) { + if (expression == null) { + return null; + } else if (expression is Expression) { + return expression; + } else if (expression is String) { + return this(expression); + } else if (expression is num) { + return new LiteralNumber('$expression'); + } else if (expression is bool) { + return new LiteralBool(expression); + } else if (expression is Map) { + if (!expression.isEmpty) { + throw new ArgumentError('expression should be an empty Map'); + } + return new ObjectInitializer([]); + } else if (expression is List) { + var values = new List.generate(expression.length, + (index) => new ArrayElement(index, toExpression(expression[index]))); + return new ArrayInitializer(values.length, values); + } else { + throw new ArgumentError('expression should be an Expression, ' + 'a String, a num, a bool, a Map, or a List;'); + } + } + + ForIn forIn(String name, object, statement) { + return new ForIn(defineVar(name), + toExpression(object), + toStatement(statement)); + } + + For for_(init, condition, update, statement) { + return new For( + toExpression(init), toExpression(condition), toExpression(update), + toStatement(statement)); + } + + While while_(condition, statement) { + return new While( + toExpression(condition), toStatement(statement)); + } + + Try try_(body, {catchPart, finallyPart}) { + if (catchPart != null) catchPart = toStatement(catchPart); + if (finallyPart != null) finallyPart = toStatement(finallyPart); + return new Try(toStatement(body), catchPart, finallyPart); + } + + Comment comment(String text) => new Comment(text); +} + +const JsBuilder js = const JsBuilder(); + +LiteralString string(String value) => js.string(value); + +class MiniJsParserError { + MiniJsParserError(this.parser, this.message) { } + + MiniJsParser parser; + String message; + + String toString() { + var codes = new List.filled(parser.lastPosition, charCodes.$SPACE); + var spaces = new String.fromCharCodes(codes); + return "Error in MiniJsParser:\n${parser.src}\n$spaces^\n$spaces$message\n"; + } +} + +/// Mini JavaScript parser for tiny snippets of code that we want to make into +/// AST nodes. Handles: +/// * identifiers. +/// * dot access. +/// * method calls. +/// * [] access. +/// * array, string, regexp, boolean, null and numeric literals. +/// * most operators. +/// * brackets. +/// * var declarations. +/// * operator precedence. +/// Notable things it can't do yet include: +/// * non-empty object literals. +/// * throw, return. +/// * statements, including any flow control (if, while, for, etc.) +/// +/// It's a fairly standard recursive descent parser. +/// +/// Literal strings are passed through to the final JS source code unchanged, +/// including the choice of surrounding quotes, so if you parse +/// r'var x = "foo\n\"bar\""' you will end up with +/// var x = "foo\n\"bar\"" in the final program. \x and \u escapes are not +/// allowed in string and regexp literals because the machinery for checking +/// their correctness is rather involved. +class MiniJsParser { + MiniJsParser(this.src) + : lastCategory = NONE, + lastToken = null, + lastPosition = 0, + position = 0 { + getToken(); + } + + int lastCategory; + String lastToken; + int lastPosition; + int position; + String src; + final List interpolatedValues = + []; + + static const NONE = -1; + static const ALPHA = 0; + static const NUMERIC = 1; + static const STRING = 2; + static const SYMBOL = 3; + static const ASSIGNMENT = 4; + static const DOT = 5; + static const LPAREN = 6; + static const RPAREN = 7; + static const LBRACE = 8; + static const RBRACE = 9; + static const LSQUARE = 10; + static const RSQUARE = 11; + static const COMMA = 12; + static const QUERY = 13; + static const COLON = 14; + static const HASH = 15; + static const WHITESPACE = 16; + static const OTHER = 17; + + // Make sure that ]] is two symbols. + bool singleCharCategory(int category) => category >= DOT; + + static String categoryToString(int cat) { + switch (cat) { + case NONE: return "NONE"; + case ALPHA: return "ALPHA"; + case NUMERIC: return "NUMERIC"; + case SYMBOL: return "SYMBOL"; + case ASSIGNMENT: return "ASSIGNMENT"; + case DOT: return "DOT"; + case LPAREN: return "LPAREN"; + case RPAREN: return "RPAREN"; + case LBRACE: return "LBRACE"; + case RBRACE: return "RBRACE"; + case LSQUARE: return "LSQUARE"; + case RSQUARE: return "RSQUARE"; + case STRING: return "STRING"; + case COMMA: return "COMMA"; + case QUERY: return "QUERY"; + case COLON: return "COLON"; + case HASH: return "HASH"; + case WHITESPACE: return "WHITESPACE"; + case OTHER: return "OTHER"; + } + return "Unknown: $cat"; + } + + static const CATEGORIES = const [ + OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 0-7 + OTHER, WHITESPACE, WHITESPACE, OTHER, OTHER, WHITESPACE, // 8-13 + OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 14-21 + OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 22-29 + OTHER, OTHER, WHITESPACE, // 30-32 + SYMBOL, OTHER, HASH, ALPHA, SYMBOL, SYMBOL, OTHER, // !"#$%&´ + LPAREN, RPAREN, SYMBOL, SYMBOL, COMMA, SYMBOL, DOT, SYMBOL, // ()*+,-./ + NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 01234 + NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 56789 + COLON, OTHER, SYMBOL, SYMBOL, SYMBOL, QUERY, OTHER, // :;<=>?@ + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ABCDEFGH + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // IJKLMNOP + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // QRSTUVWX + ALPHA, ALPHA, LSQUARE, OTHER, RSQUARE, SYMBOL, ALPHA, OTHER, // YZ[\]^_' + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // abcdefgh + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ijklmnop + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // qrstuvwx + ALPHA, ALPHA, LBRACE, SYMBOL, RBRACE, SYMBOL]; // yz{|}~ + + // This must be a >= the highest precedence number handled by parseBinary. + static var HIGHEST_PARSE_BINARY_PRECEDENCE = 16; + static bool isAssignment(String symbol) => BINARY_PRECEDENCE[symbol] == 17; + + // From https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Operator_Precedence + static final BINARY_PRECEDENCE = { + '+=': 17, '-=': 17, '*=': 17, '/=': 17, '%=': 17, '^=': 17, '|=': 17, + '&=': 17, '<<=': 17, '>>=': 17, '>>>=': 17, '=': 17, + '||': 14, + '&&': 13, + '|': 12, + '^': 11, + '&': 10, + '!=': 9, '==': 9, '!==': 9, '===': 9, + '<': 8, '<=': 8, '>=': 8, '>': 8, 'in': 8, 'instanceof': 8, + '<<': 7, '>>': 7, '>>>': 7, + '+': 6, '-': 6, + '*': 5, '/': 5, '%': 5 + }; + static final UNARY_OPERATORS = + ['++', '--', '+', '-', '~', '!', 'typeof', 'void', 'delete'].toSet(); + + static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS = + ['typeof', 'void', 'delete', 'in', 'instanceof'].toSet(); + + static int category(int code) { + if (code >= CATEGORIES.length) return OTHER; + return CATEGORIES[code]; + } + + String getDelimited(int startPosition) { + position = startPosition; + int delimiter = src.codeUnitAt(startPosition); + int currentCode; + do { + position++; + if (position >= src.length) error("Unterminated literal"); + currentCode = src.codeUnitAt(position); + if (currentCode == charCodes.$BACKSLASH) { + if (++position >= src.length) error("Unterminated literal"); + int escaped = src.codeUnitAt(position); + if (escaped == charCodes.$x || escaped == charCodes.$X || + escaped == charCodes.$u || escaped == charCodes.$U || + category(escaped) == NUMERIC) { + error('Numeric and hex escapes are not allowed in literals'); + } + } + } while (currentCode != delimiter); + position++; + return src.substring(lastPosition, position); + } + + void getToken() { + while (position < src.length && + category(src.codeUnitAt(position)) == WHITESPACE) { + position++; + } + if (position == src.length) { + lastCategory = NONE; + lastToken = null; + lastPosition = position; + return; + } + int code = src.codeUnitAt(position); + lastPosition = position; + if (code == charCodes.$SQ || code == charCodes.$DQ) { + // String literal. + lastCategory = STRING; + lastToken = getDelimited(position); + } else if (code == charCodes.$0 && + position + 2 < src.length && + src.codeUnitAt(position + 1) == charCodes.$x) { + // Hex literal. + for (position += 2; position < src.length; position++) { + int cat = category(src.codeUnitAt(position)); + if (cat != NUMERIC && cat != ALPHA) break; + } + lastCategory = NUMERIC; + lastToken = src.substring(lastPosition, position); + int.parse(lastToken, onError: (_) { + error("Unparseable number"); + }); + } else if (code == charCodes.$SLASH) { + // Tokens that start with / are special due to regexp literals. + lastCategory = SYMBOL; + position++; + if (position < src.length && src.codeUnitAt(position) == charCodes.$EQ) { + position++; + } + lastToken = src.substring(lastPosition, position); + } else { + // All other tokens handled here. + int cat = category(src.codeUnitAt(position)); + int newCat; + do { + position++; + if (position == src.length) break; + int code = src.codeUnitAt(position); + // Special code to disallow ! and / in non-first position in token, so + // that !! parses as two tokens and != parses as one, while =/ parses + // as a an equals token followed by a regexp literal start. + newCat = (code == charCodes.$BANG || code == charCodes.$SLASH) + ? NONE + : category(code); + } while (!singleCharCategory(cat) && + (cat == newCat || + (cat == ALPHA && newCat == NUMERIC) || // eg. level42. + (cat == NUMERIC && newCat == DOT))); // eg. 3.1415 + lastCategory = cat; + lastToken = src.substring(lastPosition, position); + if (cat == NUMERIC) { + double.parse(lastToken, (_) { + error("Unparseable number"); + }); + } else if (cat == SYMBOL) { + int binaryPrecendence = BINARY_PRECEDENCE[lastToken]; + if (binaryPrecendence == null && !UNARY_OPERATORS.contains(lastToken)) { + error("Unknown operator"); + } + if (isAssignment(lastToken)) lastCategory = ASSIGNMENT; + } else if (cat == ALPHA) { + if (OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(lastToken)) { + lastCategory = SYMBOL; + } + } + } + } + + void expectCategory(int cat) { + if (cat != lastCategory) error("Expected ${categoryToString(cat)}"); + getToken(); + } + + bool acceptCategory(int cat) { + if (cat == lastCategory) { + getToken(); + return true; + } + return false; + } + + bool acceptString(String string) { + if (lastToken == string) { + getToken(); + return true; + } + return false; + } + + void error(message) { + throw new MiniJsParserError(this, message); + } + + Expression parsePrimary() { + String last = lastToken; + if (acceptCategory(ALPHA)) { + if (last == "true") { + return new LiteralBool(true); + } else if (last == "false") { + return new LiteralBool(false); + } else if (last == "null") { + return new LiteralNull(); + } else { + return new VariableUse(last); + } + } else if (acceptCategory(LPAREN)) { + Expression expression = parseExpression(); + expectCategory(RPAREN); + return expression; + } else if (acceptCategory(STRING)) { + return new LiteralString(last); + } else if (acceptCategory(NUMERIC)) { + return new LiteralNumber(last); + } else if (acceptCategory(LBRACE)) { + expectCategory(RBRACE); + return new ObjectInitializer([]); + } else if (acceptCategory(LSQUARE)) { + var values = []; + if (!acceptCategory(RSQUARE)) { + do { + values.add(new ArrayElement(values.length, parseExpression())); + } while (acceptCategory(COMMA)); + expectCategory(RSQUARE); + } + return new ArrayInitializer(values.length, values); + } else if (last.startsWith("/")) { + String regexp = getDelimited(lastPosition); + getToken(); + String flags = lastToken; + if (!acceptCategory(ALPHA)) flags = ""; + Expression expression = new RegExpLiteral(regexp + flags); + return expression; + } else if (acceptCategory(HASH)) { + InterpolatedExpression expression = new InterpolatedExpression(null); + interpolatedValues.add(expression); + return expression; + } else { + error("Expected primary expression"); + return null; + } + } + + Expression parseMember() { + Expression receiver = parsePrimary(); + while (true) { + if (acceptCategory(DOT)) { + receiver = getDotRhs(receiver); + } else if (acceptCategory(LSQUARE)) { + Expression inBraces = parseExpression(); + expectCategory(RSQUARE); + receiver = new PropertyAccess(receiver, inBraces); + } else { + break; + } + } + return receiver; + } + + Expression parseCall() { + bool constructor = acceptString("new"); + Expression receiver = parseMember(); + while (true) { + if (acceptCategory(LPAREN)) { + final arguments = []; + if (!acceptCategory(RPAREN)) { + while (true) { + Expression argument = parseExpression(); + arguments.add(argument); + if (acceptCategory(RPAREN)) break; + expectCategory(COMMA); + } + } + receiver = constructor ? + new New(receiver, arguments) : + new Call(receiver, arguments); + constructor = false; + } else if (!constructor && acceptCategory(LSQUARE)) { + Expression inBraces = parseExpression(); + expectCategory(RSQUARE); + receiver = new PropertyAccess(receiver, inBraces); + } else if (!constructor && acceptCategory(DOT)) { + receiver = getDotRhs(receiver); + } else { + // JS allows new without (), but we don't. + if (constructor) error("Parentheses are required for new"); + break; + } + } + return receiver; + } + + Expression getDotRhs(Expression receiver) { + String identifier = lastToken; + // In ES5 keywords like delete and continue are allowed as property + // names, and the IndexedDB API uses that, so we need to allow it here. + if (acceptCategory(SYMBOL)) { + if (!OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(identifier)) { + error("Expected alphanumeric identifier"); + } + } else { + expectCategory(ALPHA); + } + return new PropertyAccess.field(receiver, identifier); + } + + Expression parsePostfix() { + Expression expression = parseCall(); + String operator = lastToken; + if (lastCategory == SYMBOL && (acceptString("++") || acceptString("--"))) { + return new Postfix(operator, expression); + } + return expression; + } + + Expression parseUnaryHigh() { + String operator = lastToken; + if (lastCategory == SYMBOL && UNARY_OPERATORS.contains(operator) && + (acceptString("++") || acceptString("--"))) { + return new Prefix(operator, parsePostfix()); + } + return parsePostfix(); + } + + Expression parseUnaryLow() { + String operator = lastToken; + if (lastCategory == SYMBOL && UNARY_OPERATORS.contains(operator) && + operator != "++" && operator != "--") { + expectCategory(SYMBOL); + return new Prefix(operator, parseUnaryLow()); + } + return parseUnaryHigh(); + } + + Expression parseBinary(int maxPrecedence) { + Expression lhs = parseUnaryLow(); + int minPrecedence; + String lastSymbol; + Expression rhs; // This is null first time around. + while (true) { + String symbol = lastToken; + if (lastCategory != SYMBOL || + !BINARY_PRECEDENCE.containsKey(symbol) || + BINARY_PRECEDENCE[symbol] > maxPrecedence) { + break; + } + expectCategory(SYMBOL); + if (rhs == null || BINARY_PRECEDENCE[symbol] >= minPrecedence) { + if (rhs != null) lhs = new Binary(lastSymbol, lhs, rhs); + minPrecedence = BINARY_PRECEDENCE[symbol]; + rhs = parseUnaryLow(); + lastSymbol = symbol; + } else { + Expression higher = parseBinary(BINARY_PRECEDENCE[symbol]); + rhs = new Binary(symbol, rhs, higher); + } + } + if (rhs == null) return lhs; + return new Binary(lastSymbol, lhs, rhs); + } + + Expression parseConditional() { + Expression lhs = parseBinary(HIGHEST_PARSE_BINARY_PRECEDENCE); + if (!acceptCategory(QUERY)) return lhs; + Expression ifTrue = parseAssignment(); + expectCategory(COLON); + Expression ifFalse = parseAssignment(); + return new Conditional(lhs, ifTrue, ifFalse); + } + + + Expression parseAssignment() { + Expression lhs = parseConditional(); + String assignmentOperator = lastToken; + if (acceptCategory(ASSIGNMENT)) { + Expression rhs = parseAssignment(); + if (assignmentOperator == "=") { + return new Assignment(lhs, rhs); + } else { + // Handle +=, -=, etc. + String operator = + assignmentOperator.substring(0, assignmentOperator.length - 1); + return new Assignment.compound(lhs, operator, rhs); + } + } + return lhs; + } + + Expression parseExpression() => parseAssignment(); + + Expression parseVarDeclarationOrExpression() { + if (acceptString("var")) { + var initialization = []; + do { + String variable = lastToken; + expectCategory(ALPHA); + Expression initializer = null; + if (acceptString("=")) { + initializer = parseExpression(); + } + var declaration = new VariableDeclaration(variable); + initialization.add( + new VariableInitialization(declaration, initializer)); + } while (acceptCategory(COMMA)); + return new VariableDeclarationList(initialization); + } else { + return parseExpression(); + } + } + + Expression expression() { + Expression expression = parseVarDeclarationOrExpression(); + if (lastCategory != NONE || position != src.length) { + error("Unparsed junk: ${categoryToString(lastCategory)}"); + } + if (!interpolatedValues.isEmpty) { + return new JSExpression(expression, interpolatedValues); + } + return expression; + } +} + +/** + * Clone a JSExpression node into an expression where all children + * have been cloned, and [InterpolatedExpression]s have been replaced + * with real [Expression]. + */ +class UninterpolateJSExpression extends BaseVisitor { + final List arguments; + int argumentIndex = 0; + + UninterpolateJSExpression(this.arguments); + + void error(message) { + throw message; + } + + Node visitNode(Node node) { + error('Cannot handle $node'); + return null; + } + + Node copyPosition(Node oldNode, Node newNode) { + newNode.sourcePosition = oldNode.sourcePosition; + newNode.endSourcePosition = oldNode.endSourcePosition; + return newNode; + } + + Node visit(Node node) { + return node == null ? null : node.accept(this); + } + + List visitList(List list) { + return list.map((e) => visit(e)).toList(); + } + + Node visitLiteralString(LiteralString node) { + return node; + } + + Node visitVariableUse(VariableUse node) { + return node; + } + + Node visitAccess(PropertyAccess node) { + return copyPosition(node, + new PropertyAccess(visit(node.receiver), visit(node.selector))); + } + + Node visitCall(Call node) { + return copyPosition(node, + new Call(visit(node.target), visitList(node.arguments))); + } + + Node visitInterpolatedExpression(InterpolatedExpression expression) { + return arguments[argumentIndex++]; + } + + Node visitJSExpression(JSExpression expression) { + assert(argumentIndex == 0); + Node result = visit(expression.value); + if (argumentIndex != arguments.length) { + error("Invalid number of arguments"); + } + assert(result is! JSExpression); + return result; + } + + Node visitLiteralExpression(LiteralExpression node) { + assert(argumentIndex == 0); + return copyPosition(node, + new LiteralExpression.withData(node.template, arguments)); + } + + Node visitAssignment(Assignment node) { + return copyPosition(node, + new Assignment._internal(visit(node.leftHandSide), + visit(node.compoundTarget), + visit(node.value))); + } + + Node visitRegExpLiteral(RegExpLiteral node) { + return node; + } + + Node visitLiteralNumber(LiteralNumber node) { + return node; + } + + Node visitBinary(Binary node) { + return copyPosition(node, + new Binary(node.op, visit(node.left), visit(node.right))); + } + + Node visitPrefix(Prefix node) { + return copyPosition(node, + new Prefix(node.op, visit(node.argument))); + } + + Node visitPostfix(Postfix node) { + return copyPosition(node, + new Postfix(node.op, visit(node.argument))); + } + + Node visitNew(New node) { + return copyPosition(node, + new New(visit(node.target), visitList(node.arguments))); + } + + Node visitArrayInitializer(ArrayInitializer node) { + return copyPosition(node, + new ArrayInitializer(node.length, visitList(node.elements))); + } + + Node visitArrayElement(ArrayElement node) { + return copyPosition(node, + new ArrayElement(node.index, visit(node.value))); + } + + Node visitConditional(Conditional node) { + return copyPosition(node, + new Conditional(visit(node.condition), + visit(node.then), + visit(node.otherwise))); + } + + Node visitLiteralNull(LiteralNull node) { + return node; + } + + Node visitLiteralBool(LiteralBool node) { + return node; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/js.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/js.dart new file mode 100644 index 0000000..8bfdd51 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/js.dart @@ -0,0 +1,17 @@ +// Copyright (c) 2012, 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. + +library js; + +import 'precedence.dart'; +import '../util/characters.dart' as charCodes; +import '../util/util.dart'; + +// TODO(floitsch): remove this dependency (currently necessary for the +// CodeBuffer). +import '../dart2jslib.dart' as leg; + +part 'nodes.dart'; +part 'builder.dart'; +part 'printer.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/nodes.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/nodes.dart new file mode 100644 index 0000000..95191f0 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/nodes.dart @@ -0,0 +1,1013 @@ +// Copyright (c) 2012, 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. + +part of js; + +abstract class NodeVisitor { + T visitProgram(Program node); + + T visitBlock(Block node); + T visitExpressionStatement(ExpressionStatement node); + T visitEmptyStatement(EmptyStatement node); + T visitIf(If node); + T visitFor(For node); + T visitForIn(ForIn node); + T visitWhile(While node); + T visitDo(Do node); + T visitContinue(Continue node); + T visitBreak(Break node); + T visitReturn(Return node); + T visitThrow(Throw node); + T visitTry(Try node); + T visitCatch(Catch node); + T visitSwitch(Switch node); + T visitCase(Case node); + T visitDefault(Default node); + T visitFunctionDeclaration(FunctionDeclaration node); + T visitLabeledStatement(LabeledStatement node); + T visitLiteralStatement(LiteralStatement node); + + T visitBlob(Blob node); + T visitLiteralExpression(LiteralExpression node); + T visitVariableDeclarationList(VariableDeclarationList node); + T visitSequence(Sequence node); + T visitAssignment(Assignment node); + T visitVariableInitialization(VariableInitialization node); + T visitConditional(Conditional cond); + T visitNew(New node); + T visitCall(Call node); + T visitBinary(Binary node); + T visitPrefix(Prefix node); + T visitPostfix(Postfix node); + + T visitVariableUse(VariableUse node); + T visitThis(This node); + T visitVariableDeclaration(VariableDeclaration node); + T visitParameter(Parameter node); + T visitAccess(PropertyAccess node); + + T visitNamedFunction(NamedFunction node); + T visitFun(Fun node); + + T visitLiteralBool(LiteralBool node); + T visitLiteralString(LiteralString node); + T visitLiteralNumber(LiteralNumber node); + T visitLiteralNull(LiteralNull node); + + T visitArrayInitializer(ArrayInitializer node); + T visitArrayElement(ArrayElement node); + T visitObjectInitializer(ObjectInitializer node); + T visitProperty(Property node); + T visitRegExpLiteral(RegExpLiteral node); + + T visitComment(Comment node); + + T visitInterpolatedExpression(InterpolatedExpression node); + T visitJSExpression(JSExpression node); +} + +class BaseVisitor implements NodeVisitor { + T visitNode(Node node) { + node.visitChildren(this); + return null; + } + + T visitProgram(Program node) => visitNode(node); + + T visitStatement(Statement node) => visitNode(node); + T visitLoop(Loop node) => visitStatement(node); + T visitJump(Statement node) => visitStatement(node); + + T visitBlock(Block node) => visitStatement(node); + T visitExpressionStatement(ExpressionStatement node) + => visitStatement(node); + T visitEmptyStatement(EmptyStatement node) => visitStatement(node); + T visitIf(If node) => visitStatement(node); + T visitFor(For node) => visitLoop(node); + T visitForIn(ForIn node) => visitLoop(node); + T visitWhile(While node) => visitLoop(node); + T visitDo(Do node) => visitLoop(node); + T visitContinue(Continue node) => visitJump(node); + T visitBreak(Break node) => visitJump(node); + T visitReturn(Return node) => visitJump(node); + T visitThrow(Throw node) => visitJump(node); + T visitTry(Try node) => visitStatement(node); + T visitSwitch(Switch node) => visitStatement(node); + T visitFunctionDeclaration(FunctionDeclaration node) + => visitStatement(node); + T visitLabeledStatement(LabeledStatement node) => visitStatement(node); + T visitLiteralStatement(LiteralStatement node) => visitStatement(node); + + T visitCatch(Catch node) => visitNode(node); + T visitCase(Case node) => visitNode(node); + T visitDefault(Default node) => visitNode(node); + + T visitExpression(Expression node) => visitNode(node); + T visitBlob(Blob node) => visitExpression(node); + T visitVariableReference(VariableReference node) => visitExpression(node); + + T visitLiteralExpression(LiteralExpression node) => visitExpression(node); + T visitVariableDeclarationList(VariableDeclarationList node) + => visitExpression(node); + T visitSequence(Sequence node) => visitExpression(node); + T visitAssignment(Assignment node) => visitExpression(node); + T visitVariableInitialization(VariableInitialization node) { + if (node.value != null) { + return visitAssignment(node); + } else { + return visitExpression(node); + } + } + T visitConditional(Conditional node) => visitExpression(node); + T visitNew(New node) => visitExpression(node); + T visitCall(Call node) => visitExpression(node); + T visitBinary(Binary node) => visitCall(node); + T visitPrefix(Prefix node) => visitCall(node); + T visitPostfix(Postfix node) => visitCall(node); + T visitAccess(PropertyAccess node) => visitExpression(node); + + T visitVariableUse(VariableUse node) => visitVariableReference(node); + T visitVariableDeclaration(VariableDeclaration node) + => visitVariableReference(node); + T visitParameter(Parameter node) => visitVariableDeclaration(node); + T visitThis(This node) => visitParameter(node); + + T visitNamedFunction(NamedFunction node) => visitExpression(node); + T visitFun(Fun node) => visitExpression(node); + + T visitLiteral(Literal node) => visitExpression(node); + + T visitLiteralBool(LiteralBool node) => visitLiteral(node); + T visitLiteralString(LiteralString node) => visitLiteral(node); + T visitLiteralNumber(LiteralNumber node) => visitLiteral(node); + T visitLiteralNull(LiteralNull node) => visitLiteral(node); + + T visitArrayInitializer(ArrayInitializer node) => visitExpression(node); + T visitArrayElement(ArrayElement node) => visitNode(node); + T visitObjectInitializer(ObjectInitializer node) => visitExpression(node); + T visitProperty(Property node) => visitNode(node); + T visitRegExpLiteral(RegExpLiteral node) => visitExpression(node); + + T visitInterpolatedExpression(InterpolatedExpression node) + => visitExpression(node); + T visitJSExpression(JSExpression node) => visitExpression(node); + + // Ignore comments by default. + T visitComment(Comment node) => null; +} + +abstract class Node { + var sourcePosition; + var endSourcePosition; + + accept(NodeVisitor visitor); + void visitChildren(NodeVisitor visitor); + + VariableUse asVariableUse() => null; + + Statement toStatement() { + throw new UnsupportedError('toStatement'); + } +} + +class Program extends Node { + final List body; + Program(this.body); + + accept(NodeVisitor visitor) => visitor.visitProgram(this); + void visitChildren(NodeVisitor visitor) { + for (Statement statement in body) statement.accept(visitor); + } +} + +abstract class Statement extends Node { + Statement toStatement() => this; +} + +class Block extends Statement { + final List statements; + Block(this.statements); + Block.empty() : this.statements = []; + + accept(NodeVisitor visitor) => visitor.visitBlock(this); + void visitChildren(NodeVisitor visitor) { + for (Statement statement in statements) statement.accept(visitor); + } +} + +class ExpressionStatement extends Statement { + final Expression expression; + ExpressionStatement(this.expression); + + accept(NodeVisitor visitor) => visitor.visitExpressionStatement(this); + void visitChildren(NodeVisitor visitor) { expression.accept(visitor); } +} + +class EmptyStatement extends Statement { + EmptyStatement(); + + accept(NodeVisitor visitor) => visitor.visitEmptyStatement(this); + void visitChildren(NodeVisitor visitor) {} +} + +class If extends Statement { + final Expression condition; + final Node then; + final Node otherwise; + + If(this.condition, this.then, this.otherwise); + If.noElse(this.condition, this.then) : this.otherwise = new EmptyStatement(); + + bool get hasElse => otherwise is !EmptyStatement; + + accept(NodeVisitor visitor) => visitor.visitIf(this); + + void visitChildren(NodeVisitor visitor) { + condition.accept(visitor); + then.accept(visitor); + otherwise.accept(visitor); + } +} + +abstract class Loop extends Statement { + final Statement body; + Loop(this.body); +} + +class For extends Loop { + final Expression init; + final Expression condition; + final Expression update; + + For(this.init, this.condition, this.update, Statement body) : super(body); + + accept(NodeVisitor visitor) => visitor.visitFor(this); + + void visitChildren(NodeVisitor visitor) { + if (init != null) init.accept(visitor); + if (condition != null) condition.accept(visitor); + if (update != null) update.accept(visitor); + body.accept(visitor); + } +} + +class ForIn extends Loop { + // Note that [VariableDeclarationList] is a subclass of [Expression]. + // Therefore we can type the leftHandSide as [Expression]. + final Expression leftHandSide; + final Expression object; + + ForIn(this.leftHandSide, this.object, Statement body) : super(body); + + accept(NodeVisitor visitor) => visitor.visitForIn(this); + + void visitChildren(NodeVisitor visitor) { + leftHandSide.accept(visitor); + object.accept(visitor); + body.accept(visitor); + } +} + +class While extends Loop { + final Node condition; + + While(this.condition, Statement body) : super(body); + + accept(NodeVisitor visitor) => visitor.visitWhile(this); + + void visitChildren(NodeVisitor visitor) { + condition.accept(visitor); + body.accept(visitor); + } +} + +class Do extends Loop { + final Expression condition; + + Do(Statement body, this.condition) : super(body); + + accept(NodeVisitor visitor) => visitor.visitDo(this); + + void visitChildren(NodeVisitor visitor) { + body.accept(visitor); + condition.accept(visitor); + } +} + +class Continue extends Statement { + final String targetLabel; // Can be null. + + Continue(this.targetLabel); + + accept(NodeVisitor visitor) => visitor.visitContinue(this); + void visitChildren(NodeVisitor visitor) {} +} + +class Break extends Statement { + final String targetLabel; // Can be null. + + Break(this.targetLabel); + + accept(NodeVisitor visitor) => visitor.visitBreak(this); + void visitChildren(NodeVisitor visitor) {} +} + +class Return extends Statement { + final Expression value; // Can be null. + + Return([this.value = null]); + + accept(NodeVisitor visitor) => visitor.visitReturn(this); + + void visitChildren(NodeVisitor visitor) { + if (value != null) value.accept(visitor); + } +} + +class Throw extends Statement { + final Expression expression; + + Throw(this.expression); + + accept(NodeVisitor visitor) => visitor.visitThrow(this); + + void visitChildren(NodeVisitor visitor) { + expression.accept(visitor); + } +} + +class Try extends Statement { + final Block body; + final Catch catchPart; // Can be null if [finallyPart] is non-null. + final Block finallyPart; // Can be null if [catchPart] is non-null. + + Try(this.body, this.catchPart, this.finallyPart) { + assert(catchPart != null || finallyPart != null); + } + + accept(NodeVisitor visitor) => visitor.visitTry(this); + + void visitChildren(NodeVisitor visitor) { + body.accept(visitor); + if (catchPart != null) catchPart.accept(visitor); + if (finallyPart != null) finallyPart.accept(visitor); + } +} + +class Catch extends Node { + final VariableDeclaration declaration; + final Block body; + + Catch(this.declaration, this.body); + + accept(NodeVisitor visitor) => visitor.visitCatch(this); + + void visitChildren(NodeVisitor visitor) { + declaration.accept(visitor); + body.accept(visitor); + } +} + +class Switch extends Statement { + final Expression key; + final List cases; + + Switch(this.key, this.cases); + + accept(NodeVisitor visitor) => visitor.visitSwitch(this); + + void visitChildren(NodeVisitor visitor) { + key.accept(visitor); + for (SwitchClause clause in cases) clause.accept(visitor); + } +} + +abstract class SwitchClause extends Node { + final Block body; + + SwitchClause(this.body); +} + +class Case extends SwitchClause { + final Expression expression; + + Case(this.expression, Block body) : super(body); + + accept(NodeVisitor visitor) => visitor.visitCase(this); + + void visitChildren(NodeVisitor visitor) { + expression.accept(visitor); + body.accept(visitor); + } +} + +class Default extends SwitchClause { + Default(Block body) : super(body); + + accept(NodeVisitor visitor) => visitor.visitDefault(this); + + void visitChildren(NodeVisitor visitor) { + body.accept(visitor); + } +} + +class FunctionDeclaration extends Statement { + final VariableDeclaration name; + final Fun function; + + FunctionDeclaration(this.name, this.function); + + accept(NodeVisitor visitor) => visitor.visitFunctionDeclaration(this); + + void visitChildren(NodeVisitor visitor) { + name.accept(visitor); + function.accept(visitor); + } +} + +class LabeledStatement extends Statement { + final String label; + final Statement body; + + LabeledStatement(this.label, this.body); + + accept(NodeVisitor visitor) => visitor.visitLabeledStatement(this); + + void visitChildren(NodeVisitor visitor) { + body.accept(visitor); + } +} + +class LiteralStatement extends Statement { + final String code; + + LiteralStatement(this.code); + + accept(NodeVisitor visitor) => visitor.visitLiteralStatement(this); + void visitChildren(NodeVisitor visitor) { } +} + +abstract class Expression extends Node { + int get precedenceLevel; + + Call callWith(List arguments) => new Call(this, arguments); + + PropertyAccess operator [](expression) { + if (expression is Expression) { + return new PropertyAccess(this, expression); + } else if (expression is int) { + return new PropertyAccess.indexed(this, expression); + } else if (expression is String) { + return new PropertyAccess.field(this, expression); + } else { + throw new ArgumentError('Expected an int, String, or Expression'); + } + } + + Statement toStatement() => new ExpressionStatement(this); + + Call call([expression]) { + List arguments; + if (expression == null) { + arguments = []; + } else if (expression is List) { + arguments = expression.map(js.toExpression).toList(); + } else { + arguments = [js.toExpression(expression)]; + } + return callWith(arguments); + } + + Expression operator +(expression) => binary('+', expression); + + Expression operator -(expression) => binary('-', expression); + + Expression operator &(expression) => binary('&', expression); + + Expression operator <(expression) => binary('<', expression); + + Expression operator >(expression) => binary('>', expression); + + Expression operator >=(expression) => binary('>=', expression); + + Expression binary(String operator, expression) { + return new Binary(operator, this, js.toExpression(expression)); + } + + Expression update(String operator, expression) { + return new Assignment.compound(this, operator, js.toExpression(expression)); + } + + Expression get plusPlus => new Postfix('++', this); + + Prefix get typeof => new Prefix('typeof', this); + + Prefix get not => new Prefix('!', this); +} + +/// Wrap a CodeBuffer as an expression. +class Blob extends Expression { + // TODO(ahe): This class is an aid to convert everything to ASTs, remove when + // not needed anymore. + + final leg.CodeBuffer buffer; + + Blob(this.buffer); + + accept(NodeVisitor visitor) => visitor.visitBlob(this); + + void visitChildren(NodeVisitor visitor) {} + + int get precedenceLevel => PRIMARY; +} + +class LiteralExpression extends Expression { + final String template; + final List inputs; + + LiteralExpression(this.template) : inputs = const []; + LiteralExpression.withData(this.template, this.inputs); + + accept(NodeVisitor visitor) => visitor.visitLiteralExpression(this); + + void visitChildren(NodeVisitor visitor) { + if (inputs != null) { + for (Expression expr in inputs) expr.accept(visitor); + } + } + + // Code that uses JS must take care of operator precedences, and + // put parenthesis if needed. + int get precedenceLevel => PRIMARY; +} + +/** + * [VariableDeclarationList] is a subclass of [Expression] to simplify the + * AST. + */ +class VariableDeclarationList extends Expression { + final List declarations; + + VariableDeclarationList(this.declarations); + + accept(NodeVisitor visitor) => visitor.visitVariableDeclarationList(this); + + void visitChildren(NodeVisitor visitor) { + for (VariableInitialization declaration in declarations) { + declaration.accept(visitor); + } + } + + int get precedenceLevel => EXPRESSION; +} + +class Sequence extends Expression { + final List expressions; + + Sequence(this.expressions); + + accept(NodeVisitor visitor) => visitor.visitSequence(this); + + void visitChildren(NodeVisitor visitor) { + for (Expression expr in expressions) expr.accept(visitor); + } + + int get precedenceLevel => EXPRESSION; +} + +class Assignment extends Expression { + final Expression leftHandSide; + // Null, if the assignment is not compound. + final VariableReference compoundTarget; + final Expression value; // May be null, for [VariableInitialization]s. + + Assignment(leftHandSide, value) + : this._internal(leftHandSide, null, value); + Assignment.compound(leftHandSide, String op, value) + : this._internal(leftHandSide, new VariableUse(op), value); + Assignment._internal(this.leftHandSide, this.compoundTarget, this.value); + + int get precedenceLevel => ASSIGNMENT; + + bool get isCompound => compoundTarget != null; + String get op => compoundTarget == null ? null : compoundTarget.name; + + accept(NodeVisitor visitor) => visitor.visitAssignment(this); + + void visitChildren(NodeVisitor visitor) { + leftHandSide.accept(visitor); + if (compoundTarget != null) compoundTarget.accept(visitor); + if (value != null) value.accept(visitor); + } +} + +class VariableInitialization extends Assignment { + /** [value] may be null. */ + VariableInitialization(VariableDeclaration declaration, Expression value) + : super(declaration, value); + + VariableDeclaration get declaration => leftHandSide; + + accept(NodeVisitor visitor) => visitor.visitVariableInitialization(this); +} + +class Conditional extends Expression { + final Expression condition; + final Expression then; + final Expression otherwise; + + Conditional(this.condition, this.then, this.otherwise); + + accept(NodeVisitor visitor) => visitor.visitConditional(this); + + void visitChildren(NodeVisitor visitor) { + condition.accept(visitor); + then.accept(visitor); + otherwise.accept(visitor); + } + + int get precedenceLevel => ASSIGNMENT; +} + +class Call extends Expression { + Expression target; + List arguments; + + Call(this.target, this.arguments); + + accept(NodeVisitor visitor) => visitor.visitCall(this); + + void visitChildren(NodeVisitor visitor) { + target.accept(visitor); + for (Expression arg in arguments) arg.accept(visitor); + } + + int get precedenceLevel => CALL; +} + +class New extends Call { + New(Expression cls, List arguments) : super(cls, arguments); + + accept(NodeVisitor visitor) => visitor.visitNew(this); +} + +class Binary extends Call { + Binary(String op, Expression left, Expression right) + : super(new VariableUse(op), [left, right]); + + String get op { + VariableUse use = target; + return use.name; + } + + Expression get left => arguments[0]; + Expression get right => arguments[1]; + + accept(NodeVisitor visitor) => visitor.visitBinary(this); + + int get precedenceLevel { + // TODO(floitsch): switch to constant map. + switch (op) { + case "*": + case "/": + case "%": + return MULTIPLICATIVE; + case "+": + case "-": + return ADDITIVE; + case "<<": + case ">>": + case ">>>": + return SHIFT; + case "<": + case ">": + case "<=": + case ">=": + case "instanceof": + case "in": + return RELATIONAL; + case "==": + case "===": + case "!=": + case "!==": + return EQUALITY; + case "&": + return BIT_AND; + case "^": + return BIT_XOR; + case "|": + return BIT_OR; + case "&&": + return LOGICAL_AND; + case "||": + return LOGICAL_OR; + default: + throw new leg.CompilerCancelledException( + "Internal Error: Unhandled binary operator: $op"); + } + } +} + +class Prefix extends Call { + Prefix(String op, Expression arg) + : super(new VariableUse(op), [arg]); + + String get op => (target as VariableUse).name; + Expression get argument => arguments[0]; + + accept(NodeVisitor visitor) => visitor.visitPrefix(this); + + int get precedenceLevel => UNARY; +} + +class Postfix extends Call { + Postfix(String op, Expression arg) + : super(new VariableUse(op), [arg]); + + String get op => (target as VariableUse).name; + Expression get argument => arguments[0]; + + accept(NodeVisitor visitor) => visitor.visitPostfix(this); + + int get precedenceLevel => UNARY; +} + +abstract class VariableReference extends Expression { + final String name; + + // We treat operators as if they were special functions. They can thus be + // referenced like other variables. + VariableReference(this.name); + + accept(NodeVisitor visitor); + int get precedenceLevel => PRIMARY; + void visitChildren(NodeVisitor visitor) {} +} + +class VariableUse extends VariableReference { + VariableUse(String name) : super(name); + + accept(NodeVisitor visitor) => visitor.visitVariableUse(this); + + VariableUse asVariableUse() => this; +} + +class VariableDeclaration extends VariableReference { + VariableDeclaration(String name) : super(name); + + accept(NodeVisitor visitor) => visitor.visitVariableDeclaration(this); +} + +class Parameter extends VariableDeclaration { + Parameter(String id) : super(id); + + accept(NodeVisitor visitor) => visitor.visitParameter(this); +} + +class This extends Parameter { + This() : super("this"); + + accept(NodeVisitor visitor) => visitor.visitThis(this); +} + +class NamedFunction extends Expression { + final VariableDeclaration name; + final Fun function; + + NamedFunction(this.name, this.function); + + accept(NodeVisitor visitor) => visitor.visitNamedFunction(this); + + void visitChildren(NodeVisitor visitor) { + name.accept(visitor); + function.accept(visitor); + } + + int get precedenceLevel => CALL; +} + +class Fun extends Expression { + final List params; + final Block body; + + Fun(this.params, this.body); + + accept(NodeVisitor visitor) => visitor.visitFun(this); + + void visitChildren(NodeVisitor visitor) { + for (Parameter param in params) param.accept(visitor); + body.accept(visitor); + } + + int get precedenceLevel => CALL; +} + +class PropertyAccess extends Expression { + final Expression receiver; + final Expression selector; + + PropertyAccess(this.receiver, this.selector); + PropertyAccess.field(this.receiver, String fieldName) + : selector = new LiteralString('"$fieldName"'); + PropertyAccess.indexed(this.receiver, int index) + : selector = new LiteralNumber('$index'); + + accept(NodeVisitor visitor) => visitor.visitAccess(this); + + void visitChildren(NodeVisitor visitor) { + receiver.accept(visitor); + selector.accept(visitor); + } + + int get precedenceLevel => CALL; +} + +abstract class Literal extends Expression { + void visitChildren(NodeVisitor visitor) {} + + int get precedenceLevel => PRIMARY; +} + +class LiteralBool extends Literal { + final bool value; + + LiteralBool(this.value); + + accept(NodeVisitor visitor) => visitor.visitLiteralBool(this); + // [visitChildren] inherited from [Literal]. +} + +class LiteralNull extends Literal { + LiteralNull(); + + accept(NodeVisitor visitor) => visitor.visitLiteralNull(this); +} + +class LiteralString extends Literal { + final String value; + + /** + * Constructs a LiteralString from a string value. + * + * The constructor does not add the required quotes. If [value] is + * not surrounded by quotes, the resulting object is invalid as a JS + * value. + */ + LiteralString(this.value); + + accept(NodeVisitor visitor) => visitor.visitLiteralString(this); +} + +class LiteralNumber extends Literal { + final String value; + + LiteralNumber(this.value); + + accept(NodeVisitor visitor) => visitor.visitLiteralNumber(this); +} + +class ArrayInitializer extends Expression { + final int length; + // We represent the array as sparse list of elements. Each element knows its + // position in the array. + final List elements; + + ArrayInitializer(this.length, this.elements); + + factory ArrayInitializer.from(Iterable expressions) => + new ArrayInitializer(expressions.length, _convert(expressions)); + + accept(NodeVisitor visitor) => visitor.visitArrayInitializer(this); + + void visitChildren(NodeVisitor visitor) { + for (ArrayElement element in elements) element.accept(visitor); + } + + int get precedenceLevel => PRIMARY; + + static List _convert(Iterable expressions) { + int index = 0; + return expressions.map( + (expression) => new ArrayElement(index++, expression)) + .toList(); + } +} + +/** + * An expression inside an [ArrayInitializer]. An [ArrayElement] knows + * its position in the containing [ArrayInitializer]. + */ +class ArrayElement extends Node { + int index; + Expression value; + + ArrayElement(this.index, this.value); + + accept(NodeVisitor visitor) => visitor.visitArrayElement(this); + + void visitChildren(NodeVisitor visitor) { + value.accept(visitor); + } +} + +class ObjectInitializer extends Expression { + List properties; + bool isOneLiner; + + /** + * Constructs a new object-initializer containing the given [properties]. + * + * [isOneLiner] describes the behaviour when pretty-printing (non-minified). + * If true print all properties on the same line. + * If false print each property on a seperate line. + */ + ObjectInitializer(this.properties, {this.isOneLiner: true}); + + accept(NodeVisitor visitor) => visitor.visitObjectInitializer(this); + + void visitChildren(NodeVisitor visitor) { + for (Property init in properties) init.accept(visitor); + } + + int get precedenceLevel => PRIMARY; +} + +class Property extends Node { + Literal name; + Expression value; + + Property(this.name, this.value); + + accept(NodeVisitor visitor) => visitor.visitProperty(this); + + void visitChildren(NodeVisitor visitor) { + name.accept(visitor); + value.accept(visitor); + } +} + +class InterpolatedExpression extends Expression { + Expression value; + + InterpolatedExpression(this.value); + + accept(NodeVisitor visitor) => visitor.visitInterpolatedExpression(this); + + void visitChildren(NodeVisitor visitor) { + if (value != null) value.accept(visitor); + } + + int get precedenceLevel => value.precedenceLevel; +} + +class JSExpression extends Expression { + Expression value; + List interpolatedExpressions; + + JSExpression(this.value, this.interpolatedExpressions); + + accept(NodeVisitor visitor) => visitor.visitJSExpression(this); + + void visitChildren(NodeVisitor visitor) { + value.accept(visitor); + for (InterpolatedExpression expression in interpolatedExpressions) { + expression.accept(visitor); + } + } + + int get precedenceLevel => value.precedenceLevel; +} + +/** + * [RegExpLiteral]s, despite being called "Literal", do not inherit from + * [Literal]. Indeed, regular expressions in JavaScript have a side-effect and + * are thus not in the same category as numbers or strings. + */ +class RegExpLiteral extends Expression { + /** Contains the pattern and the flags.*/ + String pattern; + + RegExpLiteral(this.pattern); + + accept(NodeVisitor visitor) => visitor.visitRegExpLiteral(this); + void visitChildren(NodeVisitor visitor) {} + + int get precedenceLevel => PRIMARY; +} + +/** + * A comment. + * + * Extends [Statement] so we can add comments before statements in + * [Block] and [Program]. + */ +class Comment extends Statement { + final String comment; + + Comment(this.comment); + + accept(NodeVisitor visitor) => visitor.visitComment(this); + + void visitChildren(NodeVisitor visitor) {} +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/precedence.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/precedence.dart new file mode 100644 index 0000000..6d66f1f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/precedence.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2012, 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. + +library precedence; + +const EXPRESSION = 0; +const ASSIGNMENT = EXPRESSION + 1; +const LOGICAL_OR = ASSIGNMENT + 1; +const LOGICAL_AND = LOGICAL_OR + 1; +const BIT_OR = LOGICAL_AND + 1; +const BIT_XOR = BIT_OR + 1; +const BIT_AND = BIT_XOR + 1; +const EQUALITY = BIT_AND + 1; +const RELATIONAL = EQUALITY + 1; +const SHIFT = RELATIONAL + 1; +const ADDITIVE = SHIFT + 1; +const MULTIPLICATIVE = ADDITIVE + 1; +const UNARY = MULTIPLICATIVE + 1; +const LEFT_HAND_SIDE = UNARY + 1; +// We merge new, call and member expressions. +// This means that we have to emit parenthesis for 'new's. For example `new X;` +// should be printed as `new X();`. This simplifies the requirements. +const CALL = LEFT_HAND_SIDE; +const PRIMARY = CALL + 1; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/printer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/printer.dart new file mode 100644 index 0000000..855eb1d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js/printer.dart @@ -0,0 +1,1154 @@ +// Copyright (c) 2012, 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. + +part of js; + +class Printer implements NodeVisitor { + final bool shouldCompressOutput; + leg.Compiler compiler; + leg.CodeBuffer outBuffer; + int indentLevel = 0; + bool inForInit = false; + bool atStatementBegin = false; + final DanglingElseVisitor danglingElseVisitor; + final LocalNamer localNamer; + bool pendingSemicolon = false; + bool pendingSpace = false; + static final identifierCharacterRegExp = new RegExp(r'^[a-zA-Z_0-9$]'); + static final expressionContinuationRegExp = new RegExp(r'^[-+([]'); + + Printer(leg.Compiler compiler, { allowVariableMinification: true }) + : shouldCompressOutput = compiler.enableMinification, + this.compiler = compiler, + outBuffer = new leg.CodeBuffer(), + danglingElseVisitor = new DanglingElseVisitor(compiler), + localNamer = determineRenamer(compiler.enableMinification, + allowVariableMinification); + + static LocalNamer determineRenamer(bool shouldCompressOutput, + bool allowVariableMinification) { + return (shouldCompressOutput && allowVariableMinification) + ? new MinifyRenamer() : new IdentityNamer(); + } + + /// Always emit a newline, even under `enableMinification`. + void forceLine() { + out("\n"); + } + /// Emits a newline for readability. + void lineOut() { + if (!shouldCompressOutput) forceLine(); + } + void spaceOut() { + if (!shouldCompressOutput) out(" "); + } + + String lastAddedString = null; + int get lastCharCode { + if (lastAddedString == null) return 0; + assert(lastAddedString.length != ""); + return lastAddedString.codeUnitAt(lastAddedString.length - 1); + } + + void out(String str) { + if (str != "") { + if (pendingSemicolon) { + if (!shouldCompressOutput) { + outBuffer.add(";"); + } else if (str != "}") { + // We want to output newline instead of semicolon because it makes + // the raw stack traces much easier to read and it also makes line- + // based tools like diff work much better. JavaScript will + // automatically insert the semicolon at the newline if it means a + // parsing error is avoided, so we can only do this trick if the + // next line is not something that can be glued onto a valid + // expression to make a new valid expression. + if (expressionContinuationRegExp.hasMatch(str)) { + outBuffer.add(";"); + } else { + outBuffer.add("\n"); + } + } + } + if (pendingSpace && + (!shouldCompressOutput || identifierCharacterRegExp.hasMatch(str))) { + outBuffer.add(" "); + } + pendingSpace = false; + pendingSemicolon = false; + outBuffer.add(str); + lastAddedString = str; + } + } + + void outLn(String str) { + out(str); + lineOut(); + } + + void outSemicolonLn() { + if (shouldCompressOutput) { + pendingSemicolon = true; + } else { + out(";"); + forceLine(); + } + } + + void outIndent(String str) { indent(); out(str); } + void outIndentLn(String str) { indent(); outLn(str); } + void indent() { + if (!shouldCompressOutput) { + for (int i = 0; i < indentLevel; i++) out(" "); + } + } + + void beginSourceRange(Node node) { + if (node.sourcePosition != null) { + outBuffer.beginMappedRange(); + outBuffer.setSourceLocation(node.sourcePosition); + } + } + + void endSourceRange(Node node) { + if (node.endSourcePosition != null) { + outBuffer.setSourceLocation(node.endSourcePosition); + } + if (node.sourcePosition != null) { + outBuffer.endMappedRange(); + } + } + + visit(Node node) { + beginSourceRange(node); + node.accept(this); + endSourceRange(node); + } + + visitCommaSeparated(List nodes, int hasRequiredType, + {bool newInForInit, bool newAtStatementBegin}) { + for (int i = 0; i < nodes.length; i++) { + if (i != 0) { + atStatementBegin = false; + out(","); + spaceOut(); + } + visitNestedExpression(nodes[i], hasRequiredType, + newInForInit: newInForInit, + newAtStatementBegin: newAtStatementBegin); + } + } + + visitAll(List nodes) { + nodes.forEach(visit); + } + + visitProgram(Program program) { + visitAll(program.body); + } + + visitBlob(Blob node) { + outBuffer.addBuffer(node.buffer); + } + + bool blockBody(Node body, {bool needsSeparation, bool needsNewline}) { + if (body is Block) { + spaceOut(); + blockOut(body, false, needsNewline); + return true; + } + if (shouldCompressOutput && needsSeparation) { + // If [shouldCompressOutput] is false, then the 'lineOut' will insert + // the separation. + out(" "); + } else { + lineOut(); + } + indentLevel++; + visit(body); + indentLevel--; + return false; + } + + void blockOutWithoutBraces(Node node) { + if (node is Block) { + beginSourceRange(node); + Block block = node; + block.statements.forEach(blockOutWithoutBraces); + endSourceRange(node); + } else { + visit(node); + } + } + + void blockOut(Block node, bool shouldIndent, bool needsNewline) { + if (shouldIndent) indent(); + beginSourceRange(node); + out("{"); + lineOut(); + indentLevel++; + node.statements.forEach(blockOutWithoutBraces); + indentLevel--; + indent(); + out("}"); + endSourceRange(node); + if (needsNewline) lineOut(); + } + + visitBlock(Block block) { + blockOut(block, true, true); + } + + visitExpressionStatement(ExpressionStatement expressionStatement) { + indent(); + visitNestedExpression(expressionStatement.expression, EXPRESSION, + newInForInit: false, newAtStatementBegin: true); + outSemicolonLn(); + } + + visitEmptyStatement(EmptyStatement nop) { + outIndentLn(";"); + } + + void ifOut(If node, bool shouldIndent) { + Node then = node.then; + Node elsePart = node.otherwise; + bool hasElse = node.hasElse; + + // Handle dangling elses. + if (hasElse) { + bool needsBraces = node.then.accept(danglingElseVisitor); + if (needsBraces) { + then = new Block([then]); + } + } + if (shouldIndent) indent(); + out("if"); + spaceOut(); + out("("); + visitNestedExpression(node.condition, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + out(")"); + bool thenWasBlock = + blockBody(then, needsSeparation: false, needsNewline: !hasElse); + if (hasElse) { + if (thenWasBlock) { + spaceOut(); + } else { + indent(); + } + out("else"); + if (elsePart is If) { + pendingSpace = true; + ifOut(elsePart, false); + } else { + blockBody(elsePart, needsSeparation: true, needsNewline: true); + } + } + } + + visitIf(If node) { + ifOut(node, true); + } + + visitFor(For loop) { + outIndent("for"); + spaceOut(); + out("("); + if (loop.init != null) { + visitNestedExpression(loop.init, EXPRESSION, + newInForInit: true, newAtStatementBegin: false); + } + out(";"); + if (loop.condition != null) { + spaceOut(); + visitNestedExpression(loop.condition, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + } + out(";"); + if (loop.update != null) { + spaceOut(); + visitNestedExpression(loop.update, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + } + out(")"); + blockBody(loop.body, needsSeparation: false, needsNewline: true); + } + + visitForIn(ForIn loop) { + outIndent("for"); + spaceOut(); + out("("); + visitNestedExpression(loop.leftHandSide, EXPRESSION, + newInForInit: true, newAtStatementBegin: false); + out(" in"); + pendingSpace = true; + visitNestedExpression(loop.object, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + out(")"); + blockBody(loop.body, needsSeparation: false, needsNewline: true); + } + + visitWhile(While loop) { + outIndent("while"); + spaceOut(); + out("("); + visitNestedExpression(loop.condition, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + out(")"); + blockBody(loop.body, needsSeparation: false, needsNewline: true); + } + + visitDo(Do loop) { + outIndent("do"); + if (blockBody(loop.body, needsSeparation: true, needsNewline: false)) { + spaceOut(); + } else { + indent(); + } + out("while"); + spaceOut(); + out("("); + visitNestedExpression(loop.condition, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + out(")"); + outSemicolonLn(); + } + + visitContinue(Continue node) { + if (node.targetLabel == null) { + outIndent("continue"); + } else { + outIndent("continue ${node.targetLabel}"); + } + outSemicolonLn(); + } + + visitBreak(Break node) { + if (node.targetLabel == null) { + outIndent("break"); + } else { + outIndent("break ${node.targetLabel}"); + } + outSemicolonLn(); + } + + visitReturn(Return node) { + if (node.value == null) { + outIndent("return"); + } else { + outIndent("return"); + pendingSpace = true; + visitNestedExpression(node.value, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + } + outSemicolonLn(); + } + + visitThrow(Throw node) { + outIndent("throw"); + pendingSpace = true; + visitNestedExpression(node.expression, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + outSemicolonLn(); + } + + visitTry(Try node) { + outIndent("try"); + blockBody(node.body, needsSeparation: true, needsNewline: false); + if (node.catchPart != null) { + visit(node.catchPart); + } + if (node.finallyPart != null) { + spaceOut(); + out("finally"); + blockBody(node.finallyPart, needsSeparation: true, needsNewline: true); + } else { + lineOut(); + } + } + + visitCatch(Catch node) { + spaceOut(); + out("catch"); + spaceOut(); + out("("); + visitNestedExpression(node.declaration, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + out(")"); + blockBody(node.body, needsSeparation: false, needsNewline: true); + } + + visitSwitch(Switch node) { + outIndent("switch"); + spaceOut(); + out("("); + visitNestedExpression(node.key, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + out(")"); + spaceOut(); + outLn("{"); + indentLevel++; + visitAll(node.cases); + indentLevel--; + outIndentLn("}"); + } + + visitCase(Case node) { + outIndent("case"); + pendingSpace = true; + visitNestedExpression(node.expression, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + outLn(":"); + if (!node.body.statements.isEmpty) { + indentLevel++; + blockOutWithoutBraces(node.body); + indentLevel--; + } + } + + visitDefault(Default node) { + outIndentLn("default:"); + if (!node.body.statements.isEmpty) { + indentLevel++; + blockOutWithoutBraces(node.body); + indentLevel--; + } + } + + visitLabeledStatement(LabeledStatement node) { + outIndent("${node.label}:"); + blockBody(node.body, needsSeparation: false, needsNewline: true); + } + + void functionOut(Fun fun, Node name, VarCollector vars) { + out("function"); + if (name != null) { + out(" "); + // Name must be a [Decl]. Therefore only test for primary expressions. + visitNestedExpression(name, PRIMARY, + newInForInit: false, newAtStatementBegin: false); + } + localNamer.enterScope(vars); + out("("); + if (fun.params != null) { + visitCommaSeparated(fun.params, PRIMARY, + newInForInit: false, newAtStatementBegin: false); + } + out(")"); + blockBody(fun.body, needsSeparation: false, needsNewline: false); + localNamer.leaveScope(); + } + + visitFunctionDeclaration(FunctionDeclaration declaration) { + VarCollector vars = new VarCollector(); + vars.visitFunctionDeclaration(declaration); + indent(); + functionOut(declaration.function, declaration.name, vars); + lineOut(); + } + + visitNestedExpression(Expression node, int requiredPrecedence, + {bool newInForInit, bool newAtStatementBegin}) { + bool needsParentheses = + // a - (b + c). + (requiredPrecedence != EXPRESSION && + node.precedenceLevel < requiredPrecedence) || + // for (a = (x in o); ... ; ... ) { ... } + (newInForInit && node is Binary && node.op == "in") || + // (function() { ... })(). + // ({a: 2, b: 3}.toString()). + (newAtStatementBegin && (node is NamedFunction || + node is Fun || + node is ObjectInitializer)); + if (needsParentheses) { + inForInit = false; + atStatementBegin = false; + out("("); + visit(node); + out(")"); + } else { + inForInit = newInForInit; + atStatementBegin = newAtStatementBegin; + visit(node); + } + } + + visitVariableDeclarationList(VariableDeclarationList list) { + out("var "); + visitCommaSeparated(list.declarations, ASSIGNMENT, + newInForInit: inForInit, newAtStatementBegin: false); + } + + visitSequence(Sequence sequence) { + // Note that we only require that the entries are expressions and not + // assignments. This means that nested sequences are not put into + // parenthesis. + visitCommaSeparated(sequence.expressions, EXPRESSION, + newInForInit: false, + newAtStatementBegin: atStatementBegin); + } + + visitAssignment(Assignment assignment) { + visitNestedExpression(assignment.leftHandSide, LEFT_HAND_SIDE, + newInForInit: inForInit, + newAtStatementBegin: atStatementBegin); + if (assignment.value != null) { + spaceOut(); + String op = assignment.op; + if (op != null) out(op); + out("="); + spaceOut(); + visitNestedExpression(assignment.value, ASSIGNMENT, + newInForInit: inForInit, + newAtStatementBegin: false); + } + } + + visitVariableInitialization(VariableInitialization initialization) { + visitAssignment(initialization); + } + + visitConditional(Conditional cond) { + visitNestedExpression(cond.condition, LOGICAL_OR, + newInForInit: inForInit, + newAtStatementBegin: atStatementBegin); + spaceOut(); + out("?"); + spaceOut(); + // The then part is allowed to have an 'in'. + visitNestedExpression(cond.then, ASSIGNMENT, + newInForInit: false, newAtStatementBegin: false); + spaceOut(); + out(":"); + spaceOut(); + visitNestedExpression(cond.otherwise, ASSIGNMENT, + newInForInit: inForInit, newAtStatementBegin: false); + } + + visitNew(New node) { + out("new "); + visitNestedExpression(node.target, CALL, + newInForInit: inForInit, newAtStatementBegin: false); + out("("); + visitCommaSeparated(node.arguments, ASSIGNMENT, + newInForInit: false, newAtStatementBegin: false); + out(")"); + } + + visitCall(Call call) { + visitNestedExpression(call.target, LEFT_HAND_SIDE, + newInForInit: inForInit, + newAtStatementBegin: atStatementBegin); + out("("); + visitCommaSeparated(call.arguments, ASSIGNMENT, + newInForInit: false, newAtStatementBegin: false); + out(")"); + } + + visitBinary(Binary binary) { + Expression left = binary.left; + Expression right = binary.right; + String op = binary.op; + int leftPrecedenceRequirement; + int rightPrecedenceRequirement; + switch (op) { + case "||": + leftPrecedenceRequirement = LOGICAL_OR; + // x || (y || z) <=> (x || y) || z. + rightPrecedenceRequirement = LOGICAL_OR; + break; + case "&&": + leftPrecedenceRequirement = LOGICAL_AND; + // x && (y && z) <=> (x && y) && z. + rightPrecedenceRequirement = LOGICAL_AND; + break; + case "|": + leftPrecedenceRequirement = BIT_OR; + // x | (y | z) <=> (x | y) | z. + rightPrecedenceRequirement = BIT_OR; + break; + case "^": + leftPrecedenceRequirement = BIT_XOR; + // x ^ (y ^ z) <=> (x ^ y) ^ z. + rightPrecedenceRequirement = BIT_XOR; + break; + case "&": + leftPrecedenceRequirement = BIT_AND; + // x & (y & z) <=> (x & y) & z. + rightPrecedenceRequirement = BIT_AND; + break; + case "==": + case "!=": + case "===": + case "!==": + leftPrecedenceRequirement = EQUALITY; + rightPrecedenceRequirement = RELATIONAL; + break; + case "<": + case ">": + case "<=": + case ">=": + case "instanceof": + case "in": + leftPrecedenceRequirement = RELATIONAL; + rightPrecedenceRequirement = SHIFT; + break; + case ">>": + case "<<": + case ">>>": + leftPrecedenceRequirement = SHIFT; + rightPrecedenceRequirement = ADDITIVE; + break; + case "+": + case "-": + leftPrecedenceRequirement = ADDITIVE; + // We cannot remove parenthesis for "+" because + // x + (y + z) (x + y) + z: + // Example: + // "a" + (1 + 2) => "a3"; + // ("a" + 1) + 2 => "a12"; + rightPrecedenceRequirement = MULTIPLICATIVE; + break; + case "*": + case "/": + case "%": + leftPrecedenceRequirement = MULTIPLICATIVE; + // We cannot remove parenthesis for "*" because of precision issues. + rightPrecedenceRequirement = UNARY; + break; + default: + compiler.internalError(NO_LOCATION_SPANNABLE, "Forgot operator: $op"); + } + + visitNestedExpression(left, leftPrecedenceRequirement, + newInForInit: inForInit, + newAtStatementBegin: atStatementBegin); + + if (op == "in" || op == "instanceof") { + // There are cases where the space is not required but without further + // analysis we cannot know. + out(" "); + out(op); + out(" "); + } else { + spaceOut(); + out(op); + spaceOut(); + } + visitNestedExpression(right, rightPrecedenceRequirement, + newInForInit: inForInit, + newAtStatementBegin: false); + } + + visitPrefix(Prefix unary) { + String op = unary.op; + switch (op) { + case "delete": + case "void": + case "typeof": + // There are cases where the space is not required but without further + // analysis we cannot know. + out(op); + out(" "); + break; + case "+": + case "++": + if (lastCharCode == charCodes.$PLUS) out(" "); + out(op); + break; + case "-": + case "--": + if (lastCharCode == charCodes.$MINUS) out(" "); + out(op); + break; + default: + out(op); + } + visitNestedExpression(unary.argument, UNARY, + newInForInit: inForInit, newAtStatementBegin: false); + } + + visitPostfix(Postfix postfix) { + visitNestedExpression(postfix.argument, LEFT_HAND_SIDE, + newInForInit: inForInit, + newAtStatementBegin: atStatementBegin); + out(postfix.op); + } + + visitVariableUse(VariableUse ref) { + out(localNamer.getName(ref.name)); + } + + visitThis(This node) { + out("this"); + } + + visitVariableDeclaration(VariableDeclaration decl) { + out(localNamer.getName(decl.name)); + } + + visitParameter(Parameter param) { + out(localNamer.getName(param.name)); + } + + bool isDigit(int charCode) { + return charCodes.$0 <= charCode && charCode <= charCodes.$9; + } + + bool isValidJavaScriptId(String field) { + if (field.length < 3) return false; + // Ignore the leading and trailing string-delimiter. + for (int i = 1; i < field.length - 1; i++) { + // TODO(floitsch): allow more characters. + int charCode = field.codeUnitAt(i); + if (!(charCodes.$a <= charCode && charCode <= charCodes.$z || + charCodes.$A <= charCode && charCode <= charCodes.$Z || + charCode == charCodes.$$ || + charCode == charCodes.$_ || + i != 1 && isDigit(charCode))) { + return false; + } + } + // TODO(floitsch): normally we should also check that the field is not a + // reserved word. We don't generate fields with reserved word names except + // for 'super'. + if (field == '"super"') return false; + return true; + } + + visitAccess(PropertyAccess access) { + visitNestedExpression(access.receiver, CALL, + newInForInit: inForInit, + newAtStatementBegin: atStatementBegin); + Node selector = access.selector; + if (selector is LiteralString) { + LiteralString selectorString = selector; + String fieldWithQuotes = selectorString.value; + if (isValidJavaScriptId(fieldWithQuotes)) { + if (access.receiver is LiteralNumber) out(" "); + out("."); + out(fieldWithQuotes.substring(1, fieldWithQuotes.length - 1)); + return; + } + } + out("["); + visitNestedExpression(selector, EXPRESSION, + newInForInit: false, newAtStatementBegin: false); + out("]"); + } + + visitNamedFunction(NamedFunction namedFunction) { + VarCollector vars = new VarCollector(); + vars.visitNamedFunction(namedFunction); + functionOut(namedFunction.function, namedFunction.name, vars); + } + + visitFun(Fun fun) { + VarCollector vars = new VarCollector(); + vars.visitFun(fun); + functionOut(fun, null, vars); + } + + visitLiteralBool(LiteralBool node) { + out(node.value ? "true" : "false"); + } + + visitLiteralString(LiteralString node) { + out(node.value); + } + + visitLiteralNumber(LiteralNumber node) { + int charCode = node.value.codeUnitAt(0); + if (charCode == charCodes.$MINUS && lastCharCode == charCodes.$MINUS) { + out(" "); + } + out(node.value); + } + + visitLiteralNull(LiteralNull node) { + out("null"); + } + + visitArrayInitializer(ArrayInitializer node) { + out("["); + List elements = node.elements; + int elementIndex = 0; + for (int i = 0; i < node.length; i++) { + if (elementIndex < elements.length && + elements[elementIndex].index == i) { + visitNestedExpression(elements[elementIndex].value, ASSIGNMENT, + newInForInit: false, newAtStatementBegin: false); + elementIndex++; + // We can avoid a trailing "," if there was an element just before. So + // `[1]` and `[1,]` are the same, but `[,]` and `[]` are not. + if (i != node.length - 1) { + out(","); + spaceOut(); + } + } else { + out(","); + } + } + out("]"); + } + + visitArrayElement(ArrayElement node) { + throw "Unreachable"; + } + + visitObjectInitializer(ObjectInitializer node) { + // Print all the properties on one line until we see a function-valued + // property. Ideally, we would use a proper pretty-printer to make the + // decision based on layout. + List properties = node.properties; + out("{"); + ++indentLevel; + for (int i = 0; i < properties.length; i++) { + Expression value = properties[i].value; + if (i != 0) { + out(","); + if (node.isOneLiner) spaceOut(); + } + if (!node.isOneLiner) { + forceLine(); + indent(); + } + visitProperty(properties[i]); + } + --indentLevel; + if (!node.isOneLiner && !properties.isEmpty) { + lineOut(); + indent(); + } + out("}"); + } + + visitProperty(Property node) { + if (node.name is LiteralString) { + LiteralString nameString = node.name; + String name = nameString.value; + if (isValidJavaScriptId(name)) { + out(name.substring(1, name.length - 1)); + } else { + out(name); + } + } else { + assert(node.name is LiteralNumber); + LiteralNumber nameNumber = node.name; + out(nameNumber.value); + } + out(":"); + spaceOut(); + visitNestedExpression(node.value, ASSIGNMENT, + newInForInit: false, newAtStatementBegin: false); + } + + visitRegExpLiteral(RegExpLiteral node) { + out(node.pattern); + } + + visitLiteralExpression(LiteralExpression node) { + String template = node.template; + List inputs = node.inputs; + + List parts = template.split('#'); + int inputsLength = inputs == null ? 0 : inputs.length; + if (parts.length != inputsLength + 1) { + compiler.internalError(NO_LOCATION_SPANNABLE, + 'Wrong number of arguments for JS: $template'); + } + // Code that uses JS must take care of operator precedences, and + // put parenthesis if needed. + out(parts[0]); + for (int i = 0; i < inputsLength; i++) { + visit(inputs[i]); + out(parts[i + 1]); + } + } + + visitLiteralStatement(LiteralStatement node) { + outLn(node.code); + } + + visitJSExpression(JSExpression node) { + compiler.internalError(NO_LOCATION_SPANNABLE, + 'JSPrinter should never see a JSExpression.'); + } + + visitInterpolatedExpression(InterpolatedExpression node) { + visit(node.value); + } + + void visitComment(Comment node) { + if (shouldCompressOutput) return; + String comment = node.comment.trim(); + if (comment.isEmpty) return; + for (var line in comment.split('\n')) { + if (comment.startsWith('//')) { + outIndentLn(line.trim()); + } else { + outIndentLn('// ${line.trim()}'); + } + } + } +} + + +class OrderedSet { + final Set set; + final List list; + + OrderedSet() : set = new Set(), list = []; + + void add(T x) { + if (!set.contains(x)) { + set.add(x); + list.add(x); + } + } + + void forEach(void fun(T x)) { + list.forEach(fun); + } +} + +// Collects all the var declarations in the function. We need to do this in a +// separate pass because JS vars are lifted to the top of the function. +class VarCollector extends BaseVisitor { + bool nested; + final OrderedSet vars; + final OrderedSet params; + + VarCollector() : nested = false, + vars = new OrderedSet(), + params = new OrderedSet(); + + void forEachVar(void fn(String v)) => vars.forEach(fn); + void forEachParam(void fn(String p)) => params.forEach(fn); + + void collectVarsInFunction(Fun fun) { + if (!nested) { + nested = true; + if (fun.params != null) { + for (int i = 0; i < fun.params.length; i++) { + params.add(fun.params[i].name); + } + } + visitBlock(fun.body); + nested = false; + } + } + + void visitFunctionDeclaration(FunctionDeclaration declaration) { + // Note that we don't bother collecting the name of the function. + collectVarsInFunction(declaration.function); + } + + void visitNamedFunction(NamedFunction namedFunction) { + // Note that we don't bother collecting the name of the function. + collectVarsInFunction(namedFunction.function); + } + + void visitFun(Fun fun) { + collectVarsInFunction(fun); + } + + void visitThis(This node) {} + + void visitVariableDeclaration(VariableDeclaration decl) { + vars.add(decl.name); + } +} + + +/** + * Returns true, if the given node must be wrapped into braces when used + * as then-statement in an [If] that has an else branch. + */ +class DanglingElseVisitor extends BaseVisitor { + leg.Compiler compiler; + + DanglingElseVisitor(this.compiler); + + bool visitProgram(Program node) => false; + + bool visitNode(Statement node) { + compiler.internalError(NO_LOCATION_SPANNABLE, "Forgot node: $node"); + return null; + } + + bool visitBlock(Block node) => false; + bool visitExpressionStatement(ExpressionStatement node) => false; + bool visitEmptyStatement(EmptyStatement node) => false; + bool visitIf(If node) { + if (!node.hasElse) return true; + return node.otherwise.accept(this); + } + bool visitFor(For node) => node.body.accept(this); + bool visitForIn(ForIn node) => node.body.accept(this); + bool visitWhile(While node) => node.body.accept(this); + bool visitDo(Do node) => false; + bool visitContinue(Continue node) => false; + bool visitBreak(Break node) => false; + bool visitReturn(Return node) => false; + bool visitThrow(Throw node) => false; + bool visitTry(Try node) { + if (node.finallyPart != null) { + return node.finallyPart.accept(this); + } else { + return node.catchPart.accept(this); + } + } + bool visitCatch(Catch node) => node.body.accept(this); + bool visitSwitch(Switch node) => false; + bool visitCase(Case node) => false; + bool visitDefault(Default node) => false; + bool visitFunctionDeclaration(FunctionDeclaration node) => false; + bool visitLabeledStatement(LabeledStatement node) + => node.body.accept(this); + bool visitLiteralStatement(LiteralStatement node) => true; + + bool visitExpression(Expression node) => false; +} + + +leg.CodeBuffer prettyPrint(Node node, leg.Compiler compiler, + { allowVariableMinification: true }) { + Printer printer = + new Printer(compiler, + allowVariableMinification: allowVariableMinification); + printer.visit(node); + return printer.outBuffer; +} + + +abstract class LocalNamer { + String getName(String oldName); + String declareVariable(String oldName); + String declareParameter(String oldName); + void enterScope(VarCollector vars); + void leaveScope(); +} + + +class IdentityNamer implements LocalNamer { + String getName(String oldName) => oldName; + String declareVariable(String oldName) => oldName; + String declareParameter(String oldName) => oldName; + void enterScope(VarCollector vars) {} + void leaveScope() {} +} + + +class MinifyRenamer implements LocalNamer { + final List> maps = []; + final List parameterNumberStack = []; + final List variableNumberStack = []; + int parameterNumber = 0; + int variableNumber = 0; + + MinifyRenamer(); + + void enterScope(VarCollector vars) { + maps.add(new Map()); + variableNumberStack.add(variableNumber); + parameterNumberStack.add(parameterNumber); + vars.forEachVar(declareVariable); + vars.forEachParam(declareParameter); + } + + void leaveScope() { + maps.removeLast(); + variableNumber = variableNumberStack.removeLast(); + parameterNumber = parameterNumberStack.removeLast(); + } + + String getName(String oldName) { + // Go from inner scope to outer looking for mapping of name. + for (int i = maps.length - 1; i >= 0; i--) { + var map = maps[i]; + var replacement = map[oldName]; + if (replacement != null) return replacement; + } + return oldName; + } + + static const LOWER_CASE_LETTERS = 26; + static const LETTERS = LOWER_CASE_LETTERS; + static const DIGITS = 10; + + static int nthLetter(int n) { + return (n < LOWER_CASE_LETTERS) ? + charCodes.$a + n : + charCodes.$A + n - LOWER_CASE_LETTERS; + } + + // Parameters go from a to z and variables go from z to a. This makes each + // argument list and each top-of-function var declaration look similar and + // helps gzip compress the file. If we have more than 26 arguments and + // variables then we meet somewhere in the middle of the alphabet. After + // that we give up trying to be nice to the compression algorithm and just + // use the same namespace for arguments and variables, starting with A, and + // moving on to a0, a1, etc. + String declareVariable(String oldName) { + var newName; + if (variableNumber + parameterNumber < LOWER_CASE_LETTERS) { + // Variables start from z and go backwards, for better gzipability. + newName = getNameNumber(oldName, LOWER_CASE_LETTERS - 1 - variableNumber); + } else { + // After 26 variables and parameters we allocate them in the same order. + newName = getNameNumber(oldName, variableNumber + parameterNumber); + } + variableNumber++; + return newName; + } + + String declareParameter(String oldName) { + var newName; + if (variableNumber + parameterNumber < LOWER_CASE_LETTERS) { + newName = getNameNumber(oldName, parameterNumber); + } else { + newName = getNameNumber(oldName, variableNumber + parameterNumber); + } + parameterNumber++; + return newName; + } + + String getNameNumber(String oldName, int n) { + if (maps.isEmpty) return oldName; + + String newName; + if (n < LETTERS) { + // Start naming variables a, b, c, ..., z, A, B, C, ..., Z. + newName = new String.fromCharCodes([nthLetter(n)]); + } else { + // Then name variables a0, a1, a2, ..., a9, b0, b1, ..., Z9, aa0, aa1, ... + // For all functions with fewer than 500 locals this is just as compact + // as using aa, ab, etc. but avoids clashes with keywords. + n -= LETTERS; + int digit = n % DIGITS; + n ~/= DIGITS; + int alphaChars = 1; + int nameSpaceSize = LETTERS; + // Find out whether we should use the 1-character namespace (size 52), the + // 2-character namespace (size 52*52), etc. + while (n >= nameSpaceSize) { + n -= nameSpaceSize; + alphaChars++; + nameSpaceSize *= LETTERS; + } + var codes = []; + for (var i = 0; i < alphaChars; i++) { + nameSpaceSize ~/= LETTERS; + codes.add(nthLetter((n ~/ nameSpaceSize) % LETTERS)); + } + codes.add(charCodes.$0 + digit); + newName = new String.fromCharCodes(codes); + } + assert(new RegExp(r'[a-zA-Z][a-zA-Z0-9]*').hasMatch(newName)); + maps.last[oldName] = newName; + return newName; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/backend.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/backend.dart new file mode 100644 index 0000000..959cba9 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/backend.dart @@ -0,0 +1,1933 @@ +// Copyright (c) 2012, 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. + +part of js_backend; + +const VERBOSE_OPTIMIZER_HINTS = false; + +class JavaScriptItemCompilationContext extends ItemCompilationContext { + final Set boundsChecked = new Set(); + final Set allocatedFixedLists = new Set(); +} + +/* + * Invariants: + * canInline(function) implies canInline(function, insideLoop:true) + * !canInline(function, insideLoop: true) implies !canInline(function) + */ +class FunctionInlineCache { + final Map canBeInlined = + new Map(); + + final Map canBeInlinedInsideLoop = + new Map(); + + // Returns [:true:]/[:false:] if we have a cached decision. + // Returns [:null:] otherwise. + bool canInline(FunctionElement element, {bool insideLoop}) { + return insideLoop ? canBeInlinedInsideLoop[element] : canBeInlined[element]; + } + + void markAsInlinable(FunctionElement element, {bool insideLoop}) { + if (insideLoop) { + canBeInlinedInsideLoop[element] = true; + } else { + // If we can inline a function outside a loop then we should do it inside + // a loop as well. + canBeInlined[element] = true; + canBeInlinedInsideLoop[element] = true; + } + } + + void markAsNonInlinable(FunctionElement element, {bool insideLoop}) { + if (insideLoop == null || insideLoop) { + // If we can't inline a function inside a loop, then we should not inline + // it outside a loop either. + canBeInlined[element] = false; + canBeInlinedInsideLoop[element] = false; + } else { + canBeInlined[element] = false; + } + } +} + + +class JavaScriptBackend extends Backend { + SsaBuilderTask builder; + SsaOptimizerTask optimizer; + SsaCodeGeneratorTask generator; + CodeEmitterTask emitter; + + /** + * The generated code as a js AST for compiled methods. + */ + Map get generatedCode { + return compiler.enqueuer.codegen.generatedCode; + } + + FunctionInlineCache inlineCache = new FunctionInlineCache(); + + ClassElement jsInterceptorClass; + ClassElement jsStringClass; + ClassElement jsArrayClass; + ClassElement jsNumberClass; + ClassElement jsIntClass; + ClassElement jsDoubleClass; + ClassElement jsNullClass; + ClassElement jsBoolClass; + ClassElement jsPlainJavaScriptObjectClass; + ClassElement jsUnknownJavaScriptObjectClass; + + ClassElement jsIndexableClass; + ClassElement jsMutableIndexableClass; + + ClassElement jsMutableArrayClass; + ClassElement jsFixedArrayClass; + ClassElement jsExtendableArrayClass; + ClassElement jsPositiveIntClass; + ClassElement jsUInt32Class; + ClassElement jsUInt31Class; + + Element jsIndexableLength; + Element jsArrayTypedConstructor; + Element jsArrayRemoveLast; + Element jsArrayAdd; + Element jsStringSplit; + Element jsStringToString; + Element jsStringOperatorAdd; + Element objectEquals; + + ClassElement typeLiteralClass; + ClassElement mapLiteralClass; + ClassElement constMapLiteralClass; + ClassElement typeVariableClass; + + ClassElement noSideEffectsClass; + ClassElement noThrowsClass; + ClassElement noInlineClass; + ClassElement irRepresentationClass; + + Element getInterceptorMethod; + Element interceptedNames; + + /** + * This element is a top-level variable (in generated output) that the + * compiler initializes to a datastructure used to map from a Type to the + * interceptor. See declaration of `mapTypeToInterceptor` in + * `interceptors.dart`. + */ + Element mapTypeToInterceptor; + + TypeMask get stringType => compiler.typesTask.stringType; + TypeMask get doubleType => compiler.typesTask.doubleType; + TypeMask get intType => compiler.typesTask.intType; + TypeMask get uint32Type => compiler.typesTask.uint32Type; + TypeMask get uint31Type => compiler.typesTask.uint31Type; + TypeMask get positiveIntType => compiler.typesTask.positiveIntType; + TypeMask get numType => compiler.typesTask.numType; + TypeMask get boolType => compiler.typesTask.boolType; + TypeMask get dynamicType => compiler.typesTask.dynamicType; + TypeMask get nullType => compiler.typesTask.nullType; + TypeMask get emptyType => const TypeMask.nonNullEmpty(); + TypeMask indexablePrimitiveType; + TypeMask readableArrayType; + TypeMask mutableArrayType; + TypeMask fixedArrayType; + TypeMask extendableArrayType; + TypeMask nonNullType; + + /// Maps special classes to their implementation (JSXxx) class. + Map implementationClasses; + + Element getNativeInterceptorMethod; + bool needToInitializeIsolateAffinityTag = false; + bool needToInitializeDispatchProperty = false; + + /// Holds the method "getIsolateAffinityTag" when dart:_js_helper has been + /// loaded. + FunctionElement getIsolateAffinityTagMarker; + + final Namer namer; + + /** + * Interface used to determine if an object has the JavaScript + * indexing behavior. The interface is only visible to specific + * libraries. + */ + ClassElement jsIndexingBehaviorInterface; + + /** + * A collection of selectors that must have a one shot interceptor + * generated. + */ + final Map oneShotInterceptors; + + /** + * The members of instantiated interceptor classes: maps a member name to the + * list of members that have that name. This map is used by the codegen to + * know whether a send must be intercepted or not. + */ + final Map> interceptedElements; + + /** + * The members of mixin classes that are mixed into an instantiated + * interceptor class. This is a cached subset of [interceptedElements]. + * + * Mixin methods are not specialized for the class they are mixed into. + * Methods mixed into intercepted classes thus always make use of the explicit + * receiver argument, even when mixed into non-interceptor classes. + * + * These members must be invoked with a correct explicit receiver even when + * the receiver is not an intercepted class. + */ + final Map> interceptedMixinElements = + new Map>(); + + /** + * A map of specialized versions of the [getInterceptorMethod]. + * Since [getInterceptorMethod] is a hot method at runtime, we're + * always specializing it based on the incoming type. The keys in + * the map are the names of these specialized versions. Note that + * the generic version that contains all possible type checks is + * also stored in this map. + */ + final Map> specializedGetInterceptors; + + /** + * Set of classes whose methods are intercepted. + */ + final Set _interceptedClasses = new Set(); + + /** + * Set of classes used as mixins on intercepted (native and primitive) + * classes. Methods on these classes might also be mixed in to regular Dart + * (unintercepted) classes. + */ + final Set classesMixedIntoInterceptedClasses = + new Set(); + + /** + * Set of classes whose `operator ==` methods handle `null` themselves. + */ + final Set specialOperatorEqClasses = new Set(); + + List get tasks { + return [builder, optimizer, generator, emitter]; + } + + final RuntimeTypes rti; + + /// Holds the method "disableTreeShaking" in js_mirrors when + /// dart:mirrors has been loaded. + FunctionElement disableTreeShakingMarker; + + /// Holds the method "preserveNames" in js_mirrors when + /// dart:mirrors has been loaded. + FunctionElement preserveNamesMarker; + + /// Holds the method "preserveMetadata" in js_mirrors when + /// dart:mirrors has been loaded. + FunctionElement preserveMetadataMarker; + + /// True if a call to preserveMetadataMarker has been seen. This means that + /// metadata must be retained for dart:mirrors to work correctly. + bool mustRetainMetadata = false; + + /// True if any metadata has been retained. This is slightly different from + /// [mustRetainMetadata] and tells us if any metadata was retained. For + /// example, if [mustRetainMetadata] is true but there is no metadata in the + /// program, this variable will stil be false. + bool hasRetainedMetadata = false; + + /// True if a call to preserveNames has been seen. + bool mustPreserveNames = false; + + /// True if a call to disableTreeShaking has been seen. + bool isTreeShakingDisabled = false; + + /// True if there isn't sufficient @MirrorsUsed data. + bool hasInsufficientMirrorsUsed = false; + + /// List of constants from metadata. If metadata must be preserved, + /// these constants must be registered. + final List metadataConstants = []; + + /// List of symbols that the user has requested for reflection. + final Set symbolsUsed = new Set(); + + /// List of elements that the user has requested for reflection. + final Set targetsUsed = new Set(); + + /// List of annotations provided by user that indicate that the annotated + /// element must be retained. + final Set metaTargetsUsed = new Set(); + + /// List of elements that the backend may use. + final Set helpersUsed = new Set(); + + + /// Set of typedefs that are used as type literals. + final Set typedefTypeLiterals = new Set(); + + /// All the checked mode helpers. + static const checkedModeHelpers = CheckedModeHelper.helpers; + + // Checked mode helpers indexed by name. + Map checkedModeHelperByName = + new Map.fromIterable( + checkedModeHelpers, + key: (helper) => helper.name); + + TypeVariableHandler typeVariableHandler; + + /// Number of methods compiled before considering reflection. + int preMirrorsMethodCount = 0; + + /// Resolution and codegen support for generating table of interceptors and + /// constructors for custom elements. + CustomElementsAnalysis customElementsAnalysis; + + JavaScriptBackend(Compiler compiler, bool generateSourceMap) + : namer = determineNamer(compiler), + oneShotInterceptors = new Map(), + interceptedElements = new Map>(), + rti = new RuntimeTypes(compiler), + specializedGetInterceptors = new Map>(), + super(compiler, JAVA_SCRIPT_CONSTANT_SYSTEM) { + emitter = new CodeEmitterTask(compiler, namer, generateSourceMap); + builder = new SsaBuilderTask(this); + optimizer = new SsaOptimizerTask(this); + generator = new SsaCodeGeneratorTask(this); + typeVariableHandler = new TypeVariableHandler(this); + customElementsAnalysis = new CustomElementsAnalysis(this); + } + + static Namer determineNamer(Compiler compiler) { + return compiler.enableMinification ? + new MinifyNamer(compiler) : + new Namer(compiler); + } + + bool usedByBackend(Element element) { + if (element.isParameter() + || element.isFieldParameter() + || element.isField()) { + if (usedByBackend(element.enclosingElement)) return true; + } + return helpersUsed.contains(element.declaration); + } + + bool invokedReflectively(Element element) { + if (element.isParameter() || element.isFieldParameter()) { + if (invokedReflectively(element.enclosingElement)) return true; + } + + if (element.isField()) { + if (Elements.isStaticOrTopLevel(element) + && (element.modifiers.isFinal() || element.modifiers.isConst())) { + return false; + } + } + + return isNeededForReflection(element.declaration); + } + + bool canBeUsedForGlobalOptimizations(Element element) { + return !usedByBackend(element) && !invokedReflectively(element); + } + + bool isInterceptorClass(ClassElement element) { + if (element == null) return false; + if (Elements.isNativeOrExtendsNative(element)) return true; + if (interceptedClasses.contains(element)) return true; + if (classesMixedIntoInterceptedClasses.contains(element)) return true; + return false; + } + + String registerOneShotInterceptor(Selector selector) { + Set classes = getInterceptedClassesOn(selector.name); + String name = namer.getOneShotInterceptorName(selector, classes); + if (!oneShotInterceptors.containsKey(name)) { + registerSpecializedGetInterceptor(classes); + oneShotInterceptors[name] = selector; + } + return name; + } + + bool isInterceptedMethod(Element element) { + if (!element.isInstanceMember()) return false; + if (element.isGenerativeConstructorBody()) { + return Elements.isNativeOrExtendsNative(element.getEnclosingClass()); + } + return interceptedElements[element.name] != null; + } + + bool fieldHasInterceptedGetter(Element element) { + assert(element.isField()); + return interceptedElements[element.name] != null; + } + + bool fieldHasInterceptedSetter(Element element) { + assert(element.isField()); + return interceptedElements[element.name] != null; + } + + bool isInterceptedName(String name) { + return interceptedElements[name] != null; + } + + bool isInterceptedSelector(Selector selector) { + return interceptedElements[selector.name] != null; + } + + /** + * Returns `true` iff [selector] matches an element defined in a class mixed + * into an intercepted class. These selectors are not eligible for the 'dummy + * explicit receiver' optimization. + */ + bool isInterceptedMixinSelector(Selector selector) { + Set elements = interceptedMixinElements.putIfAbsent( + selector.name, + () { + Set elements = interceptedElements[selector.name]; + if (elements == null) return null; + return elements + .where((element) => + classesMixedIntoInterceptedClasses.contains( + element.getEnclosingClass())) + .toSet(); + }); + + if (elements == null) return false; + if (elements.isEmpty) return false; + return elements.any((element) => selector.applies(element, compiler)); + } + + final Map> interceptedClassesCache = + new Map>(); + + /** + * Returns a set of interceptor classes that contain a member named + * [name]. Returns [:null:] if there is no class. + */ + Set getInterceptedClassesOn(String name) { + Set intercepted = interceptedElements[name]; + if (intercepted == null) return null; + return interceptedClassesCache.putIfAbsent(name, () { + // Populate the cache by running through all the elements and + // determine if the given selector applies to them. + Set result = new Set(); + for (Element element in intercepted) { + ClassElement classElement = element.getEnclosingClass(); + if (Elements.isNativeOrExtendsNative(classElement) + || interceptedClasses.contains(classElement)) { + result.add(classElement); + } + if (classesMixedIntoInterceptedClasses.contains(classElement)) { + Set nativeSubclasses = + nativeSubclassesOfMixin(classElement); + if (nativeSubclasses != null) result.addAll(nativeSubclasses); + } + } + return result; + }); + } + + Set nativeSubclassesOfMixin(ClassElement mixin) { + Set uses = compiler.world.mixinUses[mixin]; + if (uses == null) return null; + Set result = null; + for (MixinApplicationElement use in uses) { + Iterable subclasses = compiler.world.subclassesOf(use); + if (subclasses != null) { + for (ClassElement subclass in subclasses) { + if (Elements.isNativeOrExtendsNative(subclass)) { + if (result == null) result = new Set(); + result.add(subclass); + } + } + } + } + return result; + } + + bool operatorEqHandlesNullArgument(FunctionElement operatorEqfunction) { + return specialOperatorEqClasses.contains( + operatorEqfunction.getEnclosingClass()); + } + + void initializeHelperClasses() { + getInterceptorMethod = compiler.findInterceptor('getInterceptor'); + interceptedNames = compiler.findInterceptor('interceptedNames'); + mapTypeToInterceptor = compiler.findInterceptor('mapTypeToInterceptor'); + getNativeInterceptorMethod = + compiler.findInterceptor('getNativeInterceptor'); + + // These methods are overwritten with generated versions. + inlineCache.markAsNonInlinable(getInterceptorMethod, insideLoop: true); + + List classes = [ + jsInterceptorClass = + compiler.findInterceptor('Interceptor'), + jsStringClass = compiler.findInterceptor('JSString'), + jsArrayClass = compiler.findInterceptor('JSArray'), + // The int class must be before the double class, because the + // emitter relies on this list for the order of type checks. + jsIntClass = compiler.findInterceptor('JSInt'), + jsPositiveIntClass = compiler.findInterceptor('JSPositiveInt'), + jsUInt32Class = compiler.findInterceptor('JSUInt32'), + jsUInt31Class = compiler.findInterceptor('JSUInt31'), + jsDoubleClass = compiler.findInterceptor('JSDouble'), + jsNumberClass = compiler.findInterceptor('JSNumber'), + jsNullClass = compiler.findInterceptor('JSNull'), + jsBoolClass = compiler.findInterceptor('JSBool'), + jsMutableArrayClass = compiler.findInterceptor('JSMutableArray'), + jsFixedArrayClass = compiler.findInterceptor('JSFixedArray'), + jsExtendableArrayClass = compiler.findInterceptor('JSExtendableArray'), + jsPlainJavaScriptObjectClass = + compiler.findInterceptor('PlainJavaScriptObject'), + jsUnknownJavaScriptObjectClass = + compiler.findInterceptor('UnknownJavaScriptObject'), + ]; + + implementationClasses = {}; + implementationClasses[compiler.intClass] = jsIntClass; + implementationClasses[compiler.boolClass] = jsBoolClass; + implementationClasses[compiler.numClass] = jsNumberClass; + implementationClasses[compiler.doubleClass] = jsDoubleClass; + implementationClasses[compiler.stringClass] = jsStringClass; + implementationClasses[compiler.listClass] = jsArrayClass; + implementationClasses[compiler.nullClass] = jsNullClass; + + jsIndexableClass = compiler.findInterceptor('JSIndexable'); + jsMutableIndexableClass = compiler.findInterceptor('JSMutableIndexable'); + + // TODO(kasperl): Some tests do not define the special JSArray + // subclasses, so we check to see if they are defined before + // trying to resolve them. + if (jsFixedArrayClass != null) { + jsFixedArrayClass.ensureResolved(compiler); + } + if (jsExtendableArrayClass != null) { + jsExtendableArrayClass.ensureResolved(compiler); + } + + jsIndexableClass.ensureResolved(compiler); + jsIndexableLength = compiler.lookupElementIn( + jsIndexableClass, 'length'); + if (jsIndexableLength != null && jsIndexableLength.isAbstractField()) { + AbstractFieldElement element = jsIndexableLength; + jsIndexableLength = element.getter; + } + + jsArrayClass.ensureResolved(compiler); + jsArrayTypedConstructor = compiler.lookupElementIn(jsArrayClass, 'typed'); + jsArrayRemoveLast = compiler.lookupElementIn(jsArrayClass, 'removeLast'); + jsArrayAdd = compiler.lookupElementIn(jsArrayClass, 'add'); + + jsStringClass.ensureResolved(compiler); + jsStringSplit = compiler.lookupElementIn(jsStringClass, 'split'); + jsStringOperatorAdd = compiler.lookupElementIn(jsStringClass, '+'); + jsStringToString = compiler.lookupElementIn(jsStringClass, 'toString'); + + typeLiteralClass = compiler.findHelper('TypeImpl'); + mapLiteralClass = compiler.coreLibrary.find('LinkedHashMap'); + constMapLiteralClass = compiler.findHelper('ConstantMap'); + + objectEquals = compiler.lookupElementIn(compiler.objectClass, '=='); + + jsIndexingBehaviorInterface = + compiler.findHelper('JavaScriptIndexingBehavior'); + + specialOperatorEqClasses + ..add(compiler.objectClass) + ..add(jsInterceptorClass) + ..add(jsNullClass); + + validateInterceptorImplementsAllObjectMethods(jsInterceptorClass); + + typeVariableClass = compiler.findHelper('TypeVariable'); + + indexablePrimitiveType = new TypeMask.nonNullSubtype(jsIndexableClass); + readableArrayType = new TypeMask.nonNullSubclass(jsArrayClass); + mutableArrayType = new TypeMask.nonNullSubclass(jsMutableArrayClass); + fixedArrayType = new TypeMask.nonNullExact(jsFixedArrayClass); + extendableArrayType = new TypeMask.nonNullExact(jsExtendableArrayClass); + nonNullType = compiler.typesTask.dynamicType.nonNullable(); + + noSideEffectsClass = compiler.findHelper('NoSideEffects'); + noThrowsClass = compiler.findHelper('NoThrows'); + noInlineClass = compiler.findHelper('NoInline'); + irRepresentationClass = compiler.findHelper('IrRepresentation'); + } + + void validateInterceptorImplementsAllObjectMethods( + ClassElement interceptorClass) { + if (interceptorClass == null) return; + interceptorClass.ensureResolved(compiler); + compiler.objectClass.forEachMember((_, Element member) { + if (member.isGenerativeConstructor()) return; + Element interceptorMember = interceptorClass.lookupMember(member.name); + // Interceptors must override all Object methods due to calling convention + // differences. + assert(interceptorMember.getEnclosingClass() != compiler.objectClass); + }); + } + + void addInterceptorsForNativeClassMembers( + ClassElement cls, Enqueuer enqueuer) { + if (enqueuer.isResolutionQueue) { + cls.ensureResolved(compiler); + cls.forEachMember((ClassElement classElement, Element member) { + if (member.name == Compiler.CALL_OPERATOR_NAME) { + compiler.reportError( + member, + MessageKind.CALL_NOT_SUPPORTED_ON_NATIVE_CLASS); + return; + } + if (member.isSynthesized) return; + // All methods on [Object] are shadowed by [Interceptor]. + if (classElement == compiler.objectClass) return; + Set set = interceptedElements.putIfAbsent( + member.name, () => new Set()); + set.add(member); + }, + includeSuperAndInjectedMembers: true); + + // Walk superclass chain to find mixins. + for (; cls != null; cls = cls.superclass) { + if (cls.isMixinApplication) { + MixinApplicationElement mixinApplication = cls; + classesMixedIntoInterceptedClasses.add(mixinApplication.mixin); + } + } + } + } + + void addInterceptors(ClassElement cls, + Enqueuer enqueuer, + TreeElements elements) { + if (enqueuer.isResolutionQueue) { + _interceptedClasses.add(jsInterceptorClass); + _interceptedClasses.add(cls); + cls.ensureResolved(compiler); + cls.forEachMember((ClassElement classElement, Element member) { + // All methods on [Object] are shadowed by [Interceptor]. + if (classElement == compiler.objectClass) return; + Set set = interceptedElements.putIfAbsent( + member.name, () => new Set()); + set.add(member); + }, + includeSuperAndInjectedMembers: true); + } + enqueueClass(enqueuer, cls, elements); + } + + Set get interceptedClasses { + assert(compiler.enqueuer.resolution.queueIsClosed); + return _interceptedClasses; + } + + void registerSpecializedGetInterceptor(Set classes) { + String name = namer.getInterceptorName(getInterceptorMethod, classes); + if (classes.contains(jsInterceptorClass)) { + // We can't use a specialized [getInterceptorMethod], so we make + // sure we emit the one with all checks. + specializedGetInterceptors[name] = interceptedClasses; + } else { + specializedGetInterceptors[name] = classes; + } + } + + void registerCompileTimeConstant(Constant constant, TreeElements elements) { + registerCompileTimeConstantInternal(constant, elements); + for (Constant dependency in constant.getDependencies()) { + registerCompileTimeConstant(dependency, elements); + } + } + + void registerCompileTimeConstantInternal(Constant constant, + TreeElements elements) { + DartType type = constant.computeType(compiler); + registerInstantiatedConstantType(type, elements); + + if (constant.isFunction) { + FunctionConstant function = constant; + compiler.enqueuer.codegen.registerGetOfStaticFunction(function.element); + } else if (constant.isInterceptor) { + // An interceptor constant references the class's prototype chain. + InterceptorConstant interceptor = constant; + registerInstantiatedConstantType(interceptor.dispatchedType, elements); + } else if (constant.isType) { + TypeConstant typeConstant = constant; + registerTypeLiteral(typeConstant.representedType.element, + compiler.enqueuer.codegen, elements); + } + } + + void registerInstantiatedConstantType(DartType type, TreeElements elements) { + Enqueuer enqueuer = compiler.enqueuer.codegen; + DartType instantiatedType = + type.kind == TypeKind.FUNCTION ? compiler.functionClass.rawType : type; + enqueuer.registerInstantiatedType(instantiatedType, elements); + if (type is InterfaceType && !type.treatAsRaw && + classNeedsRti(type.element)) { + enqueuer.registerStaticUse(getSetRuntimeTypeInfo()); + } + if (type.element == typeImplementation) { + // If we use a type literal in a constant, the compile time + // constant emitter will generate a call to the createRuntimeType + // helper so we register a use of that. + enqueuer.registerStaticUse(getCreateRuntimeType()); + } + } + + void registerMetadataConstant(Constant constant, TreeElements elements) { + if (mustRetainMetadata) { + registerCompileTimeConstant(constant, elements); + } else { + metadataConstants.add(new Dependency(constant, elements)); + } + } + + void registerInstantiatedClass(ClassElement cls, + Enqueuer enqueuer, + TreeElements elements) { + if (!cls.typeVariables.isEmpty) { + typeVariableHandler.registerClassWithTypeVariables(cls); + } + + // Register any helper that will be needed by the backend. + if (enqueuer.isResolutionQueue) { + if (cls == compiler.intClass + || cls == compiler.doubleClass + || cls == compiler.numClass) { + // The backend will try to optimize number operations and use the + // `iae` helper directly. + enqueue(enqueuer, + compiler.findHelper('iae'), + elements); + } else if (cls == compiler.listClass + || cls == compiler.stringClass) { + // The backend will try to optimize array and string access and use the + // `ioore` and `iae` helpers directly. + enqueue(enqueuer, + compiler.findHelper('ioore'), + elements); + enqueue(enqueuer, + compiler.findHelper('iae'), + elements); + } else if (cls == compiler.functionClass) { + enqueueClass(enqueuer, compiler.closureClass, elements); + } else if (cls == compiler.mapClass) { + // The backend will use a literal list to initialize the entries + // of the map. + enqueueClass(enqueuer, compiler.listClass, elements); + enqueueClass(enqueuer, mapLiteralClass, elements); + // For map literals, the dependency between the implementation class + // and [Map] is not visible, so we have to add it manually. + rti.registerRtiDependency(mapLiteralClass, cls); + enqueueInResolution(getMapMaker(), elements); + } else if (cls == compiler.boundClosureClass) { + // TODO(ngeoffray): Move the bound closure class in the + // backend. + enqueueClass(enqueuer, compiler.boundClosureClass, elements); + } else if (Elements.isNativeOrExtendsNative(cls)) { + enqueue(enqueuer, getNativeInterceptorMethod, elements); + enqueueClass(enqueuer, jsInterceptorClass, compiler.globalDependencies); + enqueueClass(enqueuer, jsPlainJavaScriptObjectClass, elements); + } + } + if (cls == compiler.closureClass) { + enqueue(enqueuer, + compiler.findHelper('closureFromTearOff'), + elements); + } + ClassElement result = null; + if (cls == compiler.stringClass || cls == jsStringClass) { + addInterceptors(jsStringClass, enqueuer, elements); + } else if (cls == compiler.listClass + || cls == jsArrayClass + || cls == jsFixedArrayClass + || cls == jsExtendableArrayClass) { + addInterceptors(jsArrayClass, enqueuer, elements); + addInterceptors(jsMutableArrayClass, enqueuer, elements); + addInterceptors(jsFixedArrayClass, enqueuer, elements); + addInterceptors(jsExtendableArrayClass, enqueuer, elements); + } else if (cls == compiler.intClass || cls == jsIntClass) { + addInterceptors(jsIntClass, enqueuer, elements); + addInterceptors(jsPositiveIntClass, enqueuer, elements); + addInterceptors(jsUInt32Class, enqueuer, elements); + addInterceptors(jsUInt31Class, enqueuer, elements); + addInterceptors(jsNumberClass, enqueuer, elements); + } else if (cls == compiler.doubleClass || cls == jsDoubleClass) { + addInterceptors(jsDoubleClass, enqueuer, elements); + addInterceptors(jsNumberClass, enqueuer, elements); + } else if (cls == compiler.boolClass || cls == jsBoolClass) { + addInterceptors(jsBoolClass, enqueuer, elements); + } else if (cls == compiler.nullClass || cls == jsNullClass) { + addInterceptors(jsNullClass, enqueuer, elements); + } else if (cls == compiler.numClass || cls == jsNumberClass) { + addInterceptors(jsIntClass, enqueuer, elements); + addInterceptors(jsPositiveIntClass, enqueuer, elements); + addInterceptors(jsUInt32Class, enqueuer, elements); + addInterceptors(jsUInt31Class, enqueuer, elements); + addInterceptors(jsDoubleClass, enqueuer, elements); + addInterceptors(jsNumberClass, enqueuer, elements); + } else if (cls == jsPlainJavaScriptObjectClass) { + addInterceptors(jsPlainJavaScriptObjectClass, enqueuer, elements); + } else if (cls == jsUnknownJavaScriptObjectClass) { + addInterceptors(jsUnknownJavaScriptObjectClass, enqueuer, elements); + } else if (Elements.isNativeOrExtendsNative(cls)) { + addInterceptorsForNativeClassMembers(cls, enqueuer); + } else if (cls == jsIndexingBehaviorInterface) { + // These two helpers are used by the emitter and the codegen. + // Because we cannot enqueue elements at the time of emission, + // we make sure they are always generated. + enqueue( + enqueuer, + compiler.findHelper('isJsIndexable'), + elements); + enqueue( + enqueuer, + compiler.findInterceptor('dispatchPropertyName'), + elements); + } + + customElementsAnalysis.registerInstantiatedClass(cls, enqueuer); + } + + void registerUseInterceptor(Enqueuer enqueuer) { + assert(!enqueuer.isResolutionQueue); + if (!enqueuer.nativeEnqueuer.hasInstantiatedNativeClasses()) return; + TreeElements elements = compiler.globalDependencies; + enqueue(enqueuer, getNativeInterceptorMethod, elements); + enqueueClass(enqueuer, jsPlainJavaScriptObjectClass, elements); + needToInitializeIsolateAffinityTag = true; + needToInitializeDispatchProperty = true; + } + + JavaScriptItemCompilationContext createItemCompilationContext() { + return new JavaScriptItemCompilationContext(); + } + + void enqueueHelpers(ResolutionEnqueuer world, TreeElements elements) { + // TODO(ngeoffray): Not enqueuing those two classes currently make + // the compiler potentially crash. However, any reasonable program + // will instantiate those two classes. + addInterceptors(jsBoolClass, world, elements); + addInterceptors(jsNullClass, world, elements); + if (compiler.enableTypeAssertions) { + // Unconditionally register the helper that checks if the + // expression in an if/while/for is a boolean. + // TODO(ngeoffray): Should we have the resolver register those instead? + Element e = + compiler.findHelper('boolConversionCheck'); + if (e != null) enqueue(world, e, elements); + } + registerCheckedModeHelpers(elements); + } + + onResolutionComplete() => rti.computeClassesNeedingRti(); + + void registerStringInterpolation(TreeElements elements) { + enqueueInResolution(getStringInterpolationHelper(), elements); + } + + void registerCatchStatement(Enqueuer enqueuer, TreeElements elements) { + void ensure(ClassElement classElement) { + if (classElement != null) { + enqueueClass(enqueuer, classElement, elements); + } + } + enqueueInResolution(getExceptionUnwrapper(), elements); + ensure(jsPlainJavaScriptObjectClass); + ensure(jsUnknownJavaScriptObjectClass); + } + + void registerThrowExpression(TreeElements elements) { + // We don't know ahead of time whether we will need the throw in a + // statement context or an expression context, so we register both + // here, even though we may not need the throwExpression helper. + enqueueInResolution(getWrapExceptionHelper(), elements); + enqueueInResolution(getThrowExpressionHelper(), elements); + } + + void registerLazyField(TreeElements elements) { + enqueueInResolution(getCyclicThrowHelper(), elements); + } + + void registerTypeLiteral(Element element, + Enqueuer enqueuer, + TreeElements elements) { + enqueuer.registerInstantiatedClass(typeImplementation, elements); + enqueueInResolution(getCreateRuntimeType(), elements); + // TODO(ahe): Might want to register [element] as an instantiated class + // when reflection is used. However, as long as we disable tree-shaking + // eagerly it doesn't matter. + if (element.isTypedef()) { + typedefTypeLiterals.add(element); + } + customElementsAnalysis.registerTypeLiteral(element, enqueuer); + } + + void registerStackTraceInCatch(TreeElements elements) { + enqueueInResolution(getTraceFromException(), elements); + } + + void registerGetRuntimeTypeArgument(TreeElements elements) { + enqueueInResolution(getGetRuntimeTypeArgument(), elements); + enqueueInResolution(getGetTypeArgumentByIndex(), elements); + enqueueInResolution(getCopyTypeArguments(), elements); + } + + void registerGenericCallMethod(Element callMethod, + Enqueuer enqueuer, TreeElements elements) { + if (enqueuer.isResolutionQueue || methodNeedsRti(callMethod)) { + registerComputeSignature(enqueuer, elements); + } + } + + void registerGenericClosure(Element closure, + Enqueuer enqueuer, TreeElements elements) { + if (enqueuer.isResolutionQueue || methodNeedsRti(closure)) { + registerComputeSignature(enqueuer, elements); + } + } + + void registerComputeSignature(Enqueuer enqueuer, TreeElements elements) { + // Calls to [:computeSignature:] are generated by the emitter and we + // therefore need to enqueue the used elements in the codegen enqueuer as + // well as in the resolution enqueuer. + enqueue(enqueuer, getSetRuntimeTypeInfo(), elements); + enqueue(enqueuer, getGetRuntimeTypeInfo(), elements); + enqueue(enqueuer, getComputeSignature(), elements); + enqueue(enqueuer, getGetRuntimeTypeArguments(), elements); + enqueueClass(enqueuer, compiler.listClass, elements); + } + + void registerRuntimeType(Enqueuer enqueuer, TreeElements elements) { + registerComputeSignature(enqueuer, elements); + enqueueInResolution(getSetRuntimeTypeInfo(), elements); + enqueueInResolution(getGetRuntimeTypeInfo(), elements); + registerGetRuntimeTypeArgument(elements); + enqueueClass(enqueuer, compiler.listClass, elements); + } + + void registerTypeVariableExpression(TreeElements elements) { + enqueueInResolution(getSetRuntimeTypeInfo(), elements); + enqueueInResolution(getGetRuntimeTypeInfo(), elements); + registerGetRuntimeTypeArgument(elements); + enqueueClass(compiler.enqueuer.resolution, compiler.listClass, elements); + enqueueInResolution(getRuntimeTypeToString(), elements); + enqueueInResolution(getCreateRuntimeType(), elements); + } + + void registerIsCheck(DartType type, Enqueuer world, TreeElements elements) { + enqueueInResolution(getThrowRuntimeError(), elements); + type = type.unalias(compiler); + enqueueClass(world, compiler.boolClass, elements); + bool inCheckedMode = compiler.enableTypeAssertions; + // [registerIsCheck] is also called for checked mode checks, so we + // need to register checked mode helpers. + if (inCheckedMode) { + if (!world.isResolutionQueue) { + // All helpers are added to resolution queue in enqueueHelpers. These + // calls to enqueueInResolution serve as assertions that the helper was + // in fact added. + // TODO(13155): Find a way to enqueue helpers lazily. + CheckedModeHelper helper = getCheckedModeHelper(type, typeCast: false); + if (helper != null) { + enqueue(world, helper.getElement(compiler), elements); + } + // We also need the native variant of the check (for DOM types). + helper = getNativeCheckedModeHelper(type, typeCast: false); + if (helper != null) { + enqueue(world, helper.getElement(compiler), elements); + } + } + } + bool isTypeVariable = type.kind == TypeKind.TYPE_VARIABLE; + if (type.kind == TypeKind.MALFORMED_TYPE) { + enqueueInResolution(getThrowTypeError(), elements); + } + if (!type.treatAsRaw || type.containsTypeVariables) { + enqueueInResolution(getSetRuntimeTypeInfo(), elements); + enqueueInResolution(getGetRuntimeTypeInfo(), elements); + registerGetRuntimeTypeArgument(elements); + if (inCheckedMode) { + enqueueInResolution(getAssertSubtype(), elements); + } + enqueueInResolution(getCheckSubtype(), elements); + if (isTypeVariable) { + enqueueInResolution(getCheckSubtypeOfRuntimeType(), elements); + if (inCheckedMode) { + enqueueInResolution(getAssertSubtypeOfRuntimeType(), elements); + } + } + enqueueClass(world, compiler.listClass, elements); + } + if (type is FunctionType) { + enqueueInResolution( + compiler.findHelper('functionTypeTestMetaHelper'), elements); + } + if (type.element.isNative()) { + // We will neeed to add the "$is" and "$as" properties on the + // JavaScript object prototype, so we make sure + // [:defineProperty:] is compiled. + enqueue(world, + compiler.findHelper('defineProperty'), + elements); + } + } + + void registerAsCheck(DartType type, Enqueuer world, TreeElements elements) { + enqueueInResolution(getThrowRuntimeError(), elements); + type = type.unalias(compiler); + if (!world.isResolutionQueue) { + // All helpers are added to resolution queue in enqueueHelpers. These + // calls to enqueueInResolution serve as assertions that the helper was in + // fact added. + // TODO(13155): Find a way to enqueue helpers lazily. + CheckedModeHelper helper = getCheckedModeHelper(type, typeCast: true); + enqueueInResolution(helper.getElement(compiler), elements); + // We also need the native variant of the check (for DOM types). + helper = getNativeCheckedModeHelper(type, typeCast: true); + if (helper != null) { + enqueueInResolution(helper.getElement(compiler), elements); + } + } + } + + void registerThrowNoSuchMethod(TreeElements elements) { + enqueueInResolution(getThrowNoSuchMethod(), elements); + // Also register the types of the arguments passed to this method. + enqueueClass(compiler.enqueuer.resolution, compiler.listClass, elements); + enqueueClass(compiler.enqueuer.resolution, compiler.stringClass, elements); + } + + void registerThrowRuntimeError(TreeElements elements) { + enqueueInResolution(getThrowRuntimeError(), elements); + // Also register the types of the arguments passed to this method. + enqueueClass(compiler.enqueuer.resolution, compiler.stringClass, elements); + } + + void registerTypeVariableBoundsSubtypeCheck(DartType typeArgument, + DartType bound) { + rti.registerTypeVariableBoundsSubtypeCheck(typeArgument, bound); + } + + void registerTypeVariableBoundCheck(TreeElements elements) { + enqueueInResolution(getThrowTypeError(), elements); + enqueueInResolution(getAssertIsSubtype(), elements); + } + + void registerAbstractClassInstantiation(TreeElements elements) { + enqueueInResolution(getThrowAbstractClassInstantiationError(), elements); + // Also register the types of the arguments passed to this method. + enqueueClass(compiler.enqueuer.resolution, compiler.stringClass, elements); + } + + void registerFallThroughError(TreeElements elements) { + enqueueInResolution(getFallThroughError(), elements); + } + + void enableNoSuchMethod(Enqueuer world) { + enqueue(world, getCreateInvocationMirror(), compiler.globalDependencies); + world.registerInvocation(compiler.noSuchMethodSelector); + } + + void registerSuperNoSuchMethod(TreeElements elements) { + enqueueInResolution(getCreateInvocationMirror(), elements); + enqueueInResolution( + compiler.objectClass.lookupLocalMember(Compiler.NO_SUCH_METHOD), + elements); + enqueueClass(compiler.enqueuer.resolution, compiler.listClass, elements); + } + + void registerRequiredType(DartType type, Element enclosingElement) { + // If [argument] has type variables or is a type variable, this method + // registers a RTI dependency between the class where the type variable is + // defined (that is the enclosing class of the current element being + // resolved) and the class of [type]. If the class of [type] requires RTI, + // then the class of the type variable does too. + ClassElement contextClass = Types.getClassContext(type); + if (contextClass != null) { + assert(contextClass == enclosingElement.getEnclosingClass().declaration); + rti.registerRtiDependency(type.element, contextClass); + } + } + + void registerClassUsingVariableExpression(ClassElement cls) { + rti.classesUsingTypeVariableExpression.add(cls); + } + + bool classNeedsRti(ClassElement cls) { + return rti.classesNeedingRti.contains(cls.declaration) || + compiler.enabledRuntimeType; + } + + bool isDefaultNoSuchMethodImplementation(Element element) { + assert(element.name == Compiler.NO_SUCH_METHOD); + ClassElement classElement = element.getEnclosingClass(); + return classElement == compiler.objectClass + || classElement == jsInterceptorClass; + } + + bool isDefaultEqualityImplementation(Element element) { + assert(element.name == '=='); + ClassElement classElement = element.getEnclosingClass(); + return classElement == compiler.objectClass + || classElement == jsInterceptorClass + || classElement == jsNullClass; + } + + bool methodNeedsRti(FunctionElement function) { + return rti.methodsNeedingRti.contains(function) || + compiler.enabledRuntimeType; + } + + // Enqueue [e] in [enqueuer]. + // + // The backend must *always* call this method when enqueuing an + // element. Calls done by the backend are not seen by global + // optimizations, so they would make these optimizations unsound. + // Therefore we need to collect the list of helpers the backend may + // use. + void enqueue(Enqueuer enqueuer, Element e, TreeElements elements) { + if (e == null) return; + helpersUsed.add(e.declaration); + enqueuer.addToWorkList(e); + elements.registerDependency(e); + } + + void enqueueInResolution(Element e, TreeElements elements) { + if (e == null) return; + ResolutionEnqueuer enqueuer = compiler.enqueuer.resolution; + enqueue(enqueuer, e, elements); + } + + void enqueueClass(Enqueuer enqueuer, Element cls, TreeElements elements) { + if (cls == null) return; + helpersUsed.add(cls.declaration); + // Both declaration and implementation may declare fields, so we + // add both to the list of helpers. + if (cls.declaration != cls.implementation) { + helpersUsed.add(cls.implementation); + } + enqueuer.registerInstantiatedClass(cls, elements); + } + + void registerConstantMap(TreeElements elements) { + void enqueue(String name) { + Element e = compiler.findHelper(name); + if (e != null) { + enqueueClass(compiler.enqueuer.resolution, e, elements); + } + } + + enqueue(MapConstant.DART_CLASS); + enqueue(MapConstant.DART_PROTO_CLASS); + enqueue(MapConstant.DART_STRING_CLASS); + enqueue(MapConstant.DART_GENERAL_CLASS); + } + + void codegen(CodegenWorkItem work) { + Element element = work.element; + var kind = element.kind; + if (kind == ElementKind.TYPEDEF) return; + if (element.isConstructor() && element.getEnclosingClass() == jsNullClass) { + // Work around a problem compiling JSNull's constructor. + return; + } + if (kind.category == ElementCategory.VARIABLE) { + Constant initialValue = + compiler.constantHandler.getConstantForVariable(element); + if (initialValue != null) { + registerCompileTimeConstant(initialValue, work.resolutionTree); + compiler.constantHandler.addCompileTimeConstantForEmission( + initialValue); + // We don't need to generate code for static or top-level + // variables. For instance variables, we may need to generate + // the checked setter. + if (Elements.isStaticOrTopLevel(element)) return; + } else { + // If the constant-handler was not able to produce a result we have to + // go through the builder (below) to generate the lazy initializer for + // the static variable. + // We also need to register the use of the cyclic-error helper. + compiler.enqueuer.codegen.registerStaticUse(getCyclicThrowHelper()); + } + } + HGraph graph = builder.build(work); + optimizer.optimize(work, graph); + jsAst.Expression code = generator.generateCode(work, graph); + generatedCode[element] = code; + } + + native.NativeEnqueuer nativeResolutionEnqueuer(Enqueuer world) { + return new native.NativeResolutionEnqueuer(world, compiler); + } + + native.NativeEnqueuer nativeCodegenEnqueuer(Enqueuer world) { + return new native.NativeCodegenEnqueuer(world, compiler, emitter); + } + + ClassElement defaultSuperclass(ClassElement element) { + // Native classes inherit from Interceptor. + return element.isNative() ? jsInterceptorClass : compiler.objectClass; + } + + /** + * Unit test hook that returns code of an element as a String. + * + * Invariant: [element] must be a declaration element. + */ + String assembleCode(Element element) { + assert(invariant(element, element.isDeclaration)); + return jsAst.prettyPrint(generatedCode[element], compiler).getText(); + } + + void assembleProgram() { + emitter.assembleProgram(); + int totalMethodCount = generatedCode.length; + if (totalMethodCount != preMirrorsMethodCount) { + int mirrorCount = totalMethodCount - preMirrorsMethodCount; + double percentage = (mirrorCount / totalMethodCount) * 100; + compiler.reportHint( + compiler.mainApp, MessageKind.MIRROR_BLOAT, + {'count': mirrorCount, + 'total': totalMethodCount, + 'percentage': percentage.round()}); + for (LibraryElement library in compiler.libraries.values) { + if (library.isInternalLibrary) continue; + for (LibraryTag tag in library.tags) { + Import importTag = tag.asImport(); + if (importTag == null) continue; + LibraryElement importedLibrary = library.getLibraryFromTag(tag); + if (importedLibrary != compiler.mirrorsLibrary) continue; + MessageKind kind = + compiler.mirrorUsageAnalyzerTask.hasMirrorUsage(library) + ? MessageKind.MIRROR_IMPORT + : MessageKind.MIRROR_IMPORT_NO_USAGE; + compiler.withCurrentElement(library, () { + compiler.reportInfo(importTag, kind); + }); + } + } + } + } + + Element getDartClass(Element element) { + for (ClassElement dartClass in implementationClasses.keys) { + if (element == implementationClasses[dartClass]) { + return dartClass; + } + } + return element; + } + + Element getImplementationClass(Element element) { + for (ClassElement dartClass in implementationClasses.keys) { + if (element == dartClass) { + return implementationClasses[dartClass]; + } + } + return element; + } + + /** + * Returns the checked mode helper that will be needed to do a type check/type + * cast on [type] at runtime. Note that this method is being called both by + * the resolver with interface types (int, String, ...), and by the SSA + * backend with implementation types (JSInt, JSString, ...). + */ + CheckedModeHelper getCheckedModeHelper(DartType type, {bool typeCast}) { + return getCheckedModeHelperInternal( + type, typeCast: typeCast, nativeCheckOnly: false); + } + + /** + * Returns the native checked mode helper that will be needed to do a type + * check/type cast on [type] at runtime. If no native helper exists for + * [type], [:null:] is returned. + */ + CheckedModeHelper getNativeCheckedModeHelper(DartType type, {bool typeCast}) { + return getCheckedModeHelperInternal( + type, typeCast: typeCast, nativeCheckOnly: true); + } + + /** + * Returns the checked mode helper for the type check/type cast for [type]. If + * [nativeCheckOnly] is [:true:], only names for native helpers are returned. + */ + CheckedModeHelper getCheckedModeHelperInternal(DartType type, + {bool typeCast, + bool nativeCheckOnly}) { + String name = getCheckedModeHelperNameInternal(type, + typeCast: typeCast, nativeCheckOnly: nativeCheckOnly); + if (name == null) return null; + CheckedModeHelper helper = checkedModeHelperByName[name]; + assert(helper != null); + return helper; + } + + String getCheckedModeHelperNameInternal(DartType type, + {bool typeCast, + bool nativeCheckOnly}) { + assert(type.kind != TypeKind.TYPEDEF); + if (type.kind == TypeKind.MALFORMED_TYPE) { + // The same error is thrown for type test and type cast of a malformed + // type so we only need one check method. + return 'checkMalformedType'; + } + Element element = type.element; + bool nativeCheck = nativeCheckOnly || + emitter.nativeEmitter.requiresNativeIsCheck(element); + + // TODO(13955), TODO(9731). The test for non-primitive types should use an + // interceptor. The interceptor should be an argument to HTypeConversion so + // that it can be optimized by standard interceptor optimizations. + nativeCheck = true; + + if (type == compiler.types.voidType) { + assert(!typeCast); // Cannot cast to void. + if (nativeCheckOnly) return null; + return 'voidTypeCheck'; + } else if (element == jsStringClass || element == compiler.stringClass) { + if (nativeCheckOnly) return null; + return typeCast + ? 'stringTypeCast' + : 'stringTypeCheck'; + } else if (element == jsDoubleClass || element == compiler.doubleClass) { + if (nativeCheckOnly) return null; + return typeCast + ? 'doubleTypeCast' + : 'doubleTypeCheck'; + } else if (element == jsNumberClass || element == compiler.numClass) { + if (nativeCheckOnly) return null; + return typeCast + ? 'numTypeCast' + : 'numTypeCheck'; + } else if (element == jsBoolClass || element == compiler.boolClass) { + if (nativeCheckOnly) return null; + return typeCast + ? 'boolTypeCast' + : 'boolTypeCheck'; + } else if (element == jsIntClass || element == compiler.intClass + || element == jsUInt32Class || element == jsUInt31Class + || element == jsPositiveIntClass) { + if (nativeCheckOnly) return null; + return typeCast + ? 'intTypeCast' + : 'intTypeCheck'; + } else if (Elements.isNumberOrStringSupertype(element, compiler)) { + if (nativeCheck) { + return typeCast + ? 'numberOrStringSuperNativeTypeCast' + : 'numberOrStringSuperNativeTypeCheck'; + } else { + return typeCast + ? 'numberOrStringSuperTypeCast' + : 'numberOrStringSuperTypeCheck'; + } + } else if (Elements.isStringOnlySupertype(element, compiler)) { + if (nativeCheck) { + return typeCast + ? 'stringSuperNativeTypeCast' + : 'stringSuperNativeTypeCheck'; + } else { + return typeCast + ? 'stringSuperTypeCast' + : 'stringSuperTypeCheck'; + } + } else if ((element == compiler.listClass || element == jsArrayClass) && + type.treatAsRaw) { + if (nativeCheckOnly) return null; + return typeCast + ? 'listTypeCast' + : 'listTypeCheck'; + } else { + if (Elements.isListSupertype(element, compiler)) { + if (nativeCheck) { + return typeCast + ? 'listSuperNativeTypeCast' + : 'listSuperNativeTypeCheck'; + } else { + return typeCast + ? 'listSuperTypeCast' + : 'listSuperTypeCheck'; + } + } else { + if (type.kind == TypeKind.INTERFACE && !type.treatAsRaw) { + return typeCast + ? 'subtypeCast' + : 'assertSubtype'; + } else if (type.kind == TypeKind.TYPE_VARIABLE) { + return typeCast + ? 'subtypeOfRuntimeTypeCast' + : 'assertSubtypeOfRuntimeType'; + } else if (type.kind == TypeKind.FUNCTION) { + return null; + } else { + if (nativeCheck) { + // TODO(karlklose): can we get rid of this branch when we use + // interceptors? + return typeCast + ? 'interceptedTypeCast' + : 'interceptedTypeCheck'; + } else { + return typeCast + ? 'propertyTypeCast' + : 'propertyTypeCheck'; + } + } + } + } + } + + void registerCheckedModeHelpers(TreeElements elements) { + // We register all the helpers in the resolution queue. + // TODO(13155): Find a way to register fewer helpers. + for (CheckedModeHelper helper in checkedModeHelpers) { + enqueueInResolution(helper.getElement(compiler), elements); + } + } + + /** + * Returns [:true:] if the checking of [type] is performed directly on the + * object and not on an interceptor. + */ + bool hasDirectCheckFor(DartType type) { + Element element = type.element; + return element == compiler.stringClass || + element == compiler.boolClass || + element == compiler.numClass || + element == compiler.intClass || + element == compiler.doubleClass || + element == jsArrayClass || + element == jsMutableArrayClass || + element == jsExtendableArrayClass || + element == jsFixedArrayClass; + } + + Element getExceptionUnwrapper() { + return compiler.findHelper('unwrapException'); + } + + Element getThrowRuntimeError() { + return compiler.findHelper('throwRuntimeError'); + } + + Element getThrowTypeError() { + return compiler.findHelper('throwTypeError'); + } + + Element getThrowAbstractClassInstantiationError() { + return compiler.findHelper('throwAbstractClassInstantiationError'); + } + + Element getStringInterpolationHelper() { + return compiler.findHelper('S'); + } + + Element getWrapExceptionHelper() { + return compiler.findHelper(r'wrapException'); + } + + Element getThrowExpressionHelper() { + return compiler.findHelper('throwExpression'); + } + + Element getClosureConverter() { + return compiler.findHelper('convertDartClosureToJS'); + } + + Element getTraceFromException() { + return compiler.findHelper('getTraceFromException'); + } + + Element getMapMaker() { + return compiler.findHelper('makeLiteralMap'); + } + + Element getSetRuntimeTypeInfo() { + return compiler.findHelper('setRuntimeTypeInfo'); + } + + Element getGetRuntimeTypeInfo() { + return compiler.findHelper('getRuntimeTypeInfo'); + } + + Element getGetTypeArgumentByIndex() { + return compiler.findHelper('getTypeArgumentByIndex'); + } + + Element getCopyTypeArguments() { + return compiler.findHelper('copyTypeArguments'); + } + + Element getComputeSignature() { + return compiler.findHelper('computeSignature'); + } + + Element getGetRuntimeTypeArguments() { + return compiler.findHelper('getRuntimeTypeArguments'); + } + + Element getGetRuntimeTypeArgument() { + return compiler.findHelper('getRuntimeTypeArgument'); + } + + Element getRuntimeTypeToString() { + return compiler.findHelper('runtimeTypeToString'); + } + + Element getAssertIsSubtype() { + return compiler.findHelper('assertIsSubtype'); + } + + Element getCheckSubtype() { + return compiler.findHelper('checkSubtype'); + } + + Element getAssertSubtype() { + return compiler.findHelper('assertSubtype'); + } + + Element getCheckSubtypeOfRuntimeType() { + return compiler.findHelper('checkSubtypeOfRuntimeType'); + } + + Element getAssertSubtypeOfRuntimeType() { + return compiler.findHelper('assertSubtypeOfRuntimeType'); + } + + Element getThrowNoSuchMethod() { + return compiler.findHelper('throwNoSuchMethod'); + } + + Element getCreateRuntimeType() { + return compiler.findHelper('createRuntimeType'); + } + + Element getFallThroughError() { + return compiler.findHelper("getFallThroughError"); + } + + Element getCreateInvocationMirror() { + return compiler.findHelper(Compiler.CREATE_INVOCATION_MIRROR); + } + + Element getCyclicThrowHelper() { + return compiler.findHelper("throwCyclicInit"); + } + + bool isNullImplementation(ClassElement cls) { + return cls == jsNullClass; + } + + ClassElement get intImplementation => jsIntClass; + ClassElement get uint32Implementation => jsUInt32Class; + ClassElement get uint31Implementation => jsUInt31Class; + ClassElement get positiveIntImplementation => jsPositiveIntClass; + ClassElement get doubleImplementation => jsDoubleClass; + ClassElement get numImplementation => jsNumberClass; + ClassElement get stringImplementation => jsStringClass; + ClassElement get listImplementation => jsArrayClass; + ClassElement get constListImplementation => jsArrayClass; + ClassElement get fixedListImplementation => jsFixedArrayClass; + ClassElement get growableListImplementation => jsExtendableArrayClass; + ClassElement get mapImplementation => mapLiteralClass; + ClassElement get constMapImplementation => constMapLiteralClass; + ClassElement get typeImplementation => typeLiteralClass; + ClassElement get boolImplementation => jsBoolClass; + ClassElement get nullImplementation => jsNullClass; + + void registerStaticUse(Element element, Enqueuer enqueuer) { + if (element == disableTreeShakingMarker) { + compiler.disableTypeInferenceForMirrors = true; + isTreeShakingDisabled = true; + typeVariableHandler.onTreeShakingDisabled(enqueuer); + } else if (element == preserveNamesMarker) { + mustPreserveNames = true; + } else if (element == preserveMetadataMarker) { + mustRetainMetadata = true; + } else if (element == getIsolateAffinityTagMarker) { + needToInitializeIsolateAffinityTag = true; + } else if (element.isDeferredLoaderGetter()) { + // TODO(sigurdm): Create a function registerLoadLibraryAccess. + if (compiler.loadLibraryFunction == null) { + compiler.loadLibraryFunction = + compiler.findHelper("_loadLibraryWrapper"); + enqueueInResolution(compiler.loadLibraryFunction, + compiler.globalDependencies); + } + } + customElementsAnalysis.registerStaticUse(element, enqueuer); + } + + /// Called when [:const Symbol(name):] is seen. + void registerConstSymbol(String name, TreeElements elements) { + symbolsUsed.add(name); + if (name.endsWith('=')) { + symbolsUsed.add(name.substring(0, name.length - 1)); + } + } + + /// Called when [:new Symbol(...):] is seen. + void registerNewSymbol(TreeElements elements) { + } + + /// Called when resolving the `Symbol` constructor. + void registerSymbolConstructor(TreeElements elements) { + // Make sure that _internals.Symbol.validated is registered. + assert(compiler.symbolValidatedConstructor != null); + enqueueInResolution(compiler.symbolValidatedConstructor, elements); + } + + /// Should [element] (a getter) be retained for reflection? + bool shouldRetainGetter(Element element) => isNeededForReflection(element); + + /// Should [element] (a setter) be retained for reflection? + bool shouldRetainSetter(Element element) => isNeededForReflection(element); + + /// Should [name] be retained for reflection? + bool shouldRetainName(String name) { + if (hasInsufficientMirrorsUsed) return mustPreserveNames; + if (name == '') return false; + return symbolsUsed.contains(name); + } + + bool get rememberLazies => isTreeShakingDisabled; + + bool retainMetadataOf(Element element) { + if (mustRetainMetadata) hasRetainedMetadata = true; + if (mustRetainMetadata && isNeededForReflection(element)) { + for (MetadataAnnotation metadata in element.metadata) { + metadata.ensureResolved(compiler); + compiler.constantHandler.addCompileTimeConstantForEmission( + metadata.value); + } + return true; + } + return false; + } + + Future onLibraryLoaded(LibraryElement library, Uri uri) { + if (uri == Uri.parse('dart:_js_mirrors')) { + disableTreeShakingMarker = + library.find('disableTreeShaking'); + preserveMetadataMarker = + library.find('preserveMetadata'); + } else if (uri == Uri.parse('dart:_js_names')) { + preserveNamesMarker = + library.find('preserveNames'); + } else if (uri == Uri.parse('dart:_js_helper')) { + getIsolateAffinityTagMarker = + library.find('getIsolateAffinityTag'); + } + return new Future.value(); + } + + void registerMirrorUsage(Set symbols, + Set targets, + Set metaTargets) { + if (symbols == null && targets == null && metaTargets == null) { + // The user didn't specify anything, or there are imports of + // 'dart:mirrors' without @MirrorsUsed. + hasInsufficientMirrorsUsed = true; + return; + } + if (symbols != null) symbolsUsed.addAll(symbols); + if (targets != null) { + for (Element target in targets) { + if (target.isAbstractField()) { + AbstractFieldElement field = target; + targetsUsed.add(field.getter); + targetsUsed.add(field.setter); + } else { + targetsUsed.add(target); + } + } + } + if (metaTargets != null) metaTargetsUsed.addAll(metaTargets); + } + + /** + * Returns `true` if [element] can be accessed through reflection, that is, + * is in the set of elements covered by a `MirrorsUsed` annotation. + * + * This property is used to tag emitted elements with a marker which is + * checked by the runtime system to throw an exception if an element is + * accessed (invoked, get, set) that is not accessible for the reflective + * system. + */ + bool isAccessibleByReflection(Element element) { + // TODO(ahe): This isn't sufficient: simply importing dart:mirrors + // causes hasInsufficientMirrorsUsed to become true. + if (hasInsufficientMirrorsUsed) return true; + return isNeededForReflection(element); + } + + /** + * Returns `true` if the emitter must emit the element even though there + * is no direct use in the program, but because the reflective system may + * need to access it. + */ + bool isNeededForReflection(Element element) { + element = getDartClass(element); + if (hasInsufficientMirrorsUsed) return isTreeShakingDisabled; + /// Record the name of [element] in [symbolsUsed]. Return true for + /// convenience. + bool registerNameOf(Element element) { + symbolsUsed.add(element.name); + if (element.isConstructor()) { + symbolsUsed.add(element.getEnclosingClass().name); + } + return true; + } + + Element enclosing = element.enclosingElement; + if (enclosing != null && isNeededForReflection(enclosing)) { + return registerNameOf(element); + } + + if (isNeededThroughMetaTarget(element)) { + return registerNameOf(element); + } + + if (!targetsUsed.isEmpty && targetsUsed.contains(element)) { + return registerNameOf(element); + } + + // TODO(kasperl): Consider caching this information. It is consulted + // multiple times because of the way we deal with the enclosing element. + return false; + } + + /** + * Returns `true` if the element is needed because it has an annotation + * of a type that is used as a meta target for reflection. + */ + bool isNeededThroughMetaTarget(Element element) { + if (metaTargetsUsed.isEmpty) return false; + for (Link link = element.metadata; !link.isEmpty; link = link.tail) { + MetadataAnnotation metadata = link.head; + // TODO(kasperl): It would be nice if we didn't have to resolve + // all metadata but only stuff that potentially would match one + // of the used meta targets. + metadata.ensureResolved(compiler); + Constant value = metadata.value; + if (value == null) continue; + DartType type = value.computeType(compiler); + if (metaTargetsUsed.contains(type.element)) return true; + } + return false; + } + + jsAst.Call generateIsJsIndexableCall(jsAst.Expression use1, + jsAst.Expression use2) { + String dispatchPropertyName = 'init.dispatchPropertyName'; + + // We pass the dispatch property record to the isJsIndexable + // helper rather than reading it inside the helper to increase the + // chance of making the dispatch record access monomorphic. + jsAst.PropertyAccess record = new jsAst.PropertyAccess( + use2, new jsAst.VariableUse(dispatchPropertyName)); + + List arguments = [use1, record]; + FunctionElement helper = + compiler.findHelper('isJsIndexable'); + String helperName = namer.isolateAccess(helper); + return new jsAst.Call(new jsAst.VariableUse(helperName), arguments); + } + + bool isTypedArray(TypeMask mask) { + // Just checking for [:TypedData:] is not sufficient, as it is an + // abstract class any user-defined class can implement. So we also + // check for the interface [JavaScriptIndexingBehavior]. + return compiler.typedDataClass != null + && mask.satisfies(compiler.typedDataClass, compiler) + && mask.satisfies(jsIndexingBehaviorInterface, compiler); + } + + bool couldBeTypedArray(TypeMask mask) { + bool intersects(TypeMask type1, TypeMask type2) => + !type1.intersection(type2, compiler).isEmpty; + + return compiler.typedDataClass != null + && intersects(mask, new TypeMask.subtype(compiler.typedDataClass)) + && intersects(mask, new TypeMask.subtype(jsIndexingBehaviorInterface)); + } + + /// Returns all static fields that are referenced through [targetsUsed]. + /// If the target is a library or class all nested static fields are + /// included too. + Iterable _findStaticFieldTargets() { + List staticFields = []; + + void addFieldsInContainer(ScopeContainerElement container) { + container.forEachLocalMember((Element member) { + if (!member.isInstanceMember() && member.isField()) { + staticFields.add(member); + } else if (member.isClass()) { + addFieldsInContainer(member); + } + }); + } + + for (Element target in targetsUsed) { + if (target == null) continue; + if (target.isField()) { + staticFields.add(target); + } else if (target.isLibrary() || target.isClass()) { + addFieldsInContainer(target); + } + } + return staticFields; + } + + /// Called when [enqueuer] is empty, but before it is closed. + void onQueueEmpty(Enqueuer enqueuer) { + if (!enqueuer.isResolutionQueue && preMirrorsMethodCount == 0) { + preMirrorsMethodCount = generatedCode.length; + } + + if (isTreeShakingDisabled) { + enqueuer.enqueueEverything(); + } else if (!targetsUsed.isEmpty && enqueuer.isResolutionQueue) { + // Add all static elements (not classes) that have been requested for + // reflection. If there is no mirror-usage these are probably not + // necessary, but the backend relies on them being resolved. + enqueuer.enqueueReflectiveStaticFields(_findStaticFieldTargets()); + } + + if (mustPreserveNames) compiler.log('Preserving names.'); + + if (mustRetainMetadata) { + compiler.log('Retaining metadata.'); + + compiler.libraries.values.forEach(retainMetadataOf); + for (Dependency dependency in metadataConstants) { + registerCompileTimeConstant( + dependency.constant, dependency.user); + } + metadataConstants.clear(); + } + + customElementsAnalysis.onQueueEmpty(enqueuer); + } + + void onElementResolved(Element element, TreeElements elements) { + LibraryElement library = element.getLibrary(); + if (!library.isPlatformLibrary && !library.canUseNative) return; + bool hasNoInline = false; + bool hasNoThrows = false; + bool hasNoSideEffects = false; + for (MetadataAnnotation metadata in element.metadata) { + metadata.ensureResolved(compiler); + if (!metadata.value.isConstructedObject) continue; + ObjectConstant value = metadata.value; + ClassElement cls = value.type.element; + if (cls == noInlineClass) { + hasNoInline = true; + if (VERBOSE_OPTIMIZER_HINTS) { + compiler.reportHere(element, "Cannot inline"); + } + inlineCache.markAsNonInlinable(element); + } else if (cls == noThrowsClass) { + hasNoThrows = true; + if (!Elements.isStaticOrTopLevelFunction(element)) { + compiler.internalError(element, + "@NoThrows() is currently limited to top-level" + " or static functions"); + } + if (VERBOSE_OPTIMIZER_HINTS) { + compiler.reportHere(element, "Cannot throw"); + } + compiler.world.registerCannotThrow(element); + } else if (cls == noSideEffectsClass) { + hasNoSideEffects = true; + if (VERBOSE_OPTIMIZER_HINTS) { + compiler.reportHere(element, "Has no side effects"); + } + compiler.world.registerSideEffectsFree(element); + } + } + if (hasNoThrows && !hasNoInline) { + compiler.internalError(element, + "@NoThrows() should always be combined with @NoInline."); + } + if (hasNoSideEffects && !hasNoInline) { + compiler.internalError(element, + "@NoSideEffects() should always be combined with @NoInline."); + } + } + + CodeBuffer codeOf(Element element) { + return generatedCode.containsKey(element) + ? jsAst.prettyPrint(generatedCode[element], compiler) + : null; + } +} + +/// Records that [constant] is used by [user.element]. +class Dependency { + final Constant constant; + final TreeElements user; + + const Dependency(this.constant, this.user); +} + +/// Used to copy metadata to the the actual constant handler. +class ConstantCopier implements ConstantVisitor { + final ConstantHandler target; + + ConstantCopier(this.target); + + void copy(/* Constant or List */ value) { + if (value is Constant) { + target.compiledConstants.add(value); + } else { + target.compiledConstants.addAll(value); + } + } + + void visitFunction(FunctionConstant constant) => copy(constant); + + void visitNull(NullConstant constant) => copy(constant); + + void visitInt(IntConstant constant) => copy(constant); + + void visitDouble(DoubleConstant constant) => copy(constant); + + void visitTrue(TrueConstant constant) => copy(constant); + + void visitFalse(FalseConstant constant) => copy(constant); + + void visitString(StringConstant constant) => copy(constant); + + void visitType(TypeConstant constant) => copy(constant); + + void visitInterceptor(InterceptorConstant constant) => copy(constant); + + void visitDummy(DummyConstant constant) => copy(constant); + + void visitList(ListConstant constant) { + copy(constant.entries); + copy(constant); + } + void visitMap(MapConstant constant) { + copy(constant.keys); + copy(constant.values); + copy(constant.protoValue); + copy(constant); + } + + void visitConstructed(ConstructedConstant constant) { + copy(constant.fields); + copy(constant); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/checked_mode_helpers.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/checked_mode_helpers.dart new file mode 100644 index 0000000..5b3efd8 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/checked_mode_helpers.dart @@ -0,0 +1,119 @@ +// Copyright (c) 2013, 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. + +part of js_backend; + +class CheckedModeHelper { + final String name; + + const CheckedModeHelper(String this.name); + + Element getElement(Compiler compiler) => compiler.findHelper(name); + + jsAst.Expression generateCall(SsaCodeGenerator codegen, + HTypeConversion node) { + Element helperElement = getElement(codegen.compiler); + codegen.world.registerStaticUse(helperElement); + List arguments = []; + codegen.use(node.checkedInput); + arguments.add(codegen.pop()); + generateAdditionalArguments(codegen, node, arguments); + String helperName = codegen.backend.namer.isolateAccess(helperElement); + return new jsAst.Call(new jsAst.VariableUse(helperName), arguments); + } + + void generateAdditionalArguments(SsaCodeGenerator codegen, + HTypeConversion node, + List arguments) { + // No additional arguments needed. + } + + static const List helpers = const [ + const MalformedCheckedModeHelper('checkMalformedType'), + const CheckedModeHelper('voidTypeCheck'), + const CheckedModeHelper('stringTypeCast'), + const CheckedModeHelper('stringTypeCheck'), + const CheckedModeHelper('doubleTypeCast'), + const CheckedModeHelper('doubleTypeCheck'), + const CheckedModeHelper('numTypeCast'), + const CheckedModeHelper('numTypeCheck'), + const CheckedModeHelper('boolTypeCast'), + const CheckedModeHelper('boolTypeCheck'), + const CheckedModeHelper('intTypeCast'), + const CheckedModeHelper('intTypeCheck'), + const PropertyCheckedModeHelper('numberOrStringSuperNativeTypeCast'), + const PropertyCheckedModeHelper('numberOrStringSuperNativeTypeCheck'), + const PropertyCheckedModeHelper('numberOrStringSuperTypeCast'), + const PropertyCheckedModeHelper('numberOrStringSuperTypeCheck'), + const PropertyCheckedModeHelper('stringSuperNativeTypeCast'), + const PropertyCheckedModeHelper('stringSuperNativeTypeCheck'), + const PropertyCheckedModeHelper('stringSuperTypeCast'), + const PropertyCheckedModeHelper('stringSuperTypeCheck'), + const CheckedModeHelper('listTypeCast'), + const CheckedModeHelper('listTypeCheck'), + const PropertyCheckedModeHelper('listSuperNativeTypeCast'), + const PropertyCheckedModeHelper('listSuperNativeTypeCheck'), + const PropertyCheckedModeHelper('listSuperTypeCast'), + const PropertyCheckedModeHelper('listSuperTypeCheck'), + const PropertyCheckedModeHelper('interceptedTypeCast'), + const PropertyCheckedModeHelper('interceptedTypeCheck'), + const SubtypeCheckedModeHelper('subtypeCast'), + const SubtypeCheckedModeHelper('assertSubtype'), + const TypeVariableCheckedModeHelper('subtypeOfRuntimeTypeCast'), + const TypeVariableCheckedModeHelper('assertSubtypeOfRuntimeType'), + const PropertyCheckedModeHelper('propertyTypeCast'), + const PropertyCheckedModeHelper('propertyTypeCheck')]; +} + +class MalformedCheckedModeHelper extends CheckedModeHelper { + const MalformedCheckedModeHelper(String name) : super(name); + + void generateAdditionalArguments(SsaCodeGenerator codegen, + HTypeConversion node, + List arguments) { + ErroneousElement element = node.typeExpression.element; + arguments.add(js.escapedString(element.message)); + } +} + +class PropertyCheckedModeHelper extends CheckedModeHelper { + const PropertyCheckedModeHelper(String name) : super(name); + + void generateAdditionalArguments(SsaCodeGenerator codegen, + HTypeConversion node, + List arguments) { + DartType type = node.typeExpression; + String additionalArgument = codegen.backend.namer.operatorIsType(type); + arguments.add(js.string(additionalArgument)); + } +} + +class TypeVariableCheckedModeHelper extends CheckedModeHelper { + const TypeVariableCheckedModeHelper(String name) : super(name); + + void generateAdditionalArguments(SsaCodeGenerator codegen, + HTypeConversion node, + List arguments) { + assert(node.typeExpression.kind == TypeKind.TYPE_VARIABLE); + codegen.use(node.typeRepresentation); + arguments.add(codegen.pop()); + } +} + +class SubtypeCheckedModeHelper extends CheckedModeHelper { + const SubtypeCheckedModeHelper(String name) : super(name); + + void generateAdditionalArguments(SsaCodeGenerator codegen, + HTypeConversion node, + List arguments) { + DartType type = node.typeExpression; + Element element = type.element; + String isField = codegen.backend.namer.operatorIs(element); + arguments.add(js.string(isField)); + codegen.use(node.typeRepresentation); + arguments.add(codegen.pop()); + String asField = codegen.backend.namer.substitutionName(element); + arguments.add(js.string(asField)); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart new file mode 100644 index 0000000..6d7eb6b --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart @@ -0,0 +1,369 @@ +// Copyright (c) 2012, 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. + +part of js_backend; + +class ConstantEmitter { + ConstantReferenceEmitter _referenceEmitter; + ConstantInitializerEmitter _initializerEmitter; + + ConstantEmitter(Compiler compiler, Namer namer) { + _referenceEmitter = new ConstantReferenceEmitter(compiler, namer); + _initializerEmitter = new ConstantInitializerEmitter( + compiler, namer, _referenceEmitter); + } + + /** + * Constructs an expression that is a reference to the constant. Uses a + * canonical name unless the constant can be emitted multiple times (as for + * numbers and strings). + */ + jsAst.Expression reference(Constant constant) { + return _referenceEmitter.generate(constant); + } + + /** + * Constructs an expression like [reference], but the expression is valid + * during isolate initialization. + */ + jsAst.Expression referenceInInitializationContext(Constant constant) { + return _referenceEmitter.generateInInitializationContext(constant); + } + + /** + * Constructs an expression used to initialize a canonicalized constant. + */ + jsAst.Expression initializationExpression(Constant constant) { + return _initializerEmitter.generate(constant); + } +} + +/** + * Visitor for generating JavaScript expressions to refer to [Constant]s. + * Do not use directly, use methods from [ConstantEmitter]. + */ +class ConstantReferenceEmitter implements ConstantVisitor { + final Compiler compiler; + final Namer namer; + + ConstantReferenceEmitter(this.compiler, this.namer); + + jsAst.Expression generate(Constant constant) { + return _visit(constant); + } + + jsAst.Expression generateInInitializationContext(Constant constant) { + return _visit(constant); + } + + jsAst.Expression _visit(Constant constant) { + return constant.accept(this); + } + + jsAst.Expression visitFunction(FunctionConstant constant) { + return new jsAst.VariableUse( + namer.isolateStaticClosureAccess(constant.element)); + } + + jsAst.Expression visitNull(NullConstant constant) { + return new jsAst.LiteralNull(); + } + + jsAst.Expression visitInt(IntConstant constant) { + return new jsAst.LiteralNumber('${constant.value}'); + } + + jsAst.Expression visitDouble(DoubleConstant constant) { + double value = constant.value; + if (value.isNaN) { + return js("0/0"); + } else if (value == double.INFINITY) { + return js("1/0"); + } else if (value == -double.INFINITY) { + return js("-1/0"); + } else { + return new jsAst.LiteralNumber("$value"); + } + } + + jsAst.Expression visitTrue(TrueConstant constant) { + if (compiler.enableMinification) { + // Use !0 for true. + return js("!0"); + } else { + return js('true'); + } + } + + jsAst.Expression visitFalse(FalseConstant constant) { + if (compiler.enableMinification) { + // Use !1 for false. + return js("!1"); + } else { + return js('false'); + } + } + + /** + * Write the contents of the quoted string to a [CodeBuffer] in + * a form that is valid as JavaScript string literal content. + * The string is assumed quoted by double quote characters. + */ + jsAst.Expression visitString(StringConstant constant) { + // TODO(sra): If the string is long *and repeated* (and not on a hot path) + // then it should be assigned to a name. We don't have reference counts (or + // profile information) here, so this is the wrong place. + StringBuffer sb = new StringBuffer(); + writeJsonEscapedCharsOn(constant.value.slowToString(), sb); + return new jsAst.LiteralString('"$sb"'); + } + + jsAst.Expression emitCanonicalVersion(Constant constant) { + String name = namer.constantName(constant); + return new jsAst.PropertyAccess.field( + new jsAst.VariableUse(namer.globalObjectForConstant(constant)), name); + } + + jsAst.Expression visitList(ListConstant constant) { + return emitCanonicalVersion(constant); + } + + jsAst.Expression visitMap(MapConstant constant) { + return emitCanonicalVersion(constant); + } + + jsAst.Expression visitType(TypeConstant constant) { + return emitCanonicalVersion(constant); + } + + jsAst.Expression visitConstructed(ConstructedConstant constant) { + return emitCanonicalVersion(constant); + } + + jsAst.Expression visitInterceptor(InterceptorConstant constant) { + return emitCanonicalVersion(constant); + } + + jsAst.Expression visitDummy(DummyConstant constant) { + return new jsAst.LiteralNumber('0'); + } +} + +/** + * Visitor for generating JavaScript expressions to initialize [Constant]s. + * Do not use directly; use methods from [ConstantEmitter]. + */ +class ConstantInitializerEmitter implements ConstantVisitor { + final Compiler compiler; + final Namer namer; + final ConstantReferenceEmitter referenceEmitter; + + // Matches blank lines, comment lines and trailing comments that can't be part + // of a string. + static final RegExp COMMENT_RE = + new RegExp(r'''^ *(//.*)?\n| *//[^''"\n]*$''' , multiLine: true); + + ConstantInitializerEmitter(this.compiler, this.namer, this.referenceEmitter); + + jsAst.Expression generate(Constant constant) { + return _visit(constant); + } + + jsAst.Expression _visit(Constant constant) { + return constant.accept(this); + } + + jsAst.Expression _reference(Constant constant) { + return referenceEmitter.generateInInitializationContext(constant); + } + + jsAst.Expression visitFunction(FunctionConstant constant) { + compiler.internalError(NO_LOCATION_SPANNABLE, + "The function constant does not need specific JS code."); + return null; + } + + jsAst.Expression visitNull(NullConstant constant) { + return _reference(constant); + } + + jsAst.Expression visitInt(IntConstant constant) { + return _reference(constant); + } + + jsAst.Expression visitDouble(DoubleConstant constant) { + return _reference(constant); + } + + jsAst.Expression visitTrue(TrueConstant constant) { + return _reference(constant); + } + + jsAst.Expression visitFalse(FalseConstant constant) { + return _reference(constant); + } + + jsAst.Expression visitString(StringConstant constant) { + // TODO(sra): Some larger strings are worth sharing. + return _reference(constant); + } + + jsAst.Expression visitList(ListConstant constant) { + jsAst.Expression value = new jsAst.Call( + new jsAst.PropertyAccess.field( + new jsAst.VariableUse(namer.isolateName), + 'makeConstantList'), + [new jsAst.ArrayInitializer.from(_array(constant.entries))]); + return maybeAddTypeArguments(constant.type, value); + } + + String getJsConstructor(ClassElement element) { + return namer.isolateAccess(element); + } + + jsAst.Expression visitMap(MapConstant constant) { + jsAst.Expression jsMap() { + List properties = []; + for (int i = 0; i < constant.keys.entries.length; i++) { + StringConstant key = constant.keys.entries[i]; + if (key.value == MapConstant.PROTO_PROPERTY) continue; + + // Keys in literal maps must be emitted in place. + jsAst.Literal keyExpression = _visit(key); + jsAst.Expression valueExpression = + _reference(constant.values[i]); + properties.add(new jsAst.Property(keyExpression, valueExpression)); + } + return new jsAst.ObjectInitializer(properties); + } + + jsAst.Expression jsGeneralMap() { + List data = []; + for (int i = 0; i < constant.keys.entries.length; i++) { + jsAst.Expression keyExpression = + _reference(constant.keys.entries[i]); + jsAst.Expression valueExpression = + _reference(constant.values[i]); + data.add(keyExpression); + data.add(valueExpression); + } + return new jsAst.ArrayInitializer.from(data); + } + + ClassElement classElement = constant.type.element; + String className = classElement.name; + + List arguments = []; + + // The arguments of the JavaScript constructor for any given Dart class + // are in the same order as the members of the class element. + int emittedArgumentCount = 0; + classElement.implementation.forEachInstanceField( + (ClassElement enclosing, Element field) { + if (field.name == MapConstant.LENGTH_NAME) { + arguments.add( + new jsAst.LiteralNumber('${constant.keys.entries.length}')); + } else if (field.name == MapConstant.JS_OBJECT_NAME) { + arguments.add(jsMap()); + } else if (field.name == MapConstant.KEYS_NAME) { + arguments.add(_reference(constant.keys)); + } else if (field.name == MapConstant.PROTO_VALUE) { + assert(constant.protoValue != null); + arguments.add(_reference(constant.protoValue)); + } else if (field.name == MapConstant.JS_DATA_NAME) { + arguments.add(jsGeneralMap()); + } else { + compiler.internalError(field, + "Compiler has unexpected field ${field.name} for " + "${className}."); + } + emittedArgumentCount++; + }, + includeSuperAndInjectedMembers: true); + if ((className == MapConstant.DART_STRING_CLASS && + emittedArgumentCount != 3) || + (className == MapConstant.DART_PROTO_CLASS && + emittedArgumentCount != 4) || + (className == MapConstant.DART_GENERAL_CLASS && + emittedArgumentCount != 1)) { + compiler.internalError(classElement, + "Compiler and ${className} disagree on number of fields."); + } + + jsAst.Expression value = new jsAst.New( + new jsAst.VariableUse(getJsConstructor(classElement)), + arguments); + return maybeAddTypeArguments(constant.type, value); + } + + JavaScriptBackend get backend => compiler.backend; + + jsAst.PropertyAccess getHelperProperty(Element helper) { + return backend.namer.elementAccess(helper); + } + + jsAst.Expression visitType(TypeConstant constant) { + DartType type = constant.representedType; + String name = namer.getRuntimeTypeName(type.element); + jsAst.Expression typeName = new jsAst.LiteralString("'$name'"); + return new jsAst.Call(getHelperProperty(backend.getCreateRuntimeType()), + [typeName]); + } + + jsAst.Expression visitInterceptor(InterceptorConstant constant) { + return new jsAst.PropertyAccess.field( + new jsAst.VariableUse( + getJsConstructor(constant.dispatchedType.element)), + 'prototype'); + } + + jsAst.Expression visitDummy(DummyConstant constant) { + return _reference(constant); + } + + jsAst.Expression visitConstructed(ConstructedConstant constant) { + Element element = constant.type.element; + if (element.isForeign(compiler) + && element.name == 'JS_CONST') { + StringConstant str = constant.fields[0]; + String value = str.value.slowToString(); + return new jsAst.LiteralExpression(stripComments(value)); + } + jsAst.New instantiation = new jsAst.New( + new jsAst.VariableUse(getJsConstructor(constant.type.element)), + _array(constant.fields)); + return maybeAddTypeArguments(constant.type, instantiation); + } + + String stripComments(String rawJavaScript) { + return rawJavaScript.replaceAll(COMMENT_RE, ''); + } + + List _array(List values) { + List valueList = []; + for (int i = 0; i < values.length; i++) { + valueList.add(_reference(values[i])); + } + return valueList; + } + + jsAst.Expression maybeAddTypeArguments(InterfaceType type, + jsAst.Expression value) { + if (type is InterfaceType && + !type.treatAsRaw && + backend.classNeedsRti(type.element)) { + InterfaceType interface = type; + RuntimeTypes rti = backend.rti; + Iterable arguments = interface.typeArguments + .toList(growable: false) + .map((DartType type) => + rti.getTypeRepresentationWithHashes(type, (_){})); + jsAst.Expression argumentList = + new jsAst.LiteralString('[${arguments.join(', ')}]'); + return new jsAst.Call(getHelperProperty(backend.getSetRuntimeTypeInfo()), + [value, argumentList]); + } + return value; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/constant_system_javascript.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/constant_system_javascript.dart new file mode 100644 index 0000000..382a000 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/constant_system_javascript.dart @@ -0,0 +1,242 @@ +// Copyright (c) 2012, 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. + +part of js_backend; + +const JAVA_SCRIPT_CONSTANT_SYSTEM = const JavaScriptConstantSystem(); + +class JavaScriptBitNotOperation extends BitNotOperation { + const JavaScriptBitNotOperation(); + + Constant fold(Constant constant) { + if (JAVA_SCRIPT_CONSTANT_SYSTEM.isInt(constant)) { + // In JavaScript we don't check for -0 and treat it as if it was zero. + if (constant.isMinusZero) constant = DART_CONSTANT_SYSTEM.createInt(0); + IntConstant intConstant = constant; + // We convert the result of bit-operations to 32 bit unsigned integers. + return JAVA_SCRIPT_CONSTANT_SYSTEM.createInt32(~intConstant.value); + } + return null; + } +} + +/** + * In JavaScript we truncate the result to an unsigned 32 bit integer. Also, -0 + * is treated as if it was the integer 0. + */ +class JavaScriptBinaryBitOperation implements BinaryOperation { + final BinaryBitOperation dartBitOperation; + + const JavaScriptBinaryBitOperation(this.dartBitOperation); + + String get name => dartBitOperation.name; + + Constant fold(Constant left, Constant right) { + // In JavaScript we don't check for -0 and treat it as if it was zero. + if (left.isMinusZero) left = DART_CONSTANT_SYSTEM.createInt(0); + if (right.isMinusZero) right = DART_CONSTANT_SYSTEM.createInt(0); + IntConstant result = dartBitOperation.fold(left, right); + if (result != null) { + // We convert the result of bit-operations to 32 bit unsigned integers. + return JAVA_SCRIPT_CONSTANT_SYSTEM.createInt32(result.value); + } + return result; + } + + apply(left, right) => dartBitOperation.apply(left, right); +} + +class JavaScriptShiftRightOperation extends JavaScriptBinaryBitOperation { + const JavaScriptShiftRightOperation() : super(const ShiftRightOperation()); + + Constant fold(Constant left, Constant right) { + // Truncate the input value to 32 bits if necessary. + if (left.isInt) { + IntConstant intConstant = left; + int value = intConstant.value; + int truncatedValue = value & JAVA_SCRIPT_CONSTANT_SYSTEM.BITS32; + if (value < 0) { + // Sign-extend if the input was negative. The current semantics don't + // make much sense, since we only look at bit 31. + // TODO(floitsch): we should treat the input to right shifts as + // unsigned. + + // A 32 bit complement-two value x can be computed by: + // x_u - 2^32 (where x_u is its unsigned representation). + // Example: 0xFFFFFFFF - 0x100000000 => -1. + // We simply and with the sign-bit and multiply by two. If the sign-bit + // was set, then the result is 0. Otherwise it will become 2^32. + final int SIGN_BIT = 0x80000000; + truncatedValue -= 2 * (truncatedValue & SIGN_BIT); + } + if (value != truncatedValue) { + left = DART_CONSTANT_SYSTEM.createInt(truncatedValue); + } + } + return super.fold(left, right); + } +} + +class JavaScriptNegateOperation implements UnaryOperation { + final NegateOperation dartNegateOperation = const NegateOperation(); + + const JavaScriptNegateOperation(); + + String get name => dartNegateOperation.name; + + Constant fold(Constant constant) { + if (constant.isInt) { + IntConstant intConstant = constant; + if (intConstant.value == 0) { + return JAVA_SCRIPT_CONSTANT_SYSTEM.createDouble(-0.0); + } + } + return dartNegateOperation.fold(constant); + } +} + +class JavaScriptBinaryArithmeticOperation implements BinaryOperation { + final BinaryOperation dartArithmeticOperation; + + const JavaScriptBinaryArithmeticOperation(this.dartArithmeticOperation); + + String get name => dartArithmeticOperation.name; + + Constant fold(Constant left, Constant right) { + Constant result = dartArithmeticOperation.fold(left, right); + if (result == null) return result; + return JAVA_SCRIPT_CONSTANT_SYSTEM.convertToJavaScriptConstant(result); + } + + apply(left, right) => dartArithmeticOperation.apply(left, right); +} + +class JavaScriptIdentityOperation implements BinaryOperation { + final IdentityOperation dartIdentityOperation = const IdentityOperation(); + + const JavaScriptIdentityOperation(); + + String get name => dartIdentityOperation.name; + + BoolConstant fold(Constant left, Constant right) { + BoolConstant result = dartIdentityOperation.fold(left, right); + if (result == null || result.value) return result; + // In JavaScript -0.0 === 0 and all doubles are equal to their integer + // values. Furthermore NaN !== NaN. + if (left.isNum && right.isNum) { + NumConstant leftNum = left; + NumConstant rightNum = right; + double leftDouble = leftNum.value.toDouble(); + double rightDouble = rightNum.value.toDouble(); + return new BoolConstant(leftDouble == rightDouble); + } + return result; + } + + apply(left, right) => identical(left, right); +} + +/** + * Constant system following the semantics for Dart code that has been + * compiled to JavaScript. + */ +class JavaScriptConstantSystem extends ConstantSystem { + final int BITS31 = 0x8FFFFFFF; + final int BITS32 = 0xFFFFFFFF; + + final add = const JavaScriptBinaryArithmeticOperation(const AddOperation()); + final bitAnd = const JavaScriptBinaryBitOperation(const BitAndOperation()); + final bitNot = const JavaScriptBitNotOperation(); + final bitOr = const JavaScriptBinaryBitOperation(const BitOrOperation()); + final bitXor = const JavaScriptBinaryBitOperation(const BitXorOperation()); + final booleanAnd = const BooleanAndOperation(); + final booleanOr = const BooleanOrOperation(); + final divide = + const JavaScriptBinaryArithmeticOperation(const DivideOperation()); + final equal = const EqualsOperation(); + final greaterEqual = const GreaterEqualOperation(); + final greater = const GreaterOperation(); + final identity = const JavaScriptIdentityOperation(); + final lessEqual = const LessEqualOperation(); + final less = const LessOperation(); + final modulo = + const JavaScriptBinaryArithmeticOperation(const ModuloOperation()); + final multiply = + const JavaScriptBinaryArithmeticOperation(const MultiplyOperation()); + final negate = const JavaScriptNegateOperation(); + final not = const NotOperation(); + final shiftLeft = + const JavaScriptBinaryBitOperation(const ShiftLeftOperation()); + final shiftRight = const JavaScriptShiftRightOperation(); + final subtract = + const JavaScriptBinaryArithmeticOperation(const SubtractOperation()); + final truncatingDivide = const JavaScriptBinaryArithmeticOperation( + const TruncatingDivideOperation()); + + const JavaScriptConstantSystem(); + + /** + * Returns true if [value] will turn into NaN or infinity + * at runtime. + */ + bool integerBecomesNanOrInfinity(int value) { + double doubleValue = value.toDouble(); + return doubleValue.isNaN || doubleValue.isInfinite; + } + + NumConstant convertToJavaScriptConstant(NumConstant constant) { + if (constant.isInt) { + IntConstant intConstant = constant; + int intValue = intConstant.value; + if (integerBecomesNanOrInfinity(intValue)) { + return new DoubleConstant(intValue.toDouble()); + } + // If the integer loses precision with JavaScript numbers, use + // the floored version JavaScript will use. + int floorValue = intValue.toDouble().floor().toInt(); + if (floorValue != intValue) { + return new IntConstant(floorValue); + } + } else if (constant.isDouble) { + DoubleConstant doubleResult = constant; + double doubleValue = doubleResult.value; + if (!doubleValue.isInfinite && !doubleValue.isNaN && + !constant.isMinusZero) { + int intValue = doubleValue.truncate(); + if (intValue == doubleValue) { + return new IntConstant(intValue); + } + } + } + return constant; + } + + NumConstant createInt(int i) + => convertToJavaScriptConstant(new IntConstant(i)); + NumConstant createInt32(int i) => new IntConstant(i & BITS32); + NumConstant createDouble(double d) + => convertToJavaScriptConstant(new DoubleConstant(d)); + StringConstant createString(DartString string) => new StringConstant(string); + BoolConstant createBool(bool value) => new BoolConstant(value); + NullConstant createNull() => new NullConstant(); + + // Integer checks don't verify that the number is not -0.0. + bool isInt(Constant constant) => constant.isInt || constant.isMinusZero; + bool isDouble(Constant constant) + => constant.isDouble && !constant.isMinusZero; + bool isString(Constant constant) => constant.isString; + bool isBool(Constant constant) => constant.isBool; + bool isNull(Constant constant) => constant.isNull; + + bool isSubtype(Compiler compiler, DartType s, DartType t) { + // At runtime, an integer is both an integer and a double: the + // integer type check is Math.floor, which will return true only + // for real integers, and our double type check is 'typeof number' + // which will return true for both integers and doubles. + if (s.element == compiler.intClass && t.element == compiler.doubleClass) { + return true; + } + return compiler.types.isSubtype(s, t); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/custom_elements_analysis.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/custom_elements_analysis.dart new file mode 100644 index 0000000..8fa9596 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/custom_elements_analysis.dart @@ -0,0 +1,201 @@ +// Copyright (c) 2013, 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. + +part of js_backend; + +/** + * Support for Custom Elements. + * + * The support for custom elements the compiler builds a table that maps the + * custom element class's [Type] to the interceptor for the class and the + * constructor(s) for the class. + * + * We want the table to contain only the custom element classes used, and we + * want to avoid resolving and compiling constructors that are not used since + * that may bring in unused code. This class controls the resolution and code + * generation to restrict the impact. + * + * The following line of code requires the generation of the generative + * constructor factory function(s) for FancyButton, and their insertion into the + * table: + * + * document.register(FancyButton, 'x-fancy-button'); + * + * We detect this by 'joining' the classes that are referenced as type literals + * with the classes that are custom elements, enabled by detecting the presence + * of the table access code used by document.register. + * + * We have to be more conservative when the type is unknown, e.g. + * + * document.register(classMirror.reflectedType, tagFromMetadata); + * + * and + * + * class Component { + * final tag; + * Component(this.tag); + * void register() => document.register(T, tag); + * } + * const Component('x-fancy-button').register(); + * + * In these cases we conservatively generate all viable entries in the table. + */ +class CustomElementsAnalysis { + final JavaScriptBackend backend; + final Compiler compiler; + final CustomElementsAnalysisJoin resolutionJoin; + final CustomElementsAnalysisJoin codegenJoin; + bool fetchedTableAccessorMethod = false; + Element tableAccessorMethod; + + CustomElementsAnalysis(JavaScriptBackend backend) + : this.backend = backend, + this.compiler = backend.compiler, + resolutionJoin = new CustomElementsAnalysisJoin(backend), + codegenJoin = new CustomElementsAnalysisJoin(backend) { + // TODO(sra): Remove this work-around. We should mark allClassesSelected in + // both joins only when we see a construct generating an unknown [Type] but + // we can't currently recognize all cases. In particular, the work-around + // for the unimplemented `ClassMirror.reflectedType` is not recognizable. + // TODO(12607): Match on [ClassMirror.reflectedType] + resolutionJoin.allClassesSelected = true; + codegenJoin.allClassesSelected = true; + } + + CustomElementsAnalysisJoin joinFor(Enqueuer enqueuer) => + enqueuer.isResolutionQueue ? resolutionJoin : codegenJoin; + + void registerInstantiatedClass(ClassElement classElement, Enqueuer enqueuer) { + classElement.ensureResolved(compiler); + if (!Elements.isNativeOrExtendsNative(classElement)) return; + if (classElement.isMixinApplication) return; + joinFor(enqueuer).instantiatedClasses.add(classElement); + } + + void registerTypeLiteral(Element element, Enqueuer enqueuer) { + // In codegen we see the TypeConstants instead. + if (!enqueuer.isResolutionQueue) return; + + if (element.isClass()) { + // TODO(sra): If we had a flow query from the type literal expression to + // the Type argument of the metadata lookup, we could tell if this type + // literal is really a demand for the metadata. + resolutionJoin.selectedClasses.add(element); + } else { + // This is a type parameter of a parameterized class. + // TODO(sra): Is there a way to determine which types are bound to the + // parameter? + resolutionJoin.allClassesSelected = true; + } + } + + void registerTypeConstant(Element element, Enqueuer enqueuer) { + assert(element.isClass()); + assert(!enqueuer.isResolutionQueue); + codegenJoin.selectedClasses.add(element); + } + + void registerStaticUse(Element element, Enqueuer enqueuer) { + assert(element != null); + if (!fetchedTableAccessorMethod) { + fetchedTableAccessorMethod = true; + tableAccessorMethod = compiler.findInterceptor( + 'findIndexForNativeSubclassType'); + } + if (element == tableAccessorMethod) { + joinFor(enqueuer).demanded = true; + } + } + + void onQueueEmpty(Enqueuer enqueuer) { + joinFor(enqueuer).flush(enqueuer); + } + + bool get needsTable => codegenJoin.demanded; + + bool needsClass(ClassElement classElement) => + codegenJoin.activeClasses.contains(classElement); + + List constructors(ClassElement classElement) => + codegenJoin.escapingConstructors(classElement); +} + + +class CustomElementsAnalysisJoin { + final JavaScriptBackend backend; + Compiler get compiler => backend.compiler; + + // Classes that are candidates for needing constructors. Classes are moved to + // [activeClasses] when we know they need constructors. + final instantiatedClasses = new Set(); + + // Classes explicitly named. + final selectedClasses = new Set(); + + // True if we must conservatively include all extension classes. + bool allClassesSelected = false; + + // Did we see a demand for the data? + bool demanded = false; + + // ClassesOutput: classes requiring metadata. + final activeClasses = new Set(); + + CustomElementsAnalysisJoin(this.backend); + + void flush(Enqueuer enqueuer) { + if (!demanded) return; + var newActiveClasses = new Set(); + for (ClassElement classElement in instantiatedClasses) { + bool isNative = classElement.isNative(); + bool isExtension = + !isNative && Elements.isNativeOrExtendsNative(classElement); + // Generate table entries for native classes that are explicitly named and + // extensions that fix our criteria. + if ((isNative && selectedClasses.contains(classElement)) || + (isExtension && + (allClassesSelected || selectedClasses.contains(classElement)))) { + newActiveClasses.add(classElement); + escapingConstructors(classElement).forEach(enqueuer.registerStaticUse); + // Force the generaton of the type constant that is the key to an entry + // in the generated table. + Constant constant = makeTypeConstant(classElement); + backend.registerCompileTimeConstant( + constant, compiler.globalDependencies); + compiler.constantHandler.addCompileTimeConstantForEmission(constant); + } + } + activeClasses.addAll(newActiveClasses); + instantiatedClasses.removeAll(newActiveClasses); + } + + TypeConstant makeTypeConstant(ClassElement element) { + DartType elementType = element.rawType; + DartType constantType = backend.typeImplementation.rawType; + return new TypeConstant(elementType, constantType); + } + + List escapingConstructors(ClassElement classElement) { + List result = []; + // Only classes that extend native classes have constructors in the table. + // We could refine this to classes that extend Element, but that would break + // the tests and there is no sane reason to subclass other native classes. + if (classElement.isNative()) return result; + + selectGenerativeConstructors(ClassElement enclosing, Element member) { + if (member.isGenerativeConstructor()) { + // Ignore constructors that cannot be called with zero arguments. + FunctionElement constructor = member; + FunctionSignature parameters = constructor.functionSignature; + if (parameters.requiredParameterCount == 0) { + result.add(member); + } + } + } + classElement.forEachMember(selectGenerativeConstructors, + includeBackendMembers: false, + includeSuperAndInjectedMembers: false); + return result; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart new file mode 100644 index 0000000..6ca4fc7 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart @@ -0,0 +1,36 @@ +// Copyright (c) 2012, 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. + +library js_backend; + +import 'dart:async' show Future; +import 'dart:collection' show Queue, LinkedHashMap; + +import '../closure.dart'; +import '../elements/elements.dart'; +import '../js_emitter/js_emitter.dart' + show Emitter, CodeEmitterTask, ClassBuilder, MetadataEmitter; + +import '../dart2jslib.dart'; +import '../dart_types.dart'; +import '../js/js.dart' as jsAst; +import '../js/js.dart' show js; +import '../native_handler.dart' as native; +import '../ssa/ssa.dart'; +import '../tree/tree.dart'; +import '../types/types.dart'; +import '../universe/universe.dart'; +import '../util/characters.dart'; +import '../util/util.dart'; + +part 'backend.dart'; +part 'checked_mode_helpers.dart'; +part 'constant_emitter.dart'; +part 'constant_system_javascript.dart'; +part 'minify_namer.dart'; +part 'namer.dart'; +part 'native_emitter.dart'; +part 'runtime_types.dart'; +part 'type_variable_handler.dart'; +part 'custom_elements_analysis.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/minify_namer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/minify_namer.dart new file mode 100644 index 0000000..9b5c56e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/minify_namer.dart @@ -0,0 +1,224 @@ +// Copyright (c) 2011, 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. + +part of js_backend; + +/** + * Assigns JavaScript identifiers to Dart variables, class-names and members. + */ +class MinifyNamer extends Namer { + MinifyNamer(Compiler compiler) : super(compiler) { + reserveBackendNames(); + } + + String get isolateName => 'I'; + String get isolatePropertiesName => 'p'; + bool get shouldMinify => true; + + final String getterPrefix = 'g'; + final String setterPrefix = 's'; + + static const ALPHABET_CHARACTERS = 52; // a-zA-Z. + static const ALPHANUMERIC_CHARACTERS = 62; // a-zA-Z0-9. + + // You can pass an invalid identifier to this and unlike its non-minifying + // counterpart it will never return the proposedName as the new fresh name. + String getFreshName(String proposedName, + Set usedNames, + Map suggestedNames, + {bool ensureSafe: true}) { + var freshName; + var suggestion = suggestedNames[proposedName]; + if (suggestion != null && !usedNames.contains(suggestion)) { + freshName = suggestion; + } else { + freshName = _getUnusedName(proposedName, usedNames); + } + usedNames.add(freshName); + return freshName; + } + + String getClosureVariableName(String name, int id) { + if (id < ALPHABET_CHARACTERS) { + return new String.fromCharCodes([_letterNumber(id)]); + } + return "${getMappedInstanceName('closure')}_$id"; + } + + void reserveBackendNames() { + // From issue 7554. These should not be used on objects (as instance + // variables) because they clash with names from the DOM. + const reservedNativeProperties = const [ + 'Q', 'a', 'b', 'c', 'd', 'e', 'f', 'r', 'x', 'y', 'z', + // 2-letter: + 'ch', 'cx', 'cy', 'db', 'dx', 'dy', 'fr', 'fx', 'fy', 'go', 'id', 'k1', + 'k2', 'k3', 'k4', 'r1', 'r2', 'rx', 'ry', 'x1', 'x2', 'y1', 'y2', + // 3-letter: + 'add', 'all', 'alt', 'arc', 'CCW', 'cmp', 'dir', 'end', 'get', 'in1', + 'in2', 'INT', 'key', 'log', 'low', 'm11', 'm12', 'm13', 'm14', 'm21', + 'm22', 'm23', 'm24', 'm31', 'm32', 'm33', 'm34', 'm41', 'm42', 'm43', + 'm44', 'max', 'min', 'now', 'ONE', 'put', 'red', 'rel', 'rev', 'RGB', + 'sdp', 'set', 'src', 'tag', 'top', 'uid', 'uri', 'url', 'URL', + // 4-letter: + 'abbr', 'atob', 'Attr', 'axes', 'axis', 'back', 'BACK', 'beta', 'bias', + 'Blob', 'blue', 'blur', 'BLUR', 'body', 'BOOL', 'BOTH', 'btoa', 'BYTE', + 'cite', 'clip', 'code', 'cols', 'cues', 'data', 'DECR', 'DONE', 'face', + 'file', 'File', 'fill', 'find', 'font', 'form', 'gain', 'hash', 'head', + 'high', 'hint', 'host', 'href', 'HRTF', 'IDLE', 'INCR', 'info', 'INIT', + 'isId', 'item', 'KEEP', 'kind', 'knee', 'lang', 'left', 'LESS', 'line', + 'link', 'list', 'load', 'loop', 'mode', 'name', 'Node', 'None', 'NONE', + 'only', 'open', 'OPEN', 'ping', 'play', 'port', 'rect', 'Rect', 'refX', + 'refY', 'RGBA', 'root', 'rows', 'save', 'seed', 'seek', 'self', 'send', + 'show', 'SINE', 'size', 'span', 'stat', 'step', 'stop', 'tags', 'text', + 'Text', 'time', 'type', 'view', 'warn', 'wrap', 'ZERO']; + + for (var name in reservedNativeProperties) { + if (name.length < 2) { + instanceNameMap[name] = name; + } + usedInstanceNames.add(name); + // Getter and setter names are autogenerated by prepending 'g' and 's' to + // field names. Therefore there are some field names we don't want to + // use. It is implicit in the next line that the banned prefix is + // only one character. + if (_hasBannedPrefix(name)) usedInstanceNames.add(name.substring(1)); + } + + // These popular names are present in most programs and deserve + // single character minified names. We could determine the popular names + // individually per program, but that would mean that the output of the + // minifier was less stable from version to version of the program being + // minified. + _populateSuggestedNames( + suggestedInstanceNames, + usedInstanceNames, + const [ + r'$add', r'add$1', r'$and', r'codeUnitAt$1', r'$or', + r'current', r'$shr', r'$eq', r'$ne', + r'getPrototypeOf', r'hasOwnProperty', r'$index', r'$indexSet', + r'$isJavaScriptIndexingBehavior', r'$xor', + r'iterator', r'length', r'$lt', r'$gt', r'$le', r'$ge', + r'moveNext$0', r'node', r'on', r'$negate', r'push', r'self', + r'start', r'target', r'$shl', r'value', r'width', r'style', + r'noSuchMethod$1', r'$mul', r'$div', r'$sub', r'$not', r'$mod', + r'$tdiv']); + + _populateSuggestedNames( + suggestedGlobalNames, + usedGlobalNames, + const [ + r'Object', 'wrapException', r'$eq', r'S', r'ioore', + r'UnsupportedError$', r'length', r'$sub', + r'getInterceptor$JSArrayJSString', r'$add', + r'$gt', r'$ge', r'$lt', r'$le', r'add', r'getInterceptor$JSNumber', + r'iterator', r'$index', r'iae', r'getInterceptor$JSArray', + r'ArgumentError$', r'BoundClosure', r'StateError$', + r'getInterceptor', r'max', r'$mul', r'List_List', r'Map_Map', + r'getInterceptor$JSString', r'$div', r'$indexSet', + r'List_List$from', r'Set_Set$from', r'toString', r'toInt', r'min', + r'StringBuffer_StringBuffer', r'contains1', r'WhereIterable$', + r'RangeError$value', r'JSString', r'JSNumber', + r'JSArray', r'createInvocationMirror' + ]); + } + + void _populateSuggestedNames(Map suggestionMap, + Set used, + List suggestions) { + int c = $a - 1; + String letter; + for (String name in suggestions) { + do { + assert(c != $Z); + c = (c == $z) ? $A : c + 1; + letter = new String.fromCharCodes([c]); + } while (used.contains(letter)); + assert(suggestionMap[name] == null); + suggestionMap[name] = letter; + } + } + + + // This gets a minified name based on a hash of the proposed name. This + // is slightly less efficient than just getting the next name in a series, + // but it means that small changes in the input program will give smallish + // changes in the output, which can be useful for diffing etc. + String _getUnusedName(String proposedName, Set usedNames) { + int hash = _calculateHash(proposedName); + // Avoid very small hashes that won't try many names. + hash = hash < 1000 ? hash * 314159 : hash; // Yes, it's prime. + + // Try other n-character names based on the hash. We try one to three + // character identifiers. For each length we try around 10 different names + // in a predictable order determined by the proposed name. This is in order + // to make the renamer stable: small changes in the input should nornally + // result in relatively small changes in the output. + for (var n = 2; n <= 3; n++) { + int h = hash; + while (h > 10) { + var codes = [_letterNumber(h)]; + int h2 = h ~/ ALPHABET_CHARACTERS; + for (var i = 1; i < n; i++) { + codes.add(_alphaNumericNumber(h2)); + h2 ~/= ALPHANUMERIC_CHARACTERS; + } + final candidate = new String.fromCharCodes(codes); + if (!usedNames.contains(candidate) && + !jsReserved.contains(candidate) && + !_hasBannedPrefix(candidate)) { + return candidate; + } + // Try again with a slightly different hash. After around 10 turns + // around this loop h is zero and we try a longer name. + h ~/= 7; + } + } + + // If we can't find a hash based name in the three-letter space, then base + // the name on a letter and a counter. + var startLetter = new String.fromCharCodes([_letterNumber(hash)]); + var i = 0; + while (usedNames.contains("$startLetter$i")) { + i++; + } + // We don't need to check for banned prefix because the name is in the form + // xnnn, where nnn is a number. There can be no getter or setter called + // gnnn since that would imply a numeric field name. + return "$startLetter$i"; + } + + /// Instance members starting with g and s are reserved for getters and + /// setters. + bool _hasBannedPrefix(String name) { + int code = name.codeUnitAt(0); + return code == $g || code == $s; + } + + int _calculateHash(String name) { + int h = 0; + for (int i = 0; i < name.length; i++) { + h += name.codeUnitAt(i); + h &= 0xffffffff; + h += h << 10; + h &= 0xffffffff; + h ^= h >> 6; + h &= 0xffffffff; + } + return h; + } + + int _letterNumber(int x) { + if (x >= ALPHABET_CHARACTERS) x %= ALPHABET_CHARACTERS; + if (x < 26) return $a + x; + return $A + x - 26; + } + + int _alphaNumericNumber(int x) { + if (x >= ALPHANUMERIC_CHARACTERS) x %= ALPHANUMERIC_CHARACTERS; + if (x < 26) return $a + x; + if (x < 52) return $A + x - 26; + return $0 + x - 52; + } + +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/namer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/namer.dart new file mode 100644 index 0000000..00a0bdb --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/namer.dart @@ -0,0 +1,1394 @@ +// Copyright (c) 2011, 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. + +part of js_backend; + +/** + * Assigns JavaScript identifiers to Dart variables, class-names and members. + */ +class Namer implements ClosureNamer { + + static const javaScriptKeywords = const [ + // These are current keywords. + "break", "delete", "function", "return", "typeof", "case", "do", "if", + "switch", "var", "catch", "else", "in", "this", "void", "continue", + "false", "instanceof", "throw", "while", "debugger", "finally", "new", + "true", "with", "default", "for", "null", "try", + + // These are future keywords. + "abstract", "double", "goto", "native", "static", "boolean", "enum", + "implements", "package", "super", "byte", "export", "import", "private", + "synchronized", "char", "extends", "int", "protected", "throws", + "class", "final", "interface", "public", "transient", "const", "float", + "long", "short", "volatile" + ]; + + static const reservedPropertySymbols = + const ["__proto__", "prototype", "constructor", "call"]; + + // Symbols that we might be using in our JS snippets. + static const reservedGlobalSymbols = const [ + // Section references are from Ecma-262 + // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) + + // 15.1.1 Value Properties of the Global Object + "NaN", "Infinity", "undefined", + + // 15.1.2 Function Properties of the Global Object + "eval", "parseInt", "parseFloat", "isNaN", "isFinite", + + // 15.1.3 URI Handling Function Properties + "decodeURI", "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + + // 15.1.4 Constructor Properties of the Global Object + "Object", "Function", "Array", "String", "Boolean", "Number", "Date", + "RegExp", "Error", "EvalError", "RangeError", "ReferenceError", + "SyntaxError", "TypeError", "URIError", + + // 15.1.5 Other Properties of the Global Object + "Math", + + // 10.1.6 Activation Object + "arguments", + + // B.2 Additional Properties (non-normative) + "escape", "unescape", + + // Window props (https://developer.mozilla.org/en/DOM/window) + "applicationCache", "closed", "Components", "content", "controllers", + "crypto", "defaultStatus", "dialogArguments", "directories", + "document", "frameElement", "frames", "fullScreen", "globalStorage", + "history", "innerHeight", "innerWidth", "length", + "location", "locationbar", "localStorage", "menubar", + "mozInnerScreenX", "mozInnerScreenY", "mozScreenPixelsPerCssPixel", + "name", "navigator", "opener", "outerHeight", "outerWidth", + "pageXOffset", "pageYOffset", "parent", "personalbar", "pkcs11", + "returnValue", "screen", "scrollbars", "scrollMaxX", "scrollMaxY", + "self", "sessionStorage", "sidebar", "status", "statusbar", "toolbar", + "top", "window", + + // Window methods (https://developer.mozilla.org/en/DOM/window) + "alert", "addEventListener", "atob", "back", "blur", "btoa", + "captureEvents", "clearInterval", "clearTimeout", "close", "confirm", + "disableExternalCapture", "dispatchEvent", "dump", + "enableExternalCapture", "escape", "find", "focus", "forward", + "GeckoActiveXObject", "getAttention", "getAttentionWithCycleCount", + "getComputedStyle", "getSelection", "home", "maximize", "minimize", + "moveBy", "moveTo", "open", "openDialog", "postMessage", "print", + "prompt", "QueryInterface", "releaseEvents", "removeEventListener", + "resizeBy", "resizeTo", "restore", "routeEvent", "scroll", "scrollBy", + "scrollByLines", "scrollByPages", "scrollTo", "setInterval", + "setResizeable", "setTimeout", "showModalDialog", "sizeToContent", + "stop", "uuescape", "updateCommands", "XPCNativeWrapper", + "XPCSafeJSOjbectWrapper", + + // Mozilla Window event handlers, same cite + "onabort", "onbeforeunload", "onchange", "onclick", "onclose", + "oncontextmenu", "ondragdrop", "onerror", "onfocus", "onhashchange", + "onkeydown", "onkeypress", "onkeyup", "onload", "onmousedown", + "onmousemove", "onmouseout", "onmouseover", "onmouseup", + "onmozorientation", "onpaint", "onreset", "onresize", "onscroll", + "onselect", "onsubmit", "onunload", + + // Safari Web Content Guide + // http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/SafariWebContent.pdf + // WebKit Window member data, from WebKit DOM Reference + // (http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/WebKitDOMRef/DOMWindow_idl/Classes/DOMWindow/index.html) + "ontouchcancel", "ontouchend", "ontouchmove", "ontouchstart", + "ongesturestart", "ongesturechange", "ongestureend", + + // extra window methods + "uneval", + + // keywords https://developer.mozilla.org/en/New_in_JavaScript_1.7, + // https://developer.mozilla.org/en/New_in_JavaScript_1.8.1 + "getPrototypeOf", "let", "yield", + + // "future reserved words" + "abstract", "int", "short", "boolean", "interface", "static", "byte", + "long", "char", "final", "native", "synchronized", "float", "package", + "throws", "goto", "private", "transient", "implements", "protected", + "volatile", "double", "public", + + // IE methods + // (http://msdn.microsoft.com/en-us/library/ms535873(VS.85).aspx#) + "attachEvent", "clientInformation", "clipboardData", "createPopup", + "dialogHeight", "dialogLeft", "dialogTop", "dialogWidth", + "onafterprint", "onbeforedeactivate", "onbeforeprint", + "oncontrolselect", "ondeactivate", "onhelp", "onresizeend", + + // Common browser-defined identifiers not defined in ECMAScript + "event", "external", "Debug", "Enumerator", "Global", "Image", + "ActiveXObject", "VBArray", "Components", + + // Functions commonly defined on Object + "toString", "getClass", "constructor", "prototype", "valueOf", + + // Client-side JavaScript identifiers + "Anchor", "Applet", "Attr", "Canvas", "CanvasGradient", + "CanvasPattern", "CanvasRenderingContext2D", "CDATASection", + "CharacterData", "Comment", "CSS2Properties", "CSSRule", + "CSSStyleSheet", "Document", "DocumentFragment", "DocumentType", + "DOMException", "DOMImplementation", "DOMParser", "Element", "Event", + "ExternalInterface", "FlashPlayer", "Form", "Frame", "History", + "HTMLCollection", "HTMLDocument", "HTMLElement", "IFrame", "Image", + "Input", "JSObject", "KeyEvent", "Link", "Location", "MimeType", + "MouseEvent", "Navigator", "Node", "NodeList", "Option", "Plugin", + "ProcessingInstruction", "Range", "RangeException", "Screen", "Select", + "Table", "TableCell", "TableRow", "TableSelection", "Text", "TextArea", + "UIEvent", "Window", "XMLHttpRequest", "XMLSerializer", + "XPathException", "XPathResult", "XSLTProcessor", + + // These keywords trigger the loading of the java-plugin. For the + // next-generation plugin, this results in starting a new Java process. + "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass", + "JavaArray", "JavaMember", + ]; + + static const reservedGlobalObjectNames = const [ + "A", + "B", + "C", // Global object for *C*onstants. + "D", + "E", + "F", + "G", + "H", // Global object for internal (*H*elper) libraries. + // I is used for used for the Isolate function. + "J", // Global object for the interceptor library. + "K", + "L", + "M", + "N", + "O", + "P", // Global object for other *P*latform libraries. + "Q", + "R", + "S", + "T", + "U", + "V", + "W", // Global object for *W*eb libraries (dart:html). + "X", + "Y", + "Z", + ]; + + static const reservedGlobalHelperFunctions = const [ + "init", + "Isolate", + ]; + + static final userGlobalObjects = new List.from(reservedGlobalObjectNames) + ..remove('C') + ..remove('H') + ..remove('J') + ..remove('P') + ..remove('W'); + + Set _jsReserved = null; + /// Names that cannot be used by members, top level and static + /// methods. + Set get jsReserved { + if (_jsReserved == null) { + _jsReserved = new Set(); + _jsReserved.addAll(javaScriptKeywords); + _jsReserved.addAll(reservedPropertySymbols); + } + return _jsReserved; + } + + Set _jsVariableReserved = null; + /// Names that cannot be used by local variables and parameters. + Set get jsVariableReserved { + if (_jsVariableReserved == null) { + _jsVariableReserved = new Set(); + _jsVariableReserved.addAll(javaScriptKeywords); + _jsVariableReserved.addAll(reservedPropertySymbols); + _jsVariableReserved.addAll(reservedGlobalSymbols); + _jsVariableReserved.addAll(reservedGlobalObjectNames); + // 26 letters in the alphabet, 25 not counting I. + assert(reservedGlobalObjectNames.length == 25); + _jsVariableReserved.addAll(reservedGlobalHelperFunctions); + } + return _jsVariableReserved; + } + + final String currentIsolate = r'$'; + final String getterPrefix = r'get$'; + final String setterPrefix = r'set$'; + final String metadataField = '@'; + final String callCatchAllName = r'call$catchAll'; + final String reflectableField = r'$reflectable'; + final String defaultValuesField = r'$defaultValues'; + final String methodsWithOptionalArgumentsField = + r'$methodsWithOptionalArguments'; + + final String classDescriptorProperty = r'^'; + + // Name of property in a class description for the native dispatch metadata. + final String nativeSpecProperty = '%'; + + static final RegExp IDENTIFIER = new RegExp(r'^[A-Za-z_$][A-Za-z0-9_$]*$'); + static final RegExp NON_IDENTIFIER_CHAR = new RegExp(r'[^A-Za-z_0-9$]'); + + /** + * Map from top-level or static elements to their unique identifiers provided + * by [getName]. + * + * Invariant: Keys must be declaration elements. + */ + final Compiler compiler; + final Map globals; + final Map shortPrivateNameOwners; + + final Set usedGlobalNames; + final Set usedInstanceNames; + final Map globalNameMap; + final Map suggestedGlobalNames; + final Map instanceNameMap; + final Map suggestedInstanceNames; + + final Map operatorNameMap; + final Map popularNameCounters; + + final Map constantNames; + final Map constantLongNames; + ConstantCanonicalHasher constantHasher; + + Namer(Compiler compiler) + : compiler = compiler, + globals = new Map(), + shortPrivateNameOwners = new Map(), + usedGlobalNames = new Set(), + usedInstanceNames = new Set(), + instanceNameMap = new Map(), + operatorNameMap = new Map(), + globalNameMap = new Map(), + suggestedGlobalNames = new Map(), + suggestedInstanceNames = new Map(), + popularNameCounters = new Map(), + constantNames = new Map(), + constantLongNames = new Map(), + constantHasher = new ConstantCanonicalHasher(compiler), + functionTypeNamer = new FunctionTypeNamer(compiler); + + String get isolateName => 'Isolate'; + String get isolatePropertiesName => r'$isolateProperties'; + /** + * Some closures must contain their name. The name is stored in + * [STATIC_CLOSURE_NAME_NAME]. + */ + String get STATIC_CLOSURE_NAME_NAME => r'$name'; + String get closureInvocationSelectorName => Compiler.CALL_OPERATOR_NAME; + bool get shouldMinify => false; + + String getNameForJsGetName(Node node, String name) { + switch (name) { + case 'GETTER_PREFIX': return getterPrefix; + case 'SETTER_PREFIX': return setterPrefix; + case 'CALL_CATCH_ALL': return callCatchAllName; + case 'REFLECTABLE': return reflectableField; + case 'CLASS_DESCRIPTOR_PROPERTY': return classDescriptorProperty; + default: + compiler.reportError( + node, MessageKind.GENERIC, + {'text': 'Error: Namer has no name for "$name".'}); + return 'BROKEN'; + } + } + + String constantName(Constant constant) { + // In the current implementation it doesn't make sense to give names to + // function constants since the function-implementation itself serves as + // constant and can be accessed directly. + assert(!constant.isFunction); + String result = constantNames[constant]; + if (result == null) { + String longName = constantLongName(constant); + result = getFreshName(longName, usedGlobalNames, suggestedGlobalNames, + ensureSafe: true); + constantNames[constant] = result; + } + return result; + } + + // The long name is unminified and may have collisions. + String constantLongName(Constant constant) { + String longName = constantLongNames[constant]; + if (longName == null) { + longName = new ConstantNamingVisitor(compiler, constantHasher) + .getName(constant); + constantLongNames[constant] = longName; + } + return longName; + } + + String breakLabelName(LabelElement label) { + return '\$${label.labelName}\$${label.target.nestingLevel}'; + } + + String implicitBreakLabelName(TargetElement target) { + return '\$${target.nestingLevel}'; + } + + // We sometimes handle continue targets differently from break targets, + // so we have special continue-only labels. + String continueLabelName(LabelElement label) { + return 'c\$${label.labelName}\$${label.target.nestingLevel}'; + } + + String implicitContinueLabelName(TargetElement target) { + return 'c\$${target.nestingLevel}'; + } + + /** + * If the [name] is not private returns [:name:]. Otherwise + * mangles the [name] so that each library has a unique name. + */ + String privateName(LibraryElement library, String name) { + // Public names are easy. + String nameString = name; + if (!isPrivateName(name)) return nameString; + + // The first library asking for a short private name wins. + LibraryElement owner = shouldMinify + ? library + : shortPrivateNameOwners.putIfAbsent(nameString, () => library); + + if (owner == library && !shouldMinify && !nameString.contains('\$')) { + // Since the name doesn't contain $ it doesn't clash with any + // of the private names that have the library name as the prefix. + return nameString; + } else { + // Make sure to return a private name that starts with _ so it + // cannot clash with any public names. + String libraryName = getNameOfLibrary(library); + return '_$libraryName\$$nameString'; + } + } + + String instanceMethodName(FunctionElement element) { + // TODO(ahe): Could this be: return invocationName(new + // Selector.fromElement(element))? + String elementName = element.name; + String name = operatorNameToIdentifier(elementName); + if (name != elementName) return getMappedOperatorName(name); + + LibraryElement library = element.getLibrary(); + if (element.isGenerativeConstructorBody()) { + name = Elements.reconstructConstructorNameSourceString(element); + } + FunctionSignature signature = element.functionSignature; + String methodName = + '${privateName(library, name)}\$${signature.parameterCount}'; + if (signature.optionalParametersAreNamed && + !signature.optionalParameters.isEmpty) { + StringBuffer buffer = new StringBuffer(); + signature.orderedOptionalParameters.forEach((Element element) { + buffer.write('\$${safeName(element.name)}'); + }); + methodName = '$methodName$buffer'; + } + if (name == closureInvocationSelectorName) return methodName; + return getMappedInstanceName(methodName); + } + + String publicInstanceMethodNameByArity(String name, int arity) { + String newName = operatorNameToIdentifier(name); + if (newName != name) return getMappedOperatorName(newName); + assert(!isPrivateName(name)); + // We don't mangle the closure invoking function name because it + // is generated by string concatenation in applyFunction from + // js_helper.dart. + String proposedName = '$name\$$arity'; + if (name == closureInvocationSelectorName) return proposedName; + return getMappedInstanceName(proposedName); + } + + String invocationName(Selector selector) { + if (selector.isGetter()) { + String proposedName = privateName(selector.library, selector.name); + return '$getterPrefix${getMappedInstanceName(proposedName)}'; + } else if (selector.isSetter()) { + String proposedName = privateName(selector.library, selector.name); + return '$setterPrefix${getMappedInstanceName(proposedName)}'; + } else { + String name = selector.name; + if (selector.kind == SelectorKind.OPERATOR + || selector.kind == SelectorKind.INDEX) { + name = operatorNameToIdentifier(name); + assert(name != selector.name); + return getMappedOperatorName(name); + } + assert(name == operatorNameToIdentifier(name)); + StringBuffer buffer = new StringBuffer(); + for (String argumentName in selector.getOrderedNamedArguments()) { + buffer.write('\$${safeName(argumentName)}'); + } + String suffix = '\$${selector.argumentCount}$buffer'; + // We don't mangle the closure invoking function name because it + // is generated by string concatenation in applyFunction from + // js_helper.dart. + if (selector.isClosureCall()) { + return "$name$suffix"; + } else { + String proposedName = privateName(selector.library, name); + return getMappedInstanceName('$proposedName$suffix'); + } + } + } + + /** + * Returns the internal name used for an invocation mirror of this selector. + */ + String invocationMirrorInternalName(Selector selector) + => invocationName(selector); + + /** + * Returns name of accessor (root to getter and setter) for a static or + * instance field. + */ + String fieldAccessorName(Element element) { + return element.isInstanceMember() + ? instanceFieldAccessorName(element) + : getNameOfField(element); + } + + /** + * Returns name of the JavaScript property used to store a static or instance + * field. + */ + String fieldPropertyName(Element element) { + return element.isInstanceMember() + ? instanceFieldPropertyName(element) + : getNameOfField(element); + } + + /** + * Returns name of accessor (root to getter and setter) for an instance field. + */ + String instanceFieldAccessorName(Element element) { + String proposedName = privateName(element.getLibrary(), element.name); + return getMappedInstanceName(proposedName); + } + + String readTypeVariableName(TypeVariableElement element) { + return '\$tv_${instanceFieldAccessorName(element)}'; + } + + /** + * Returns name of the JavaScript property used to store an instance field. + */ + String instanceFieldPropertyName(Element element) { + if (element.hasFixedBackendName()) { + return element.fixedBackendName(); + } + // If a class is used anywhere as a mixin, we must make the name unique so + // that it does not accidentally shadow. Also, the mixin name must be + // constant over all mixins. + if (compiler.world.isUsedAsMixin(element.getEnclosingClass()) || + shadowingAnotherField(element)) { + // Construct a new name for the element based on the library and class it + // is in. The name here is not important, we just need to make sure it is + // unique. If we are minifying, we actually construct the name from the + // minified version of the class name, but the result is minified once + // again, so that is not visible in the end result. + String libraryName = getNameOfLibrary(element.getLibrary()); + String className = getNameOfClass(element.getEnclosingClass()); + String instanceName = privateName(element.getLibrary(), element.name); + return getMappedInstanceName('$libraryName\$$className\$$instanceName'); + } + + String proposedName = privateName(element.getLibrary(), element.name); + return getMappedInstanceName(proposedName); + } + + + bool shadowingAnotherField(Element element) { + return element.getEnclosingClass().hasFieldShadowedBy(element); + } + + String setterName(Element element) { + // We dynamically create setters from the field-name. The setter name must + // therefore be derived from the instance field-name. + LibraryElement library = element.getLibrary(); + String name = getMappedInstanceName(privateName(library, element.name)); + return '$setterPrefix$name'; + } + + String setterNameFromAccessorName(String name) { + // We dynamically create setters from the field-name. The setter name must + // therefore be derived from the instance field-name. + return '$setterPrefix$name'; + } + + String getterNameFromAccessorName(String name) { + // We dynamically create getters from the field-name. The getter name must + // therefore be derived from the instance field-name. + return '$getterPrefix$name'; + } + + String getterName(Element element) { + // We dynamically create getters from the field-name. The getter name must + // therefore be derived from the instance field-name. + LibraryElement library = element.getLibrary(); + String name = getMappedInstanceName(privateName(library, element.name)); + return '$getterPrefix$name'; + } + + String getMappedGlobalName(String proposedName, {bool ensureSafe: true}) { + var newName = globalNameMap[proposedName]; + if (newName == null) { + newName = getFreshName(proposedName, usedGlobalNames, + suggestedGlobalNames, ensureSafe: ensureSafe); + globalNameMap[proposedName] = newName; + } + return newName; + } + + String getMappedInstanceName(String proposedName) { + var newName = instanceNameMap[proposedName]; + if (newName == null) { + newName = getFreshName(proposedName, usedInstanceNames, + suggestedInstanceNames, ensureSafe: true); + instanceNameMap[proposedName] = newName; + } + return newName; + } + + String getMappedOperatorName(String proposedName) { + var newName = operatorNameMap[proposedName]; + if (newName == null) { + newName = getFreshName(proposedName, usedInstanceNames, + suggestedInstanceNames, ensureSafe: false); + operatorNameMap[proposedName] = newName; + } + return newName; + } + + String getFreshName(String proposedName, + Set usedNames, + Map suggestedNames, + {bool ensureSafe: true}) { + var candidate; + if (ensureSafe) { + proposedName = safeName(proposedName); + } + assert(!jsReserved.contains(proposedName)); + if (!usedNames.contains(proposedName)) { + candidate = proposedName; + } else { + var counter = popularNameCounters[proposedName]; + var i = counter == null ? 0 : counter; + while (usedNames.contains("$proposedName$i")) { + i++; + } + popularNameCounters[proposedName] = i + 1; + candidate = "$proposedName$i"; + } + usedNames.add(candidate); + return candidate; + } + + String getClosureVariableName(String name, int id) { + return "${name}_$id"; + } + + /** + * Returns a preferred JS-id for the given top-level or static element. + * The returned id is guaranteed to be a valid JS-id. + */ + String _computeGuess(Element element) { + assert(!element.isInstanceMember()); + String name; + if (element.isGenerativeConstructor()) { + name = "${element.getEnclosingClass().name}\$" + "${element.name}"; + } else if (element.isFactoryConstructor()) { + // TODO(johnniwinther): Change factory name encoding as to not include + // the class-name twice. + String className = element.getEnclosingClass().name; + name = '${className}_${Elements.reconstructConstructorName(element)}'; + } else if (Elements.isStaticOrTopLevel(element)) { + if (element.isMember()) { + ClassElement enclosingClass = element.getEnclosingClass(); + name = "${enclosingClass.name}_" + "${element.name}"; + } else { + name = element.name.replaceAll('+', '_'); + } + } else if (element.isLibrary()) { + LibraryElement library = element; + name = library.getLibraryOrScriptName(); + if (name.contains('.')) { + // For libraries that have a library tag, we use the last part + // of the fully qualified name as their base name. For all other + // libraries, we use the first part of their filename. + name = library.hasLibraryName() + ? name.substring(name.lastIndexOf('.') + 1) + : name.substring(0, name.indexOf('.')); + } + // The filename based name can contain all kinds of nasty characters. Make + // sure it is an identifier. + if (!IDENTIFIER.hasMatch(name)) { + name = name.replaceAllMapped(NON_IDENTIFIER_CHAR, + (match) => match[0].codeUnitAt(0).toRadixString(16)); + if (!IDENTIFIER.hasMatch(name)) { // e.g. starts with digit. + name = 'lib_$name'; + } + } + } else { + name = element.name; + } + return name; + } + + String getInterceptorSuffix(Iterable classes) { + String abbreviate(ClassElement cls) { + if (cls == compiler.objectClass) return "o"; + JavaScriptBackend backend = compiler.backend; + if (cls == backend.jsStringClass) return "s"; + if (cls == backend.jsArrayClass) return "a"; + if (cls == backend.jsDoubleClass) return "d"; + if (cls == backend.jsIntClass) return "i"; + if (cls == backend.jsNumberClass) return "n"; + if (cls == backend.jsNullClass) return "u"; + if (cls == backend.jsBoolClass) return "b"; + if (cls == backend.jsInterceptorClass) return "I"; + return cls.name; + } + List names = classes + .where((cls) => !Elements.isNativeOrExtendsNative(cls)) + .map(abbreviate) + .toList(); + // There is one dispatch mechanism for all native classes. + if (classes.any((cls) => Elements.isNativeOrExtendsNative(cls))) { + names.add("x"); + } + // Sort the names of the classes after abbreviating them to ensure + // the suffix is stable and predictable for the suggested names. + names.sort(); + return names.join(); + } + + String getInterceptorName(Element element, Iterable classes) { + JavaScriptBackend backend = compiler.backend; + if (classes.contains(backend.jsInterceptorClass)) { + // If the base Interceptor class is in the set of intercepted classes, we + // need to go through the generic getInterceptorMethod, since any subclass + // of the base Interceptor could match. + return getNameOfInstanceMember(element); + } + String suffix = getInterceptorSuffix(classes); + return getMappedGlobalName("${element.name}\$$suffix"); + } + + String getOneShotInterceptorName(Selector selector, + Iterable classes) { + JavaScriptBackend backend = compiler.backend; + // The one-shot name is a global name derived from the invocation name. To + // avoid instability we would like the names to be unique and not clash with + // other global names. + + String root = invocationName(selector); // Is already safe. + + if (classes.contains(backend.jsInterceptorClass)) { + // If the base Interceptor class is in the set of intercepted classes, + // this is the most general specialization which uses the generic + // getInterceptor method. To keep the name short, we add '$' only to + // distinguish from global getters or setters; operators and methods can't + // clash. + // TODO(sra): Find a way to get the simple name when Object is not in the + // set of classes for most general variant, e.g. "$lt$n" could be "$lt". + if (selector.isGetter() || selector.isSetter()) root = '$root\$'; + return getMappedGlobalName(root, ensureSafe: false); + } else { + String suffix = getInterceptorSuffix(classes); + return getMappedGlobalName("$root\$$suffix", ensureSafe: false); + } + } + + /// Returns the runtime name for [element]. The result is not safe as an id. + String getRuntimeTypeName(Element element) { + if (identical(element, compiler.dynamicClass)) return 'dynamic'; + JavaScriptBackend backend = compiler.backend; + element = backend.getImplementationClass(element); + String name = getPrimitiveInterceptorRuntimeName(element); + // TODO(ahe): Creating a string here is unfortunate. It is slow (due to + // string concatenation in the implementation), and may prevent + // segmentation of '$'. + return name != null ? name : getNameForRti(element); + } + + /** + * Return a string to be used as the runtime name of this class (instead of + * the class name) or [:null:] if the class name should be used. + */ + String getPrimitiveInterceptorRuntimeName(Element cls) { + JavaScriptBackend backend = compiler.backend; + if (cls == backend.jsIntClass) { + return 'int'; + } else if (cls == backend.jsNumberClass) { + return 'num'; + } else if (cls == backend.jsBoolClass) { + return 'bool'; + } else if (cls == backend.jsDoubleClass) { + return 'double'; + } else if (cls == backend.jsStringClass) { + return 'String'; + } else if (cls == backend.jsArrayClass) { + return 'List'; + } else if (cls == backend.jsNullClass) { + return 'Null'; + } else { + return null; + } + } + + /** + * Returns a preferred JS-id for the given element. The returned id is + * guaranteed to be a valid JS-id. Globals and static fields are furthermore + * guaranteed to be unique. + * + * For accessing statics consider calling + * [isolateAccess] or [isolatePropertyAccess] instead. + */ + // TODO(ahe): This is an internal method to the Namer (and its subclasses) + // and should not be call from outside. + String getNameX(Element element) { + if (element.isInstanceMember()) { + if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY + || element.kind == ElementKind.FUNCTION) { + return instanceMethodName(element); + } else if (element.kind == ElementKind.GETTER) { + return getterName(element); + } else if (element.kind == ElementKind.SETTER) { + return setterName(element); + } else if (element.kind == ElementKind.FIELD) { + compiler.internalError(element, + 'Use instanceFieldPropertyName or instanceFieldAccessorName.'); + return null; + } else { + compiler.internalError(element, + 'getName for bad kind: ${element.kind}.'); + return null; + } + } else { + // Use declaration element to ensure invariant on [globals]. + element = element.declaration; + // Dealing with a top-level or static element. + String cached = globals[element]; + if (cached != null) return cached; + + String guess = _computeGuess(element); + ElementKind kind = element.kind; + if (kind == ElementKind.VARIABLE || + kind == ElementKind.PARAMETER) { + // The name is not guaranteed to be unique. + return safeName(guess); + } + if (kind == ElementKind.GENERATIVE_CONSTRUCTOR || + kind == ElementKind.FUNCTION || + kind == ElementKind.CLASS || + kind == ElementKind.FIELD || + kind == ElementKind.GETTER || + kind == ElementKind.SETTER || + kind == ElementKind.TYPEDEF || + kind == ElementKind.LIBRARY) { + bool fixedName = false; + if (Elements.isInstanceField(element)) { + fixedName = element.hasFixedBackendName(); + } + String result = fixedName + ? guess + : getFreshName(guess, usedGlobalNames, suggestedGlobalNames, + ensureSafe: true); + globals[element] = result; + return result; + } + compiler.internalError(element, + 'getName for unknown kind: ${element.kind}.'); + return null; + } + } + + String getNameForRti(Element element) => getNameX(element); + + String getNameOfLibrary(LibraryElement library) => getNameX(library); + + String getNameOfClass(ClassElement cls) => getNameX(cls); + + String getNameOfField(VariableElement field) => getNameX(field); + + // TODO(ahe): Remove this method. Use get getNameOfMember instead. + String getNameOfInstanceMember(Element member) => getNameX(member); + + String getNameOfMember(Element member) => getNameX(member); + + String getNameOfGlobalField(VariableElement field) => getNameX(field); + + /// Returns true if [element] is stored on current isolate ('$'). We intend + /// to store only mutable static state in [currentIsolate], constants are + /// stored in 'C', and functions, accessors, classes, etc. are stored in one + /// of the other objects in [reservedGlobalObjectNames]. + bool isPropertyOfCurrentIsolate(Element element) { + // TODO(ahe): Make sure this method's documentation is always true and + // remove the word "intend". + return + // TODO(ahe): Re-write these tests to be positive (so it only returns + // true for static/top-level mutable fields). Right now, a number of + // other elements, such as bound closures also live in [currentIsolate]. + !element.isAccessor() && + !element.isClass() && + !element.isConstructor() && + !element.isFunction() && + !element.isLibrary(); + } + + /// Returns [currentIsolate] or one of [reservedGlobalObjectNames]. + String globalObjectFor(Element element) { + if (isPropertyOfCurrentIsolate(element)) return currentIsolate; + LibraryElement library = element.getLibrary(); + if (library == compiler.interceptorsLibrary) return 'J'; + if (library.isInternalLibrary) return 'H'; + if (library.isPlatformLibrary) { + if ('${library.canonicalUri}' == 'dart:html') return 'W'; + return 'P'; + } + return userGlobalObjects[ + library.getLibraryOrScriptName().hashCode % userGlobalObjects.length]; + } + + jsAst.PropertyAccess elementAccess(Element element) { + String name = getNameX(element); + return new jsAst.PropertyAccess.field( + new jsAst.VariableUse(globalObjectFor(element)), + name); + } + + String getLazyInitializerName(Element element) { + assert(Elements.isStaticOrTopLevelField(element)); + return getMappedGlobalName("$getterPrefix${getNameX(element)}"); + } + + String getStaticClosureName(Element element) { + assert(Elements.isStaticOrTopLevelFunction(element)); + return getMappedGlobalName("${getNameX(element)}\$closure"); + } + + String isolateAccess(Element element) { + return "${globalObjectFor(element)}.${getNameX(element)}"; + } + + String isolateLazyInitializerAccess(Element element) { + return "${globalObjectFor(element)}.${getLazyInitializerName(element)}"; + } + + String isolateStaticClosureAccess(Element element) { + return "${globalObjectFor(element)}.${getStaticClosureName(element)}()"; + } + + String globalObjectForConstant(Constant constant) => 'C'; + + String operatorIsPrefix() => r'$is'; + + String operatorAsPrefix() => r'$as'; + + String operatorSignature() => r'$signature'; + + String functionTypeTag() => r'func'; + + String functionTypeVoidReturnTag() => r'void'; + + String functionTypeReturnTypeTag() => r'ret'; + + String functionTypeRequiredParametersTag() => r'args'; + + String functionTypeOptionalParametersTag() => r'opt'; + + String functionTypeNamedParametersTag() => r'named'; + + Map functionTypeNameMap = + new Map(); + final FunctionTypeNamer functionTypeNamer; + + String getFunctionTypeName(FunctionType functionType) { + return functionTypeNameMap.putIfAbsent(functionType, () { + String proposedName = functionTypeNamer.computeName(functionType); + String freshName = getFreshName(proposedName, usedInstanceNames, + suggestedInstanceNames, ensureSafe: true); + return freshName; + }); + } + + String operatorIsType(DartType type) { + if (type.kind == TypeKind.FUNCTION) { + // TODO(erikcorry): Reduce from $isx to ix when we are minifying. + return '${operatorIsPrefix()}_${getFunctionTypeName(type)}'; + } + return operatorIs(type.element); + } + + String operatorIs(Element element) { + // TODO(erikcorry): Reduce from $isx to ix when we are minifying. + return '${operatorIsPrefix()}${getRuntimeTypeName(element)}'; + } + + /* + * Returns a name that does not clash with reserved JS keywords, + * and also ensures it won't clash with other identifiers. + */ + String _safeName(String name, Set reserved) { + if (reserved.contains(name) || name.startsWith(r'$')) { + name = '\$$name'; + } + assert(!reserved.contains(name)); + return name; + } + + String substitutionName(Element element) { + // TODO(ahe): Creating a string here is unfortunate. It is slow (due to + // string concatenation in the implementation), and may prevent + // segmentation of '$'. + return '${operatorAsPrefix()}${getNameForRti(element)}'; + } + + String safeName(String name) => _safeName(name, jsReserved); + String safeVariableName(String name) => _safeName(name, jsVariableReserved); + + String operatorNameToIdentifier(String name) { + if (name == null) return null; + if (name == '==') { + return r'$eq'; + } else if (name == '~') { + return r'$not'; + } else if (name == '[]') { + return r'$index'; + } else if (name == '[]=') { + return r'$indexSet'; + } else if (name == '*') { + return r'$mul'; + } else if (name == '/') { + return r'$div'; + } else if (name == '%') { + return r'$mod'; + } else if (name == '~/') { + return r'$tdiv'; + } else if (name == '+') { + return r'$add'; + } else if (name == '<<') { + return r'$shl'; + } else if (name == '>>') { + return r'$shr'; + } else if (name == '>=') { + return r'$ge'; + } else if (name == '>') { + return r'$gt'; + } else if (name == '<=') { + return r'$le'; + } else if (name == '<') { + return r'$lt'; + } else if (name == '&') { + return r'$and'; + } else if (name == '^') { + return r'$xor'; + } else if (name == '|') { + return r'$or'; + } else if (name == '-') { + return r'$sub'; + } else if (name == 'unary-') { + return r'$negate'; + } else { + return name; + } + } +} + +/** + * Generator of names for [Constant] values. + * + * The names are stable under perturbations of the source. The name is either a + * short sequence of words, if this can be found from the constant, or a type + * followed by a hash tag. + * + * List_imX // A List, with hash tag. + * C_Sentinel // const Sentinel(), "C_" added to avoid clash + * // with class name. + * JSInt_methods // an interceptor. + * Duration_16000 // const Duration(milliseconds: 16) + * EventKeyProvider_keyup // const EventKeyProvider('keyup') + * + */ +class ConstantNamingVisitor implements ConstantVisitor { + + static final RegExp IDENTIFIER = new RegExp(r'^[A-Za-z_$][A-Za-z0-9_$]*$'); + static const MAX_FRAGMENTS = 5; + static const MAX_EXTRA_LENGTH = 30; + static const DEFAULT_TAG_LENGTH = 3; + + final Compiler compiler; + final ConstantCanonicalHasher hasher; + + String root = null; // First word, usually a type name. + bool failed = false; // Failed to generate something pretty. + List fragments = []; + int length = 0; + + ConstantNamingVisitor(this.compiler, this.hasher); + + String getName(Constant constant) { + _visit(constant); + if (root == null) return 'CONSTANT'; + if (failed) return '${root}_${getHashTag(constant, DEFAULT_TAG_LENGTH)}'; + if (fragments.length == 1) return 'C_${root}'; + return fragments.join('_'); + } + + String getHashTag(Constant constant, int width) => + hashWord(hasher.getHash(constant), width); + + String hashWord(int hash, int length) { + hash &= 0x1fffffff; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < length; i++) { + int digit = hash % 62; + sb.write('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + [digit]); + hash ~/= 62; + if (hash == 0) break; + } + return sb.toString(); + } + + void addRoot(String fragment) { + if (root == null && fragments.isEmpty) { + root = fragment; + } + add(fragment); + } + + void add(String fragment) { + assert(fragment.length > 0); + fragments.add(fragment); + length += fragment.length; + if (fragments.length > MAX_FRAGMENTS) failed = true; + if (root != null && length > root.length + 1 + MAX_EXTRA_LENGTH) { + failed = true; + } + } + + void addIdentifier(String fragment) { + if (fragment.length <= MAX_EXTRA_LENGTH && IDENTIFIER.hasMatch(fragment)) { + add(fragment); + } else { + failed = true; + } + } + + _visit(Constant constant) { + return constant.accept(this); + } + + visitFunction(FunctionConstant constant) { + add(constant.element.name); + } + + visitNull(NullConstant constant) { + add('null'); + } + + visitInt(IntConstant constant) { + // No `addRoot` since IntConstants are always inlined. + if (constant.value < 0) { + add('m${-constant.value}'); + } else { + add('${constant.value}'); + } + } + + visitDouble(DoubleConstant constant) { + failed = true; + } + + visitTrue(TrueConstant constant) { + add('true'); + } + + visitFalse(FalseConstant constant) { + add('false'); + } + + visitString(StringConstant constant) { + // No `addRoot` since string constants are always inlined. + addIdentifier(constant.value.slowToString()); + } + + visitList(ListConstant constant) { + // TODO(9476): Incorporate type parameters into name. + addRoot('List'); + int length = constant.length; + if (constant.length == 0) { + add('empty'); + } else if (length >= MAX_FRAGMENTS) { + failed = true; + } else { + for (int i = 0; i < length; i++) { + _visit(constant.entries[i]); + if (failed) break; + } + } + } + + visitMap(MapConstant constant) { + // TODO(9476): Incorporate type parameters into name. + addRoot('Map'); + if (constant.length == 0) { + add('empty'); + } else { + // Using some bits from the keys hash tag groups the names Maps with the + // same structure. + add(getHashTag(constant.keys, 2) + getHashTag(constant, 3)); + } + } + + visitConstructed(ConstructedConstant constant) { + addRoot(constant.type.element.name); + for (int i = 0; i < constant.fields.length; i++) { + _visit(constant.fields[i]); + if (failed) return; + } + } + + visitType(TypeConstant constant) { + addRoot('Type'); + DartType type = constant.representedType; + JavaScriptBackend backend = compiler.backend; + String name = backend.rti.getRawTypeRepresentation(type); + addIdentifier(name); + } + + visitInterceptor(InterceptorConstant constant) { + addRoot(constant.dispatchedType.element.name); + add('methods'); + } + + visitDummy(DummyConstant constant) { + add('dummy_receiver'); + } +} + +/** + * Generates canonical hash values for [Constant]s. + * + * Unfortunately, [Constant.hashCode] is not stable under minor perturbations, + * so it can't be used for generating names. This hasher keeps consistency + * between runs by basing hash values of the names of elements, rather than + * their hashCodes. + */ +class ConstantCanonicalHasher implements ConstantVisitor { + + static const _MASK = 0x1fffffff; + static const _UINT32_LIMIT = 4 * 1024 * 1024 * 1024; + + + final Compiler compiler; + final Map hashes = new Map(); + + ConstantCanonicalHasher(this.compiler); + + int getHash(Constant constant) => _visit(constant); + + int _visit(Constant constant) { + int hash = hashes[constant]; + if (hash == null) { + hash = _finish(constant.accept(this)); + hashes[constant] = hash; + } + return hash; + } + + int visitNull(NullConstant constant) => 1; + int visitTrue(TrueConstant constant) => 2; + int visitFalse(FalseConstant constant) => 3; + + int visitFunction(FunctionConstant constant) { + return _hashString(1, constant.element.name); + } + + int visitInt(IntConstant constant) => _hashInt(constant.value); + + int visitDouble(DoubleConstant constant) => _hashDouble(constant.value); + + int visitString(StringConstant constant) { + return _hashString(2, constant.value.slowToString()); + } + + int visitList(ListConstant constant) { + return _hashList(constant.length, constant.entries); + } + + int visitMap(MapConstant constant) { + int hash = _visit(constant.keys); + return _hashList(hash, constant.values); + } + + int visitConstructed(ConstructedConstant constant) { + int hash = _hashString(3, constant.type.element.name); + for (int i = 0; i < constant.fields.length; i++) { + hash = _combine(hash, _visit(constant.fields[i])); + } + return hash; + } + + int visitType(TypeConstant constant) { + DartType type = constant.representedType; + JavaScriptBackend backend = compiler.backend; + String name = backend.rti.getRawTypeRepresentation(type); + return _hashString(4, name); + } + + visitInterceptor(InterceptorConstant constant) { + String typeName = constant.dispatchedType.element.name; + return _hashString(5, typeName); + } + + visitDummy(DummyConstant constant) { + compiler.internalError(NO_LOCATION_SPANNABLE, + 'DummyReceiverConstant should never be named and never be subconstant'); + } + + int _hashString(int hash, String s) { + int length = s.length; + hash = _combine(hash, length); + // Increasing stride is O(log N) on large strings which are unlikely to have + // many collisions. + for (int i = 0; i < length; i += 1 + (i >> 2)) { + hash = _combine(hash, s.codeUnitAt(i)); + } + return hash; + } + + int _hashList(int hash, List constants) { + for (Constant constant in constants) { + hash = _combine(hash, _visit(constant)); + } + return hash; + } + + static int _hashInt(int value) { + if (value.abs() < _UINT32_LIMIT) return _MASK & value; + return _hashDouble(value.toDouble()); + } + + static int _hashDouble(double value) { + double magnitude = value.abs(); + int sign = value < 0 ? 1 : 0; + if (magnitude < _UINT32_LIMIT) { // 2^32 + int intValue = value.toInt(); + // Integer valued doubles in 32-bit range hash to the same values as ints. + int hash = _hashInt(intValue); + if (value == intValue) return hash; + hash = _combine(hash, sign); + int fraction = ((magnitude - intValue.abs()) * (_MASK + 1)).toInt(); + hash = _combine(hash, fraction); + return hash; + } else if (value.isInfinite) { + return _combine(6, sign); + } else if (value.isNaN) { + return 7; + } else { + int hash = 0; + while (magnitude >= _UINT32_LIMIT) { + magnitude = magnitude / _UINT32_LIMIT; + hash++; + } + hash = _combine(hash, sign); + return _combine(hash, _hashDouble(magnitude)); + } + } + + /** + * [_combine] and [_finish] are parts of the [Jenkins hash function][1], + * modified by using masking to keep values in SMI range. + * + * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function + */ + static int _combine(int hash, int value) { + hash = _MASK & (hash + value); + hash = _MASK & (hash + (((_MASK >> 10) & hash) << 10)); + hash = hash ^ (hash >> 6); + return hash; + } + + static int _finish(int hash) { + hash = _MASK & (hash + (((_MASK >> 3) & hash) << 3)); + hash = hash & (hash >> 11); + return _MASK & (hash + (((_MASK >> 15) & hash) << 15)); + } +} + +class FunctionTypeNamer extends DartTypeVisitor { + final Compiler compiler; + StringBuffer sb; + + FunctionTypeNamer(this.compiler); + + JavaScriptBackend get backend => compiler.backend; + + String computeName(DartType type) { + sb = new StringBuffer(); + visit(type); + return sb.toString(); + } + + visit(DartType type) { + type.accept(this, null); + } + + visitType(DartType type, _) { + sb.write(type.name); + } + + visitFunctionType(FunctionType type, _) { + if (backend.rti.isSimpleFunctionType(type)) { + sb.write('args${type.parameterTypes.slowLength()}'); + return; + } + visit(type.returnType); + sb.write('_'); + for (Link link = type.parameterTypes; + !link.isEmpty; + link = link.tail) { + sb.write('_'); + visit(link.head); + } + bool first = false; + for (Link link = type.optionalParameterTypes; + !link.isEmpty; + link = link.tail) { + if (!first) { + sb.write('_'); + } + sb.write('_'); + visit(link.head); + first = true; + } + if (!type.namedParameterTypes.isEmpty) { + first = false; + for (Link link = type.namedParameterTypes; + !link.isEmpty; + link = link.tail) { + if (!first) { + sb.write('_'); + } + sb.write('_'); + visit(link.head); + first = true; + } + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart new file mode 100644 index 0000000..ad0e1ff --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart @@ -0,0 +1,519 @@ +// Copyright (c) 2012, 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. + +part of js_backend; + +class NativeEmitter { + + CodeEmitterTask emitter; + CodeBuffer nativeBuffer; + + // Native classes found in the application. + Set nativeClasses = new Set(); + + // Caches the native subtypes of a native class. + Map> subtypes; + + // Caches the direct native subtypes of a native class. + Map> directSubtypes; + + // Caches the methods that have a native body. + Set nativeMethods; + + // Do we need the native emitter to take care of handling + // noSuchMethod for us? This flag is set to true in the emitter if + // it finds any native class that needs noSuchMethod handling. + bool handleNoSuchMethod = false; + + NativeEmitter(this.emitter) + : subtypes = new Map>(), + directSubtypes = new Map>(), + nativeMethods = new Set(), + nativeBuffer = new CodeBuffer(); + + Compiler get compiler => emitter.compiler; + JavaScriptBackend get backend => compiler.backend; + + String get _ => emitter.space; + String get n => emitter.n; + String get N => emitter.N; + + String get dynamicName { + Element element = compiler.findHelper('dynamicFunction'); + return backend.namer.isolateAccess(element); + } + + String get dynamicFunctionTableName { + Element element = compiler.findHelper('dynamicFunctionTable'); + return backend.namer.isolateAccess(element); + } + + String get typeNameOfName { + Element element = compiler.findHelper('getTypeNameOf'); + return backend.namer.isolateAccess(element); + } + + String get defPropName { + Element element = compiler.findHelper('defineProperty'); + return backend.namer.isolateAccess(element); + } + + String get toStringHelperName { + Element element = compiler.findHelper('toStringForNativeObject'); + return backend.namer.isolateAccess(element); + } + + String get hashCodeHelperName { + Element element = compiler.findHelper('hashCodeForNativeObject'); + return backend.namer.isolateAccess(element); + } + + String get dispatchPropertyNameVariable { + Element element = compiler.findInterceptor('dispatchPropertyName'); + return backend.namer.isolateAccess(element); + } + + // The tags string contains comma-separated 'words' which are either dispatch + // tags (having JavaScript identifier syntax) and directives that begin with + // `!`. + List nativeTagsOfClassRaw(ClassElement cls) { + String quotedName = cls.nativeTagInfo; + return quotedName.substring(1, quotedName.length - 1).split(','); + } + + List nativeTagsOfClass(ClassElement cls) { + return nativeTagsOfClassRaw(cls).where((s) => !s.startsWith('!')).toList(); + } + + bool nativeHasTagsMarker(ClassElement cls, String marker) { + return nativeTagsOfClassRaw(cls).contains(marker); + } + + bool nativeForcedNonLeaf(ClassElement cls) => + nativeHasTagsMarker(cls, '!nonleaf'); + + /** + * Writes the class definitions for the interceptors to [mainBuffer]. + * Writes code to associate dispatch tags with interceptors to [nativeBuffer]. + * + * The interceptors are filtered to avoid emitting trivial interceptors. For + * example, if the program contains no code that can distinguish between the + * numerous subclasses of `Element` then we can pretend that `Element` is a + * leaf class, and all instances of subclasses of `Element` are instances of + * `Element`. + * + * There is also a performance benefit (in addition to the obvious code size + * benefit), due to how [getNativeInterceptor] works. Finding the interceptor + * of a leaf class in the hierarchy is more efficient that a non-leaf, so it + * improves performance when more classes can be treated as leaves. + * + * [classes] contains native classes, mixin applications, and user subclasses + * of native classes. ONLY the native classes are generated here. [classes] + * is sorted in desired output order. + * + * [additionalProperties] is used to collect properties that are pushed up + * from the above optimizations onto a non-native class, e.g, `Interceptor`. + */ + void generateNativeClasses( + List classes, + CodeBuffer mainBuffer, + Map> additionalProperties) { + // Compute a pre-order traversal of the subclass forest. We actually want a + // post-order traversal but it is easier to compute the pre-order and use it + // in reverse. + + List preOrder = []; + Set seen = new Set(); + seen..add(compiler.objectClass) + ..add(backend.jsInterceptorClass); + void walk(ClassElement element) { + if (seen.contains(element)) return; + seen.add(element); + walk(element.superclass); + preOrder.add(element); + } + classes.forEach(walk); + + // Generate code for each native class into [ClassBuilder]s. + + Map builders = + new Map(); + for (ClassElement classElement in classes) { + if (classElement.isNative()) { + ClassBuilder builder = generateNativeClass(classElement); + builders[classElement] = builder; + } + } + + // Find which classes are needed and which are non-leaf classes. Any class + // that is not needed can be treated as a leaf class equivalent to some + // needed class. + + Set neededClasses = new Set(); + Set nonleafClasses = new Set(); + + Map> extensionPoints = + computeExtensionPoints(preOrder); + + neededClasses.add(compiler.objectClass); + + Set neededByConstant = + emitter.interceptorEmitter.interceptorsReferencedFromConstants(); + Set modifiedClasses = + emitter.typeTestEmitter.classesModifiedByEmitRuntimeTypeSupport(); + + for (ClassElement classElement in preOrder.reversed) { + // Post-order traversal ensures we visit the subclasses before their + // superclass. This makes it easy to tell if a class is needed because a + // subclass is needed. + ClassBuilder builder = builders[classElement]; + bool needed = false; + if (builder == null) { + // Mixin applications (native+mixin) are non-native, so [classElement] + // has already been emitted as a regular class. Mark [classElement] as + // 'needed' to ensure the native superclass is needed. + needed = true; + } else if (!builder.isTrivial) { + needed = true; + } else if (neededByConstant.contains(classElement)) { + needed = true; + } else if (modifiedClasses.contains(classElement)) { + // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer + // adds information to a class prototype or constructor. + needed = true; + } else if (extensionPoints.containsKey(classElement)) { + needed = true; + } + if (classElement.isNative() && nativeForcedNonLeaf(classElement)) { + needed = true; + nonleafClasses.add(classElement); + } + + if (needed || neededClasses.contains(classElement)) { + neededClasses.add(classElement); + neededClasses.add(classElement.superclass); + nonleafClasses.add(classElement.superclass); + } + } + + // Collect all the tags that map to each native class. + + Map> leafTags = + new Map>(); + Map> nonleafTags = + new Map>(); + + for (ClassElement classElement in classes) { + if (!classElement.isNative()) continue; + List nativeTags = nativeTagsOfClass(classElement); + + if (nonleafClasses.contains(classElement) || + extensionPoints.containsKey(classElement)) { + nonleafTags + .putIfAbsent(classElement, () => new Set()) + .addAll(nativeTags); + } else { + ClassElement sufficingInterceptor = classElement; + while (!neededClasses.contains(sufficingInterceptor)) { + sufficingInterceptor = sufficingInterceptor.superclass; + } + if (sufficingInterceptor == compiler.objectClass) { + sufficingInterceptor = backend.jsInterceptorClass; + } + leafTags + .putIfAbsent(sufficingInterceptor, () => new Set()) + .addAll(nativeTags); + } + } + + // Add properties containing the information needed to construct maps used + // by getNativeInterceptor and custom elements. + if (compiler.enqueuer.codegen.nativeEnqueuer + .hasInstantiatedNativeClasses()) { + void generateClassInfo(ClassElement classElement) { + // Property has the form: + // + // "%": "leafTag1|leafTag2|...;nonleafTag1|...;Class1|Class2|...", + // + // If there is no data following a semicolon, the semicolon can be + // omitted. + + String formatTags(Iterable tags) { + if (tags == null) return ''; + return (tags.toList()..sort()).join('|'); + } + + List extensions = extensionPoints[classElement]; + + String leafStr = formatTags(leafTags[classElement]); + String nonleafStr = formatTags(nonleafTags[classElement]); + + StringBuffer sb = new StringBuffer(leafStr); + if (nonleafStr != '') { + sb..write(';')..write(nonleafStr); + } + if (extensions != null) { + sb..write(';') + ..writeAll(extensions.map(backend.namer.getNameOfClass), '|'); + } + String encoding = sb.toString(); + + ClassBuilder builder = builders[classElement]; + if (builder == null) { + // No builder because this is an intermediate mixin application or + // Interceptor - these are not direct native classes. + if (encoding != '') { + Map properties = + additionalProperties.putIfAbsent(classElement, + () => new LinkedHashMap()); + properties[backend.namer.nativeSpecProperty] = js.string(encoding); + } + } else { + builder.addProperty( + backend.namer.nativeSpecProperty, js.string(encoding)); + } + } + generateClassInfo(backend.jsInterceptorClass); + for (ClassElement classElement in classes) { + generateClassInfo(classElement); + } + } + + // Emit the native class interceptors that were actually used. + for (ClassElement classElement in classes) { + if (!classElement.isNative()) continue; + if (neededClasses.contains(classElement)) { + // Define interceptor class for [classElement]. + emitter.classEmitter.emitClassBuilderWithReflectionData( + backend.namer.getNameOfClass(classElement), + classElement, builders[classElement], + emitter.getElementDecriptor(classElement)); + emitter.needsDefineClass = true; + } + } + } + + /** + * Computes the native classes that are extended (subclassed) by non-native + * classes and the set non-mative classes that extend them. (A List is used + * instead of a Set for out stability). + */ + Map> computeExtensionPoints( + List classes) { + ClassElement nativeSuperclassOf(ClassElement element) { + if (element == null) return null; + if (element.isNative()) return element; + return nativeSuperclassOf(element.superclass); + } + + ClassElement nativeAncestorOf(ClassElement element) { + return nativeSuperclassOf(element.superclass); + } + + Map> map = + new Map>(); + + for (ClassElement classElement in classes) { + if (classElement.isNative()) continue; + ClassElement nativeAncestor = nativeAncestorOf(classElement); + if (nativeAncestor != null) { + map + .putIfAbsent(nativeAncestor, () => []) + .add(classElement); + } + } + return map; + } + + ClassBuilder generateNativeClass(ClassElement classElement) { + // TODO(sra): Issue #13731- this is commented out as part of custom element + // constructor work. + //assert(!classElement.hasBackendMembers); + nativeClasses.add(classElement); + + ClassElement superclass = classElement.superclass; + assert(superclass != null); + // Fix superclass. TODO(sra): make native classes inherit from Interceptor. + assert(superclass != compiler.objectClass); + if (superclass == compiler.objectClass) { + superclass = backend.jsInterceptorClass; + } + + String superName = backend.namer.getNameOfClass(superclass); + + ClassBuilder builder = new ClassBuilder(backend.namer); + emitter.classEmitter.emitClassConstructor(classElement, builder, null); + bool hasFields = emitter.classEmitter.emitFields( + classElement, builder, superName, classIsNative: true); + int propertyCount = builder.properties.length; + emitter.classEmitter.emitClassGettersSetters(classElement, builder); + emitter.classEmitter.emitInstanceMembers(classElement, builder); + emitter.typeTestEmitter.emitIsTests(classElement, builder); + + if (!hasFields && + builder.properties.length == propertyCount && + superclass is! MixinApplicationElement) { + builder.isTrivial = true; + } + + return builder; + } + + void finishGenerateNativeClasses() { + // TODO(sra): Put specialized version of getNativeMethods on + // `Object.prototype` to avoid checking in `getInterceptor` and + // specializations. + } + + void potentiallyConvertDartClosuresToJs( + List statements, + FunctionElement member, + List stubParameters) { + FunctionSignature parameters = member.functionSignature; + Element converter = + compiler.findHelper('convertDartClosureToJS'); + String closureConverter = backend.namer.isolateAccess(converter); + Set stubParameterNames = new Set.from( + stubParameters.map((param) => param.name)); + parameters.forEachParameter((ParameterElement parameter) { + String name = parameter.name; + // If [name] is not in [stubParameters], then the parameter is an optional + // parameter that was not provided for this stub. + for (jsAst.Parameter stubParameter in stubParameters) { + if (stubParameter.name == name) { + DartType type = parameter.type.unalias(compiler); + if (type is FunctionType) { + // The parameter type is a function type either directly or through + // typedef(s). + FunctionType functionType = type; + int arity = functionType.computeArity(); + statements.add( + js('$name = $closureConverter($name, $arity)').toStatement()); + break; + } + } + } + }); + } + + List generateParameterStubStatements( + Element member, + bool isInterceptedMethod, + String invocationName, + List stubParameters, + List argumentsBuffer, + int indexOfLastOptionalArgumentInParameters) { + // The target JS function may check arguments.length so we need to + // make sure not to pass any unspecified optional arguments to it. + // For example, for the following Dart method: + // foo([x, y, z]); + // The call: + // foo(y: 1) + // must be turned into a JS call to: + // foo(null, y). + + ClassElement classElement = member.enclosingElement; + + List statements = []; + potentiallyConvertDartClosuresToJs(statements, member, stubParameters); + + String target; + jsAst.Expression receiver; + List arguments; + + assert(invariant(member, nativeMethods.contains(member))); + // When calling a JS method, we call it with the native name, and only the + // arguments up until the last one provided. + target = member.fixedBackendName(); + + if (isInterceptedMethod) { + receiver = argumentsBuffer[0]; + arguments = argumentsBuffer.sublist(1, + indexOfLastOptionalArgumentInParameters + 1); + } else { + receiver = js('this'); + arguments = argumentsBuffer.sublist(0, + indexOfLastOptionalArgumentInParameters + 1); + } + statements.add(new jsAst.Return(receiver[target](arguments))); + + return statements; + } + + bool isSupertypeOfNativeClass(Element element) { + if (element.isTypeVariable()) { + compiler.internalError(element, "Is check for type variable."); + return false; + } + if (element.computeType(compiler).unalias(compiler) is FunctionType) { + // The element type is a function type either directly or through + // typedef(s). + return false; + } + + if (!element.isClass()) { + compiler.internalError(element, "Is check does not handle element."); + return false; + } + + if (backend.classesMixedIntoInterceptedClasses.contains(element)) { + return true; + } + + return subtypes[element] != null; + } + + bool requiresNativeIsCheck(Element element) { + // TODO(sra): Remove this function. It determines if a native type may + // satisfy a check against [element], in which case an interceptor must be + // used. We should also use an interceptor if the check can't be satisfied + // by a native class in case we get a native instance that tries to spoof + // the type info. i.e the criteria for whether or not to use an interceptor + // is whether the receiver can be native, not the type of the test. + if (!element.isClass()) return false; + ClassElement cls = element; + if (Elements.isNativeOrExtendsNative(cls)) return true; + return isSupertypeOfNativeClass(element); + } + + void assembleCode(CodeBuffer targetBuffer) { + List objectProperties = []; + + void addProperty(String name, jsAst.Expression value) { + objectProperties.add(new jsAst.Property(js.string(name), value)); + } + + if (!nativeClasses.isEmpty) { + // If the native emitter has been asked to take care of the + // noSuchMethod handlers, we do that now. + if (handleNoSuchMethod) { + emitter.nsmEmitter.emitNoSuchMethodHandlers(addProperty); + } + } + + // If we have any properties to add to Object.prototype, we run + // through them and add them using defineProperty. + if (!objectProperties.isEmpty) { + jsAst.Expression init = + js.fun(['table'], + new jsAst.ForIn( + new jsAst.VariableDeclarationList( + [new jsAst.VariableInitialization( + new jsAst.VariableDeclaration('key'), + null)]), + js('table'), + new jsAst.ExpressionStatement( + js('$defPropName(Object.prototype, key, table[key])'))))( + new jsAst.ObjectInitializer(objectProperties)); + + if (emitter.compiler.enableMinification) targetBuffer.add(';'); + targetBuffer.add(jsAst.prettyPrint( + new jsAst.ExpressionStatement(init), compiler)); + targetBuffer.add('\n'); + } + + targetBuffer.add(nativeBuffer); + targetBuffer.add('\n'); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart new file mode 100644 index 0000000..db58f56 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart @@ -0,0 +1,897 @@ +// Copyright (c) 2013, 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. + +part of js_backend; + +/// For each class, stores the possible class subtype tests that could succeed. +abstract class TypeChecks { + /// Get the set of checks required for class [element]. + Iterable operator[](ClassElement element); + /// Get the iterator for all classes that need type checks. + Iterator get iterator; +} + +typedef jsAst.Expression OnVariableCallback(TypeVariableType variable); + +class RuntimeTypes { + final Compiler compiler; + final TypeRepresentationGenerator representationGenerator; + + final Map> rtiDependencies; + final Set classesNeedingRti; + final Set methodsNeedingRti; + // The set of classes that use one of their type variables as expressions + // to get the runtime type. + final Set classesUsingTypeVariableExpression; + // The set of type arguments tested against type variable bounds. + final Set checkedTypeArguments; + // The set of tested type variable bounds. + final Set checkedBounds; + + JavaScriptBackend get backend => compiler.backend; + + RuntimeTypes(Compiler compiler) + : this.compiler = compiler, + representationGenerator = new TypeRepresentationGenerator(compiler), + classesNeedingRti = new Set(), + methodsNeedingRti = new Set(), + rtiDependencies = new Map>(), + classesUsingTypeVariableExpression = new Set(), + checkedTypeArguments = new Set(), + checkedBounds = new Set(); + + Set directlyInstantiatedArguments; + Set allInstantiatedArguments; + Set checkedArguments; + + bool isJsNative(Element element) { + return (element == compiler.intClass || + element == compiler.boolClass || + element == compiler.numClass || + element == compiler.doubleClass || + element == compiler.stringClass || + element == compiler.listClass); + } + + void registerRtiDependency(Element element, Element dependency) { + // We're not dealing with typedef for now. + if (!element.isClass() || !dependency.isClass()) return; + Set classes = + rtiDependencies.putIfAbsent(element, () => new Set()); + classes.add(dependency); + } + + void registerTypeVariableBoundsSubtypeCheck(DartType typeArgument, + DartType bound) { + checkedTypeArguments.add(typeArgument); + checkedBounds.add(bound); + } + + bool usingFactoryWithTypeArguments = false; + + /** + * Compute type arguments of classes that use one of their type variables in + * is-checks and add the is-checks that they imply. + * + * This function must be called after all is-checks have been registered. + * + * TODO(karlklose): move these computations into a function producing an + * immutable datastructure. + */ + void addImplicitChecks(Universe universe, + Iterable classesUsingChecks) { + // If there are no classes that use their variables in checks, there is + // nothing to do. + if (classesUsingChecks.isEmpty) return; + Set instantiatedTypes = universe.instantiatedTypes; + if (universe.usingFactoryWithTypeArguments) { + for (DartType type in instantiatedTypes) { + if (type.kind != TypeKind.INTERFACE) continue; + InterfaceType interface = type; + do { + for (DartType argument in interface.typeArguments) { + universe.registerIsCheck(argument, compiler); + } + interface = interface.element.supertype; + } while (interface != null && !instantiatedTypes.contains(interface)); + } + } else { + // Find all instantiated types that are a subtype of a class that uses + // one of its type arguments in an is-check and add the arguments to the + // set of is-checks. + // TODO(karlklose): replace this with code that uses a subtype lookup + // datastructure in the world. + for (DartType type in instantiatedTypes) { + if (type.kind != TypeKind.INTERFACE) continue; + InterfaceType classType = type; + for (ClassElement cls in classesUsingChecks) { + InterfaceType current = classType; + do { + // We need the type as instance of its superclass anyway, so we just + // try to compute the substitution; if the result is [:null:], the + // classes are not related. + InterfaceType instance = current.asInstanceOf(cls); + if (instance == null) break; + for (DartType argument in instance.typeArguments) { + universe.registerIsCheck(argument, compiler); + } + current = current.element.supertype; + } while (current != null && !instantiatedTypes.contains(current)); + } + } + } + } + + void computeClassesNeedingRti() { + // Find the classes that need runtime type information. Such + // classes are: + // (1) used in a is check with type variables, + // (2) dependencies of classes in (1), + // (3) subclasses of (2) and (3). + void potentiallyAddForRti(ClassElement cls) { + assert(invariant(cls, cls.isDeclaration)); + if (cls.typeVariables.isEmpty) return; + if (classesNeedingRti.contains(cls)) return; + classesNeedingRti.add(cls); + + // TODO(ngeoffray): This should use subclasses, not subtypes. + Set classes = compiler.world.subtypesOf(cls); + if (classes != null) { + classes.forEach((ClassElement sub) { + potentiallyAddForRti(sub); + }); + } + + Set dependencies = rtiDependencies[cls]; + if (dependencies != null) { + dependencies.forEach((ClassElement other) { + potentiallyAddForRti(other); + }); + } + } + + Set classesUsingTypeVariableTests = new Set(); + compiler.resolverWorld.isChecks.forEach((DartType type) { + if (type.kind == TypeKind.TYPE_VARIABLE) { + TypeVariableElement variable = type.element; + classesUsingTypeVariableTests.add(variable.enclosingElement); + } + }); + // Add is-checks that result from classes using type variables in checks. + addImplicitChecks(compiler.resolverWorld, classesUsingTypeVariableTests); + // Add the rti dependencies that are implicit in the way the backend + // generates code: when we create a new [List], we actually create + // a JSArray in the backend and we need to add type arguments to + // the calls of the list constructor whenever we determine that + // JSArray needs type arguments. + // TODO(karlklose): make this dependency visible from code. + if (backend.jsArrayClass != null) { + registerRtiDependency(backend.jsArrayClass, compiler.listClass); + } + // Compute the set of all classes and methods that need runtime type + // information. + compiler.resolverWorld.isChecks.forEach((DartType type) { + if (type.kind == TypeKind.INTERFACE) { + InterfaceType itf = type; + if (!itf.treatAsRaw) { + potentiallyAddForRti(itf.element); + } + } else { + ClassElement contextClass = Types.getClassContext(type); + if (contextClass != null) { + // [type] contains type variables (declared in [contextClass]) if + // [contextClass] is non-null. This handles checks against type + // variables and function types containing type variables. + potentiallyAddForRti(contextClass); + } + if (type.kind == TypeKind.FUNCTION) { + void analyzeMethod(TypedElement method) { + DartType memberType = method.type; + ClassElement contextClass = Types.getClassContext(memberType); + if (contextClass != null && + compiler.types.isPotentialSubtype(memberType, type)) { + potentiallyAddForRti(contextClass); + methodsNeedingRti.add(method); + } + } + compiler.resolverWorld.genericClosures.forEach(analyzeMethod); + compiler.resolverWorld.genericCallMethods.forEach(analyzeMethod); + } + } + }); + if (compiler.enableTypeAssertions) { + void analyzeMethod(TypedElement method) { + DartType memberType = method.type; + ClassElement contextClass = Types.getClassContext(memberType); + if (contextClass != null) { + potentiallyAddForRti(contextClass); + methodsNeedingRti.add(method); + } + } + compiler.resolverWorld.genericClosures.forEach(analyzeMethod); + compiler.resolverWorld.genericCallMethods.forEach(analyzeMethod); + } + // Add the classes that need RTI because they use a type variable as + // expression. + classesUsingTypeVariableExpression.forEach(potentiallyAddForRti); + } + + TypeChecks cachedRequiredChecks; + + TypeChecks get requiredChecks { + if (cachedRequiredChecks == null) { + computeRequiredChecks(); + } + assert(cachedRequiredChecks != null); + return cachedRequiredChecks; + } + + /// Compute the required type checkes and substitutions for the given + /// instantitated and checked classes. + TypeChecks computeChecks(Set instantiated, + Set checked) { + // Run through the combination of instantiated and checked + // arguments and record all combination where the element of a checked + // argument is a superclass of the element of an instantiated type. + TypeCheckMapping result = new TypeCheckMapping(); + for (ClassElement element in instantiated) { + if (element == compiler.dynamicClass) continue; + if (checked.contains(element)) { + result.add(element, element, null); + } + // Find all supertypes of [element] in [checkedArguments] and add checks + // and precompute the substitutions for them. + assert(invariant(element, element.allSupertypes != null, + message: 'Supertypes have not been computed for $element.')); + for (DartType supertype in element.allSupertypes) { + ClassElement superelement = supertype.element; + if (checked.contains(superelement)) { + Substitution substitution = + computeSubstitution(element, superelement); + result.add(element, superelement, substitution); + } + } + } + return result; + } + + void computeRequiredChecks() { + Set isChecks = compiler.codegenWorld.isChecks; + bool hasFunctionTypeCheck = + isChecks.any((type) => identical(type.kind, TypeKind.FUNCTION)); + Set instantiatedTypesAndClosures = hasFunctionTypeCheck + ? computeInstantiatedTypesAndClosures(compiler.codegenWorld) + : compiler.codegenWorld.instantiatedTypes; + computeInstantiatedArguments(instantiatedTypesAndClosures, isChecks); + computeCheckedArguments(instantiatedTypesAndClosures, isChecks); + cachedRequiredChecks = + computeChecks(allInstantiatedArguments, checkedArguments); + } + + Set computeInstantiatedTypesAndClosures(Universe universe) { + Set instantiatedTypes = + new Set.from(universe.instantiatedTypes); + for (DartType instantiatedType in universe.instantiatedTypes) { + if (instantiatedType.kind == TypeKind.INTERFACE) { + InterfaceType interface = instantiatedType; + FunctionType callType = interface.callType; + if (callType != null) { + instantiatedTypes.add(callType); + } + } + } + for (FunctionElement element in universe.staticFunctionsNeedingGetter) { + instantiatedTypes.add(element.type); + } + // TODO(johnniwinther): We should get this information through the + // [neededClasses] computed in the emitter instead of storing it and pulling + // it from resolution, but currently it would introduce a cyclic dependency + // between [computeRequiredChecks] and [computeNeededClasses]. + for (TypedElement element in compiler.resolverWorld.closurizedMembers) { + instantiatedTypes.add(element.type); + } + return instantiatedTypes; + } + + /** + * Collects all types used in type arguments of instantiated types. + * + * This includes type arguments used in supertype relations, because we may + * have a type check against this supertype that includes a check against + * the type arguments. + */ + void computeInstantiatedArguments(Set instantiatedTypes, + Set isChecks) { + ArgumentCollector superCollector = new ArgumentCollector(backend); + ArgumentCollector directCollector = new ArgumentCollector(backend); + FunctionArgumentCollector functionArgumentCollector = + new FunctionArgumentCollector(backend); + + // We need to add classes occuring in function type arguments, like for + // instance 'I' for [: o is C :] where f is [: typedef I f(); :]. + void collectFunctionTypeArguments(Iterable types) { + for (DartType type in types) { + functionArgumentCollector.collect(type); + } + } + collectFunctionTypeArguments(isChecks); + collectFunctionTypeArguments(checkedBounds); + + void collectTypeArguments(Iterable types, + {bool isTypeArgument: false}) { + for (DartType type in types) { + directCollector.collect(type, isTypeArgument: isTypeArgument); + if (type.kind == TypeKind.INTERFACE) { + ClassElement cls = type.element; + for (DartType supertype in cls.allSupertypes) { + superCollector.collect(supertype, isTypeArgument: isTypeArgument); + } + } + } + } + collectTypeArguments(instantiatedTypes); + collectTypeArguments(checkedTypeArguments, isTypeArgument: true); + + for (ClassElement cls in superCollector.classes.toList()) { + for (DartType supertype in cls.allSupertypes) { + superCollector.collect(supertype); + } + } + + directlyInstantiatedArguments = + directCollector.classes..addAll(functionArgumentCollector.classes); + allInstantiatedArguments = + superCollector.classes..addAll(directlyInstantiatedArguments); + } + + /// Collects all type arguments used in is-checks. + void computeCheckedArguments(Set instantiatedTypes, + Set isChecks) { + ArgumentCollector collector = new ArgumentCollector(backend); + FunctionArgumentCollector functionArgumentCollector = + new FunctionArgumentCollector(backend); + + // We need to add types occuring in function type arguments, like for + // instance 'J' for [: (J j) {} is f :] where f is + // [: typedef void f(I i); :] and 'J' is a subtype of 'I'. + void collectFunctionTypeArguments(Iterable types) { + for (DartType type in types) { + functionArgumentCollector.collect(type); + } + } + collectFunctionTypeArguments(instantiatedTypes); + collectFunctionTypeArguments(checkedTypeArguments); + + void collectTypeArguments(Iterable types, + {bool isTypeArgument: false}) { + for (DartType type in types) { + collector.collect(type, isTypeArgument: isTypeArgument); + } + } + collectTypeArguments(isChecks); + collectTypeArguments(checkedBounds, isTypeArgument: true); + + checkedArguments = + collector.classes..addAll(functionArgumentCollector.classes); + } + + Set getClassesUsedInSubstitutions(JavaScriptBackend backend, + TypeChecks checks) { + Set instantiated = new Set(); + ArgumentCollector collector = new ArgumentCollector(backend); + for (ClassElement target in checks) { + instantiated.add(target); + for (TypeCheck check in checks[target]) { + Substitution substitution = check.substitution; + if (substitution != null) { + collector.collectAll(substitution.arguments); + } + } + } + return instantiated..addAll(collector.classes); + } + + Set getRequiredArgumentClasses(JavaScriptBackend backend) { + Set requiredArgumentClasses = + new Set.from( + getClassesUsedInSubstitutions(backend, requiredChecks)); + return requiredArgumentClasses + ..addAll(directlyInstantiatedArguments) + ..addAll(checkedArguments); + } + + /// Return the unique name for the element as an unquoted string. + String getJsName(Element element) { + JavaScriptBackend backend = compiler.backend; + Namer namer = backend.namer; + return namer.isolateAccess(element); + } + + String getRawTypeRepresentation(DartType type) { + String name = getJsName(type.element); + if (!type.element.isClass()) return name; + InterfaceType interface = type; + Link variables = interface.element.typeVariables; + if (variables.isEmpty) return name; + String arguments = + new List.filled(variables.slowLength(), 'dynamic').join(', '); + return '$name<$arguments>'; + } + + // TODO(karlklose): maybe precompute this value and store it in typeChecks? + bool isTrivialSubstitution(ClassElement cls, ClassElement check) { + if (cls.isClosure()) { + // TODO(karlklose): handle closures. + return true; + } + + // If there are no type variables or the type is the same, we do not need + // a substitution. + if (check.typeVariables.isEmpty || cls == check) { + return true; + } + + InterfaceType originalType = cls.thisType; + InterfaceType type = originalType.asInstanceOf(check); + // [type] is not a subtype of [check]. we do not generate a check and do not + // need a substitution. + if (type == null) return true; + + // Run through both lists of type variables and check if the type variables + // are identical at each position. If they are not, we need to calculate a + // substitution function. + Link variables = cls.typeVariables; + Link arguments = type.typeArguments; + while (!variables.isEmpty && !arguments.isEmpty) { + if (variables.head.element != arguments.head.element) { + return false; + } + variables = variables.tail; + arguments = arguments.tail; + } + return (variables.isEmpty == arguments.isEmpty); + } + + /** + * Compute a JavaScript expression that describes the necessary substitution + * for type arguments in a subtype test. + * + * The result can be: + * 1) [:null:], if no substituted check is necessary, because the + * type variables are the same or there are no type variables in the class + * that is checked for. + * 2) A list expression describing the type arguments to be used in the + * subtype check, if the type arguments to be used in the check do not + * depend on the type arguments of the object. + * 3) A function mapping the type variables of the object to be checked to + * a list expression. + */ + jsAst.Expression getSupertypeSubstitution( + ClassElement cls, + ClassElement check, + {bool alwaysGenerateFunction: false}) { + Substitution substitution = getSubstitution(cls, check); + if (substitution != null) { + return substitution.getCode(this, alwaysGenerateFunction); + } else { + return null; + } + } + + Substitution getSubstitution(ClassElement cls, ClassElement other) { + // Look for a precomputed check. + for (TypeCheck check in cachedRequiredChecks[cls]) { + if (check.cls == other) { + return check.substitution; + } + } + // There is no precomputed check for this pair (because the check is not + // done on type arguments only. Compute a new substitution. + return computeSubstitution(cls, other); + } + + Substitution computeSubstitution(ClassElement cls, ClassElement check, + { bool alwaysGenerateFunction: false }) { + if (isTrivialSubstitution(cls, check)) return null; + + // Unnamed mixin application classes do not need substitutions, because they + // are never instantiated and their checks are overwritten by the class that + // they are mixed into. + InterfaceType type = cls.thisType; + InterfaceType target = type.asInstanceOf(check); + Link typeVariables = cls.typeVariables; + if (typeVariables.isEmpty && !alwaysGenerateFunction) { + return new Substitution.list(target.typeArguments); + } else { + return new Substitution.function(target.typeArguments, typeVariables); + } + } + + jsAst.Expression getSubstitutionRepresentation( + Link types, + OnVariableCallback onVariable) { + List elements = []; + int index = 0; + for (; !types.isEmpty; types = types.tail, index++) { + jsAst.Expression representation = + getTypeRepresentation(types.head, onVariable); + elements.add(new jsAst.ArrayElement(index, representation)); + } + return new jsAst.ArrayInitializer(index, elements); + } + + jsAst.Expression getTypeEncoding(DartType type, + {bool alwaysGenerateFunction: false}) { + ClassElement contextClass = Types.getClassContext(type); + jsAst.Expression onVariable(TypeVariableType v) { + return new jsAst.VariableUse(v.name); + }; + jsAst.Expression encoding = getTypeRepresentation(type, onVariable); + if (contextClass == null && !alwaysGenerateFunction) { + return encoding; + } else { + List parameters = const []; + if (contextClass != null) { + parameters = contextClass.typeVariables.toList().map((type) { + return type.toString(); + }).toList(); + } + return js.fun(parameters, js.return_(encoding)); + } + } + + jsAst.Expression getSignatureEncoding(DartType type, jsAst.Expression this_) { + ClassElement contextClass = Types.getClassContext(type); + jsAst.Expression encoding = + getTypeEncoding(type, alwaysGenerateFunction: true); + if (contextClass != null) { + JavaScriptBackend backend = compiler.backend; + String contextName = backend.namer.getNameOfClass(contextClass); + List arguments = + [encoding, this_, js.string(contextName)]; + return js.fun([], js.return_( + new jsAst.Call( + backend.namer.elementAccess(backend.getComputeSignature()), + arguments))); + } else { + return encoding; + } + } + + String getTypeRepresentationWithHashes(DartType type, + OnVariableCallback onVariable) { + // Create a type representation. For type variables call the original + // callback for side effects and return a template placeholder. + jsAst.Expression representation = getTypeRepresentation(type, (variable) { + onVariable(variable); + return new jsAst.LiteralString('#'); + }); + return jsAst.prettyPrint(representation, compiler).buffer.toString(); + } + + jsAst.Expression getTypeRepresentation(DartType type, + OnVariableCallback onVariable) { + return representationGenerator.getTypeRepresentation(type, onVariable); + } + + bool isSimpleFunctionType(FunctionType type) { + if (!type.returnType.isDynamic) return false; + if (!type.optionalParameterTypes.isEmpty) return false; + if (!type.namedParameterTypes.isEmpty) return false; + for (Link link = type.parameterTypes; + !link.isEmpty; + link = link.tail) { + if (!link.head.isDynamic) return false; + } + return true; + } + + static bool hasTypeArguments(DartType type) { + if (type is InterfaceType) { + InterfaceType interfaceType = type; + return !interfaceType.treatAsRaw; + } + return false; + } + + static int getTypeVariableIndex(TypeVariableElement variable) { + ClassElement classElement = variable.getEnclosingClass(); + Link variables = classElement.typeVariables; + for (int index = 0; !variables.isEmpty; + index++, variables = variables.tail) { + if (variables.head.element == variable) return index; + } + throw invariant(variable, false, + message: "Couldn't find type-variable index"); + } + + /// Return all classes that are referenced in the type of the function, i.e., + /// in the return type or the argument types. + Set getReferencedClasses(FunctionType type) { + FunctionArgumentCollector collector = + new FunctionArgumentCollector(backend); + collector.collect(type); + return collector.classes; + } +} + +class TypeRepresentationGenerator extends DartTypeVisitor { + final Compiler compiler; + OnVariableCallback onVariable; + + JavaScriptBackend get backend => compiler.backend; + Namer get namer => backend.namer; + + TypeRepresentationGenerator(Compiler this.compiler); + + /** + * Creates a type representation for [type]. [onVariable] is called to provide + * the type representation for type variables. + */ + jsAst.Expression getTypeRepresentation(DartType type, + OnVariableCallback onVariable) { + this.onVariable = onVariable; + jsAst.Expression representation = visit(type); + this.onVariable = null; + return representation; + } + + jsAst.Expression getJavaScriptClassName(Element element) { + return js(namer.isolateAccess(backend.getImplementationClass(element))); + } + + visit(DartType type) { + return type.unalias(compiler).accept(this, null); + } + + visitTypeVariableType(TypeVariableType type, _) { + return onVariable(type); + } + + visitDynamicType(DynamicType type, _) { + return js('null'); + } + + visitInterfaceType(InterfaceType type, _) { + jsAst.Expression name = getJavaScriptClassName(type.element); + return type.treatAsRaw ? name : visitList(type.typeArguments, head: name); + } + + jsAst.Expression visitList(Link types, {jsAst.Expression head}) { + int index = 0; + List elements = []; + if (head != null) { + elements.add(new jsAst.ArrayElement(0, head)); + index++; + } + for (Link link = types; !link.isEmpty; link = link.tail) { + elements.add(new jsAst.ArrayElement(index++, visit(link.head))); + } + return new jsAst.ArrayInitializer(elements.length, elements); + } + + visitFunctionType(FunctionType type, _) { + List properties = []; + + void addProperty(String name, jsAst.Expression value) { + properties.add(new jsAst.Property(js.string(name), value)); + } + + jsAst.LiteralString name = js.string(namer.getFunctionTypeName(type)); + addProperty(namer.functionTypeTag(), name); + if (type.returnType.isVoid) { + addProperty(namer.functionTypeVoidReturnTag(), js('true')); + } else if (!type.returnType.treatAsDynamic) { + addProperty(namer.functionTypeReturnTypeTag(), visit(type.returnType)); + } + if (!type.parameterTypes.isEmpty) { + addProperty(namer.functionTypeRequiredParametersTag(), + visitList(type.parameterTypes)); + } + if (!type.optionalParameterTypes.isEmpty) { + addProperty(namer.functionTypeOptionalParametersTag(), + visitList(type.optionalParameterTypes)); + } + if (!type.namedParameterTypes.isEmpty) { + List namedArguments = []; + Link names = type.namedParameters; + Link types = type.namedParameterTypes; + while (!types.isEmpty) { + assert(!names.isEmpty); + jsAst.Expression name = js.string(names.head); + namedArguments.add(new jsAst.Property(name, visit(types.head))); + names = names.tail; + types = types.tail; + } + addProperty(namer.functionTypeNamedParametersTag(), + new jsAst.ObjectInitializer(namedArguments)); + } + return new jsAst.ObjectInitializer(properties); + } + + visitMalformedType(MalformedType type, _) { + // Treat malformed types as dynamic at runtime. + return js('null'); + } + + visitVoidType(VoidType type, _) { + // TODO(ahe): Reify void type ("null" means "dynamic"). + return js('null'); + } + + visitType(DartType type, _) { + compiler.internalError(NO_LOCATION_SPANNABLE, + 'Unexpected type: $type (${type.kind}).'); + } +} + + +class TypeCheckMapping implements TypeChecks { + final Map> map = + new Map>(); + + Iterable operator[](ClassElement element) { + Set result = map[element]; + return result != null ? result : const []; + } + + void add(ClassElement cls, ClassElement check, Substitution substitution) { + map.putIfAbsent(cls, () => new Set()); + map[cls].add(new TypeCheck(check, substitution)); + } + + Iterator get iterator => map.keys.iterator; + + String toString() { + StringBuffer sb = new StringBuffer(); + for (ClassElement holder in this) { + for (ClassElement check in [holder]) { + sb.write('${holder.name}.' '${check.name}, '); + } + } + return '[$sb]'; + } +} + +class ArgumentCollector extends DartTypeVisitor { + final JavaScriptBackend backend; + final Set classes = new Set(); + + ArgumentCollector(this.backend); + + collect(DartType type, {bool isTypeArgument: false}) { + type.accept(this, isTypeArgument); + } + + /// Collect all types in the list as if they were arguments of an + /// InterfaceType. + collectAll(Link types) { + for (Link link = types; !link.isEmpty; link = link.tail) { + link.head.accept(this, true); + } + } + + visitType(DartType type, _) { + // Do nothing. + } + + visitDynamicType(DynamicType type, _) { + // Do not collect [:dynamic:]. + } + + visitTypedefType(TypedefType type, bool isTypeArgument) { + type.unalias(backend.compiler).accept(this, isTypeArgument); + } + + visitInterfaceType(InterfaceType type, bool isTypeArgument) { + if (isTypeArgument) { + classes.add(backend.getImplementationClass(type.element)); + } + type.visitChildren(this, true); + } + + visitFunctionType(FunctionType type, _) { + type.visitChildren(this, true); + } +} + +class FunctionArgumentCollector extends DartTypeVisitor { + final JavaScriptBackend backend; + final Set classes = new Set(); + + FunctionArgumentCollector(this.backend); + + collect(DartType type) { + type.accept(this, false); + } + + /// Collect all types in the list as if they were arguments of an + /// InterfaceType. + collectAll(Link types) { + for (Link link = types; !link.isEmpty; link = link.tail) { + link.head.accept(this, true); + } + } + + visitType(DartType type, _) { + // Do nothing. + } + + visitDynamicType(DynamicType type, _) { + // Do not collect [:dynamic:]. + } + + visitTypedefType(TypedefType type, bool inFunctionType) { + type.unalias(backend.compiler).accept(this, inFunctionType); + } + + visitInterfaceType(InterfaceType type, bool inFunctionType) { + if (inFunctionType) { + classes.add(backend.getImplementationClass(type.element)); + } + type.visitChildren(this, inFunctionType); + } + + visitFunctionType(FunctionType type, _) { + type.visitChildren(this, true); + } +} + +/** + * Representation of the substitution of type arguments + * when going from the type of a class to one of its supertypes. + * + * For [:class B extends A, int>:], the substitution is + * the representation of [: (T) => [, int] :]. For more details + * of the representation consult the documentation of + * [getSupertypeSubstitution]. + */ +class Substitution { + final bool isFunction; + final Link arguments; + final Link parameters; + + Substitution.list(this.arguments) + : isFunction = false, + parameters = const Link(); + + Substitution.function(this.arguments, this.parameters) + : isFunction = true; + + jsAst.Expression getCode(RuntimeTypes rti, bool ensureIsFunction) { + jsAst.Expression declaration(TypeVariableType variable) { + return new jsAst.Parameter( + rti.backend.namer.safeVariableName(variable.name)); + } + + jsAst.Expression use(TypeVariableType variable) { + return new jsAst.VariableUse( + rti.backend.namer.safeVariableName(variable.name)); + } + + jsAst.Expression value = + rti.getSubstitutionRepresentation(arguments, use); + if (isFunction) { + List formals = parameters.toList().map(declaration).toList(); + return js.fun(formals, js.return_(value)); + } else if (ensureIsFunction) { + return js.fun([], js.return_(value)); + } else { + return value; + } + } +} + +/** + * A pair of a class that we need a check against and the type argument + * substition for this check. + */ +class TypeCheck { + final ClassElement cls; + final Substitution substitution; + final int hashCode = (nextHash++) & 0x3fffffff; + static int nextHash = 49; + + TypeCheck(this.cls, this.substitution); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/type_variable_handler.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/type_variable_handler.dart new file mode 100644 index 0000000..9c467ba --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_backend/type_variable_handler.dart @@ -0,0 +1,168 @@ +// Copyright (c) 2013, 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. + +part of js_backend; + +/** + * Handles construction of TypeVariable constants needed at runtime. + */ +class TypeVariableHandler { + JavaScriptBackend backend; + FunctionElement typeVariableConstructor; + CompileTimeConstantEvaluator evaluator; + + /** + * Contains all instantiated classes that have type variables and are needed + * for reflection. + */ + List typeVariableClasses = new List(); + + /** + * Maps a class element to a list with indices that point to type variables + * constants for each of the class' type variables. + */ + Map> typeVariables = + new Map>(); + + /** + * Maps a TypeVariableType to the index pointing to the constant representing + * the corresponding type variable at runtime. + */ + Map typeVariableConstants = + new Map(); + + TypeVariableHandler(this.backend); + + ClassElement get typeVariableClass => backend.typeVariableClass; + CodeEmitterTask get task => backend.emitter; + MetadataEmitter get emitter => task.metadataEmitter; + Compiler get compiler => backend.compiler; + + void registerClassWithTypeVariables(ClassElement cls) { + if (!backend.isTreeShakingDisabled || typeVariableConstructor == null) { + typeVariableClasses.add(cls); + } else { + processTypeVariablesOf(cls); + } + } + + void processTypeVariablesOf(ClassElement cls) { + //TODO(zarah): Running through all the members is suboptimal. Change this + // as part of marking elements for reflection. + bool hasMemberNeededForReflection(ClassElement cls) { + bool result = false; + cls.implementation.forEachMember((ClassElement cls, Element member) { + result = result || backend.isNeededForReflection(member); + }); + return result; + } + + if (!backend.isNeededForReflection(cls) && + !hasMemberNeededForReflection(cls)) { + return; + } + + InterfaceType typeVariableType = typeVariableClass.thisType; + List constants = []; + evaluator = new CompileTimeConstantEvaluator( + compiler.constantHandler, + compiler.globalDependencies, + compiler); + + for (TypeVariableType currentTypeVariable in cls.typeVariables) { + List createArguments(FunctionElement constructor) { + if (constructor != typeVariableConstructor) { + compiler.internalError(currentTypeVariable.element, + 'Unexpected constructor $constructor'); + } + Constant name = backend.constantSystem.createString( + new DartString.literal(currentTypeVariable.name)); + Constant bound = backend.constantSystem.createInt( + emitter.reifyType(currentTypeVariable.element.bound)); + Constant type = evaluator.makeTypeConstant(cls); + return [type, name, bound]; + } + + Constant c = evaluator.makeConstructedConstant( + currentTypeVariable.element, typeVariableType, + typeVariableConstructor, createArguments); + backend.registerCompileTimeConstant(c, compiler.globalDependencies); + compiler.constantHandler.addCompileTimeConstantForEmission(c); + constants.add( + reifyTypeVariableConstant(c, currentTypeVariable.element)); + } + typeVariables[cls] = constants; + } + + void onTreeShakingDisabled(Enqueuer enqueuer) { + if (!enqueuer.isResolutionQueue || typeVariableClasses == null) return; + backend.enqueueClass( + enqueuer, typeVariableClass, compiler.globalDependencies); + typeVariableClass.ensureResolved(compiler); + Link constructors = typeVariableClass.constructors; + if (constructors.isEmpty && constructors.tail.isEmpty) { + compiler.internalError(typeVariableClass, + "Class '$typeVariableClass' should only have one constructor"); + } + typeVariableConstructor = typeVariableClass.constructors.head; + backend.enqueueInResolution(typeVariableConstructor, + compiler.globalDependencies); + enqueuer.registerInstantiatedType(typeVariableClass.rawType, + compiler.globalDependencies); + List worklist = typeVariableClasses; + typeVariableClasses = null; + worklist.forEach((cls) => processTypeVariablesOf(cls)); + } + + /** + * Adds [c] to [emitter.globalMetadata] and returns the index pointing to + * the entry. + * + * If the corresponding type variable has already been encountered an + * entry in the list has already been reserved and the constant is added + * there, otherwise a new entry for [c] is created. + */ + int reifyTypeVariableConstant(Constant c, TypeVariableElement variable) { + String name = + jsAst.prettyPrint(task.constantReference(c), compiler).getText(); + int index; + if (typeVariableConstants.containsKey(variable)) { + index = typeVariableConstants[variable]; + emitter.globalMetadata[index] = name; + } else { + index = emitter.addGlobalMetadata(name); + typeVariableConstants[variable] = index; + } + return index; + } + + /** + * Returns the index pointing to the constant in [emitter.globalMetadata] + * representing this type variable + *. + * + * If the constant has not yet been constructed, an entry is allocated in + * the global metadata list and the index pointing to this entry is returned. + * When the corresponding constant is constructed later, + * [reifyTypeVariableConstant] will be called and the constant will be added + * on the allocated entry. + */ + int reifyTypeVariable(TypeVariableElement variable) { + if (typeVariableConstants.containsKey(variable)) { + return typeVariableConstants[variable]; + } + + // TODO(15613): Remove quotes. + emitter.globalMetadata.add('"Placeholder for ${variable}"'); + return typeVariableConstants[variable] = emitter.globalMetadata.length - 1; + } + + List typeVariablesOf(ClassElement classElement) { + List result = typeVariables[classElement]; + if (result == null) { + result = const []; + } + return result; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/class_builder.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/class_builder.dart new file mode 100644 index 0000000..044368b --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/class_builder.dart @@ -0,0 +1,63 @@ +// Copyright (c) 2013, 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. + +part of dart2js.js_emitter; + +/** + * A data structure for collecting fragments of a class definition. + */ +class ClassBuilder { + final List properties = []; + final List fields = []; + + String superName; + String nativeName; + String functionType; + List fieldMetadata; + + final Namer namer; + + /// Set to true by user if class is indistinguishable from its superclass. + bool isTrivial = false; + + ClassBuilder(this.namer); + + // Has the same signature as [DefineStubFunction]. + void addProperty(String name, jsAst.Expression value) { + properties.add(new jsAst.Property(js.string(name), value)); + } + + void addField(String field) { + fields.add(field); + } + + jsAst.ObjectInitializer toObjectInitializer() { + StringBuffer buffer = new StringBuffer(); + if (superName != null) { + if (nativeName != null) { + buffer.write('$nativeName/'); + } + buffer.write('$superName'); + if (functionType != null) { + buffer.write(':$functionType'); + } + buffer.write(';'); + } + buffer.writeAll(fields, ','); + var classData = js.string('$buffer'); + if (fieldMetadata != null) { + // If we need to store fieldMetadata, classData is turned into an array, + // and the field metadata is appended. So if classData is just a string, + // there is no field metadata. + classData = + new jsAst.ArrayInitializer.from([classData]..addAll(fieldMetadata)); + } + var fieldsAndProperties = + [new jsAst.Property(js.string(namer.classDescriptorProperty), + classData)] + ..addAll(properties); + return new jsAst.ObjectInitializer(fieldsAndProperties, isOneLiner: false); + } + +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/class_emitter.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/class_emitter.dart new file mode 100644 index 0000000..cd8f703 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/class_emitter.dart @@ -0,0 +1,603 @@ +// Copyright (c) 2013, 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. + +part of dart2js.js_emitter; + +class ClassEmitter extends CodeEmitterHelper { + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [classElement] must be a declaration element. + */ + void generateClass(ClassElement classElement, + ClassBuilder properties, + Map additionalProperties) { + final onlyForRti = + task.typeTestEmitter.rtiNeededClasses.contains(classElement); + + assert(invariant(classElement, classElement.isDeclaration)); + assert(invariant(classElement, !classElement.isNative() || onlyForRti)); + + task.needsDefineClass = true; + String className = namer.getNameOfClass(classElement); + + ClassElement superclass = classElement.superclass; + String superName = ""; + if (superclass != null) { + superName = namer.getNameOfClass(superclass); + } + String runtimeName = + namer.getPrimitiveInterceptorRuntimeName(classElement); + + if (classElement.isMixinApplication) { + String mixinName = namer.getNameOfClass(computeMixinClass(classElement)); + superName = '$superName+$mixinName'; + task.needsMixinSupport = true; + } + + ClassBuilder builder = new ClassBuilder(namer); + emitClassConstructor(classElement, builder, runtimeName, + onlyForRti: onlyForRti); + emitFields(classElement, builder, superName, onlyForRti: onlyForRti); + emitClassGettersSetters(classElement, builder, onlyForRti: onlyForRti); + emitInstanceMembers(classElement, builder, onlyForRti: onlyForRti); + task.typeTestEmitter.emitIsTests(classElement, builder); + if (additionalProperties != null) { + additionalProperties.forEach(builder.addProperty); + } + + emitTypeVariableReaders(classElement, builder); + + emitClassBuilderWithReflectionData( + className, classElement, builder, properties); + } + + void emitClassConstructor(ClassElement classElement, + ClassBuilder builder, + String runtimeName, + {bool onlyForRti: false}) { + List fields = []; + if (!onlyForRti && !classElement.isNative()) { + visitFields(classElement, false, + (Element member, + String name, + String accessorName, + bool needsGetter, + bool needsSetter, + bool needsCheckedSetter) { + fields.add(name); + }); + } + String constructorName = namer.getNameOfClass(classElement); + task.precompiledFunction.add(new jsAst.FunctionDeclaration( + new jsAst.VariableDeclaration(constructorName), + js.fun(fields, fields.map( + (name) => js('this.$name = $name')).toList()))); + if (runtimeName == null) { + runtimeName = constructorName; + } + task.precompiledFunction.addAll([ + js('$constructorName.builtin\$cls = "$runtimeName"'), + js.if_('!"name" in $constructorName', + js('$constructorName.name = "$constructorName"')), + js('\$desc=\$collectedClasses.$constructorName'), + js.if_('\$desc instanceof Array', js('\$desc = \$desc[1]')), + js('$constructorName.prototype = \$desc'), + ]); + + task.precompiledConstructorNames.add(js(constructorName)); + } + + /// Returns `true` if fields added. + bool emitFields(Element element, + ClassBuilder builder, + String superName, + { bool classIsNative: false, + bool emitStatics: false, + bool onlyForRti: false }) { + assert(!emitStatics || !onlyForRti); + if (element.isLibrary()) { + assert(invariant(element, emitStatics)); + } else if (!element.isClass()) { + throw new SpannableAssertionFailure( + element, 'Must be a ClassElement or a LibraryElement'); + } + if (emitStatics) { + assert(invariant(element, superName == null, message: superName)); + } else { + assert(invariant(element, superName != null)); + String nativeName = + namer.getPrimitiveInterceptorRuntimeName(element); + if (nativeName != null) { + builder.nativeName = nativeName; + } + builder.superName = superName; + } + var fieldMetadata = []; + bool hasMetadata = false; + bool fieldsAdded = false; + + if (!onlyForRti) { + visitFields(element, emitStatics, + (VariableElement field, + String name, + String accessorName, + bool needsGetter, + bool needsSetter, + bool needsCheckedSetter) { + // Ignore needsCheckedSetter - that is handled below. + bool needsAccessor = (needsGetter || needsSetter); + // We need to output the fields for non-native classes so we can auto- + // generate the constructor. For native classes there are no + // constructors, so we don't need the fields unless we are generating + // accessors at runtime. + if (!classIsNative || needsAccessor) { + var metadata = task.metadataEmitter.buildMetadataFunction(field); + if (metadata != null) { + hasMetadata = true; + } else { + metadata = new jsAst.LiteralNull(); + } + fieldMetadata.add(metadata); + recordMangledField(field, accessorName, field.name); + String fieldName = name; + String fieldCode = ''; + String reflectionMarker = ''; + if (!needsAccessor) { + // Emit field for constructor generation. + assert(!classIsNative); + } else { + // Emit (possibly renaming) field name so we can add accessors at + // runtime. + if (name != accessorName) { + fieldName = '$accessorName:$name'; + } + + int getterCode = 0; + if (needsAccessor && backend.fieldHasInterceptedGetter(field)) { + task.interceptorEmitter.interceptorInvocationNames.add( + namer.getterName(field)); + } + if (needsAccessor && backend.fieldHasInterceptedGetter(field)) { + task.interceptorEmitter.interceptorInvocationNames.add( + namer.setterName(field)); + } + if (needsGetter) { + if (field.isInstanceMember()) { + // 01: function() { return this.field; } + // 10: function(receiver) { return receiver.field; } + // 11: function(receiver) { return this.field; } + bool isIntercepted = backend.fieldHasInterceptedGetter(field); + getterCode += isIntercepted ? 2 : 0; + getterCode += backend.isInterceptorClass(element) ? 0 : 1; + // TODO(sra): 'isInterceptorClass' might not be the correct test + // for methods forced to use the interceptor convention because + // the method's class was elsewhere mixed-in to an interceptor. + assert(!field.isInstanceMember() || getterCode != 0); + if (isIntercepted) { + task.interceptorEmitter.interceptorInvocationNames.add( + namer.getterName(field)); + } + } else { + getterCode = 1; + } + } + int setterCode = 0; + if (needsSetter) { + if (field.isInstanceMember()) { + // 01: function(value) { this.field = value; } + // 10: function(receiver, value) { receiver.field = value; } + // 11: function(receiver, value) { this.field = value; } + bool isIntercepted = backend.fieldHasInterceptedSetter(field); + setterCode += isIntercepted ? 2 : 0; + setterCode += backend.isInterceptorClass(element) ? 0 : 1; + assert(!field.isInstanceMember() || setterCode != 0); + if (isIntercepted) { + task.interceptorEmitter.interceptorInvocationNames.add( + namer.setterName(field)); + } + } else { + setterCode = 1; + } + } + int code = getterCode + (setterCode << 2); + if (code == 0) { + compiler.internalError(field, + 'Field code is 0 ($element/$field).'); + } else { + fieldCode = FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]; + } + } + if (backend.isAccessibleByReflection(field)) { + reflectionMarker = '-'; + if (backend.isNeededForReflection(field)) { + DartType type = field.computeType(compiler); + reflectionMarker = '-${task.metadataEmitter.reifyType(type)}'; + } + } + builder.addField('$fieldName$fieldCode$reflectionMarker'); + fieldsAdded = true; + } + }); + } + + if (hasMetadata) { + builder.fieldMetadata = fieldMetadata; + } + return fieldsAdded; + } + + void emitClassGettersSetters(ClassElement classElement, + ClassBuilder builder, + {bool onlyForRti: false}) { + if (onlyForRti) return; + + visitFields(classElement, false, + (VariableElement member, + String name, + String accessorName, + bool needsGetter, + bool needsSetter, + bool needsCheckedSetter) { + compiler.withCurrentElement(member, () { + if (needsCheckedSetter) { + assert(!needsSetter); + generateCheckedSetter(member, name, accessorName, builder); + } + if (needsGetter) { + generateGetter(member, name, accessorName, builder); + } + if (needsSetter) { + generateSetter(member, name, accessorName, builder); + } + }); + }); + } + + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [classElement] must be a declaration element. + */ + void emitInstanceMembers(ClassElement classElement, + ClassBuilder builder, + {bool onlyForRti: false}) { + assert(invariant(classElement, classElement.isDeclaration)); + + if (onlyForRti || classElement.isMixinApplication) return; + + void visitMember(ClassElement enclosing, Element member) { + assert(invariant(classElement, member.isDeclaration)); + if (member.isInstanceMember()) { + task.containerBuilder.addMember(member, builder); + } + } + + classElement.implementation.forEachMember( + visitMember, + includeBackendMembers: true); + + if (identical(classElement, compiler.objectClass) + && compiler.enabledNoSuchMethod) { + // Emit the noSuchMethod handlers on the Object prototype now, + // so that the code in the dynamicFunction helper can find + // them. Note that this helper is invoked before analyzing the + // full JS script. + if (!task.nativeEmitter.handleNoSuchMethod) { + task.nsmEmitter.emitNoSuchMethodHandlers(builder.addProperty); + } + } + } + + void emitClassBuilderWithReflectionData(String className, + ClassElement classElement, + ClassBuilder classBuilder, + ClassBuilder enclosingBuilder) { + var metadata = task.metadataEmitter.buildMetadataFunction(classElement); + if (metadata != null) { + classBuilder.addProperty("@", metadata); + } + + if (backend.isNeededForReflection(classElement)) { + Link typeVars = classElement.typeVariables; + Iterable typeVariableProperties = task.typeVariableHandler + .typeVariablesOf(classElement).map(js.toExpression); + + ClassElement superclass = classElement.superclass; + bool hasSuper = superclass != null; + if ((!typeVariableProperties.isEmpty && !hasSuper) || + (hasSuper && superclass.typeVariables != typeVars)) { + classBuilder.addProperty('<>', + new jsAst.ArrayInitializer.from(typeVariableProperties)); + } + } + + List statics = new List(); + ClassBuilder staticsBuilder = new ClassBuilder(namer); + if (emitFields(classElement, staticsBuilder, null, emitStatics: true)) { + statics.add(staticsBuilder.toObjectInitializer().properties.single); + } + + Map classPropertyLists = + task.elementDescriptors.remove(classElement); + if (classPropertyLists != null) { + for (ClassBuilder classProperties in classPropertyLists.values) { + // TODO(sigurdm): What about deferred? + if (classProperties != null) { + statics.addAll(classProperties.properties); + } + } + } + + if (!statics.isEmpty) { + classBuilder.addProperty('static', new jsAst.ObjectInitializer(statics)); + } + + // TODO(ahe): This method (generateClass) should return a jsAst.Expression. + enclosingBuilder.addProperty(className, classBuilder.toObjectInitializer()); + + String reflectionName = task.getReflectionName(classElement, className); + if (reflectionName != null) { + if (!backend.isNeededForReflection(classElement)) { + enclosingBuilder.addProperty("+$reflectionName", js.number(0)); + } else { + List types = []; + if (classElement.supertype != null) { + types.add(task.metadataEmitter.reifyType(classElement.supertype)); + } + for (DartType interface in classElement.interfaces) { + types.add(task.metadataEmitter.reifyType(interface)); + } + enclosingBuilder.addProperty("+$reflectionName", + new jsAst.ArrayInitializer.from(types.map(js.number))); + } + } + } + + /** + * Calls [addField] for each of the fields of [element]. + * + * [element] must be a [ClassElement] or a [LibraryElement]. + * + * If [element] is a [ClassElement], the static fields of the class are + * visited if [visitStatics] is true and the instance fields are visited if + * [visitStatics] is false. + * + * If [element] is a [LibraryElement], [visitStatics] must be true. + * + * When visiting the instance fields of a class, the fields of its superclass + * are also visited if the class is instantiated. + * + * Invariant: [element] must be a declaration element. + */ + void visitFields(Element element, bool visitStatics, AcceptField f) { + assert(invariant(element, element.isDeclaration)); + + bool isClass = false; + bool isLibrary = false; + if (element.isClass()) { + isClass = true; + } else if (element.isLibrary()) { + isLibrary = true; + assert(invariant(element, visitStatics)); + } else { + throw new SpannableAssertionFailure( + element, 'Expected a ClassElement or a LibraryElement.'); + } + + // If the class is never instantiated we still need to set it up for + // inheritance purposes, but we can simplify its JavaScript constructor. + bool isInstantiated = + compiler.codegenWorld.instantiatedClasses.contains(element); + + void visitField(Element holder, VariableElement field) { + assert(invariant(element, field.isDeclaration)); + String name = field.name; + + // Keep track of whether or not we're dealing with a field mixin + // into a native class. + bool isMixinNativeField = + isClass && element.isNative() && holder.isMixinApplication; + + // See if we can dynamically create getters and setters. + // We can only generate getters and setters for [element] since + // the fields of super classes could be overwritten with getters or + // setters. + bool needsGetter = false; + bool needsSetter = false; + if (isLibrary || isMixinNativeField || holder == element) { + needsGetter = fieldNeedsGetter(field); + needsSetter = fieldNeedsSetter(field); + } + + if ((isInstantiated && !holder.isNative()) + || needsGetter + || needsSetter) { + String accessorName = namer.fieldAccessorName(field); + String fieldName = namer.fieldPropertyName(field); + bool needsCheckedSetter = false; + if (compiler.enableTypeAssertions + && needsSetter + && !canAvoidGeneratedCheckedSetter(field)) { + needsCheckedSetter = true; + needsSetter = false; + } + // Getters and setters with suffixes will be generated dynamically. + f(field, fieldName, accessorName, needsGetter, needsSetter, + needsCheckedSetter); + } + } + + if (isLibrary) { + LibraryElement library = element; + library.implementation.forEachLocalMember((Element member) { + if (member.isField()) visitField(library, member); + }); + } else if (visitStatics) { + ClassElement cls = element; + cls.implementation.forEachStaticField(visitField); + } else { + ClassElement cls = element; + // TODO(kasperl): We should make sure to only emit one version of + // overridden fields. Right now, we rely on the ordering so the + // fields pulled in from mixins are replaced with the fields from + // the class definition. + + // If a class is not instantiated then we add the field just so we can + // generate the field getter/setter dynamically. Since this is only + // allowed on fields that are in [element] we don't need to visit + // superclasses for non-instantiated classes. + cls.implementation.forEachInstanceField( + visitField, includeSuperAndInjectedMembers: isInstantiated); + } + } + + void recordMangledField(Element member, + String accessorName, + String memberName) { + if (!backend.shouldRetainGetter(member)) return; + String previousName; + if (member.isInstanceMember()) { + previousName = task.mangledFieldNames.putIfAbsent( + '${namer.getterPrefix}$accessorName', + () => memberName); + } else { + previousName = task.mangledGlobalFieldNames.putIfAbsent( + accessorName, + () => memberName); + } + assert(invariant(member, previousName == memberName, + message: '$previousName != ${memberName}')); + } + + bool fieldNeedsGetter(VariableElement field) { + assert(field.isField()); + if (fieldAccessNeverThrows(field)) return false; + return backend.shouldRetainGetter(field) + || compiler.codegenWorld.hasInvokedGetter(field, compiler); + } + + bool fieldNeedsSetter(VariableElement field) { + assert(field.isField()); + if (fieldAccessNeverThrows(field)) return false; + return (!field.modifiers.isFinalOrConst()) + && (backend.shouldRetainSetter(field) + || compiler.codegenWorld.hasInvokedSetter(field, compiler)); + } + + // We never access a field in a closure (a captured variable) without knowing + // that it is there. Therefore we don't need to use a getter (that will throw + // if the getter method is missing), but can always access the field directly. + static bool fieldAccessNeverThrows(VariableElement field) { + return field is ClosureFieldElement; + } + + bool canAvoidGeneratedCheckedSetter(VariableElement member) { + // We never generate accessors for top-level/static fields. + if (!member.isInstanceMember()) return true; + DartType type = member.type; + return type.treatAsDynamic || (type.element == compiler.objectClass); + } + + void generateCheckedSetter(Element member, + String fieldName, + String accessorName, + ClassBuilder builder) { + jsAst.Expression code = backend.generatedCode[member]; + assert(code != null); + String setterName = namer.setterNameFromAccessorName(accessorName); + builder.addProperty(setterName, code); + generateReflectionDataForFieldGetterOrSetter( + member, setterName, builder, isGetter: false); + } + + void generateGetter(Element member, String fieldName, String accessorName, + ClassBuilder builder) { + String getterName = namer.getterNameFromAccessorName(accessorName); + ClassElement cls = member.getEnclosingClass(); + String className = namer.getNameOfClass(cls); + String receiver = backend.isInterceptorClass(cls) ? 'receiver' : 'this'; + List args = backend.isInterceptedMethod(member) ? ['receiver'] : []; + task.precompiledFunction.add( + js('$className.prototype.$getterName = #', + js.fun(args, js.return_(js('$receiver.$fieldName'))))); + if (backend.isNeededForReflection(member)) { + task.precompiledFunction.add( + js('$className.prototype.$getterName.${namer.reflectableField} = 1')); + } + } + + void generateSetter(Element member, String fieldName, String accessorName, + ClassBuilder builder) { + String setterName = namer.setterNameFromAccessorName(accessorName); + ClassElement cls = member.getEnclosingClass(); + String className = namer.getNameOfClass(cls); + String receiver = backend.isInterceptorClass(cls) ? 'receiver' : 'this'; + List args = + backend.isInterceptedMethod(member) ? ['receiver', 'v'] : ['v']; + task.precompiledFunction.add( + js('$className.prototype.$setterName = #', + js.fun(args, js.return_(js('$receiver.$fieldName = v'))))); + if (backend.isNeededForReflection(member)) { + task.precompiledFunction.add( + js('$className.prototype.$setterName.${namer.reflectableField} = 1')); + } + } + + void generateReflectionDataForFieldGetterOrSetter(Element member, + String name, + ClassBuilder builder, + {bool isGetter}) { + Selector selector = isGetter + ? new Selector.getter(member.name, member.getLibrary()) + : new Selector.setter(member.name, member.getLibrary()); + String reflectionName = task.getReflectionName(selector, name); + if (reflectionName != null) { + var reflectable = + js(backend.isAccessibleByReflection(member) ? '1' : '0'); + builder.addProperty('+$reflectionName', reflectable); + } + } + + void emitTypeVariableReaders(ClassElement cls, ClassBuilder builder) { + List typeVariables = []; + ClassElement superclass = cls; + while (superclass != null) { + for (TypeVariableType parameter in superclass.typeVariables) { + if (task.readTypeVariables.contains(parameter.element)) { + emitTypeVariableReader(cls, builder, parameter.element); + } + } + superclass = superclass.superclass; + } + } + + void emitTypeVariableReader(ClassElement cls, + ClassBuilder builder, + TypeVariableElement element) { + String name = namer.readTypeVariableName(element); + jsAst.Expression index = + js.toExpression(RuntimeTypes.getTypeVariableIndex(element)); + jsAst.Expression computeTypeVariable; + + Substitution substitution = + backend.rti.computeSubstitution( + cls, element.enclosingElement, alwaysGenerateFunction: true); + if (substitution != null) { + jsAst.Expression typeArguments = + substitution.getCode(backend.rti, true)['apply']( + ['null', r'this.$builtinTypeInfo']); + computeTypeVariable = typeArguments[index]; + } else { + // TODO(ahe): These can be generated dynamically. + computeTypeVariable = + js(r'this.$builtinTypeInfo && this.$builtinTypeInfo[#]', index); + } + jsAst.Expression convertRtiToRuntimeType = + namer.elementAccess(compiler.findHelper('convertRtiToRuntimeType')); + builder.addProperty( + name, js.fun( + [], [js.return_(convertRtiToRuntimeType(computeTypeVariable))])); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_helper.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_helper.dart new file mode 100644 index 0000000..1adfce4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_helper.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2013, 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. + +part of dart2js.js_emitter; + +class CodeEmitterHelper { + CodeEmitterTask task; + + Namer get namer => task.namer; + + JavaScriptBackend get backend => task.backend; + + Compiler get compiler => task.compiler; + + String get n => task.n; + + String get _ => task._; + + String get N => task.N; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart new file mode 100644 index 0000000..231035a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart @@ -0,0 +1,1686 @@ +// Copyright (c) 2012, 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. + +part of dart2js.js_emitter; + +/** + * Generates the code for all used classes in the program. Static fields (even + * in classes) are ignored, since they can be treated as non-class elements. + * + * The code for the containing (used) methods must exist in the [:universe:]. + */ +class CodeEmitterTask extends CompilerTask { + final ContainerBuilder containerBuilder = new ContainerBuilder(); + final ClassEmitter classEmitter = new ClassEmitter(); + final NsmEmitter nsmEmitter = new NsmEmitter(); + final TypeTestEmitter typeTestEmitter = new TypeTestEmitter(); + final InterceptorEmitter interceptorEmitter = new InterceptorEmitter(); + final MetadataEmitter metadataEmitter = new MetadataEmitter(); + + bool needsDefineClass = false; + bool needsMixinSupport = false; + bool needsLazyInitializer = false; + final Namer namer; + ConstantEmitter constantEmitter; + NativeEmitter nativeEmitter; + Map outputBuffers = new Map(); + final CodeBuffer deferredConstants = new CodeBuffer(); + /** Shorter access to [isolatePropertiesName]. Both here in the code, as + well as in the generated code. */ + String isolateProperties; + String classesCollector; + final Set neededClasses = new Set(); + final Map> outputClassLists = + new Map>(); + final List nativeClasses = []; + final Map mangledFieldNames = {}; + final Map mangledGlobalFieldNames = {}; + final Set recordedMangledNames = new Set(); + + final Map> additionalProperties = + new Map>(); + + /// Records if a type variable is read dynamically for type tests. + final Set readTypeVariables = + new Set(); + + // TODO(ngeoffray): remove this field. + Set instantiatedClasses; + + JavaScriptBackend get backend => compiler.backend; + TypeVariableHandler get typeVariableHandler => backend.typeVariableHandler; + + String get _ => space; + String get space => compiler.enableMinification ? "" : " "; + String get n => compiler.enableMinification ? "" : "\n"; + String get N => compiler.enableMinification ? "\n" : ";\n"; + + CodeBuffer get mainBuffer { + return outputBuffers.putIfAbsent(compiler.deferredLoadTask.mainOutputUnit, + () => new CodeBuffer()); + } + + /** + * List of expressions and statements that will be included in the + * precompiled function. + * + * To save space, dart2js normally generates constructors and accessors + * dynamically. This doesn't work in CSP mode, and may impact startup time + * negatively. So dart2js will emit these functions to a separate file that + * can be optionally included to support CSP mode or for faster startup. + */ + List precompiledFunction = []; + + List precompiledConstructorNames = []; + + // True if Isolate.makeConstantList is needed. + bool hasMakeConstantList = false; + + /** + * Accumulate properties for classes and libraries, describing their + * static/top-level members. + * Later, these members are emitted when the class or library is emitted. + * + * For supporting deferred loading we keep one list per output unit. + * + * See [getElementDecriptor]. + */ + // TODO(ahe): Generate statics with their class, and store only libraries in + // this map. + final Map> elementDescriptors + = new Map>(); + + final bool generateSourceMap; + + CodeEmitterTask(Compiler compiler, Namer namer, this.generateSourceMap) + : this.namer = namer, + constantEmitter = new ConstantEmitter(compiler, namer), + super(compiler) { + nativeEmitter = new NativeEmitter(this); + containerBuilder.task = this; + classEmitter.task = this; + nsmEmitter.task = this; + typeTestEmitter.task = this; + interceptorEmitter.task = this; + metadataEmitter.task = this; + } + + void addComment(String comment, CodeBuffer buffer) { + buffer.write(jsAst.prettyPrint(js.comment(comment), compiler)); + } + + jsAst.Expression constantReference(Constant value) { + return constantEmitter.reference(value); + } + + jsAst.Expression constantInitializerExpression(Constant value) { + return constantEmitter.initializationExpression(value); + } + + String get name => 'CodeEmitter'; + + String get currentGenerateAccessorName + => '${namer.currentIsolate}.\$generateAccessor'; + String get generateAccessorHolder + => '$isolatePropertiesName.\$generateAccessor'; + String get finishClassesProperty + => r'$finishClasses'; + String get finishClassesName + => '${namer.isolateName}.$finishClassesProperty'; + String get finishIsolateConstructorName + => '${namer.isolateName}.\$finishIsolateConstructor'; + String get isolatePropertiesName + => '${namer.isolateName}.${namer.isolatePropertiesName}'; + String get lazyInitializerName + => '${namer.isolateName}.\$lazy'; + String get initName => 'init'; + + jsAst.FunctionDeclaration get generateAccessorFunction { + const RANGE1_SIZE = RANGE1_LAST - RANGE1_FIRST + 1; + const RANGE2_SIZE = RANGE2_LAST - RANGE2_FIRST + 1; + const RANGE1_ADJUST = - (FIRST_FIELD_CODE - RANGE1_FIRST); + const RANGE2_ADJUST = - (FIRST_FIELD_CODE + RANGE1_SIZE - RANGE2_FIRST); + const RANGE3_ADJUST = + - (FIRST_FIELD_CODE + RANGE1_SIZE + RANGE2_SIZE - RANGE3_FIRST); + + String receiverParamName = compiler.enableMinification ? "r" : "receiver"; + String valueParamName = compiler.enableMinification ? "v" : "value"; + String reflectableField = namer.reflectableField; + + // function generateAccessor(field, prototype, cls) { + jsAst.Fun fun = js.fun(['fieldDescriptor', 'accessors', 'cls'], [ + js('var fieldInformation = fieldDescriptor.split("-")'), + js('var field = fieldInformation[0]'), + js('var len = field.length'), + js('var code = field.charCodeAt(len - 1)'), + js('var reflectable'), + js.if_('fieldInformation.length > 1', js('reflectable = true'), + js('reflectable = false')), + js('code = ((code >= $RANGE1_FIRST) && (code <= $RANGE1_LAST))' + ' ? code - $RANGE1_ADJUST' + ' : ((code >= $RANGE2_FIRST) && (code <= $RANGE2_LAST))' + ' ? code - $RANGE2_ADJUST' + ' : ((code >= $RANGE3_FIRST) && (code <= $RANGE3_LAST))' + ' ? code - $RANGE3_ADJUST' + ' : $NO_FIELD_CODE'), + + // if (needsAccessor) { + js.if_('code', [ + js('var getterCode = code & 3'), + js('var setterCode = code >> 2'), + js('var accessorName = field = field.substring(0, len - 1)'), + + js('var divider = field.indexOf(":")'), + js.if_('divider > 0', [ // Colon never in first position. + js('accessorName = field.substring(0, divider)'), + js('field = field.substring(divider + 1)') + ]), + + // if (needsGetter) { + js.if_('getterCode', [ + js('var args = (getterCode & 2) ? "$receiverParamName" : ""'), + js('var receiver = (getterCode & 1) ? "this" : "$receiverParamName"'), + js('var body = "return " + receiver + "." + field'), + js('var property =' + ' cls + ".prototype.${namer.getterPrefix}" + accessorName + "="'), + js('var fn = "function(" + args + "){" + body + "}"'), + js.if_( + 'reflectable', + js('accessors.push(property + "\$reflectable(" + fn + ");\\n")'), + js('accessors.push(property + fn + ";\\n")')), + ]), + + // if (needsSetter) { + js.if_('setterCode', [ + js('var args = (setterCode & 2)' + ' ? "$receiverParamName,${_}$valueParamName"' + ' : "$valueParamName"'), + js('var receiver = (setterCode & 1) ? "this" : "$receiverParamName"'), + js('var body = receiver + "." + field + "$_=$_$valueParamName"'), + js('var property =' + ' cls + ".prototype.${namer.setterPrefix}" + accessorName + "="'), + js('var fn = "function(" + args + "){" + body + "}"'), + js.if_( + 'reflectable', + js('accessors.push(property + "\$reflectable(" + fn + ");\\n")'), + js('accessors.push(property + fn + ";\\n")')), + ]), + + ]), + + // return field; + js.return_('field') + ]); + + return new jsAst.FunctionDeclaration( + new jsAst.VariableDeclaration('generateAccessor'), + fun); + } + + List get defineClassFunction { + // First the class name, then the field names in an array and the members + // (inside an Object literal). + // The caller can also pass in the constructor as a function if needed. + // + // Example: + // defineClass("A", ["x", "y"], { + // foo$1: function(y) { + // print(this.x + y); + // }, + // bar$2: function(t, v) { + // this.x = t - v; + // }, + // }); + + var defineClass = js.fun(['name', 'cls', 'fields'], [ + js('var accessors = []'), + + js('var str = "function " + cls + "("'), + js('var body = ""'), + + js.for_('var i = 0', 'i < fields.length', 'i++', [ + js.if_('i != 0', js('str += ", "')), + + js('var field = generateAccessor(fields[i], accessors, cls)'), + js('var parameter = "parameter_" + field'), + js('str += parameter'), + js('body += ("this." + field + " = " + parameter + ";\\n")') + ]), + js('str += ") {\\n" + body + "}\\n"'), + js('str += cls + ".builtin\$cls=\\"" + name + "\\";\\n"'), + js('str += "\$desc=\$collectedClasses." + cls + ";\\n"'), + js('str += "if(\$desc instanceof Array) \$desc = \$desc[1];\\n"'), + js('str += cls + ".prototype = \$desc;\\n"'), + js.if_( + 'typeof defineClass.name != "string"', + [js('str += cls + ".name=\\"" + cls + "\\";\\n"')]), + js('str += accessors.join("")'), + + js.return_('str') + ]); + // Declare a function called "generateAccessor". This is used in + // defineClassFunction (it's a local declaration in init()). + return [ + generateAccessorFunction, + js('$generateAccessorHolder = generateAccessor'), + new jsAst.FunctionDeclaration( + new jsAst.VariableDeclaration('defineClass'), defineClass) ]; + } + + /** Needs defineClass to be defined. */ + List buildInheritFrom() { + return [ + js('var inheritFrom = #', + js.fun([], [ + new jsAst.FunctionDeclaration( + new jsAst.VariableDeclaration('tmp'), js.fun([], [])), + js('var hasOwnProperty = Object.prototype.hasOwnProperty'), + js.return_(js.fun(['constructor', 'superConstructor'], [ + js('tmp.prototype = superConstructor.prototype'), + js('var object = new tmp()'), + js('var properties = constructor.prototype'), + js.forIn('member', 'properties', + js.if_('hasOwnProperty.call(properties, member)', + js('object[member] = properties[member]'))), + js('object.constructor = constructor'), + js('constructor.prototype = object'), + js.return_('object') + ]))])())]; + } + + jsAst.Fun get finishClassesFunction { + // Class descriptions are collected in a JS object. + // 'finishClasses' takes all collected descriptions and sets up + // the prototype. + // Once set up, the constructors prototype field satisfy: + // - it contains all (local) members. + // - its internal prototype (__proto__) points to the superclass' + // prototype field. + // - the prototype's constructor field points to the JavaScript + // constructor. + // For engines where we have access to the '__proto__' we can manipulate + // the object literal directly. For other engines we have to create a new + // object and copy over the members. + + String reflectableField = namer.reflectableField; + List statements = [ + js('var pendingClasses = {}'), + js.if_('!init.allClasses', js('init.allClasses = {}')), + js('var allClasses = init.allClasses'), + + optional( + DEBUG_FAST_OBJECTS, + js('print("Number of classes: "' + r' + Object.getOwnPropertyNames($$).length)')), + + js('var hasOwnProperty = Object.prototype.hasOwnProperty'), + + js.if_('typeof dart_precompiled == "function"', + [js('var constructors = dart_precompiled(collectedClasses)')], + + [js('var combinedConstructorFunction = "function \$reflectable(fn){' + 'fn.$reflectableField=1;return fn};\\n"+ "var \$desc;\\n"'), + js('var constructorsList = []')]), + js.forIn('cls', 'collectedClasses', [ + js.if_('hasOwnProperty.call(collectedClasses, cls)', [ + js('var desc = collectedClasses[cls]'), + js.if_('desc instanceof Array', js('desc = desc[1]')), + + /* The 'fields' are either a constructor function or a + * string encoding fields, constructor and superclass. Get + * the superclass and the fields in the format + * '[name/]Super;field1,field2' + * from the CLASS_DESCRIPTOR_PROPERTY property on the descriptor. + * The 'name/' is optional and contains the name that should be used + * when printing the runtime type string. It is used, for example, to + * print the runtime type JSInt as 'int'. + */ + js('var classData = desc["${namer.classDescriptorProperty}"], ' + 'supr, name = cls, fields = classData'), + optional( + backend.hasRetainedMetadata, + js.if_('typeof classData == "object" && ' + 'classData instanceof Array', + [js('classData = fields = classData[0]')])), + + js.if_('typeof classData == "string"', [ + js('var split = classData.split("/")'), + js.if_('split.length == 2', [ + js('name = split[0]'), + js('fields = split[1]') + ]) + ]), + + js('var s = fields.split(";")'), + js('fields = s[1] == "" ? [] : s[1].split(",")'), + js('supr = s[0]'), + js('split = supr.split(":")'), + js.if_('split.length == 2', [ + js('supr = split[0]'), + js('var functionSignature = split[1]'), + js.if_('functionSignature', + js('desc.\$signature = #', + js.fun('s', + js.return_(js.fun([], js.return_('init.metadata[s]'))))( + js('functionSignature')))) + ]), + + optional(needsMixinSupport, js.if_('supr && supr.indexOf("+") > 0', [ + js('s = supr.split("+")'), + js('supr = s[0]'), + js('var mixin = collectedClasses[s[1]]'), + js.if_('mixin instanceof Array', js('mixin = mixin[1]')), + js.forIn('d', 'mixin', [ + js.if_('hasOwnProperty.call(mixin, d)' + '&& !hasOwnProperty.call(desc, d)', + js('desc[d] = mixin[d]')) + ]), + ])), + + js.if_('typeof dart_precompiled != "function"', + [js('combinedConstructorFunction +=' + ' defineClass(name, cls, fields)'), + js('constructorsList.push(cls)')]), + js.if_('supr', js('pendingClasses[cls] = supr')) + ]) + ]), + js.if_('typeof dart_precompiled != "function"', + [js('combinedConstructorFunction +=' + ' "return [\\n " + constructorsList.join(",\\n ") + "\\n]"'), + js('var constructors =' + ' new Function("\$collectedClasses", combinedConstructorFunction)' + '(collectedClasses)'), + js('combinedConstructorFunction = null')]), + js.for_('var i = 0', 'i < constructors.length', 'i++', [ + js('var constructor = constructors[i]'), + js('var cls = constructor.name'), + js('var desc = collectedClasses[cls]'), + js('var globalObject = isolateProperties'), + js.if_('desc instanceof Array', [ + js('globalObject = desc[0] || isolateProperties'), + js('desc = desc[1]') + ]), + optional(backend.isTreeShakingDisabled, + js('constructor["${namer.metadataField}"] = desc')), + js('allClasses[cls] = constructor'), + js('globalObject[cls] = constructor'), + ]), + js('constructors = null'), + + js('var finishedClasses = {}'), + js('init.interceptorsByTag = Object.create(null)'), + js('init.leafTags = {}'), + + buildFinishClass(), + ]; + + nsmEmitter.addTrivialNsmHandlers(statements); + + statements.add( + js.forIn('cls', 'pendingClasses', js('finishClass(cls)')) + ); + // function(collectedClasses, + // isolateProperties, + // existingIsolateProperties) + return js.fun(['collectedClasses', 'isolateProperties', + 'existingIsolateProperties'], statements); + } + + jsAst.Node optional(bool condition, jsAst.Node node) { + return condition ? node : new jsAst.EmptyStatement(); + } + + jsAst.FunctionDeclaration buildFinishClass() { + String specProperty = '"${namer.nativeSpecProperty}"'; // "%" + + // function finishClass(cls) { + jsAst.Fun fun = js.fun(['cls'], [ + + // TODO(8540): Remove this work around. + /* Opera does not support 'getOwnPropertyNames'. Therefore we use + hasOwnProperty instead. */ + js('var hasOwnProperty = Object.prototype.hasOwnProperty'), + + // if (hasOwnProperty.call(finishedClasses, cls)) return; + js.if_('hasOwnProperty.call(finishedClasses, cls)', + js.return_()), + + js('finishedClasses[cls] = true'), + + js('var superclass = pendingClasses[cls]'), + + // The superclass is only false (empty string) for Dart's Object class. + // The minifier together with noSuchMethod can put methods on the + // Object.prototype object, and they show through here, so we check that + // we have a string. + js.if_('!superclass || typeof superclass != "string"', js.return_()), + js('finishClass(superclass)'), + js('var constructor = allClasses[cls]'), + js('var superConstructor = allClasses[superclass]'), + + js.if_(js('!superConstructor'), + js('superConstructor =' + 'existingIsolateProperties[superclass]')), + + js('var prototype = inheritFrom(constructor, superConstructor)'), + + optional(!nativeClasses.isEmpty, + // The property looks like this: + // + // HtmlElement: { + // "%": "HTMLDivElement|HTMLAnchorElement;HTMLElement;FancyButton" + // + // The first two semicolon-separated parts contain dispatch tags, the + // third contains the JavaScript names for classes. + // + // The tags indicate that JavaScript objects with the dispatch tags + // (usually constructor names) HTMLDivElement, HTMLAnchorElement and + // HTMLElement all map to the Dart native class named HtmlElement. + // The first set is for effective leaf nodes in the hierarchy, the + // second set is non-leaf nodes. + // + // The third part contains the JavaScript names of Dart classes that + // extend the native class. Here, FancyButton extends HtmlElement, so + // the runtime needs to know that window.HTMLElement.prototype is the + // prototype that needs to be extended in creating the custom element. + // + // The information is used to build tables referenced by + // getNativeInterceptor and custom element support. + js.if_('hasOwnProperty.call(prototype, $specProperty)', [ + js('var nativeSpec = prototype[$specProperty].split(";")'), + js.if_('nativeSpec[0]', [ + js('var tags = nativeSpec[0].split("|")'), + js.for_('var i = 0', 'i < tags.length', 'i++', [ + js('init.interceptorsByTag[tags[i]] = constructor'), + js('init.leafTags[tags[i]] = true')])]), + js.if_('nativeSpec[1]', [ + js('tags = nativeSpec[1].split("|")'), + optional(true, // User subclassing of native classes? + js.if_('nativeSpec[2]', [ + js('var subclasses = nativeSpec[2].split("|")'), + js.for_('var i = 0', 'i < subclasses.length', 'i++', [ + js('var subclass = allClasses[subclasses[i]]'), + js('subclass.\$nativeSuperclassTag = ' + 'tags[0]')])])), + js.for_('i = 0', 'i < tags.length', 'i++', [ + js('init.interceptorsByTag[tags[i]] = constructor'), + js('init.leafTags[tags[i]] = false')])])])) + ]); + + return new jsAst.FunctionDeclaration( + new jsAst.VariableDeclaration('finishClass'), + fun); + } + + jsAst.Fun get finishIsolateConstructorFunction { + // We replace the old Isolate function with a new one that initializes + // all its fields with the initial (and often final) value of all globals. + // + // We also copy over old values like the prototype, and the + // isolateProperties themselves. + return js.fun('oldIsolate', [ + js('var isolateProperties = oldIsolate.${namer.isolatePropertiesName}'), + new jsAst.FunctionDeclaration( + new jsAst.VariableDeclaration('Isolate'), + js.fun([], [ + js('var hasOwnProperty = Object.prototype.hasOwnProperty'), + js.forIn('staticName', 'isolateProperties', + js.if_('hasOwnProperty.call(isolateProperties, staticName)', + js('this[staticName] = isolateProperties[staticName]'))), + // Use the newly created object as prototype. In Chrome, + // this creates a hidden class for the object and makes + // sure it is fast to access. + new jsAst.FunctionDeclaration( + new jsAst.VariableDeclaration('ForceEfficientMap'), + js.fun([], [])), + js('ForceEfficientMap.prototype = this'), + js('new ForceEfficientMap()')])), + js('Isolate.prototype = oldIsolate.prototype'), + js('Isolate.prototype.constructor = Isolate'), + js('Isolate.${namer.isolatePropertiesName} = isolateProperties'), + optional(needsDefineClass, + js('Isolate.$finishClassesProperty =' + ' oldIsolate.$finishClassesProperty')), + optional(hasMakeConstantList, + js('Isolate.makeConstantList = oldIsolate.makeConstantList')), + js.return_('Isolate')]); + } + + jsAst.Fun get lazyInitializerFunction { + // function(prototype, staticName, fieldName, getterName, lazyValue) { + var parameters = ['prototype', 'staticName', 'fieldName', + 'getterName', 'lazyValue']; + return js.fun(parameters, addLazyInitializerLogic()); + } + + List addLazyInitializerLogic() { + String isolate = namer.currentIsolate; + String cyclicThrow = namer.isolateAccess(backend.getCyclicThrowHelper()); + var lazies = []; + if (backend.rememberLazies) { + lazies = [ + js.if_('!init.lazies', js('init.lazies = {}')), + js('init.lazies[fieldName] = getterName')]; + } + + return lazies..addAll([ + js('var sentinelUndefined = {}'), + js('var sentinelInProgress = {}'), + js('prototype[fieldName] = sentinelUndefined'), + + // prototype[getterName] = function() + js('prototype[getterName] = #', js.fun([], [ + js('var result = $isolate[fieldName]'), + + // try + js.try_([ + js.if_('result === sentinelUndefined', [ + js('$isolate[fieldName] = sentinelInProgress'), + + // try + js.try_([ + js('result = $isolate[fieldName] = lazyValue()'), + + ], finallyPart: [ + // Use try-finally, not try-catch/throw as it destroys the + // stack trace. + + // if (result === sentinelUndefined) + js.if_('result === sentinelUndefined', [ + // if ($isolate[fieldName] === sentinelInProgress) + js.if_('$isolate[fieldName] === sentinelInProgress', [ + js('$isolate[fieldName] = null'), + ]) + ]) + ]) + ], /* else */ [ + js.if_('result === sentinelInProgress', + js('$cyclicThrow(staticName)') + ) + ]), + + // return result; + js.return_('result') + + ], finallyPart: [ + js('$isolate[getterName] = #', + js.fun([], [js.return_('this[fieldName]')])) + ]) + ])) + ]); + } + + List buildDefineClassAndFinishClassFunctionsIfNecessary() { + if (!needsDefineClass) return []; + return defineClassFunction + ..addAll(buildInheritFrom()) + ..addAll([ + js('$finishClassesName = #', finishClassesFunction) + ]); + } + + List buildLazyInitializerFunctionIfNecessary() { + if (!needsLazyInitializer) return []; + + return [js('$lazyInitializerName = #', lazyInitializerFunction)]; + } + + List buildFinishIsolateConstructor() { + return [ + js('$finishIsolateConstructorName = #', finishIsolateConstructorFunction) + ]; + } + + void emitFinishIsolateConstructorInvocation(CodeBuffer buffer) { + String isolate = namer.isolateName; + buffer.write("$isolate = $finishIsolateConstructorName($isolate)$N"); + } + + /// Returns the "reflection name" of an [Element] or [Selector]. + /// The reflection name of a getter 'foo' is 'foo'. + /// The reflection name of a setter 'foo' is 'foo='. + /// The reflection name of a method 'foo' is 'foo:N:M:O', where N is the + /// number of required arguments, M is the number of optional arguments, and + /// O is the named arguments. + /// The reflection name of a constructor is similar to a regular method but + /// starts with 'new '. + /// The reflection name of class 'C' is 'C'. + /// An anonymous mixin application has no reflection name. + /// This is used by js_mirrors.dart. + String getReflectionName(elementOrSelector, String mangledName) { + String name = elementOrSelector.name; + if (!backend.shouldRetainName(name)) { + if (name == '' && elementOrSelector is Element) { + // Make sure to retain names of unnamed constructors. + if (!backend.isNeededForReflection(elementOrSelector)) return null; + } else { + return null; + } + } + // TODO(ahe): Enable the next line when I can tell the difference between + // an instance method and a global. They may have the same mangled name. + // if (recordedMangledNames.contains(mangledName)) return null; + recordedMangledNames.add(mangledName); + return getReflectionNameInternal(elementOrSelector, mangledName); + } + + String getReflectionNameInternal(elementOrSelector, String mangledName) { + String name = elementOrSelector.name; + if (elementOrSelector.isGetter()) return name; + if (elementOrSelector.isSetter()) { + if (!mangledName.startsWith(namer.setterPrefix)) return '$name='; + String base = mangledName.substring(namer.setterPrefix.length); + String getter = '${namer.getterPrefix}$base'; + mangledFieldNames[getter] = name; + recordedMangledNames.add(getter); + // TODO(karlklose,ahe): we do not actually need to store information + // about the name of this setter in the output, but it is needed for + // marking the function as invokable by reflection. + return '$name='; + } + if (elementOrSelector is Element && elementOrSelector.isClosure()) { + // Closures are synthesized and their name might conflict with existing + // globals. Assign an illegal name, and make sure they don't clash + // with each other. + return " $mangledName"; + } + if (elementOrSelector is Selector + || elementOrSelector.isFunction() + || elementOrSelector.isConstructor()) { + int requiredParameterCount; + int optionalParameterCount; + String namedArguments = ''; + bool isConstructor = false; + if (elementOrSelector is Selector) { + Selector selector = elementOrSelector; + requiredParameterCount = selector.argumentCount; + optionalParameterCount = 0; + namedArguments = namedParametersAsReflectionNames(selector); + } else { + FunctionElement function = elementOrSelector; + if (function.isConstructor()) { + isConstructor = true; + name = Elements.reconstructConstructorName(function); + } + FunctionSignature signature = function.functionSignature; + requiredParameterCount = signature.requiredParameterCount; + optionalParameterCount = signature.optionalParameterCount; + if (signature.optionalParametersAreNamed) { + var names = []; + for (Element e in signature.optionalParameters) { + names.add(e.name); + } + Selector selector = new Selector.call( + function.name, + function.getLibrary(), + requiredParameterCount, + names); + namedArguments = namedParametersAsReflectionNames(selector); + } else { + // Named parameters are handled differently by mirrors. For unnamed + // parameters, they are actually required if invoked + // reflectively. Also, if you have a method c(x) and c([x]) they both + // get the same mangled name, so they must have the same reflection + // name. + requiredParameterCount += optionalParameterCount; + optionalParameterCount = 0; + } + } + String suffix = + // TODO(ahe): We probably don't need optionalParameterCount in the + // reflection name. + '$name:$requiredParameterCount:$optionalParameterCount' + '$namedArguments'; + return (isConstructor) ? 'new $suffix' : suffix; + } + Element element = elementOrSelector; + if (element.isGenerativeConstructorBody()) { + return null; + } else if (element.isClass()) { + ClassElement cls = element; + if (cls.isUnnamedMixinApplication) return null; + return cls.name; + } + throw compiler.internalError(element, + 'Do not know how to reflect on this $element.'); + } + + String namedParametersAsReflectionNames(Selector selector) { + if (selector.getOrderedNamedArguments().isEmpty) return ''; + String names = selector.getOrderedNamedArguments().join(':'); + return ':$names'; + } + + jsAst.FunctionDeclaration buildPrecompiledFunction() { + // TODO(ahe): Compute a hash code. + String name = 'dart_precompiled'; + + precompiledFunction.add( + js.return_( + new jsAst.ArrayInitializer.from(precompiledConstructorNames))); + precompiledFunction.insert(0, js(r'var $desc')); + return new jsAst.FunctionDeclaration( + new jsAst.VariableDeclaration(name), + js.fun([r'$collectedClasses'], precompiledFunction)); + } + + void generateClass(ClassElement classElement, ClassBuilder properties) { + compiler.withCurrentElement(classElement, () { + classEmitter.generateClass( + classElement, properties, additionalProperties[classElement]); + }); + } + + /** + * Return a function that returns true if its argument is a class + * that needs to be emitted. + */ + Function computeClassFilter() { + if (backend.isTreeShakingDisabled) return (ClassElement cls) => true; + + Set unneededClasses = new Set(); + // The [Bool] class is not marked as abstract, but has a factory + // constructor that always throws. We never need to emit it. + unneededClasses.add(compiler.boolClass); + + // Go over specialized interceptors and then constants to know which + // interceptors are needed. + Set needed = new Set(); + backend.specializedGetInterceptors.forEach( + (_, Iterable elements) { + needed.addAll(elements); + } + ); + + // Add interceptors referenced by constants. + needed.addAll(interceptorEmitter.interceptorsReferencedFromConstants()); + + // Add unneeded interceptors to the [unneededClasses] set. + for (ClassElement interceptor in backend.interceptedClasses) { + if (!needed.contains(interceptor) + && interceptor != compiler.objectClass) { + unneededClasses.add(interceptor); + } + } + + // These classes are just helpers for the backend's type system. + unneededClasses.add(backend.jsMutableArrayClass); + unneededClasses.add(backend.jsFixedArrayClass); + unneededClasses.add(backend.jsExtendableArrayClass); + unneededClasses.add(backend.jsUInt32Class); + unneededClasses.add(backend.jsUInt31Class); + unneededClasses.add(backend.jsPositiveIntClass); + unneededClasses.add(compiler.dynamicClass); + + return (ClassElement cls) => !unneededClasses.contains(cls); + } + + void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) { + if (needsDefineClass) { + buffer.write('$finishClassesName($classesCollector,' + '$_$isolateProperties,' + '${_}null)$N'); + + // Reset the map. + buffer.write("$classesCollector$_=${_}null$N$n"); + } + } + + void emitStaticFunctions() { + bool isStaticFunction(Element element) => + !element.isInstanceMember() && !element.isField(); + + Iterable elements = + backend.generatedCode.keys.where(isStaticFunction); + + for (Element element in Elements.sortedByPosition(elements)) { + ClassBuilder builder = new ClassBuilder(namer); + containerBuilder.addMember(element, builder); + getElementDecriptor(element).properties.addAll(builder.properties); + } + } + + void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) { + ConstantHandler handler = compiler.constantHandler; + Iterable staticNonFinalFields = + handler.getStaticNonFinalFieldsForEmission(); + for (Element element in Elements.sortedByPosition(staticNonFinalFields)) { + // [:interceptedNames:] is handled in [emitInterceptedNames]. + if (element == backend.interceptedNames) continue; + // `mapTypeToInterceptor` is handled in [emitMapTypeToInterceptor]. + if (element == backend.mapTypeToInterceptor) continue; + compiler.withCurrentElement(element, () { + Constant initialValue = handler.getInitialValueFor(element); + jsAst.Expression init = + js('$isolateProperties.${namer.getNameOfGlobalField(element)} = #', + constantEmitter.referenceInInitializationContext(initialValue)); + buffer.write(jsAst.prettyPrint(init, compiler)); + buffer.write('$N'); + }); + } + } + + void emitLazilyInitializedStaticFields(CodeBuffer buffer) { + ConstantHandler handler = compiler.constantHandler; + List lazyFields = + handler.getLazilyInitializedFieldsForEmission(); + if (!lazyFields.isEmpty) { + needsLazyInitializer = true; + for (VariableElement element in Elements.sortedByPosition(lazyFields)) { + jsAst.Expression code = backend.generatedCode[element]; + // The code is null if we ended up not needing the lazily + // initialized field after all because of constant folding + // before code generation. + if (code == null) continue; + // The code only computes the initial value. We build the lazy-check + // here: + // lazyInitializer(prototype, 'name', fieldName, getterName, initial); + // The name is used for error reporting. The 'initial' must be a + // closure that constructs the initial value. + List arguments = []; + arguments.add(js(isolateProperties)); + arguments.add(js.string(element.name)); + arguments.add(js.string(namer.getNameX(element))); + arguments.add(js.string(namer.getLazyInitializerName(element))); + arguments.add(code); + jsAst.Expression getter = buildLazyInitializedGetter(element); + if (getter != null) { + arguments.add(getter); + } + jsAst.Expression init = js(lazyInitializerName)(arguments); + buffer.write(jsAst.prettyPrint(init, compiler)); + buffer.write("$N"); + } + } + } + + jsAst.Expression buildLazyInitializedGetter(VariableElement element) { + // Nothing to do, the 'lazy' function will create the getter. + return null; + } + + void emitCompileTimeConstants(CodeBuffer buffer, OutputUnit outputUnit) { + ConstantHandler handler = compiler.constantHandler; + List constants = handler.getConstantsForEmission( + compareConstants); + Set outputUnitConstants = null; + // TODO(sigurdm): We shouldn't run through all constants for every + // outputUnit. + for (Constant constant in constants) { + if (isConstantInlinedOrAlreadyEmitted(constant)) continue; + OutputUnit constantUnit = + compiler.deferredLoadTask.outputUnitForConstant(constant); + if (constantUnit != outputUnit && constantUnit != null) continue; + if (outputUnit != compiler.deferredLoadTask.mainOutputUnit + && constantUnit == null) { + // The back-end introduces some constants, like "InterceptorConstant" or + // some list constants. They are emitted in the main output-unit, and + // ignored otherwise. + // TODO(sigurdm): We should track those constants. + continue; + } + + String name = namer.constantName(constant); + if (constant.isList) emitMakeConstantListIfNotEmitted(buffer); + jsAst.Expression init = js( + '${namer.globalObjectForConstant(constant)}.$name = #', + constantInitializerExpression(constant)); + buffer.write(jsAst.prettyPrint(init, compiler)); + buffer.write('$N'); + } + } + + bool isConstantInlinedOrAlreadyEmitted(Constant constant) { + if (constant.isFunction) return true; // Already emitted. + if (constant.isPrimitive) return true; // Inlined. + if (constant.isDummy) return true; // Inlined. + // The name is null when the constant is already a JS constant. + // TODO(floitsch): every constant should be registered, so that we can + // share the ones that take up too much space (like some strings). + if (namer.constantName(constant) == null) return true; + return false; + } + + int compareConstants(Constant a, Constant b) { + // Inlined constants don't affect the order and sometimes don't even have + // names. + int cmp1 = isConstantInlinedOrAlreadyEmitted(a) ? 0 : 1; + int cmp2 = isConstantInlinedOrAlreadyEmitted(b) ? 0 : 1; + if (cmp1 + cmp2 < 2) return cmp1 - cmp2; + // Sorting by the long name clusters constants with the same constructor + // which compresses a tiny bit better. + int r = namer.constantLongName(a).compareTo(namer.constantLongName(b)); + if (r != 0) return r; + // Resolve collisions in the long name by using the constant name (i.e. JS + // name) which is unique. + return namer.constantName(a).compareTo(namer.constantName(b)); + } + + void emitMakeConstantListIfNotEmitted(CodeBuffer buffer) { + if (hasMakeConstantList) return; + hasMakeConstantList = true; + buffer + ..write(namer.isolateName) + ..write('''.makeConstantList = function(list) { + list.immutable\$list = $initName; + list.fixed\$length = $initName; + return list; +}; +'''); + } + + /// Returns the code equivalent to: + /// `function(args) { $.startRootIsolate(X.main$closure(), args); }` + String buildIsolateSetupClosure(CodeBuffer buffer, + Element appMain, + Element isolateMain) { + String mainAccess = "${namer.isolateStaticClosureAccess(appMain)}"; + // Since we pass the closurized version of the main method to + // the isolate method, we must make sure that it exists. + return "(function(a){${namer.isolateAccess(isolateMain)}($mainAccess,a)})"; + } + + /** + * Emits code that sets `init.isolateTag` to a unique string. + */ + jsAst.Expression generateIsolateAffinityTagInitialization() { + return js('!#', js.fun([], [ + + // On V8, the 'intern' function converts a string to a symbol, which + // makes property access much faster. + new jsAst.FunctionDeclaration(new jsAst.VariableDeclaration('intern'), + js.fun(['s'], [ + js('var o = {}'), + js('o[s] = 1'), + js.return_(js('Object.keys(convertToFastObject(o))[0]'))])), + + + js('init.getIsolateTag = #', + js.fun(['name'], + js.return_('intern("___dart_" + name + init.isolateTag)'))), + + // To ensure that different programs loaded into the same context (page) + // use distinct dispatch properies, we place an object on `Object` to + // contain the names already in use. + js('var tableProperty = "___dart_isolate_tags_"'), + js('var usedProperties = Object[tableProperty] ||' + '(Object[tableProperty] = Object.create(null))'), + + js('var rootProperty = "_${generateIsolateTagRoot()}"'), + js.for_('var i = 0', null, 'i++', [ + js('var property = intern(rootProperty + "_" + i + "_")'), + js.if_('!(property in usedProperties)', [ + js('usedProperties[property] = 1'), + js('init.isolateTag = property'), + new jsAst.Break(null)])])]) + ()); + + } + + jsAst.Expression generateDispatchPropertyNameInitialization() { + return js( + 'init.dispatchPropertyName = init.getIsolateTag("dispatch_record")'); + } + + String generateIsolateTagRoot() { + // TODO(sra): MD5 of contributing source code or URIs? + return 'ZxYxX'; + } + + emitMain(CodeBuffer buffer) { + if (compiler.isMockCompilation) return; + Element main = compiler.mainFunction; + String mainCallClosure = null; + if (compiler.hasIsolateSupport()) { + Element isolateMain = + compiler.isolateHelperLibrary.find(Compiler.START_ROOT_ISOLATE); + mainCallClosure = buildIsolateSetupClosure(buffer, main, isolateMain); + } else { + mainCallClosure = '${namer.isolateAccess(main)}'; + } + + if (backend.needToInitializeIsolateAffinityTag) { + buffer.write( + jsAst.prettyPrint(generateIsolateAffinityTagInitialization(), + compiler)); + buffer.write(N); + } + if (backend.needToInitializeDispatchProperty) { + assert(backend.needToInitializeIsolateAffinityTag); + buffer.write( + jsAst.prettyPrint(generateDispatchPropertyNameInitialization(), + compiler)); + buffer.write(N); + } + + addComment('BEGIN invoke [main].', buffer); + // This code finds the currently executing script by listening to the + // onload event of all script tags and getting the first script which + // finishes. Since onload is called immediately after execution this should + // not substantially change execution order. + buffer.write(''' +;(function (callback) { + if (typeof document === "undefined") { + callback(null); + return; + } + if (document.currentScript) { + callback(document.currentScript); + return; + } + + var scripts = document.scripts; + function onLoad(event) { + for (var i = 0; i < scripts.length; ++i) { + scripts[i].removeEventListener("load", onLoad, false); + } + callback(event.target); + } + for (var i = 0; i < scripts.length; ++i) { + scripts[i].addEventListener("load", onLoad, false); + } +})(function(currentScript) { + init.currentScript = currentScript; + + if (typeof dartMainRunner === "function") { + dartMainRunner(${mainCallClosure}, []); + } else { + ${mainCallClosure}([]); + } +})$N'''); + addComment('END invoke [main].', buffer); + } + + /** + * Compute all the classes that must be emitted. + */ + void computeNeededClasses() { + instantiatedClasses = + compiler.codegenWorld.instantiatedClasses.where(computeClassFilter()) + .toSet(); + + void addClassWithSuperclasses(ClassElement cls) { + neededClasses.add(cls); + for (ClassElement superclass = cls.superclass; + superclass != null; + superclass = superclass.superclass) { + neededClasses.add(superclass); + } + } + + void addClassesWithSuperclasses(Iterable classes) { + for (ClassElement cls in classes) { + addClassWithSuperclasses(cls); + } + } + + // 1. We need to generate all classes that are instantiated. + addClassesWithSuperclasses(instantiatedClasses); + + // 2. Add all classes used as mixins. + Set mixinClasses = neededClasses + .where((ClassElement element) => element.isMixinApplication) + .map(computeMixinClass) + .toSet(); + neededClasses.addAll(mixinClasses); + + // 3. If we need noSuchMethod support, we run through all needed + // classes to figure out if we need the support on any native + // class. If so, we let the native emitter deal with it. + if (compiler.enabledNoSuchMethod) { + String noSuchMethodName = Compiler.NO_SUCH_METHOD; + Selector noSuchMethodSelector = compiler.noSuchMethodSelector; + for (ClassElement element in neededClasses) { + if (!element.isNative()) continue; + Element member = element.lookupLocalMember(noSuchMethodName); + if (member == null) continue; + if (noSuchMethodSelector.applies(member, compiler)) { + nativeEmitter.handleNoSuchMethod = true; + break; + } + } + } + + // 4. Find all classes needed for rti. + // It is important that this is the penultimate step, at this point, + // neededClasses must only contain classes that have been resolved and + // codegen'd. The rtiNeededClasses may contain additional classes, but + // these are thought to not have been instantiated, so we neeed to be able + // to identify them later and make sure we only emit "empty shells" without + // fields, etc. + typeTestEmitter.computeRtiNeededClasses(); + typeTestEmitter.rtiNeededClasses.removeAll(neededClasses); + // rtiNeededClasses now contains only the "empty shells". + neededClasses.addAll(typeTestEmitter.rtiNeededClasses); + + // 5. Finally, sort the classes. + List sortedClasses = Elements.sortedByPosition(neededClasses); + + for (ClassElement element in sortedClasses) { + if (typeTestEmitter.rtiNeededClasses.contains(element)) { + // TODO(sigurdm): We might be able to defer some of these. + outputClassLists.putIfAbsent(compiler.deferredLoadTask.mainOutputUnit, + () => new List()).add(element); + } else if (Elements.isNativeOrExtendsNative(element)) { + // For now, native classes and related classes cannot be deferred. + nativeClasses.add(element); + if (!element.isNative()) { + assert(invariant(element, + !compiler.deferredLoadTask.isDeferred(element))); + outputClassLists.putIfAbsent(compiler.deferredLoadTask.mainOutputUnit, + () => new List()).add(element); + } + } else { + outputClassLists.putIfAbsent( + compiler.deferredLoadTask.outputUnitForElement(element), + () => new List()) + .add(element); + } + } + } + + void emitInitFunction(CodeBuffer buffer) { + jsAst.Fun fun = js.fun([], [ + js('$isolateProperties = {}'), + ] + ..addAll(buildDefineClassAndFinishClassFunctionsIfNecessary()) + ..addAll(buildLazyInitializerFunctionIfNecessary()) + ..addAll(buildFinishIsolateConstructor()) + ); + jsAst.FunctionDeclaration decl = new jsAst.FunctionDeclaration( + new jsAst.VariableDeclaration('init'), fun); + buffer.write(jsAst.prettyPrint(decl, compiler).getText()); + if (compiler.enableMinification) buffer.write('\n'); + } + + void emitConvertToFastObjectFunction() { + // Create an instance that uses 'properties' as prototype. This should make + // 'properties' a fast object. + mainBuffer.add(r'''function convertToFastObject(properties) { + function MyClass() {}; + MyClass.prototype = properties; + new MyClass(); +'''); + if (DEBUG_FAST_OBJECTS) { + ClassElement primitives = + compiler.findHelper('Primitives'); + FunctionElement printHelper = + compiler.lookupElementIn( + primitives, 'printString'); + String printHelperName = namer.isolateAccess(printHelper); + mainBuffer.add(''' +// The following only works on V8 when run with option "--allow-natives-syntax". +if (typeof $printHelperName === "function") { + $printHelperName("Size of global object: " + + String(Object.getOwnPropertyNames(properties).length) + + ", fast properties " + %HasFastProperties(properties)); +} +'''); + } +mainBuffer.add(r''' + return properties; +} +'''); + } + + void writeLibraryDescriptors(LibraryElement library) { + var uri = library.canonicalUri; + if (uri.scheme == 'file' && compiler.sourceMapUri != null) { + // TODO(ahe): It is a hack to use compiler.sourceMapUri + // here. It should be relative to the main JavaScript + // output file. + uri = relativize( + compiler.sourceMapUri, library.canonicalUri, false); + } + Map descriptors = + elementDescriptors[library]; + + for (OutputUnit outputUnit in compiler.deferredLoadTask.allOutputUnits) { + ClassBuilder descriptor = + descriptors.putIfAbsent(outputUnit, () => new ClassBuilder(namer)); + if (descriptor.properties.isEmpty) continue; + bool isDeferred = + outputUnit != compiler.deferredLoadTask.mainOutputUnit; + jsAst.Fun metadata = metadataEmitter.buildMetadataFunction(library); + + jsAst.ObjectInitializer initializers = + descriptor.toObjectInitializer(); + CodeBuffer outputBuffer = + outputBuffers.putIfAbsent(outputUnit, () => new CodeBuffer()); + int sizeBefore = outputBuffer.length; + outputBuffers[outputUnit] + ..write('["${library.getLibraryName()}",$_') + ..write('"${uri}",$_') + ..write(metadata == null ? "" : jsAst.prettyPrint(metadata, compiler)) + ..write(',$_') + ..write(namer.globalObjectFor(library)) + ..write(',$_') + ..write(jsAst.prettyPrint(initializers, compiler)) + ..write(library == compiler.mainApp ? ',${n}1' : "") + ..write('],$n'); + int sizeAfter = outputBuffer.length; + compiler.dumpInfoTask.codeSizeCounter + .countCode(library, sizeAfter - sizeBefore); + } + } + + String assembleProgram() { + measure(() { + // Compute the required type checks to know which classes need a + // 'is$' method. + typeTestEmitter.computeRequiredTypeChecks(); + + computeNeededClasses(); + + mainBuffer.add(buildGeneratedBy()); + addComment(HOOKS_API_USAGE, mainBuffer); + + if (!compiler.deferredLoadTask.splitProgram) { + mainBuffer.add('(function(${namer.currentIsolate})$_{$n'); + } + + // Using a named function here produces easier to read stack traces in + // Chrome/V8. + mainBuffer.add('function dart(){${_}this.x$_=${_}0$_}'); + for (String globalObject in Namer.reservedGlobalObjectNames) { + // The global objects start as so-called "slow objects". For V8, this + // means that it won't try to make map transitions as we add properties + // to these objects. Later on, we attempt to turn these objects into + // fast objects by calling "convertToFastObject" (see + // [emitConvertToFastObjectFunction]). + mainBuffer + ..write('var ${globalObject}$_=${_}new dart$N') + ..write('delete ${globalObject}.x$N'); + } + + mainBuffer.add('function ${namer.isolateName}()$_{}\n'); + mainBuffer.add('init()$N$n'); + // Shorten the code by using [namer.currentIsolate] as temporary. + isolateProperties = namer.currentIsolate; + mainBuffer.add( + '$isolateProperties$_=$_$isolatePropertiesName$N'); + + emitStaticFunctions(); + + // Only output the classesCollector if we actually have any classes. + if (!(nativeClasses.isEmpty && + compiler.codegenWorld.staticFunctionsNeedingGetter.isEmpty && + outputClassLists.values.every((classList) => classList.isEmpty))) { + // Shorten the code by using "$$" as temporary. + classesCollector = r"$$"; + mainBuffer.add('var $classesCollector$_=$_{}$N$n'); + } + + // Emit native classes on [nativeBuffer]. + // Might create methodClosures. + final CodeBuffer nativeBuffer = new CodeBuffer(); + if (!nativeClasses.isEmpty) { + addComment('Native classes', nativeBuffer); + addComment('Native classes', mainBuffer); + nativeEmitter.generateNativeClasses(nativeClasses, mainBuffer, + additionalProperties); + } + + // As a side-effect, emitting classes will produce "bound closures" in + // [methodClosures]. The bound closures are JS AST nodes that add + // properties to $$ [classesCollector]. The bound closures are not + // emitted until we have emitted all other classes (native or not). + + // Might create methodClosures. + for (List outputClassList in outputClassLists.values) { + for (ClassElement element in outputClassList) { + generateClass(element, getElementDecriptor(element)); + } + } + + nativeEmitter.finishGenerateNativeClasses(); + nativeEmitter.assembleCode(nativeBuffer); + + + // After this assignment we will produce invalid JavaScript code if we use + // the classesCollector variable. + classesCollector = 'classesCollector should not be used from now on'; + + // TODO(sigurdm): Need to check this for each outputUnit. + if (!elementDescriptors.isEmpty) { + var oldClassesCollector = classesCollector; + classesCollector = r"$$"; + if (compiler.enableMinification) { + mainBuffer.write(';'); + } + + for (Element element in elementDescriptors.keys) { + // TODO(ahe): Should iterate over all libraries. Otherwise, we will + // not see libraries that only have fields. + if (element.isLibrary()) { + LibraryElement library = element; + ClassBuilder builder = new ClassBuilder(namer); + if (classEmitter.emitFields( + library, builder, null, emitStatics: true)) { + OutputUnit mainUnit = compiler.deferredLoadTask.mainOutputUnit; + getElementDescriptorForOutputUnit(library, mainUnit) + .properties.addAll(builder.toObjectInitializer().properties); + } + } + } + + if (!mangledFieldNames.isEmpty) { + var keys = mangledFieldNames.keys.toList(); + keys.sort(); + var properties = []; + for (String key in keys) { + var value = js.string('${mangledFieldNames[key]}'); + properties.add(new jsAst.Property(js.string(key), value)); + } + var map = new jsAst.ObjectInitializer(properties); + mainBuffer.write( + jsAst.prettyPrint( + js('init.mangledNames = #', map).toStatement(), compiler)); + if (compiler.enableMinification) { + mainBuffer.write(';'); + } + } + if (!mangledGlobalFieldNames.isEmpty) { + var keys = mangledGlobalFieldNames.keys.toList(); + keys.sort(); + var properties = []; + for (String key in keys) { + var value = js.string('${mangledGlobalFieldNames[key]}'); + properties.add(new jsAst.Property(js.string(key), value)); + } + var map = new jsAst.ObjectInitializer(properties); + mainBuffer.write( + jsAst.prettyPrint( + js('init.mangledGlobalNames = #', map).toStatement(), + compiler)); + if (compiler.enableMinification) { + mainBuffer.write(';'); + } + } + mainBuffer + ..write(getReflectionDataParser(classesCollector, backend)) + ..write('([$n'); + + List sortedElements = + Elements.sortedByPosition(elementDescriptors.keys); + + Iterable pendingStatics = sortedElements.where((element) { + return !element.isLibrary() && + elementDescriptors[element].values.any((descriptor) => + descriptor != null); + }); + + pendingStatics.forEach((element) => + compiler.reportInfo( + element, MessageKind.GENERIC, {'text': 'Pending statics.'})); + + for (LibraryElement library in sortedElements.where((element) => + element.isLibrary())) { + writeLibraryDescriptors(library); + elementDescriptors[library] = const {}; + } + if (!pendingStatics.isEmpty) { + compiler.internalError(pendingStatics.first, + 'Pending statics (see above).'); + } + mainBuffer.write('])$N'); + + emitFinishClassesInvocationIfNecessary(mainBuffer); + classesCollector = oldClassesCollector; + } + OutputUnit mainOutputUnit = compiler.deferredLoadTask.mainOutputUnit; + typeTestEmitter.emitRuntimeTypeSupport(mainBuffer, mainOutputUnit); + interceptorEmitter.emitGetInterceptorMethods(mainBuffer); + interceptorEmitter.emitOneShotInterceptors(mainBuffer); + // Constants in checked mode call into RTI code to set type information + // which may need getInterceptor (and one-shot interceptor) methods, so + // we have to make sure that [emitGetInterceptorMethods] and + // [emitOneShotInterceptors] have been called. + emitCompileTimeConstants(mainBuffer, mainOutputUnit); + + // We write a javascript mapping from DeferredLibrary elements + // (really their String argument) to the js hunk to load. + // TODO(sigurdm): Create a syntax tree for this. + // TODO(sigurdm): Also find out where to place it. + mainBuffer.write("\$.libraries_to_load = {"); + for (String constant in compiler.deferredLoadTask.hunksToLoad.keys) { + // TODO(sigurdm): Escape these strings. + mainBuffer.write('"$constant":['); + for (OutputUnit outputUnit in + compiler.deferredLoadTask.hunksToLoad[constant]) { + mainBuffer.write('"${outputUnit.partFileName(compiler)}.part.js", '); + } + mainBuffer.write("],\n"); + } + mainBuffer.write("}$N"); + // Static field initializations require the classes and compile-time + // constants to be set up. + emitStaticNonFinalFieldInitializations(mainBuffer); + interceptorEmitter.emitInterceptedNames(mainBuffer); + interceptorEmitter.emitMapTypeToInterceptor(mainBuffer); + emitLazilyInitializedStaticFields(mainBuffer); + + mainBuffer.add(nativeBuffer); + + metadataEmitter.emitMetadata(mainBuffer); + + isolateProperties = isolatePropertiesName; + // The following code should not use the short-hand for the + // initialStatics. + mainBuffer.add('${namer.currentIsolate}$_=${_}null$N'); + + emitFinishIsolateConstructorInvocation(mainBuffer); + mainBuffer.add( + '${namer.currentIsolate}$_=${_}new ${namer.isolateName}()$N'); + + emitConvertToFastObjectFunction(); + for (String globalObject in Namer.reservedGlobalObjectNames) { + mainBuffer.add('$globalObject = convertToFastObject($globalObject)$N'); + } + if (DEBUG_FAST_OBJECTS) { + ClassElement primitives = + compiler.findHelper('Primitives'); + FunctionElement printHelper = + compiler.lookupElementIn( + primitives, 'printString'); + String printHelperName = namer.isolateAccess(printHelper); + + mainBuffer.add(''' +// The following only works on V8 when run with option "--allow-natives-syntax". +if (typeof $printHelperName === "function") { + $printHelperName("Size of global helper object: " + + String(Object.getOwnPropertyNames(H).length) + + ", fast properties " + %HasFastProperties(H)); + $printHelperName("Size of global platform object: " + + String(Object.getOwnPropertyNames(P).length) + + ", fast properties " + %HasFastProperties(P)); + $printHelperName("Size of global dart:html object: " + + String(Object.getOwnPropertyNames(W).length) + + ", fast properties " + %HasFastProperties(W)); + $printHelperName("Size of isolate properties object: " + + String(Object.getOwnPropertyNames(\$).length) + + ", fast properties " + %HasFastProperties(\$)); + $printHelperName("Size of constant object: " + + String(Object.getOwnPropertyNames(C).length) + + ", fast properties " + %HasFastProperties(C)); + var names = Object.getOwnPropertyNames(\$); + for (var i = 0; i < names.length; i++) { + $printHelperName("\$." + names[i]); + } +} +'''); + for (String object in Namer.userGlobalObjects) { + mainBuffer.add(''' +if (typeof $printHelperName === "function") { + $printHelperName("Size of $object: " + + String(Object.getOwnPropertyNames($object).length) + + ", fast properties " + %HasFastProperties($object)); +} +'''); + } + } + + emitMain(mainBuffer); + jsAst.FunctionDeclaration precompiledFunctionAst = + buildPrecompiledFunction(); + emitInitFunction(mainBuffer); + if (!compiler.deferredLoadTask.splitProgram) { + mainBuffer.add('})()\n'); + } else { + mainBuffer.add('\n'); + } + String assembledCode = mainBuffer.getText(); + String sourceMapTags = ""; + if (generateSourceMap) { + outputSourceMap(assembledCode, mainBuffer, '', + compiler.sourceMapUri, compiler.outputUri); + sourceMapTags = + generateSourceMapTag(compiler.sourceMapUri, compiler.outputUri); + } + compiler.outputProvider('', 'js') + ..add(assembledCode) + ..add(sourceMapTags) + ..close(); + compiler.assembledCode = assembledCode; + + mainBuffer.write( + jsAst.prettyPrint( + precompiledFunctionAst, compiler, + allowVariableMinification: false).getText()); + + compiler.outputProvider('', 'precompiled.js') + ..add(mainBuffer.getText()) + ..close(); + + emitDeferredCode(); + + }); + return compiler.assembledCode; + } + + String generateSourceMapTag(Uri sourceMapUri, Uri fileUri) { + if (sourceMapUri != null && fileUri != null) { + // Using # is the new proposed standard. @ caused problems in Internet + // Explorer due to "Conditional Compilation Statements" in JScript, + // see: + // http://msdn.microsoft.com/en-us/library/7kx09ct1(v=vs.80).aspx + // About source maps, see: + // https://docs.google.com/a/google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit + // TODO(http://dartbug.com/11914): Remove @ line. + String sourceMapFileName = relativize(fileUri, sourceMapUri, false); + return ''' + +//# sourceMappingURL=$sourceMapFileName +//@ sourceMappingURL=$sourceMapFileName +'''; + } + return ''; + } + + ClassBuilder getElementDescriptorForOutputUnit(Element element, + OutputUnit outputUnit) { + Map descriptors = + elementDescriptors.putIfAbsent( + element, () => new Map()); + return descriptors.putIfAbsent(outputUnit, + () => new ClassBuilder(namer)); + } + + ClassBuilder getElementDecriptor(Element element) { + Element owner = element.getLibrary(); + if (!element.isTopLevel() && !element.isNative()) { + // For static (not top level) elements, record their code in a buffer + // specific to the class. For now, not supported for native classes and + // native elements. + ClassElement cls = + element.getEnclosingClassOrCompilationUnit().declaration; + if (compiler.codegenWorld.instantiatedClasses.contains(cls) + && !cls.isNative()) { + owner = cls; + } + } + if (owner == null) { + compiler.internalError(element, 'Owner is null.'); + } + return getElementDescriptorForOutputUnit(owner, + compiler.deferredLoadTask.outputUnitForElement(element)); + } + + void emitDeferredCode() { + for (OutputUnit outputUnit in compiler.deferredLoadTask.allOutputUnits) { + if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) continue; + CodeBuffer outputBuffer = outputBuffers.putIfAbsent(outputUnit, + () => new CodeBuffer()); + + var oldClassesCollector = classesCollector; + classesCollector = r"$$"; + + var buffer = new CodeBuffer() + ..write(buildGeneratedBy()) + ..write('var old${namer.currentIsolate}$_=' + '$_${namer.currentIsolate}$N' + // TODO(ahe): This defines a lot of properties on the + // Isolate.prototype object. We know this will turn it into a + // slow object in V8, so instead we should do something similar + // to Isolate.$finishIsolateConstructor. + '${namer.currentIsolate}$_=' + '$_${namer.isolateName}.prototype$N$n' + // The classesCollector object ($$). + '$classesCollector$_=$_{};$n') + ..write(getReflectionDataParser(classesCollector, backend)) + ..write('([$n') + ..addBuffer(outputBuffer) + ..write('])$N'); + + if (outputClassLists.containsKey(outputUnit)) { + buffer.write( + '$finishClassesName($classesCollector,$_${namer.currentIsolate},' + '$_$isolatePropertiesName)$N'); + } + + buffer.write( + // Reset the classesCollector ($$). + '$classesCollector$_=${_}null$N$n' + '${namer.currentIsolate}$_=${_}old${namer.currentIsolate}$N'); + + classesCollector = oldClassesCollector; + + typeTestEmitter.emitRuntimeTypeSupport(buffer, outputUnit); + + emitCompileTimeConstants(buffer, outputUnit); + + String code = buffer.getText(); + compiler.outputProvider(outputUnit.partFileName(compiler), 'part.js') + ..add(code) + ..close(); + + // TODO(johnniwinther): Support source maps for deferred code. + } + } + + String buildGeneratedBy() { + var suffix = ''; + if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}'; + return '// Generated by dart2js, the Dart to JavaScript compiler$suffix.\n'; + } + + void outputSourceMap(String code, CodeBuffer buffer, String name, + [Uri sourceMapUri, Uri fileUri]) { + if (!generateSourceMap) return; + // Create a source file for the compilation output. This allows using + // [:getLine:] to transform offsets to line numbers in [SourceMapBuilder]. + SourceFile compiledFile = new StringSourceFile(null, code); + SourceMapBuilder sourceMapBuilder = + new SourceMapBuilder(sourceMapUri, fileUri); + buffer.forEachSourceLocation(sourceMapBuilder.addMapping); + String sourceMap = sourceMapBuilder.build(compiledFile); + compiler.outputProvider(name, 'js.map') + ..add(sourceMap) + ..close(); + } + + void registerReadTypeVariable(TypeVariableElement element) { + readTypeVariables.add(element); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/container_builder.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/container_builder.dart new file mode 100644 index 0000000..eeb19be --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/container_builder.dart @@ -0,0 +1,540 @@ +// Copyright (c) 2013, 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. + +part of dart2js.js_emitter; + +/// This class should morph into something that makes it easy to build +/// JavaScript representations of libraries, class-sides, and instance-sides. +/// Initially, it is just a placeholder for code that is moved from +/// [CodeEmitterTask]. +class ContainerBuilder extends CodeEmitterHelper { + final Map staticGetters = new Map(); + + /// A cache of synthesized closures for top-level, static or + /// instance methods. + final Map methodClosures = {}; + + bool needsSuperGetter(FunctionElement element) => + compiler.codegenWorld.methodsNeedingSuperGetter.contains(element); + + /** + * Generate stubs to handle invocation of methods with optional + * arguments. + * + * A method like [: foo([x]) :] may be invoked by the following + * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this + * function for detailed examples. + */ + void addParameterStub(FunctionElement member, + Selector selector, + AddStubFunction addStub, + Set alreadyGenerated) { + FunctionSignature parameters = member.functionSignature; + int positionalArgumentCount = selector.positionalArgumentCount; + if (positionalArgumentCount == parameters.parameterCount) { + assert(selector.namedArgumentCount == 0); + return; + } + if (parameters.optionalParametersAreNamed + && selector.namedArgumentCount == parameters.optionalParameterCount) { + // If the selector has the same number of named arguments as the element, + // we don't need to add a stub. The call site will hit the method + // directly. + return; + } + ConstantHandler handler = compiler.constantHandler; + List names = selector.getOrderedNamedArguments(); + + String invocationName = namer.invocationName(selector); + if (alreadyGenerated.contains(invocationName)) return; + alreadyGenerated.add(invocationName); + + bool isInterceptedMethod = backend.isInterceptedMethod(member); + + // If the method is intercepted, we need to also pass the actual receiver. + int extraArgumentCount = isInterceptedMethod ? 1 : 0; + // Use '$receiver' to avoid clashes with other parameter names. Using + // '$receiver' works because [:namer.safeName:] used for getting parameter + // names never returns a name beginning with a single '$'. + String receiverArgumentName = r'$receiver'; + + // The parameters that this stub takes. + List parametersBuffer = + new List(selector.argumentCount + extraArgumentCount); + // The arguments that will be passed to the real method. + List argumentsBuffer = + new List( + parameters.parameterCount + extraArgumentCount); + + int count = 0; + if (isInterceptedMethod) { + count++; + parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); + argumentsBuffer[0] = js(receiverArgumentName); + task.interceptorEmitter.interceptorInvocationNames.add(invocationName); + } + + int optionalParameterStart = positionalArgumentCount + extraArgumentCount; + // Includes extra receiver argument when using interceptor convention + int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; + + TreeElements elements = + compiler.enqueuer.resolution.getCachedElements(member); + + int parameterIndex = 0; + parameters.orderedForEachParameter((Element element) { + String jsName = backend.namer.safeName(element.name); + assert(jsName != receiverArgumentName); + if (count < optionalParameterStart) { + parametersBuffer[count] = new jsAst.Parameter(jsName); + argumentsBuffer[count] = js(jsName); + } else { + int index = names.indexOf(element.name); + if (index != -1) { + indexOfLastOptionalArgumentInParameters = count; + // The order of the named arguments is not the same as the + // one in the real method (which is in Dart source order). + argumentsBuffer[count] = js(jsName); + parametersBuffer[optionalParameterStart + index] = + new jsAst.Parameter(jsName); + } else { + Constant value = handler.initialVariableValues[element]; + if (value == null) { + argumentsBuffer[count] = task.constantReference(new NullConstant()); + } else { + if (!value.isNull) { + // If the value is the null constant, we should not pass it + // down to the native method. + indexOfLastOptionalArgumentInParameters = count; + } + argumentsBuffer[count] = task.constantReference(value); + } + } + } + count++; + }); + + List body; + if (member.hasFixedBackendName()) { + body = task.nativeEmitter.generateParameterStubStatements( + member, isInterceptedMethod, invocationName, + parametersBuffer, argumentsBuffer, + indexOfLastOptionalArgumentInParameters); + } else if (member.isInstanceMember()) { + if (needsSuperGetter(member)) { + ClassElement superClass = member.getEnclosingClass(); + String methodName = namer.getNameOfInstanceMember(member); + // When redirecting, we must ensure that we don't end up in a subclass. + // We thus can't just invoke `this.foo$1.call(filledInArguments)`. + // Instead we need to call the statically resolved target. + // `.prototype.bar$1.call(this, argument0, ...)`. + body = [js.return_( + backend.namer.elementAccess(superClass)['prototype'][methodName] + ["call"](["this"]..addAll(argumentsBuffer)))]; + } else { + body = [js.return_( + js('this') + [namer.getNameOfInstanceMember(member)](argumentsBuffer))]; + } + } else { + body = [js.return_(namer.elementAccess(member)(argumentsBuffer))]; + } + + jsAst.Fun function = js.fun(parametersBuffer, body); + + addStub(selector, function); + } + + void addParameterStubs(FunctionElement member, AddStubFunction defineStub, + [bool canTearOff = false]) { + if (member.enclosingElement.isClosure()) { + ClosureClassElement cls = member.enclosingElement; + if (cls.supertype.element == compiler.boundClosureClass) { + compiler.internalError(cls.methodElement, 'Bound closure1.'); + } + if (cls.methodElement.isInstanceMember()) { + compiler.internalError(cls.methodElement, 'Bound closure2.'); + } + } + + // We fill the lists depending on the selector. For example, + // take method foo: + // foo(a, b, {c, d}); + // + // We may have multiple ways of calling foo: + // (1) foo(1, 2); + // (2) foo(1, 2, c: 3); + // (3) foo(1, 2, d: 4); + // (4) foo(1, 2, c: 3, d: 4); + // (5) foo(1, 2, d: 4, c: 3); + // + // What we generate at the call sites are: + // (1) foo$2(1, 2); + // (2) foo$3$c(1, 2, 3); + // (3) foo$3$d(1, 2, 4); + // (4) foo$4$c$d(1, 2, 3, 4); + // (5) foo$4$c$d(1, 2, 3, 4); + // + // The stubs we generate are (expressed in Dart): + // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) + // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); + // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); + // (4) No stub generated, call is direct. + // (5) No stub generated, call is direct. + // + // We need to pay attention if this stub is for a function that has been + // invoked from a subclass. Then we cannot just redirect, since that + // would invoke the methods of the subclass. We have to compile to: + // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null) + // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null); + // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d); + + Set selectors = member.isInstanceMember() + ? compiler.codegenWorld.invokedNames[member.name] + : null; // No stubs needed for static methods. + + /// Returns all closure call selectors renamed to match this member. + Set callSelectorsAsNamed() { + if (!canTearOff) return null; + Set callSelectors = compiler.codegenWorld.invokedNames[ + namer.closureInvocationSelectorName]; + if (callSelectors == null) return null; + return callSelectors.map((Selector callSelector) { + return new Selector.call( + member.name, member.getLibrary(), + callSelector.argumentCount, callSelector.namedArguments); + }).toSet(); + } + if (selectors == null) { + selectors = callSelectorsAsNamed(); + if (selectors == null) return; + } else { + Set callSelectors = callSelectorsAsNamed(); + if (callSelectors != null) { + selectors = selectors.union(callSelectors); + } + } + Set untypedSelectors = new Set(); + if (selectors != null) { + for (Selector selector in selectors) { + if (!selector.appliesUnnamed(member, compiler)) continue; + if (untypedSelectors.add(selector.asUntyped)) { + // TODO(ahe): Is the last argument to [addParameterStub] needed? + addParameterStub(member, selector, defineStub, new Set()); + } + } + } + if (canTearOff) { + selectors = compiler.codegenWorld.invokedNames[ + namer.closureInvocationSelectorName]; + if (selectors != null) { + for (Selector selector in selectors) { + selector = new Selector.call( + member.name, member.getLibrary(), + selector.argumentCount, selector.namedArguments); + if (!selector.appliesUnnamed(member, compiler)) continue; + if (untypedSelectors.add(selector)) { + // TODO(ahe): Is the last argument to [addParameterStub] needed? + addParameterStub(member, selector, defineStub, new Set()); + } + } + } + } + } + + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [member] must be a declaration element. + */ + void emitCallStubForGetter(Element member, + Set selectors, + AddPropertyFunction addProperty) { + assert(invariant(member, member.isDeclaration)); + LibraryElement memberLibrary = member.getLibrary(); + // If the method is intercepted, the stub gets the + // receiver explicitely and we need to pass it to the getter call. + bool isInterceptedMethod = backend.isInterceptedMethod(member); + bool isInterceptorClass = + backend.isInterceptorClass(member.getEnclosingClass()); + + const String receiverArgumentName = r'$receiver'; + + jsAst.Expression buildGetter() { + jsAst.Expression receiver = + js(isInterceptorClass ? receiverArgumentName : 'this'); + if (member.isGetter()) { + String getterName = namer.getterName(member); + if (isInterceptedMethod) { + return js('this')[getterName]([receiver]); + } + return receiver[getterName]([]); + } else { + String fieldName = namer.instanceFieldPropertyName(member); + return receiver[fieldName]; + } + } + + // Two selectors may match but differ only in type. To avoid generating + // identical stubs for each we track untyped selectors which already have + // stubs. + Set generatedSelectors = new Set(); + for (Selector selector in selectors) { + if (selector.applies(member, compiler)) { + selector = selector.asUntyped; + if (generatedSelectors.contains(selector)) continue; + generatedSelectors.add(selector); + + String invocationName = namer.invocationName(selector); + Selector callSelector = new Selector.callClosureFrom(selector); + String closureCallName = namer.invocationName(callSelector); + + List parameters = []; + List arguments = []; + if (isInterceptedMethod) { + parameters.add(new jsAst.Parameter(receiverArgumentName)); + } + + for (int i = 0; i < selector.argumentCount; i++) { + String name = 'arg$i'; + parameters.add(new jsAst.Parameter(name)); + arguments.add(js(name)); + } + + jsAst.Fun function = js.fun( + parameters, + js.return_(buildGetter()[closureCallName](arguments))); + + addProperty(invocationName, function); + } + } + } + + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [member] must be a declaration element. + */ + void emitExtraAccessors(Element member, ClassBuilder builder) { + assert(invariant(member, member.isDeclaration)); + if (member.isGetter() || member.isField()) { + Set selectors = compiler.codegenWorld.invokedNames[member.name]; + if (selectors != null && !selectors.isEmpty) { + emitCallStubForGetter(member, selectors, builder.addProperty); + } + } + } + + void addMember(Element member, ClassBuilder builder) { + assert(invariant(member, member.isDeclaration)); + + if (member.isField()) { + addMemberField(member, builder); + } else if (member.isFunction() || + member.isGenerativeConstructorBody() || + member.isGenerativeConstructor() || + member.isAccessor()) { + addMemberMethod(member, builder); + } else { + compiler.internalError(member, + 'Unexpected kind: "${member.kind}".'); + } + if (member.isInstanceMember()) emitExtraAccessors(member, builder); + } + + void addMemberMethod(FunctionElement member, ClassBuilder builder) { + if (member.isAbstract) return; + jsAst.Expression code = backend.generatedCode[member]; + if (code == null) return; + String name = namer.getNameOfMember(member); + task.interceptorEmitter.recordMangledNameOfMemberMethod(member, name); + FunctionSignature parameters = member.functionSignature; + bool needsStubs = !parameters.optionalParameters.isEmpty; + bool canTearOff = false; + bool isClosure = false; + bool isNotApplyTarget = + !member.isFunction() || member.isConstructor() || member.isAccessor(); + String tearOffName; + if (isNotApplyTarget) { + canTearOff = false; + } else if (member.isInstanceMember()) { + if (member.getEnclosingClass().isClosure()) { + canTearOff = false; + isClosure = true; + } else { + // Careful with operators. + canTearOff = compiler.codegenWorld.hasInvokedGetter(member, compiler); + assert(!needsSuperGetter(member) || canTearOff); + tearOffName = namer.getterName(member); + } + } else { + canTearOff = + compiler.codegenWorld.staticFunctionsNeedingGetter.contains(member); + tearOffName = namer.getStaticClosureName(member); + } + final bool canBeApplied = !isNotApplyTarget && + compiler.enabledFunctionApply && + (canTearOff || member.name == 'call' || !member.isInstanceMember()); + final bool canBeReflected = backend.isAccessibleByReflection(member); + final bool needStructuredInfo = + canTearOff || canBeReflected || canBeApplied; + if (!needStructuredInfo) { + builder.addProperty(name, code); + if (needsStubs) { + addParameterStubs( + member, + (Selector selector, jsAst.Fun function) { + builder.addProperty(namer.invocationName(selector), function); + }); + } + return; + } + + if (canTearOff) { + assert(invariant(member, !member.isGenerativeConstructor())); + assert(invariant(member, !member.isGenerativeConstructorBody())); + assert(invariant(member, !member.isConstructor())); + } + + // This element is needed for reflection or needs additional stubs. So we + // need to retain additional information. + + // The information is stored in an array with this format: + // + // 1. The JS function for this member. + // 2. First stub. + // 3. Name of first stub. + // ... + // M. Call name of this member. + // M+1. Call name of first stub. + // ... + // N. Getter name for tearOff. + // N+1. (Required parameter count << 1) + (member.isAccessor() ? 1 : 0). + // N+2. (Optional parameter count << 1) + + // (parameters.optionalParametersAreNamed ? 1 : 0). + // N+3. Index to function type in constant pool. + // N+4. First default argument. + // ... + // O. First parameter name (if needed for reflection or Function.apply). + // ... + // P. Unmangled name (if reflectable). + // P+1. First metadata (if reflectable). + // ... + // TODO(ahe): Consider one of the parameter counts can be replaced by the + // length property of the JavaScript function object. + + List expressions = []; + + String callSelectorString = 'null'; + if (member.isFunction()) { + Selector callSelector = + new Selector.fromElement(member, compiler).toCallSelector(); + callSelectorString = '"${namer.invocationName(callSelector)}"'; + } + + // On [requiredParameterCount], the lower bit is set if this method can be + // called reflectively. + int requiredParameterCount = parameters.requiredParameterCount << 1; + if (member.isAccessor()) requiredParameterCount++; + + int optionalParameterCount = parameters.optionalParameterCount << 1; + if (parameters.optionalParametersAreNamed) optionalParameterCount++; + + expressions.add(code); + + List tearOffInfo = [new jsAst.LiteralString(callSelectorString)]; + + if (needsStubs || canTearOff) { + addParameterStubs(member, (Selector selector, jsAst.Fun function) { + expressions.add(function); + if (member.isInstanceMember()) { + Set invokedSelectors = + compiler.codegenWorld.invokedNames[member.name]; + expressions.add(js.string(namer.invocationName(selector))); + } else { + expressions.add("null"); + // TOOD(ahe): Since we know when reading static data versus instance + // data, we can eliminate this element. + } + Set callSelectors = compiler.codegenWorld.invokedNames[ + namer.closureInvocationSelectorName]; + Selector callSelector = selector.toCallSelector(); + String callSelectorString = 'null'; + if (canTearOff && callSelectors != null && + callSelectors.contains(callSelector)) { + callSelectorString = '"${namer.invocationName(callSelector)}"'; + } + tearOffInfo.add(new jsAst.LiteralString(callSelectorString)); + }, canTearOff); + } + + jsAst.Expression memberTypeExpression; + if (canTearOff || canBeReflected) { + DartType memberType; + if (member.isGenerativeConstructorBody()) { + var body = member; + memberType = body.constructor.type; + } else { + memberType = member.type; + } + if (memberType.containsTypeVariables) { + jsAst.Expression thisAccess = js(r'this.$receiver'); + memberTypeExpression = + backend.rti.getSignatureEncoding(memberType, thisAccess); + } else { + memberTypeExpression = + js.toExpression(task.metadataEmitter.reifyType(memberType)); + } + } else { + memberTypeExpression = js('null'); + } + + expressions + ..addAll(tearOffInfo) + ..add((tearOffName == null || member.isAccessor()) + ? js("null") : js.string(tearOffName)) + ..add(requiredParameterCount) + ..add(optionalParameterCount) + ..add(memberTypeExpression) + ..addAll(task.metadataEmitter.reifyDefaultArguments(member)); + + if (canBeReflected || canBeApplied) { + parameters.forEachParameter((Element parameter) { + expressions.add(task.metadataEmitter.reifyName(parameter.name)); + if (backend.mustRetainMetadata) { + List annotations = parameter.metadata.toList(); + Iterable metadataIndices = + annotations.map((MetadataAnnotation a) { + compiler.constantHandler.addCompileTimeConstantForEmission(a.value); + return task.metadataEmitter.reifyMetadata(a); + }); + expressions.add(metadataIndices.isNotEmpty ? metadataIndices.toList() + : js('[]')); + } + }); + } + if (canBeReflected) { + jsAst.LiteralString reflectionName; + if (member.isConstructor()) { + String reflectionNameString = task.getReflectionName(member, name); + reflectionName = + new jsAst.LiteralString( + '"new ${Elements.reconstructConstructorName(member)}"'); + } else { + reflectionName = js.string(member.name); + } + expressions + ..add(reflectionName) + ..addAll(task.metadataEmitter.computeMetadata(member)); + } else if (isClosure && canBeApplied) { + expressions.add(js.string(member.name)); + } + + builder.addProperty(name, js.toExpression(expressions)); + } + + void addMemberField(VariableElement member, ClassBuilder builder) { + // For now, do nothing. + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/declarations.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/declarations.dart new file mode 100644 index 0000000..a370cd2 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/declarations.dart @@ -0,0 +1,91 @@ +// Copyright (c) 2013, 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. + +part of dart2js.js_emitter; + +/// Enables debugging of fast/slow objects using V8-specific primitives. +const DEBUG_FAST_OBJECTS = false; + +/** + * Call-back for adding stub [function] for [selector]. + */ +typedef void AddStubFunction(Selector selector, jsAst.Fun function); + +/** + * Call-back for adding property with [name] and [value]. + */ +typedef void AddPropertyFunction(String name, jsAst.Expression value); + +/** + * [member] is a field (instance, static, or top level). + * + * [name] is the field name that the [Namer] has picked for this field's + * storage, that is, the JavaScript property name. + * + * [accessorName] is the name of the accessor. For instance fields this is + * mostly the same as [name] except when [member] is shadowing a field in its + * superclass. For other fields, they are rarely the same. + * + * [needsGetter] and [needsSetter] represent if a getter or a setter + * respectively is needed. There are many factors in this, for example, if the + * accessor can be inlined. + * + * [needsCheckedSetter] indicates that a checked getter is needed, and in this + * case, [needsSetter] is always false. [needsCheckedSetter] is only true when + * type assertions are enabled (checked mode). + */ +typedef void AcceptField(VariableElement member, + String name, + String accessorName, + bool needsGetter, + bool needsSetter, + bool needsCheckedSetter); + +// Function signatures used in the generation of runtime type information. +typedef void FunctionTypeSignatureEmitter(Element method, + FunctionType methodType); + +// TODO(johnniwinther): Clean up terminology for rti in the emitter. +typedef void FunctionTypeTestEmitter(FunctionType functionType); + +typedef void SubstitutionEmitter(Element element, {bool emitNull}); + +const String GENERATED_BY = """ +// Generated by dart2js, the Dart to JavaScript compiler. +"""; + +const String HOOKS_API_USAGE = """ +// The code supports the following hooks: +// dartPrint(message): +// if this function is defined it is called instead of the Dart [print] +// method. +// +// dartMainRunner(main, args): +// if this function is defined, the Dart [main] method will not be invoked +// directly. Instead, a closure that will invoke [main], and its arguments +// [args] is passed to [dartMainRunner]. +"""; + +// Compact field specifications. The format of the field specification is +// : where the suffix and accessor name +// prefix are optional. The suffix directs the generation of getter and +// setter methods. Each of the getter and setter has two bits to determine +// the calling convention. Setter listed below, getter is similar. +// +// 00: no setter +// 01: function(value) { this.field = value; } +// 10: function(receiver, value) { receiver.field = value; } +// 11: function(receiver, value) { this.field = value; } +// +// The suffix encodes 4 bits using three ASCII ranges of non-identifier +// characters. +const FIELD_CODE_CHARACTERS = r"<=>?@{|}~%&'()*"; +const NO_FIELD_CODE = 0; +const FIRST_FIELD_CODE = 1; +const RANGE1_FIRST = 0x3c; // <=>?@ encodes 1..5 +const RANGE1_LAST = 0x40; +const RANGE2_FIRST = 0x7b; // {|}~ encodes 6..9 +const RANGE2_LAST = 0x7e; +const RANGE3_FIRST = 0x25; // %&'()*+ encodes 10..16 +const RANGE3_LAST = 0x2b; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/helpers.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/helpers.dart new file mode 100644 index 0000000..017094b --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/helpers.dart @@ -0,0 +1,14 @@ +// Copyright (c) 2013, 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. + +part of dart2js.js_emitter; + +ClassElement computeMixinClass(MixinApplicationElement mixinApplication) { + ClassElement mixin = mixinApplication.mixin; + while (mixin.isMixinApplication) { + mixinApplication = mixin; + mixin = mixinApplication.mixin; + } + return mixin; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/interceptor_emitter.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/interceptor_emitter.dart new file mode 100644 index 0000000..94fe5e4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/interceptor_emitter.dart @@ -0,0 +1,498 @@ +// Copyright (c) 2013, 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. + +part of dart2js.js_emitter; + +class InterceptorEmitter extends CodeEmitterHelper { + final Set interceptorInvocationNames = new Set(); + + void recordMangledNameOfMemberMethod(FunctionElement member, String name) { + if (backend.isInterceptedMethod(member)) { + interceptorInvocationNames.add(name); + } + } + + Set interceptorsReferencedFromConstants() { + Set classes = new Set(); + ConstantHandler handler = compiler.constantHandler; + List constants = handler.getConstantsForEmission(); + for (Constant constant in constants) { + if (constant is InterceptorConstant) { + InterceptorConstant interceptorConstant = constant; + classes.add(interceptorConstant.dispatchedType.element); + } + } + return classes; + } + + void emitGetInterceptorMethod(CodeBuffer buffer, + String key, + Set classes) { + jsAst.Statement buildReturnInterceptor(ClassElement cls) { + return js.return_(js(namer.isolateAccess(cls))['prototype']); + } + + /** + * Build a JavaScrit AST node for doing a type check on + * [cls]. [cls] must be an interceptor class. + */ + jsAst.Statement buildInterceptorCheck(ClassElement cls) { + jsAst.Expression condition; + assert(backend.isInterceptorClass(cls)); + if (cls == backend.jsBoolClass) { + condition = js('(typeof receiver) == "boolean"'); + } else if (cls == backend.jsIntClass || + cls == backend.jsDoubleClass || + cls == backend.jsNumberClass) { + throw 'internal error'; + } else if (cls == backend.jsArrayClass || + cls == backend.jsMutableArrayClass || + cls == backend.jsFixedArrayClass || + cls == backend.jsExtendableArrayClass) { + condition = js('receiver.constructor == Array'); + } else if (cls == backend.jsStringClass) { + condition = js('(typeof receiver) == "string"'); + } else if (cls == backend.jsNullClass) { + condition = js('receiver == null'); + } else { + throw 'internal error'; + } + return js.if_(condition, buildReturnInterceptor(cls)); + } + + bool hasArray = false; + bool hasBool = false; + bool hasDouble = false; + bool hasInt = false; + bool hasNull = false; + bool hasNumber = false; + bool hasString = false; + bool hasNative = false; + bool anyNativeClasses = compiler.enqueuer.codegen.nativeEnqueuer + .hasInstantiatedNativeClasses(); + + for (ClassElement cls in classes) { + if (cls == backend.jsArrayClass || + cls == backend.jsMutableArrayClass || + cls == backend.jsFixedArrayClass || + cls == backend.jsExtendableArrayClass) hasArray = true; + else if (cls == backend.jsBoolClass) hasBool = true; + else if (cls == backend.jsDoubleClass) hasDouble = true; + else if (cls == backend.jsIntClass) hasInt = true; + else if (cls == backend.jsNullClass) hasNull = true; + else if (cls == backend.jsNumberClass) hasNumber = true; + else if (cls == backend.jsStringClass) hasString = true; + else { + // The set of classes includes classes mixed-in to interceptor classes + // and user extensions of native classes. + // + // The set of classes also includes the 'primitive' interceptor + // PlainJavaScriptObject even when it has not been resolved, since it is + // only resolved through the reference in getNativeInterceptor when + // getNativeInterceptor is marked as used. Guard against probing + // unresolved PlainJavaScriptObject by testing for anyNativeClasses. + + if (anyNativeClasses) { + if (Elements.isNativeOrExtendsNative(cls)) hasNative = true; + } + } + } + if (hasDouble) { + hasNumber = true; + } + if (hasInt) hasNumber = true; + + if (classes.containsAll(backend.interceptedClasses)) { + // I.e. this is the general interceptor. + hasNative = anyNativeClasses; + } + + jsAst.Block block = new jsAst.Block.empty(); + + if (hasNumber) { + jsAst.Statement whenNumber; + + /// Note: there are two number classes in play: Dart's [num], + /// and JavaScript's Number (typeof receiver == 'number'). This + /// is the fallback used when we have determined that receiver + /// is a JavaScript Number. + jsAst.Return returnNumberClass = buildReturnInterceptor( + hasDouble ? backend.jsDoubleClass : backend.jsNumberClass); + + if (hasInt) { + jsAst.Expression isInt = js('Math.floor(receiver) == receiver'); + whenNumber = js.block([ + js.if_(isInt, buildReturnInterceptor(backend.jsIntClass)), + returnNumberClass]); + } else { + whenNumber = returnNumberClass; + } + block.statements.add( + js.if_('(typeof receiver) == "number"', + whenNumber)); + } + + if (hasString) { + block.statements.add(buildInterceptorCheck(backend.jsStringClass)); + } + if (hasNull) { + block.statements.add(buildInterceptorCheck(backend.jsNullClass)); + } else { + // Returning "undefined" or "null" here will provoke a JavaScript + // TypeError which is later identified as a null-error by + // [unwrapException] in js_helper.dart. + block.statements.add(js.if_('receiver == null', + js.return_(js('receiver')))); + } + if (hasBool) { + block.statements.add(buildInterceptorCheck(backend.jsBoolClass)); + } + // TODO(ahe): It might be faster to check for Array before + // function and bool. + if (hasArray) { + block.statements.add(buildInterceptorCheck(backend.jsArrayClass)); + } + + if (hasNative) { + block.statements.add( + js.if_( + js('(typeof receiver) != "object"'), + js.return_(js('receiver')))); + + // if (receiver instanceof $.Object) return receiver; + // return $.getNativeInterceptor(receiver); + block.statements.add( + js.if_(js('receiver instanceof #', + js(namer.isolateAccess(compiler.objectClass))), + js.return_(js('receiver')))); + block.statements.add( + js.return_( + js(namer.isolateAccess(backend.getNativeInterceptorMethod))( + ['receiver']))); + + } else { + ClassElement jsUnknown = backend.jsUnknownJavaScriptObjectClass; + if (compiler.codegenWorld.instantiatedClasses.contains(jsUnknown)) { + block.statements.add( + js.if_(js('!(receiver instanceof #)', + js(namer.isolateAccess(compiler.objectClass))), + buildReturnInterceptor(jsUnknown))); + } + + block.statements.add(js.return_(js('receiver'))); + } + + buffer.write(jsAst.prettyPrint( + js('${namer.globalObjectFor(compiler.interceptorsLibrary)}.$key = #', + js.fun(['receiver'], block)), + compiler)); + buffer.write(N); + } + + /** + * Emit all versions of the [:getInterceptor:] method. + */ + void emitGetInterceptorMethods(CodeBuffer buffer) { + task.addComment('getInterceptor methods', buffer); + Map> specializedGetInterceptors = + backend.specializedGetInterceptors; + for (String name in specializedGetInterceptors.keys.toList()..sort()) { + Set classes = specializedGetInterceptors[name]; + emitGetInterceptorMethod(buffer, name, classes); + } + } + + // Returns a statement that takes care of performance critical + // common case for a one-shot interceptor, or null if there is no + // fast path. + jsAst.Statement fastPathForOneShotInterceptor(Selector selector, + Set classes) { + jsAst.Expression isNumber(String variable) { + return js('typeof $variable == "number"'); + } + + jsAst.Expression isNotObject(String variable) { + return js('typeof $variable != "object"'); + } + + jsAst.Expression isInt(String variable) { + return isNumber(variable).binary('&&', + js('Math.floor($variable) == $variable')); + } + + jsAst.Expression tripleShiftZero(jsAst.Expression receiver) { + return receiver.binary('>>>', js('0')); + } + + if (selector.isOperator()) { + String name = selector.name; + if (name == '==') { + // Unfolds to: + // if (receiver == null) return a0 == null; + // if (typeof receiver != 'object') { + // return a0 != null && receiver === a0; + // } + List body = []; + body.add(js.if_('receiver == null', js.return_(js('a0 == null')))); + body.add(js.if_( + isNotObject('receiver'), + js.return_(js('a0 != null && receiver === a0')))); + return new jsAst.Block(body); + } + if (!classes.contains(backend.jsIntClass) + && !classes.contains(backend.jsNumberClass) + && !classes.contains(backend.jsDoubleClass)) { + return null; + } + if (selector.argumentCount == 1) { + // The following operators do not map to a JavaScript + // operator. + if (name == '~/' || name == '<<' || name == '%' || name == '>>') { + return null; + } + jsAst.Expression result = js('receiver').binary(name, js('a0')); + if (name == '&' || name == '|' || name == '^') { + result = tripleShiftZero(result); + } + // Unfolds to: + // if (typeof receiver == "number" && typeof a0 == "number") + // return receiver op a0; + return js.if_( + isNumber('receiver').binary('&&', isNumber('a0')), + js.return_(result)); + } else if (name == 'unary-') { + // [: if (typeof receiver == "number") return -receiver :]. + return js.if_(isNumber('receiver'), + js.return_(js('-receiver'))); + } else { + assert(name == '~'); + return js.if_(isInt('receiver'), + js.return_(js('~receiver >>> 0'))); + } + } else if (selector.isIndex() || selector.isIndexSet()) { + // For an index operation, this code generates: + // + // if (receiver.constructor == Array || typeof receiver == "string") { + // if (a0 >>> 0 === a0 && a0 < receiver.length) { + // return receiver[a0]; + // } + // } + // + // For an index set operation, this code generates: + // + // if (receiver.constructor == Array && !receiver.immutable$list) { + // if (a0 >>> 0 === a0 && a0 < receiver.length) { + // return receiver[a0] = a1; + // } + // } + bool containsArray = classes.contains(backend.jsArrayClass); + bool containsString = classes.contains(backend.jsStringClass); + bool containsJsIndexable = classes.any((cls) { + return compiler.world.isSubtype( + backend.jsIndexingBehaviorInterface, cls); + }); + // The index set operator requires a check on its set value in + // checked mode, so we don't optimize the interceptor if the + // compiler has type assertions enabled. + if (selector.isIndexSet() + && (compiler.enableTypeAssertions || !containsArray)) { + return null; + } + if (!containsArray && !containsString) { + return null; + } + jsAst.Expression isIntAndAboveZero = js('a0 >>> 0 === a0'); + jsAst.Expression belowLength = js('a0 < receiver.length'); + jsAst.Expression arrayCheck = js('receiver.constructor == Array'); + jsAst.Expression indexableCheck = + backend.generateIsJsIndexableCall(js('receiver'), js('receiver')); + + jsAst.Expression orExp(left, right) { + return left == null ? right : left.binary('||', right); + } + + if (selector.isIndex()) { + jsAst.Expression stringCheck = js('typeof receiver == "string"'); + jsAst.Expression typeCheck; + if (containsArray) { + typeCheck = arrayCheck; + } + + if (containsString) { + typeCheck = orExp(typeCheck, stringCheck); + } + + if (containsJsIndexable) { + typeCheck = orExp(typeCheck, indexableCheck); + } + + return js.if_(typeCheck, + js.if_(isIntAndAboveZero.binary('&&', belowLength), + js.return_(js('receiver[a0]')))); + } else { + jsAst.Expression typeCheck; + if (containsArray) { + typeCheck = arrayCheck; + } + + if (containsJsIndexable) { + typeCheck = orExp(typeCheck, indexableCheck); + } + + jsAst.Expression isImmutableArray = typeCheck.binary( + '&&', js(r'!receiver.immutable$list')); + return js.if_(isImmutableArray.binary( + '&&', isIntAndAboveZero.binary('&&', belowLength)), + js.return_(js('receiver[a0] = a1'))); + } + } + return null; + } + + void emitOneShotInterceptors(CodeBuffer buffer) { + List names = backend.oneShotInterceptors.keys.toList(); + names.sort(); + for (String name in names) { + Selector selector = backend.oneShotInterceptors[name]; + Set classes = + backend.getInterceptedClassesOn(selector.name); + String getInterceptorName = + namer.getInterceptorName(backend.getInterceptorMethod, classes); + + List parameters = []; + List arguments = []; + parameters.add(new jsAst.Parameter('receiver')); + arguments.add(js('receiver')); + + if (selector.isSetter()) { + parameters.add(new jsAst.Parameter('value')); + arguments.add(js('value')); + } else { + for (int i = 0; i < selector.argumentCount; i++) { + String argName = 'a$i'; + parameters.add(new jsAst.Parameter(argName)); + arguments.add(js(argName)); + } + } + + List body = []; + jsAst.Statement optimizedPath = + fastPathForOneShotInterceptor(selector, classes); + if (optimizedPath != null) { + body.add(optimizedPath); + } + + String invocationName = backend.namer.invocationName(selector); + String globalObject = namer.globalObjectFor(compiler.interceptorsLibrary); + body.add(js.return_( + js(globalObject)[getInterceptorName]('receiver')[invocationName]( + arguments))); + + jsAst.Expression assignment = + js('${globalObject}.$name = #', js.fun(parameters, body)); + + buffer.write(jsAst.prettyPrint(assignment, compiler)); + buffer.write(N); + } + } + + /** + * If [JSInvocationMirror._invokeOn] has been compiled, emit all the + * possible selector names that are intercepted into the + * [interceptedNames] top-level variable. The implementation of + * [_invokeOn] will use it to determine whether it should call the + * method with an extra parameter. + */ + void emitInterceptedNames(CodeBuffer buffer) { + // TODO(ahe): We should not generate the list of intercepted names at + // compile time, it can be generated automatically at runtime given + // subclasses of Interceptor (which can easily be identified). + if (!compiler.enabledInvokeOn) return; + + // TODO(ahe): We should roll this into + // [emitStaticNonFinalFieldInitializations]. + String name = backend.namer.getNameOfGlobalField(backend.interceptedNames); + + int index = 0; + var invocationNames = interceptorInvocationNames.toList()..sort(); + List elements = invocationNames.map( + (String invocationName) { + jsAst.Literal str = js.string(invocationName); + return new jsAst.ArrayElement(index++, str); + }).toList(); + jsAst.ArrayInitializer array = + new jsAst.ArrayInitializer(invocationNames.length, elements); + + jsAst.Expression assignment = + js('${task.isolateProperties}.$name = #', array); + + buffer.write(jsAst.prettyPrint(assignment, compiler)); + buffer.write(N); + } + + /** + * Emit initializer for [mapTypeToInterceptor] data structure used by + * [findInterceptorForType]. See declaration of [mapTypeToInterceptor] in + * `interceptors.dart`. + */ + void emitMapTypeToInterceptor(CodeBuffer buffer) { + // TODO(sra): Perhaps inject a constant instead? + CustomElementsAnalysis analysis = backend.customElementsAnalysis; + if (!analysis.needsTable) return; + + List elements = []; + ConstantHandler handler = compiler.constantHandler; + List constants = + handler.getConstantsForEmission(task.compareConstants); + for (Constant constant in constants) { + if (constant is TypeConstant) { + TypeConstant typeConstant = constant; + Element element = typeConstant.representedType.element; + if (element is ClassElement) { + ClassElement classElement = element; + if (!analysis.needsClass(classElement)) continue; + + elements.add(backend.emitter.constantReference(constant)); + elements.add(js(namer.isolateAccess(classElement))); + + // Create JavaScript Object map for by-name lookup of generative + // constructors. For example, the class A has three generative + // constructors + // + // class A { + // A() {} + // A.foo() {} + // A.bar() {} + // } + // + // Which are described by the map + // + // {"": A.A$, "foo": A.A$foo, "bar": A.A$bar} + // + // We expect most of the time the map will be a singleton. + var properties = []; + for (Element member in analysis.constructors(classElement)) { + properties.add( + new jsAst.Property( + js.string(member.name), + new jsAst.VariableUse( + backend.namer.isolateAccess(member)))); + } + + var map = new jsAst.ObjectInitializer(properties); + elements.add(map); + } + } + } + + jsAst.ArrayInitializer array = new jsAst.ArrayInitializer.from(elements); + String name = + backend.namer.getNameOfGlobalField(backend.mapTypeToInterceptor); + jsAst.Expression assignment = + js('${task.isolateProperties}.$name = #', array); + + buffer.write(jsAst.prettyPrint(assignment, compiler)); + buffer.write(N); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/js_emitter.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/js_emitter.dart new file mode 100644 index 0000000..2881d91 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/js_emitter.dart @@ -0,0 +1,74 @@ +// Copyright (c) 2013, 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. + +library dart2js.js_emitter; + +import '../common.dart'; + +import '../js/js.dart' as jsAst; + +import '../closure.dart' show + ClosureClassElement, + ClosureClassMap, + ClosureFieldElement; + +import '../dart2jslib.dart' show + CodeBuffer; + +import '../elements/elements.dart' show + TypeVariableElement, + ConstructorBodyElement; + +import '../js/js.dart' show + js; + +import '../js_backend/js_backend.dart' show + CheckedModeHelper, + CheckedModeHelper, + ConstantEmitter, + CustomElementsAnalysis, + JavaScriptBackend, + JavaScriptBackend, + Namer, + NativeEmitter, + RuntimeTypes, + Substitution, + TypeCheck, + TypeChecks, + TypeVariableHandler; + +import '../source_file.dart' show + SourceFile, + StringSourceFile; + +import '../source_map_builder.dart' show + SourceMapBuilder; + +import '../util/characters.dart' show + $$, + $A, + $HASH, + $PERIOD, + $Z, + $a, + $z; + +import '../util/uri_extras.dart' show + relativize; + +import '../deferred_load.dart' show + OutputUnit; + +part 'class_builder.dart'; +part 'class_emitter.dart'; +part 'code_emitter_helper.dart'; +part 'code_emitter_task.dart'; +part 'container_builder.dart'; +part 'declarations.dart'; +part 'helpers.dart'; +part 'interceptor_emitter.dart'; +part 'metadata_emitter.dart'; +part 'nsm_emitter.dart'; +part 'reflection_data_parser.dart'; +part 'type_test_emitter.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/metadata_emitter.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/metadata_emitter.dart new file mode 100644 index 0000000..394ee95 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/metadata_emitter.dart @@ -0,0 +1,133 @@ +// Copyright (c) 2013, 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. + +part of dart2js.js_emitter; + +class MetadataEmitter extends CodeEmitterHelper { + /// A list of JS expressions that represent metadata, parameter names and + /// type, and return types. + final List globalMetadata = []; + + /// A map used to canonicalize the entries of globalMetadata. + final Map globalMetadataMap = {}; + + /// The metadata function returns the metadata associated with + /// [element] in generated code. The metadata needs to be wrapped + /// in a function as it refers to constants that may not have been + /// constructed yet. For example, a class is allowed to be + /// annotated with itself. The metadata function is used by + /// mirrors_patch to implement DeclarationMirror.metadata. + jsAst.Fun buildMetadataFunction(Element element) { + if (!backend.retainMetadataOf(element)) return null; + return compiler.withCurrentElement(element, () { + var metadata = []; + Link link = element.metadata; + // TODO(ahe): Why is metadata sometimes null? + if (link != null) { + for (; !link.isEmpty; link = link.tail) { + MetadataAnnotation annotation = link.head; + Constant value = annotation.value; + if (value == null) { + compiler.internalError(annotation, 'Annotation value is null.'); + } else { + metadata.add(task.constantReference(value)); + } + } + } + if (metadata.isEmpty) return null; + return js.fun( + [], [js.return_(new jsAst.ArrayInitializer.from(metadata))]); + }); + } + + List reifyDefaultArguments(FunctionElement function) { + FunctionSignature signature = function.functionSignature; + if (signature.optionalParameterCount == 0) return const []; + List defaultValues = []; + for (Element element in signature.optionalParameters) { + Constant value = + compiler.constantHandler.initialVariableValues[element]; + String stringRepresentation = (value == null) + ? "null" + : jsAst.prettyPrint(task.constantReference(value), compiler) + .getText(); + defaultValues.add(addGlobalMetadata(stringRepresentation)); + } + return defaultValues; + } + + int reifyMetadata(MetadataAnnotation annotation) { + Constant value = annotation.value; + if (value == null) { + compiler.internalError(annotation, 'Annotation value is null.'); + return -1; + } + return addGlobalMetadata( + jsAst.prettyPrint(task.constantReference(value), compiler).getText()); + } + + int reifyType(DartType type) { + jsAst.Expression representation = + backend.rti.getTypeRepresentation(type, (variable) { + return js.toExpression( + task.typeVariableHandler.reifyTypeVariable(variable.element)); + }); + + return addGlobalMetadata( + jsAst.prettyPrint(representation, compiler).getText()); + } + + int reifyName(String name) { + return addGlobalMetadata('"$name"'); + } + + int addGlobalMetadata(String string) { + return globalMetadataMap.putIfAbsent(string, () { + globalMetadata.add(string); + return globalMetadata.length - 1; + }); + } + + void emitMetadata(CodeBuffer buffer) { + var literals = backend.typedefTypeLiterals.toList(); + Elements.sortedByPosition(literals); + var properties = []; + for (TypedefElement literal in literals) { + var key = namer.getNameX(literal); + var value = js.toExpression(reifyType(literal.rawType)); + properties.add(new jsAst.Property(js.string(key), value)); + } + var map = new jsAst.ObjectInitializer(properties); + buffer.write( + jsAst.prettyPrint( + js('init.functionAliases = #', map).toStatement(), compiler)); + buffer.write('${N}init.metadata$_=$_['); + for (var metadata in globalMetadata) { + if (metadata is String) { + if (metadata != 'null') { + buffer.write(metadata); + } + } else { + throw 'Unexpected value in metadata: ${Error.safeToString(metadata)}'; + } + buffer.write(',$n'); + } + buffer.write('];$n'); + } + + List computeMetadata(FunctionElement element) { + return compiler.withCurrentElement(element, () { + if (!backend.retainMetadataOf(element)) return const []; + List metadata = []; + Link link = element.metadata; + // TODO(ahe): Why is metadata sometimes null? + if (link != null) { + for (; !link.isEmpty; link = link.tail) { + metadata.add(reifyMetadata(link.head)); + } + } + return metadata; + }); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/nsm_emitter.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/nsm_emitter.dart new file mode 100644 index 0000000..c298c57 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/nsm_emitter.dart @@ -0,0 +1,373 @@ +// Copyright (c) 2013, 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. + +part of dart2js.js_emitter; + +class NsmEmitter extends CodeEmitterHelper { + final List trivialNsmHandlers = []; + + /// If this is true then we can generate the noSuchMethod handlers at startup + /// time, instead of them being emitted as part of the Object class. + bool get generateTrivialNsmHandlers => true; + + // If we need fewer than this many noSuchMethod handlers we can save space by + // just emitting them in JS, rather than emitting the JS needed to generate + // them at run time. + static const VERY_FEW_NO_SUCH_METHOD_HANDLERS = 10; + + static const MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING = 4; + + void emitNoSuchMethodHandlers(AddPropertyFunction addProperty) { + // Do not generate no such method handlers if there is no class. + if (compiler.codegenWorld.instantiatedClasses.isEmpty) return; + + String noSuchMethodName = namer.publicInstanceMethodNameByArity( + Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); + + // Keep track of the JavaScript names we've already added so we + // do not introduce duplicates (bad for code size). + Map addedJsNames = new Map(); + + void addNoSuchMethodHandlers(String ignore, Set selectors) { + // Cache the object class and type. + ClassElement objectClass = compiler.objectClass; + DartType objectType = objectClass.rawType; + + for (Selector selector in selectors) { + TypeMask mask = selector.mask; + if (mask == null) { + mask = new TypeMask.subclass(compiler.objectClass); + } + + if (!mask.needsNoSuchMethodHandling(selector, compiler)) continue; + String jsName = namer.invocationMirrorInternalName(selector); + addedJsNames[jsName] = selector; + String reflectionName = task.getReflectionName(selector, jsName); + if (reflectionName != null) { + task.mangledFieldNames[jsName] = reflectionName; + } + } + } + + compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers); + compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers); + compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers); + + // Set flag used by generateMethod helper below. If we have very few + // handlers we use addProperty for them all, rather than try to generate + // them at runtime. + bool haveVeryFewNoSuchMemberHandlers = + (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS); + + jsAst.Expression generateMethod(String jsName, Selector selector) { + // Values match JSInvocationMirror in js-helper library. + int type = selector.invocationMirrorKind; + List parameters = []; + for (int i = 0; i < selector.argumentCount; i++) { + parameters.add(new jsAst.Parameter('\$$i')); + } + + List argNames = + selector.getOrderedNamedArguments().map((String name) => + js.string(name)).toList(); + + String methodName = selector.invocationMirrorMemberName; + String internalName = namer.invocationMirrorInternalName(selector); + String reflectionName = task.getReflectionName(selector, internalName); + if (!haveVeryFewNoSuchMemberHandlers && + isTrivialNsmHandler(type, argNames, selector, internalName) && + reflectionName == null) { + trivialNsmHandlers.add(selector); + return null; + } + + assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD)); + jsAst.Expression expression = js('this.$noSuchMethodName')( + [js('this'), + namer.elementAccess(backend.getCreateInvocationMirror())([ + js.string(compiler.enableMinification ? + internalName : methodName), + js.string(internalName), + type, + new jsAst.ArrayInitializer.from( + parameters.map((param) => js(param.name)).toList()), + new jsAst.ArrayInitializer.from(argNames)])]); + parameters = backend.isInterceptedName(selector.name) + ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters)) + : parameters; + return js.fun(parameters, js.return_(expression)); + } + + for (String jsName in addedJsNames.keys.toList()..sort()) { + Selector selector = addedJsNames[jsName]; + jsAst.Expression method = generateMethod(jsName, selector); + if (method != null) { + addProperty(jsName, method); + String reflectionName = task.getReflectionName(selector, jsName); + if (reflectionName != null) { + bool accessible = compiler.world.allFunctions.filter(selector).any( + (Element e) => backend.isAccessibleByReflection(e)); + addProperty('+$reflectionName', js(accessible ? '2' : '0')); + } + } + } + } + + // Identify the noSuchMethod handlers that are so simple that we can + // generate them programatically. + bool isTrivialNsmHandler( + int type, List argNames, Selector selector, String internalName) { + if (!generateTrivialNsmHandlers) return false; + // Check for interceptor calling convention. + if (backend.isInterceptedName(selector.name)) { + // We can handle the calling convention used by intercepted names in the + // diff encoding, but we don't use that for non-minified code. + if (!compiler.enableMinification) return false; + String shortName = namer.invocationMirrorInternalName(selector); + if (shortName.length > MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING) { + return false; + } + } + // Check for named arguments. + if (argNames.length != 0) return false; + // Check for unexpected name (this doesn't really happen). + if (internalName.startsWith(namer.getterPrefix[0])) return type == 1; + if (internalName.startsWith(namer.setterPrefix[0])) return type == 2; + return type == 0; + } + + /** + * Adds (at runtime) the handlers to the Object class which catch calls to + * methods that the object does not have. The handlers create an invocation + * mirror object. + * + * The current version only gives you the minified name when minifying (when + * not minifying this method is not called). + * + * In order to generate the noSuchMethod handlers we only need the minified + * name of the method. We test the first character of the minified name to + * determine if it is a getter or a setter, and we use the arguments array at + * runtime to get the number of arguments and their values. If the method + * involves named arguments etc. then we don't handle it here, but emit the + * handler method directly on the Object class. + * + * The minified names are mostly 1-4 character names, which we emit in sorted + * order (primary key is length, secondary ordering is lexicographic). This + * gives an order like ... dD dI dX da ... + * + * Gzip is good with repeated text, but it can't diff-encode, so we do that + * for it. We encode the minified names in a comma-separated string, but all + * the 1-4 character names are encoded before the first comma as a series of + * base 26 numbers. The last digit of each number is lower case, the others + * are upper case, so 1 is "b" and 26 is "Ba". + * + * We think of the minified names as base 88 numbers using the ASCII + * characters from # to z. The base 26 numbers each encode the delta from + * the previous minified name to the next. So if there is a minified name + * called Df and the next is Dh, then they are 2971 and 2973 when thought of + * as base 88 numbers. The difference is 2, which is "c" in lower-case- + * terminated base 26. + * + * The reason we don't encode long minified names with this method is that + * decoding the base 88 numbers would overflow JavaScript's puny integers. + * + * There are some selectors that have a special calling convention (because + * they are called with the receiver as the first argument). They need a + * slightly different noSuchMethod handler, so we handle these first. + */ + void addTrivialNsmHandlers(List statements) { + if (trivialNsmHandlers.length == 0) return; + // Sort by calling convention, JS name length and by JS name. + trivialNsmHandlers.sort((a, b) { + bool aIsIntercepted = backend.isInterceptedName(a.name); + bool bIsIntercepted = backend.isInterceptedName(b.name); + if (aIsIntercepted != bIsIntercepted) return aIsIntercepted ? -1 : 1; + String aName = namer.invocationMirrorInternalName(a); + String bName = namer.invocationMirrorInternalName(b); + if (aName.length != bName.length) return aName.length - bName.length; + return aName.compareTo(bName); + }); + + // Find out how many selectors there are with the special calling + // convention. + int firstNormalSelector = trivialNsmHandlers.length; + for (int i = 0; i < trivialNsmHandlers.length; i++) { + if (!backend.isInterceptedName(trivialNsmHandlers[i].name)) { + firstNormalSelector = i; + break; + } + } + + // Get the short names (JS names, perhaps minified). + Iterable shorts = trivialNsmHandlers.map((selector) => + namer.invocationMirrorInternalName(selector)); + final diffShorts = []; + var diffEncoding = new StringBuffer(); + + // Treat string as a number in base 88 with digits in ASCII order from # to + // z. The short name sorting is based on length, and uses ASCII order for + // equal length strings so this means that names are ascending. The hash + // character, #, is never given as input, but we need it because it's the + // implicit leading zero (otherwise we could not code names with leading + // dollar signs). + int fromBase88(String x) { + int answer = 0; + for (int i = 0; i < x.length; i++) { + int c = x.codeUnitAt(i); + // No support for Unicode minified identifiers in JS. + assert(c >= $$ && c <= $z); + answer *= 88; + answer += c - $HASH; + } + return answer; + } + + // Big endian encoding, A = 0, B = 1... + // A lower case letter terminates the number. + String toBase26(int x) { + int c = x; + var encodingChars = []; + encodingChars.add($a + (c % 26)); + while (true) { + c ~/= 26; + if (c == 0) break; + encodingChars.add($A + (c % 26)); + } + return new String.fromCharCodes(encodingChars.reversed.toList()); + } + + bool minify = compiler.enableMinification; + bool useDiffEncoding = minify && shorts.length > 30; + + int previous = 0; + int nameCounter = 0; + for (String short in shorts) { + // Emit period that resets the diff base to zero when we switch to normal + // calling convention (this avoids the need to code negative diffs). + if (useDiffEncoding && nameCounter == firstNormalSelector) { + diffEncoding.write("."); + previous = 0; + } + if (short.length <= MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING && + useDiffEncoding) { + int base63 = fromBase88(short); + int diff = base63 - previous; + previous = base63; + String base26Diff = toBase26(diff); + diffEncoding.write(base26Diff); + } else { + if (useDiffEncoding || diffEncoding.length != 0) { + diffEncoding.write(","); + } + diffEncoding.write(short); + } + nameCounter++; + } + + // Startup code that loops over the method names and puts handlers on the + // Object class to catch noSuchMethod invocations. + ClassElement objectClass = compiler.objectClass; + String createInvocationMirror = namer.isolateAccess( + backend.getCreateInvocationMirror()); + String noSuchMethodName = namer.publicInstanceMethodNameByArity( + Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); + var type = 0; + if (useDiffEncoding) { + statements.addAll([ + js('var objectClassObject = ' + ' collectedClasses["${namer.getNameOfClass(objectClass)}"],' + ' shortNames = "$diffEncoding".split(","),' + ' nameNumber = 0,' + ' diffEncodedString = shortNames[0],' + ' calculatedShortNames = [0, 1]'), // 0, 1 are args for splice. + // If we are loading a deferred library the object class will not be in + // the collectedClasses so objectClassObject is undefined, and we skip + // setting up the names. + js.if_('objectClassObject', [ + js.if_('objectClassObject instanceof Array', + js('objectClassObject = objectClassObject[1]')), + js.for_('var i = 0', 'i < diffEncodedString.length', 'i++', [ + js('var codes = [],' + ' diff = 0,' + ' digit = diffEncodedString.charCodeAt(i)'), + js.if_('digit == ${$PERIOD}', [ + js('nameNumber = 0'), + js('digit = diffEncodedString.charCodeAt(++i)') + ]), + js.while_('digit <= ${$Z}', [ + js('diff *= 26'), + js('diff += (digit - ${$A})'), + js('digit = diffEncodedString.charCodeAt(++i)') + ]), + js('diff *= 26'), + js('diff += (digit - ${$a})'), + js('nameNumber += diff'), + js.for_('var remaining = nameNumber', + 'remaining > 0', + 'remaining = (remaining / 88) | 0', [ + js('codes.unshift(${$HASH} + remaining % 88)') + ]), + js('calculatedShortNames.push(' + ' String.fromCharCode.apply(String, codes))') + ]), + js('shortNames.splice.apply(shortNames, calculatedShortNames)')]) + ]); + } else { + // No useDiffEncoding version. + Iterable longs = trivialNsmHandlers.map((selector) => + selector.invocationMirrorMemberName); + String longNamesConstant = minify ? "" : + ',longNames = "${longs.join(",")}".split(",")'; + statements.add( + js('var objectClassObject = ' + ' collectedClasses["${namer.getNameOfClass(objectClass)}"],' + ' shortNames = "$diffEncoding".split(",")' + ' $longNamesConstant')); + statements.add( + js.if_('objectClassObject instanceof Array', + js('objectClassObject = objectClassObject[1]'))); + } + + String sliceOffset = ', (j < $firstNormalSelector) ? 1 : 0'; + if (firstNormalSelector == 0) sliceOffset = ''; + if (firstNormalSelector == shorts.length) sliceOffset = ', 1'; + + String whatToPatch = task.nativeEmitter.handleNoSuchMethod ? + "Object.prototype" : + "objectClassObject"; + + var params = ['name', 'short', 'type']; + var sliceOffsetParam = ''; + var slice = 'Array.prototype.slice.call'; + if (!sliceOffset.isEmpty) { + sliceOffsetParam = ', sliceOffset'; + params.add('sliceOffset'); + } + statements.addAll([ + // If we are loading a deferred library the object class will not be in + // the collectedClasses so objectClassObject is undefined, and we skip + // setting up the names. + js.if_('objectClassObject', [ + js.for_('var j = 0', 'j < shortNames.length', 'j++', [ + js('var type = 0'), + js('var short = shortNames[j]'), + js.if_('short[0] == "${namer.getterPrefix[0]}"', js('type = 1')), + js.if_('short[0] == "${namer.setterPrefix[0]}"', js('type = 2')), + // Generate call to: + // createInvocationMirror(String name, internalName, type, arguments, + // argumentNames) + js('$whatToPatch[short] = #(${minify ? "shortNames" : "longNames"}[j], ' + 'short, type$sliceOffset)', + js.fun(params, [js.return_(js.fun([], + [js.return_(js( + 'this.$noSuchMethodName(' + 'this, ' + '$createInvocationMirror(' + 'name, short, type, ' + '$slice(arguments$sliceOffsetParam), []))'))]))])) + ]) + ]) + ]); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart new file mode 100644 index 0000000..1e7210f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart @@ -0,0 +1,360 @@ +// Copyright (c) 2013, 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. + +part of dart2js.js_emitter; + +// TODO(ahe): Share these with js_helper.dart. +const FUNCTION_INDEX = 0; +const NAME_INDEX = 1; +const CALL_NAME_INDEX = 2; +const REQUIRED_PARAMETER_INDEX = 3; +const OPTIONAL_PARAMETER_INDEX = 4; +const DEFAULT_ARGUMENTS_INDEX = 5; + +const bool VALIDATE_DATA = false; + +// TODO(ahe): This code should be integrated in CodeEmitterTask.finishClasses. +String getReflectionDataParser(String classesCollector, + JavaScriptBackend backend) { + Namer namer = backend.namer; + Compiler compiler = backend.compiler; + Element closureFromTearOff = compiler.findHelper('closureFromTearOff'); + String tearOffAccess; + String tearOffGlobalObjectName; + String tearOffGlobalObject; + if (closureFromTearOff != null) { + tearOffAccess = namer.isolateAccess(closureFromTearOff); + tearOffGlobalObjectName = tearOffGlobalObject = + namer.globalObjectFor(closureFromTearOff); + } else { + // Default values for mocked-up test libraries. + tearOffAccess = + 'function() { throw "Helper \'closureFromTearOff\' missing." }'; + tearOffGlobalObjectName = 'MissingHelperFunction'; + tearOffGlobalObject = '($tearOffAccess())'; + } + + String metadataField = '"${namer.metadataField}"'; + String reflectableField = namer.reflectableField; + + // TODO(ahe): Move this string constants to namer. + String reflectionInfoField = r'$reflectionInfo'; + String reflectionNameField = r'$reflectionName'; + String metadataIndexField = r'$metadataIndex'; + + String defaultValuesField = namer.defaultValuesField; + String methodsWithOptionalArgumentsField = + namer.methodsWithOptionalArgumentsField; + + String header = ''' +(function (reflectionData) { + "use strict"; +''' +// [map] returns an object literal that V8 shouldn't try to optimize with a +// hidden class. This prevents a potential performance problem where V8 tries +// to build a hidden class for an object used as a hashMap. +''' + function map(x){x={x:x};delete x.x;return x} +'''; + + String unmangledNameIndex = backend.mustRetainMetadata + ? ' 3 * optionalParameterCount + 2 * requiredParameterCount + 3' + : ' 2 * optionalParameterCount + requiredParameterCount + 3'; + + /** + * See [dart2js.js_emitter.ContainerBuilder.addMemberMethod] for format of + * [array]. + */ + String addStubs = ''' + function addStubs(descriptor, array, name, isStatic,''' // Break long line. + ''' originalDescriptor, functions) { + var f, funcs =''' // Break long line. + ''' [originalDescriptor[name] =''' // Break long line. + ''' descriptor[name] = f = ${readFunction("array", "$FUNCTION_INDEX")}]; + f.\$stubName = name; + functions.push(name); + for (var index = $FUNCTION_INDEX; index < array.length; index += 2) { + f = array[index + 1]; + if (typeof f != "function") break; + f.\$stubName = ${readString("array", "index + 2")}; + funcs.push(f); + if (f.\$stubName) { + originalDescriptor[f.\$stubName] = descriptor[f.\$stubName] = f; + functions.push(f.\$stubName); + } + } + for (var i = 0; i < funcs.length; index++, i++) { + funcs[i].\$callName = ${readString("array", "index + 1")}; + } + var getterStubName = ${readString("array", "++index")}; + array = array.slice(++index); + var requiredParameterInfo = ${readInt("array", "0")}; + var requiredParameterCount = requiredParameterInfo >> 1; + var isAccessor = (requiredParameterInfo & 1) === 1; + var isSetter = requiredParameterInfo === 3; + var isGetter = requiredParameterInfo === 1; + var optionalParameterInfo = ${readInt("array", "1")}; + var optionalParameterCount = optionalParameterInfo >> 1; + var optionalParametersAreNamed = (optionalParameterInfo & 1) === 1; + var isIntercepted =''' // Break long line. + ''' requiredParameterCount + optionalParameterCount != funcs[0].length; + var functionTypeIndex = ${readFunctionType("array", "2")}; + var unmangledNameIndex = $unmangledNameIndex; + var isReflectable = array.length > unmangledNameIndex; + + if (getterStubName) { + f = tearOff(funcs, array, isStatic, name, isIntercepted); + f.getterStub = true; +''' + /* Used to create an isolate using spawnFunction.*/ +''' + if (isStatic) init.globalFunctions[name] = f; + originalDescriptor[getterStubName] = descriptor[getterStubName] = f; + funcs.push(f); + if (getterStubName) functions.push(getterStubName); + f.\$stubName = getterStubName; + f.\$callName = null; + if (isIntercepted) init.interceptedNames[getterStubName] = true; + } + if (isReflectable) { + for (var i = 0; i < funcs.length; i++) { + funcs[i].$reflectableField = 1; + funcs[i].$reflectionInfoField = array; + } + var mangledNames = isStatic ? init.mangledGlobalNames : init.mangledNames; + var unmangledName = ${readString("array", "unmangledNameIndex")}; +''' + // The function is either a getter, a setter, or a method. + // If it is a method, it might also have a tear-off closure. + // The unmangledName is the same as the getter-name. +''' + var reflectionName = unmangledName; + if (getterStubName) mangledNames[getterStubName] = reflectionName; + if (isSetter) { + reflectionName += "="; + } else if (!isGetter) { + reflectionName += ":" + requiredParameterCount +''' // Break long line. + ''' ":" + optionalParameterCount; + } + mangledNames[name] = reflectionName; + funcs[0].$reflectionNameField = reflectionName; + funcs[0].$metadataIndexField = unmangledNameIndex + 1; + if (optionalParameterCount) descriptor[unmangledName + "*"] = funcs[0]; + } + } +'''; + + String tearOff = ''' + function tearOffGetterNoCsp(funcs, reflectionInfo, name, isIntercepted) { + return isIntercepted + ? new Function("funcs", "reflectionInfo", "name",''' // Break long line. + ''' "$tearOffGlobalObjectName", "c", + "return function tearOff_" + name + (functionCounter++)+ "(x) {" + + "if (c === null) c = $tearOffAccess(" + + "this, funcs, reflectionInfo, false, [x], name);" + + "return new c(this, funcs[0], x, name);" + + "}")(funcs, reflectionInfo, name, $tearOffGlobalObject, null) + : new Function("funcs", "reflectionInfo", "name",''' // Break long line. + ''' "$tearOffGlobalObjectName", "c", + "return function tearOff_" + name + (functionCounter++)+ "() {" + + "if (c === null) c = $tearOffAccess(" + + "this, funcs, reflectionInfo, false, [], name);" + + "return new c(this, funcs[0], null, name);" + + "}")(funcs, reflectionInfo, name, $tearOffGlobalObject, null) + } + function tearOffGetterCsp(funcs, reflectionInfo, name, isIntercepted) { + var cache = null; + return isIntercepted + ? function(x) { + if (cache === null) cache = $tearOffAccess(''' // Break long line. + '''this, funcs, reflectionInfo, false, [x], name); + return new cache(this, funcs[0], x, name) + } + : function() { + if (cache === null) cache = $tearOffAccess(''' // Break long line. + '''this, funcs, reflectionInfo, false, [], name); + return new cache(this, funcs[0], null, name) + } + } + function tearOff(funcs, reflectionInfo, isStatic, name, isIntercepted) { + var cache; + return isStatic + ? function() { + if (cache === void 0) cache = $tearOffAccess(''' // Break long line. + '''this, funcs, reflectionInfo, true, [], name).prototype; + return cache; + } + : tearOffGetter(funcs, reflectionInfo, name, isIntercepted); + } +'''; + + + + + String init = ''' + var functionCounter = 0; + var tearOffGetter = (typeof dart_precompiled == "function") + ? tearOffGetterCsp : tearOffGetterNoCsp; + if (!init.libraries) init.libraries = []; + if (!init.mangledNames) init.mangledNames = map(); + if (!init.mangledGlobalNames) init.mangledGlobalNames = map(); + if (!init.statics) init.statics = map(); + if (!init.typeInformation) init.typeInformation = map(); + if (!init.globalFunctions) init.globalFunctions = map(); + if (!init.interceptedNames) init.interceptedNames = map(); + var libraries = init.libraries; + var mangledNames = init.mangledNames; + var mangledGlobalNames = init.mangledGlobalNames; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var length = reflectionData.length; + for (var i = 0; i < length; i++) { + var data = reflectionData[i]; +''' +// [data] contains these elements: +// 0. The library name (not unique). +// 1. The library URI (unique). +// 2. A function returning the metadata associated with this library. +// 3. The global object to use for this library. +// 4. An object literal listing the members of the library. +// 5. This element is optional and if present it is true and signals that this +// library is the root library (see dart:mirrors IsolateMirror.rootLibrary). +// +// The entries of [data] are built in [assembleProgram] above. +''' + var name = data[0]; + var uri = data[1]; + var metadata = data[2]; + var globalObject = data[3]; + var descriptor = data[4]; + var isRoot = !!data[5]; + var fields = descriptor && descriptor["${namer.classDescriptorProperty}"]; + var classes = []; + var functions = []; +'''; + + String processStatics = ''' + function processStatics(descriptor) { + for (var property in descriptor) { + if (!hasOwnProperty.call(descriptor, property)) continue; + if (property === "${namer.classDescriptorProperty}") continue; + var element = descriptor[property]; + var firstChar = property.substring(0, 1); + var previousProperty; + if (firstChar === "+") { + mangledGlobalNames[previousProperty] = property.substring(1); + var flag = descriptor[property]; + if (flag > 0) ''' // Break long line. + '''descriptor[previousProperty].$reflectableField = flag; + if (element && element.length) ''' // Break long line. + '''init.typeInformation[previousProperty] = element; + } else if (firstChar === "@") { + property = property.substring(1); + ${namer.currentIsolate}[property][$metadataField] = element; + } else if (firstChar === "*") { + globalObject[previousProperty].$defaultValuesField = element; + var optionalMethods = descriptor.$methodsWithOptionalArgumentsField; + if (!optionalMethods) { + descriptor.$methodsWithOptionalArgumentsField = optionalMethods = {} + } + optionalMethods[property] = previousProperty; + } else if (typeof element === "function") { + globalObject[previousProperty = property] = element; + functions.push(property); + init.globalFunctions[property] = element; + } else if (element.constructor === Array) { + addStubs(globalObject, element, property, ''' // Break long line. + '''true, descriptor, functions); + } else { + previousProperty = property; + var newDesc = {}; + var previousProp; + for (var prop in element) { + if (!hasOwnProperty.call(element, prop)) continue; + firstChar = prop.substring(0, 1); + if (prop === "static") { + processStatics(init.statics[property] = element[prop]); + } else if (firstChar === "+") { + mangledNames[previousProp] = prop.substring(1); + var flag = element[prop]; + if (flag > 0) ''' // Break long line. + '''element[previousProp].$reflectableField = flag; + } else if (firstChar === "@" && prop !== "@") { + newDesc[prop.substring(1)][$metadataField] = element[prop]; + } else if (firstChar === "*") { + newDesc[previousProp].$defaultValuesField = element[prop]; + var optionalMethods = newDesc.$methodsWithOptionalArgumentsField; + if (!optionalMethods) { + newDesc.$methodsWithOptionalArgumentsField = optionalMethods={} + } + optionalMethods[prop] = previousProp; + } else { + var elem = element[prop]; + if (prop !== "${namer.classDescriptorProperty}" &&''' + ''' elem != null &&''' // Break long line. + ''' elem.constructor === Array &&''' // Break long line. + ''' prop !== "<>") { + addStubs(newDesc, elem, prop, false, element, []); + } else { + newDesc[previousProp = prop] = elem; + } + } + } + $classesCollector[property] = [globalObject, newDesc]; + classes.push(property); + } + } + } +'''; + +String footer = ''' + processStatics(descriptor); + libraries.push([name, uri, classes, functions, metadata, fields, isRoot, + globalObject]); + } +}) +'''; + + return '$header$processStatics$addStubs$tearOff$init$footer'; +} + +String readString(String array, String index) { + return readChecked( + array, index, 'result != null && typeof result != "string"', 'string'); +} + +String readInt(String array, String index) { + return readChecked( + array, index, + 'result != null && (typeof result != "number" || (result|0) !== result)', + 'int'); +} + +String readFunction(String array, String index) { + return readChecked( + array, index, 'result != null && typeof result != "function"', + 'function'); +} + +String readFunctionType(String array, String index) { + return readChecked( + array, index, + 'result != null && ' + '(typeof result != "number" || (result|0) !== result) && ' + 'typeof result != "function"', + 'function or int'); +} + +String readChecked(String array, String index, String check, String type) { + if (!VALIDATE_DATA) return '$array[$index]'; + return ''' +(function() { + var result = $array[$index]; + if ($check) { + throw new Error( + name + ": expected value of type \'$type\' at index " + ($index) + + " but got " + (typeof result)); + } + return result; +})()'''; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/type_test_emitter.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/type_test_emitter.dart new file mode 100644 index 0000000..f1a67f0 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/js_emitter/type_test_emitter.dart @@ -0,0 +1,415 @@ +// Copyright (c) 2013, 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. + +part of dart2js.js_emitter; + +class TypeTestEmitter extends CodeEmitterHelper { + static const int MAX_FUNCTION_TYPE_PREDICATES = 10; + + /** + * Raw ClassElement symbols occuring in is-checks and type assertions. If the + * program contains parameterized checks `x is Set` and + * `x is Set` then the ClassElement `Set` will occur once in + * [checkedClasses]. + */ + Set checkedClasses; + + /** + * The set of function types that checked, both explicity through tests of + * typedefs and implicitly through type annotations in checked mode. + */ + Set checkedFunctionTypes; + + Map> checkedGenericFunctionTypes = + new Map>(); + + Set checkedNonGenericFunctionTypes = + new Set(); + + final Set rtiNeededClasses = new Set(); + + Iterable cachedClassesUsingTypeVariableTests; + + Iterable get classesUsingTypeVariableTests { + if (cachedClassesUsingTypeVariableTests == null) { + cachedClassesUsingTypeVariableTests = compiler.codegenWorld.isChecks + .where((DartType t) => t is TypeVariableType) + .map((TypeVariableType v) => v.element.getEnclosingClass()) + .toList(); + } + return cachedClassesUsingTypeVariableTests; + } + + void emitIsTests(ClassElement classElement, ClassBuilder builder) { + assert(invariant(classElement, classElement.isDeclaration)); + + void generateIsTest(Element other) { + if (other == compiler.objectClass && other != classElement) { + // Avoid emitting [:$isObject:] on all classes but [Object]. + return; + } + other = backend.getImplementationClass(other); + builder.addProperty(namer.operatorIs(other), js('true')); + } + + void generateFunctionTypeSignature(Element method, FunctionType type) { + assert(method.isImplementation); + jsAst.Expression thisAccess = new jsAst.This(); + Node node = method.parseNode(compiler); + ClosureClassMap closureData = + compiler.closureToClassMapper.closureMappingCache[node]; + if (closureData != null) { + Element thisElement = + closureData.freeVariableMapping[closureData.thisElement]; + if (thisElement != null) { + String thisName = namer.instanceFieldPropertyName(thisElement); + thisAccess = js('this')[js.string(thisName)]; + } + } + RuntimeTypes rti = backend.rti; + jsAst.Expression encoding = rti.getSignatureEncoding(type, thisAccess); + String operatorSignature = namer.operatorSignature(); + if (!type.containsTypeVariables) { + builder.functionType = '${task.metadataEmitter.reifyType(type)}'; + } else { + builder.addProperty(operatorSignature, encoding); + } + } + + void generateSubstitution(ClassElement cls, {bool emitNull: false}) { + if (cls.typeVariables.isEmpty) return; + RuntimeTypes rti = backend.rti; + jsAst.Expression expression; + bool needsNativeCheck = task.nativeEmitter.requiresNativeIsCheck(cls); + expression = rti.getSupertypeSubstitution( + classElement, cls, alwaysGenerateFunction: true); + if (expression == null && (emitNull || needsNativeCheck)) { + expression = new jsAst.LiteralNull(); + } + if (expression != null) { + builder.addProperty(namer.substitutionName(cls), expression); + } + } + + generateIsTestsOn(classElement, generateIsTest, + generateFunctionTypeSignature, + generateSubstitution); + } + + /** + * Generate "is tests" for [cls]: itself, and the "is tests" for the + * classes it implements and type argument substitution functions for these + * tests. We don't need to add the "is tests" of the super class because + * they will be inherited at runtime, but we may need to generate the + * substitutions, because they may have changed. + */ + void generateIsTestsOn(ClassElement cls, + void emitIsTest(Element element), + FunctionTypeSignatureEmitter emitFunctionTypeSignature, + SubstitutionEmitter emitSubstitution) { + if (checkedClasses.contains(cls)) { + emitIsTest(cls); + emitSubstitution(cls); + } + + RuntimeTypes rti = backend.rti; + ClassElement superclass = cls.superclass; + + bool haveSameTypeVariables(ClassElement a, ClassElement b) { + if (a.isClosure()) return true; + return backend.rti.isTrivialSubstitution(a, b); + } + + if (superclass != null && superclass != compiler.objectClass && + !haveSameTypeVariables(cls, superclass)) { + // We cannot inherit the generated substitutions, because the type + // variable layout for this class is different. Instead we generate + // substitutions for all checks and make emitSubstitution a NOP for the + // rest of this function. + Set emitted = new Set(); + // TODO(karlklose): move the computation of these checks to + // RuntimeTypeInformation. + while (superclass != null) { + if (backend.classNeedsRti(superclass)) { + emitSubstitution(superclass, emitNull: true); + emitted.add(superclass); + } + superclass = superclass.superclass; + } + for (DartType supertype in cls.allSupertypes) { + ClassElement superclass = supertype.element; + if (classesUsingTypeVariableTests.contains(superclass)) { + emitSubstitution(superclass, emitNull: true); + emitted.add(superclass); + } + for (ClassElement check in checkedClasses) { + if (supertype.element == check && !emitted.contains(check)) { + // Generate substitution. If no substitution is necessary, emit + // [:null:] to overwrite a (possibly) existing substitution from the + // super classes. + emitSubstitution(check, emitNull: true); + emitted.add(check); + } + } + } + void emitNothing(_, {emitNull}) {}; + emitSubstitution = emitNothing; + } + + Set generated = new Set(); + // A class that defines a [:call:] method implicitly implements + // [Function] and needs checks for all typedefs that are used in is-checks. + if (checkedClasses.contains(compiler.functionClass) || + !checkedFunctionTypes.isEmpty) { + Element call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME); + if (call == null) { + // If [cls] is a closure, it has a synthetic call operator method. + call = cls.lookupBackendMember(Compiler.CALL_OPERATOR_NAME); + } + if (call != null && call.isFunction()) { + generateInterfacesIsTests(compiler.functionClass, + emitIsTest, + emitSubstitution, + generated); + FunctionType callType = call.computeType(compiler); + Map functionTypeChecks = + getFunctionTypeChecksOn(callType); + generateFunctionTypeTests( + call, callType, functionTypeChecks, + emitFunctionTypeSignature); + } + } + + for (DartType interfaceType in cls.interfaces) { + generateInterfacesIsTests(interfaceType.element, emitIsTest, + emitSubstitution, generated); + } + } + + /** + * Generate "is tests" where [cls] is being implemented. + */ + void generateInterfacesIsTests(ClassElement cls, + void emitIsTest(ClassElement element), + SubstitutionEmitter emitSubstitution, + Set alreadyGenerated) { + void tryEmitTest(ClassElement check) { + if (!alreadyGenerated.contains(check) && checkedClasses.contains(check)) { + alreadyGenerated.add(check); + emitIsTest(check); + emitSubstitution(check); + } + }; + + tryEmitTest(cls); + + for (DartType interfaceType in cls.interfaces) { + Element element = interfaceType.element; + tryEmitTest(element); + generateInterfacesIsTests(element, emitIsTest, emitSubstitution, + alreadyGenerated); + } + + // We need to also emit "is checks" for the superclass and its supertypes. + ClassElement superclass = cls.superclass; + if (superclass != null) { + tryEmitTest(superclass); + generateInterfacesIsTests(superclass, emitIsTest, emitSubstitution, + alreadyGenerated); + } + } + + /** + * Returns a mapping containing all checked function types for which [type] + * can be a subtype. A function type is mapped to [:true:] if [type] is + * statically known to be a subtype of it and to [:false:] if [type] might + * be a subtype, provided with the right type arguments. + */ + // TODO(johnniwinther): Change to return a mapping from function types to + // a set of variable points and use this to detect statically/dynamically + // known subtype relations. + Map getFunctionTypeChecksOn(DartType type) { + Map functionTypeMap = new Map(); + for (FunctionType functionType in checkedFunctionTypes) { + int maybeSubtype = compiler.types.computeSubtypeRelation(type, functionType); + if (maybeSubtype == Types.IS_SUBTYPE) { + functionTypeMap[functionType] = true; + } else if (maybeSubtype == Types.MAYBE_SUBTYPE) { + functionTypeMap[functionType] = false; + } + } + // TODO(johnniwinther): Ensure stable ordering of the keys. + return functionTypeMap; + } + + /** + * Generates function type checks on [method] with type [methodType] against + * the function type checks in [functionTypeChecks]. + */ + void generateFunctionTypeTests( + Element method, + FunctionType methodType, + Map functionTypeChecks, + FunctionTypeSignatureEmitter emitFunctionTypeSignature) { + + // TODO(ahe): We should be able to remove this forEach loop. + functionTypeChecks.forEach((FunctionType functionType, bool knownSubtype) { + registerDynamicFunctionTypeCheck(functionType); + }); + + emitFunctionTypeSignature(method, methodType); + } + + void registerDynamicFunctionTypeCheck(FunctionType functionType) { + ClassElement classElement = Types.getClassContext(functionType); + if (classElement != null) { + checkedGenericFunctionTypes.putIfAbsent(classElement, + () => new Set()).add(functionType); + } else { + checkedNonGenericFunctionTypes.add(functionType); + } + } + + void emitRuntimeTypeSupport(CodeBuffer buffer, OutputUnit outputUnit) { + task.addComment('Runtime type support', buffer); + RuntimeTypes rti = backend.rti; + TypeChecks typeChecks = rti.requiredChecks; + + // Add checks to the constructors of instantiated classes. + // TODO(sigurdm): We should avoid running through this list for each + // output unit. + for (ClassElement cls in typeChecks) { + OutputUnit destination = + compiler.deferredLoadTask.outputUnitForElement(cls); + if (destination != outputUnit) continue; + // TODO(9556). The properties added to 'holder' should be generated + // directly as properties of the class object, not added later. + String holder = namer.isolateAccess(backend.getImplementationClass(cls)); + for (TypeCheck check in typeChecks[cls]) { + ClassElement cls = check.cls; + buffer.write('$holder.${namer.operatorIs(cls)}$_=${_}true$N'); + Substitution substitution = check.substitution; + if (substitution != null) { + CodeBuffer body = + jsAst.prettyPrint(substitution.getCode(rti, false), compiler); + buffer.write('$holder.${namer.substitutionName(cls)}$_=${_}'); + buffer.write(body); + buffer.write('$N'); + } + }; + } + } + + /** + * Returns the classes with constructors used as a 'holder' in + * [emitRuntimeTypeSupport]. + * TODO(9556): Some cases will go away when the class objects are created as + * complete. Not all classes will go away while constructors are referenced + * from type substitutions. + */ + Set classesModifiedByEmitRuntimeTypeSupport() { + TypeChecks typeChecks = backend.rti.requiredChecks; + Set result = new Set(); + for (ClassElement cls in typeChecks) { + for (TypeCheck check in typeChecks[cls]) { + result.add(backend.getImplementationClass(cls)); + break; + } + } + return result; + } + + Set computeRtiNeededClasses() { + void addClassWithSuperclasses(ClassElement cls) { + rtiNeededClasses.add(cls); + for (ClassElement superclass = cls.superclass; + superclass != null; + superclass = superclass.superclass) { + rtiNeededClasses.add(superclass); + } + } + + void addClassesWithSuperclasses(Iterable classes) { + for (ClassElement cls in classes) { + addClassWithSuperclasses(cls); + } + } + + // 1. Add classes that are referenced by type arguments or substitutions in + // argument checks. + // TODO(karlklose): merge this case with 2 when unifying argument and + // object checks. + RuntimeTypes rti = backend.rti; + rti.getRequiredArgumentClasses(backend).forEach((ClassElement c) { + // Types that we represent with JS native types (like int and String) do + // not need a class definition as we use the interceptor classes instead. + if (!rti.isJsNative(c)) { + addClassWithSuperclasses(c); + } + }); + + // 2. Add classes that are referenced by substitutions in object checks and + // their superclasses. + TypeChecks requiredChecks = + rti.computeChecks(rtiNeededClasses, checkedClasses); + Set classesUsedInSubstitutions = + rti.getClassesUsedInSubstitutions(backend, requiredChecks); + addClassesWithSuperclasses(classesUsedInSubstitutions); + + // 3. Add classes that contain checked generic function types. These are + // needed to store the signature encoding. + for (FunctionType type in checkedFunctionTypes) { + ClassElement contextClass = Types.getClassContext(type); + if (contextClass != null) { + rtiNeededClasses.add(contextClass); + } + } + + bool canTearOff(Element function) { + if (!function.isFunction() || + function.isConstructor() || + function.isAccessor()) { + return false; + } else if (function.isInstanceMember()) { + if (!function.getEnclosingClass().isClosure()) { + return compiler.codegenWorld.hasInvokedGetter(function, compiler); + } + } + return false; + } + + backend.generatedCode.keys.where((element) { + return element is FunctionElement && + element is! ConstructorBodyElement && + (canTearOff(element) || backend.isAccessibleByReflection(element)); + }).forEach((FunctionElement function) { + DartType type = function.computeType(compiler); + for (ClassElement cls in backend.rti.getReferencedClasses(type)) { + while (cls != null) { + rtiNeededClasses.add(cls); + cls = cls.superclass; + } + } + }); + + return rtiNeededClasses; + } + + void computeRequiredTypeChecks() { + assert(checkedClasses == null && checkedFunctionTypes == null); + + backend.rti.addImplicitChecks(compiler.codegenWorld, + classesUsingTypeVariableTests); + + checkedClasses = new Set(); + checkedFunctionTypes = new Set(); + compiler.codegenWorld.isChecks.forEach((DartType t) { + if (t is InterfaceType) { + checkedClasses.add(t.element); + } else if (t is FunctionType) { + checkedFunctionTypes.add(t); + } + }); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/library_loader.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/library_loader.dart new file mode 100644 index 0000000..6592d2a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/library_loader.dart @@ -0,0 +1,944 @@ +// Copyright (c) 2012, 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. + +part of dart2js; + +/** + * [CompilerTask] for loading libraries and setting up the import/export scopes. + * + * The library loader uses four different kinds of URIs in different parts of + * the loading process. + * + * ## User URI ## + * + * A 'user URI' is a URI provided by the user in code and as the main entry URI + * at the command line. These generally come in 3 versions: + * + * * A relative URI such as 'foo.dart', '../bar.dart', and 'baz/boz.dart'. + * + * * A dart URI such as 'dart:core' and 'dart:_js_helper'. + * + * * A package URI such as 'package:foo.dart' and 'package:bar/baz.dart'. + * + * A user URI can also be absolute, like 'file:///foo.dart' or + * 'http://example.com/bar.dart', but such URIs cannot necessarily be used for + * locating source files, since the scheme must be supported by the input + * provider. The standard input provider for dart2js only supports the 'file' + * scheme. + * + * ## Resolved URI ## + * + * A 'resolved URI' is a (user) URI that has been resolved to an absolute URI + * based on the readable URI (see below) from which it was loaded. A URI with an + * explicit scheme (such as 'dart:', 'package:' or 'file:') is already resolved. + * A relative URI like for instance '../foo/bar.dart' is translated into an + * resolved URI in one of three ways: + * + * * If provided as the main entry URI at the command line, the URI is resolved + * relative to the current working directory, say + * 'file:///current/working/dir/', and the resolved URI is therefore + * 'file:///current/working/foo/bar.dart'. + * + * * If the relative URI is provided in an import, export or part tag, and the + * readable URI of the enclosing compilation unit is a file URI, + * 'file://some/path/baz.dart', then the resolved URI is + * 'file://some/foo/bar.dart'. + * + * * If the relative URI is provided in an import, export or part tag, and the + * readable URI of the enclosing compilation unit is a package URI, + * 'package:some/path/baz.dart', then the resolved URI is + * 'package:some/foo/bar.dart'. + * + * The resolved URI thus preserves the scheme through resolution: A readable + * file URI results in an resolved file URI and a readable package URI results + * in an resolved package URI. Note that since a dart URI is not a readable URI, + * import, export or part tags within platform libraries are not interpreted as + * dart URIs but instead relative to the library source file location. + * + * The resolved URI of a library is also used as the canonical URI + * ([LibraryElement.canonicalUri]) by which we identify which libraries are + * identical. This means that libraries loaded through the 'package' scheme will + * resolve to the same library when loaded from within using relative URIs (see + * for instance the test 'standalone/package/package1_test.dart'). But loading a + * platform library using a relative URI will _not_ result in the same library + * as when loaded through the dart URI. + * + * ## Readable URI ## + * + * A 'readable URI' is an absolute URI whose scheme is either 'package' or + * something supported by the input provider, normally 'file'. Dart URIs such as + * 'dart:core' and 'dart:_js_helper' are not readable themselves but are instead + * resolved into a readable URI using the library root URI provided from the + * command line and the list of platform libraries found in + * 'sdk/lib/_internal/libraries.dart'. This is done through the + * [Compiler.translateResolvedUri] method which checks whether a library by that + * name exists and in case of internal libraries whether access is granted. + * + * ## Resource URI ## + * + * A 'resource URI' is an absolute URI with a scheme supported by the input + * provider. For the standard implementation this means a URI with the 'file' + * scheme. Readable URIs are converted into resource URIs as part of the + * [Compiler.readScript] method. In the standard implementation the package URIs + * are converted to file URIs using the package root URI provided on the + * command line as base. If the package root URI is + * 'file:///current/working/dir/' then the package URI 'package:foo/bar.dart' + * will be resolved to the resource URI + * 'file:///current/working/dir/foo/bar.dart'. + * + * The distinction between readable URI and resource URI is necessary to ensure + * that these imports + * + * import 'package:foo.dart' as a; + * import 'packages/foo.dart' as b; + * + * do _not_ resolve to the same library when the package root URI happens to + * point to the 'packages' folder. + * + */ +abstract class LibraryLoader extends CompilerTask { + LibraryLoader(Compiler compiler) : super(compiler); + + /** + * Loads the library specified by the [resolvedUri] and returns its + * [LibraryElement]. + * + * If the library is not already loaded, the method creates the + * [LibraryElement] for the library and computes the import/export scope, + * loading and computing the import/export scopes of all required libraries in + * the process. The method handles cyclic dependency between libraries. + * + * This is the main entry point for [LibraryLoader]. + */ + // TODO(johnniwinther): Remove [canonicalUri] together with + // [Compiler.scanBuiltinLibrary]. + Future loadLibrary(Uri resolvedUri, Node node, + Uri canonicalUri); + + // TODO(johnniwinther): Remove this when patches don't need special parsing. + Future registerLibraryFromTag(LibraryDependencyHandler handler, + LibraryElement library, + LibraryDependency tag); + + /** + * Adds the elements in the export scope of [importedLibrary] to the import + * scope of [importingLibrary]. + */ + // TODO(johnniwinther): Move handling of 'js_helper' to the library loader + // to remove this method from the [LibraryLoader] interface. + void importLibrary(LibraryElement importingLibrary, + LibraryElement importedLibrary, + Import tag); +} + +/** + * [CombinatorFilter] is a succinct representation of a list of combinators from + * a library dependency tag. + */ +class CombinatorFilter { + const CombinatorFilter(); + + /** + * Returns [:true:] if [element] is excluded by this filter. + */ + bool exclude(Element element) => false; + + /** + * Creates a filter based on the combinators of [tag]. + */ + factory CombinatorFilter.fromTag(LibraryDependency tag) { + if (tag == null || tag.combinators == null) { + return const CombinatorFilter(); + } + + // If the list of combinators contain at least one [:show:] we can create + // a positive list of elements to include, otherwise we create a negative + // list of elements to exclude. + bool show = false; + Set nameSet; + for (Combinator combinator in tag.combinators) { + if (combinator.isShow) { + show = true; + var set = new Set(); + for (Identifier identifier in combinator.identifiers) { + set.add(identifier.source); + } + if (nameSet == null) { + nameSet = set; + } else { + nameSet = nameSet.intersection(set); + } + } + } + if (nameSet == null) { + nameSet = new Set(); + } + for (Combinator combinator in tag.combinators) { + if (combinator.isHide) { + for (Identifier identifier in combinator.identifiers) { + if (show) { + // We have a positive list => Remove hidden elements. + nameSet.remove(identifier.source); + } else { + // We have no positive list => Accumulate hidden elements. + nameSet.add(identifier.source); + } + } + } + } + return show ? new ShowFilter(nameSet) : new HideFilter(nameSet); + } +} + +/** + * A list of combinators represented as a list of element names to include. + */ +class ShowFilter extends CombinatorFilter { + final Set includedNames; + + ShowFilter(this.includedNames); + + bool exclude(Element element) => !includedNames.contains(element.name); +} + +/** + * A list of combinators represented as a list of element names to exclude. + */ +class HideFilter extends CombinatorFilter { + final Set excludedNames; + + HideFilter(this.excludedNames); + + bool exclude(Element element) => excludedNames.contains(element.name); +} + +/** + * Implementation class for [LibraryLoader]. The distinction between + * [LibraryLoader] and [LibraryLoaderTask] is made to hide internal members from + * the [LibraryLoader] interface. + */ +class LibraryLoaderTask extends LibraryLoader { + LibraryLoaderTask(Compiler compiler) : super(compiler); + String get name => 'LibraryLoader'; + List onLibraryLoadedCallbacks = []; + + final Map libraryResourceUriMap = + new Map(); + final Map libraryNames = + new Map(); + + LibraryDependencyHandler currentHandler; + + Future loadLibrary(Uri resolvedUri, Node node, + Uri canonicalUri) { + return measure(() { + assert(currentHandler == null); + // TODO(johnniwinther): Ensure that currentHandler correctly encloses the + // loading of a library cluster. + currentHandler = new LibraryDependencyHandler(compiler); + return createLibrary(currentHandler, null, resolvedUri, node, + canonicalUri).then((LibraryElement library) { + return compiler.withCurrentElement(library, () { + return measure(() { + currentHandler.computeExports(); + currentHandler = null; + var workList = onLibraryLoadedCallbacks; + onLibraryLoadedCallbacks = []; + return Future.forEach(workList, (f) => f()).then((_) => library); + }); + }); + }); + }); + } + + /** + * Processes the library tags in [library]. + * + * The imported/exported libraries are loaded and processed recursively but + * the import/export scopes are not set up. + */ + Future processLibraryTags(LibraryDependencyHandler handler, + LibraryElement library) { + int tagState = TagState.NO_TAG_SEEN; + + /** + * If [value] is less than [tagState] complain and return + * [tagState]. Otherwise return the new value for [tagState] + * (transition function for state machine). + */ + int checkTag(int value, LibraryTag tag) { + if (tagState > value) { + compiler.reportFatalError( + tag, + MessageKind.GENERIC, {'text': 'Error: Out of order.'}); + return tagState; + } + return TagState.NEXT[value]; + } + + bool importsDartCore = false; + var libraryDependencies = new LinkBuilder(); + Uri base = library.entryCompilationUnit.script.readableUri; + + // TODO(rnystrom): Remove .toList() here if #11523 is fixed. + return Future.forEach(library.tags.reverse().toList(), (LibraryTag tag) { + return compiler.withCurrentElement(library, () { + if (tag.isImport) { + Import import = tag; + tagState = checkTag(TagState.IMPORT_OR_EXPORT, import); + if (import.uri.dartString.slowToString() == 'dart:core') { + importsDartCore = true; + } + libraryDependencies.addLast(import); + } else if (tag.isExport) { + tagState = checkTag(TagState.IMPORT_OR_EXPORT, tag); + libraryDependencies.addLast(tag); + } else if (tag.isLibraryName) { + tagState = checkTag(TagState.LIBRARY, tag); + if (library.libraryTag != null) { + compiler.internalError(tag, "Duplicated library declaration."); + } else { + library.libraryTag = tag; + } + } else if (tag.isPart) { + Part part = tag; + StringNode uri = part.uri; + Uri resolvedUri = base.resolve(uri.dartString.slowToString()); + tagState = checkTag(TagState.SOURCE, part); + return scanPart(part, resolvedUri, library); + } else { + compiler.internalError(tag, "Unhandled library tag."); + } + }); + }).then((_) { + return compiler.withCurrentElement(library, () { + checkDuplicatedLibraryName(library); + // Apply patch, if any. + if (library.isPlatformLibrary) { + return patchDartLibrary(handler, library, library.canonicalUri.path); + } + }); + }).then((_) { + return compiler.withCurrentElement(library, () { + // Import dart:core if not already imported. + if (!importsDartCore && !isDartCore(library.canonicalUri)) { + return loadCoreLibrary(handler).then((LibraryElement coreLibrary) { + handler.registerDependency(library, null, coreLibrary); + }); + } + }); + }).then((_) { + // TODO(rnystrom): Remove .toList() here if #11523 is fixed. + return Future.forEach(libraryDependencies.toLink().toList(), (tag) { + return compiler.withCurrentElement(library, () { + return registerLibraryFromTag(handler, library, tag); + }); + }); + }); + } + + void checkDuplicatedLibraryName(LibraryElement library) { + Uri resourceUri = library.entryCompilationUnit.script.resourceUri; + LibraryName tag = library.libraryTag; + LibraryElement existing = + libraryResourceUriMap.putIfAbsent(resourceUri, () => library); + if (!identical(existing, library)) { + if (tag != null) { + compiler.withCurrentElement(library, () { + compiler.reportWarning(tag.name, + MessageKind.DUPLICATED_LIBRARY_RESOURCE, + {'libraryName': tag.name, + 'resourceUri': resourceUri, + 'canonicalUri1': library.canonicalUri, + 'canonicalUri2': existing.canonicalUri}); + }); + } else { + compiler.reportHint(library, + MessageKind.DUPLICATED_RESOURCE, + {'resourceUri': resourceUri, + 'canonicalUri1': library.canonicalUri, + 'canonicalUri2': existing.canonicalUri}); + } + } else if (tag != null) { + String name = library.getLibraryOrScriptName(); + existing = libraryNames.putIfAbsent(name, () => library); + if (!identical(existing, library)) { + compiler.withCurrentElement(library, () { + compiler.reportWarning(tag.name, + MessageKind.DUPLICATED_LIBRARY_NAME, + {'libraryName': name}); + }); + compiler.withCurrentElement(existing, () { + compiler.reportWarning(existing.libraryTag.name, + MessageKind.DUPLICATED_LIBRARY_NAME, + {'libraryName': name}); + }); + } + } + } + + bool isDartCore(Uri uri) => uri.scheme == "dart" && uri.path == "core"; + + /** + * Lazily loads and returns the [LibraryElement] for the dart:core library. + */ + Future loadCoreLibrary(LibraryDependencyHandler handler) { + if (compiler.coreLibrary != null) { + return new Future.value(compiler.coreLibrary); + } + + Uri coreUri = new Uri(scheme: 'dart', path: 'core'); + return createLibrary(handler, null, coreUri, null, coreUri) + .then((LibraryElement library) { + compiler.coreLibrary = library; + return library; + }); + } + + Future patchDartLibrary(LibraryDependencyHandler handler, + LibraryElement library, String dartLibraryPath) { + if (library.isPatched) return new Future.value(); + Uri patchUri = compiler.resolvePatchUri(dartLibraryPath); + if (patchUri == null) return new Future.value(); + + return compiler.patchParser.patchLibrary(handler, patchUri, library); + } + + /** + * Handle a part tag in the scope of [library]. The [resolvedUri] given is + * used as is, any URI resolution should be done beforehand. + */ + Future scanPart(Part part, Uri resolvedUri, LibraryElement library) { + if (!resolvedUri.isAbsolute) throw new ArgumentError(resolvedUri); + Uri readableUri = compiler.translateResolvedUri(library, resolvedUri, part); + if (readableUri == null) return new Future.value(); + return compiler.withCurrentElement(library, () { + return compiler.readScript(part, readableUri). + then((Script sourceScript) { + if (sourceScript == null) return; + + CompilationUnitElement unit = + new CompilationUnitElementX(sourceScript, library); + compiler.withCurrentElement(unit, () { + compiler.scanner.scan(unit); + if (unit.partTag == null) { + compiler.reportError(unit, MessageKind.MISSING_PART_OF_TAG); + } + }); + }); + }); + } + + /** + * Handle an import/export tag by loading the referenced library and + * registering its dependency in [handler] for the computation of the import/ + * export scope. + */ + Future registerLibraryFromTag(LibraryDependencyHandler handler, + LibraryElement library, + LibraryDependency tag) { + Uri base = library.entryCompilationUnit.script.readableUri; + Uri resolvedUri = base.resolve(tag.uri.dartString.slowToString()); + return createLibrary(handler, library, resolvedUri, tag.uri, resolvedUri) + .then((LibraryElement loadedLibrary) { + if (loadedLibrary == null) return; + compiler.withCurrentElement(library, () { + handler.registerDependency(library, tag, loadedLibrary); + }); + }); + } + + /** + * Create (or reuse) a library element for the library specified by the + * [resolvedUri]. + * + * If a new library is created, the [handler] is notified. + */ + // TODO(johnniwinther): Remove [canonicalUri] and make [resolvedUri] the + // canonical uri when [Compiler.scanBuiltinLibrary] is removed. + Future createLibrary(LibraryDependencyHandler handler, + LibraryElement importingLibrary, + Uri resolvedUri, + Node node, + Uri canonicalUri) { + // TODO(johnniwinther): Create erroneous library elements for missing + // libraries. + Uri readableUri = + compiler.translateResolvedUri(importingLibrary, resolvedUri, node); + if (readableUri == null) return new Future.value(); + LibraryElement library; + if (canonicalUri != null) { + library = compiler.libraries[canonicalUri.toString()]; + } + if (library != null) { + return new Future.value(library); + } + return compiler.withCurrentElement(importingLibrary, () { + return compiler.readScript(node, readableUri) + .then((Script script) { + if (script == null) return null; + LibraryElement element = new LibraryElementX(script, canonicalUri); + compiler.withCurrentElement(element, () { + handler.registerNewLibrary(element); + native.maybeEnableNative(compiler, element); + if (canonicalUri != null) { + compiler.libraries[canonicalUri.toString()] = element; + } + compiler.scanner.scanLibrary(element); + }); + return processLibraryTags(handler, element).then((_) { + compiler.withCurrentElement(element, () { + handler.registerLibraryExports(element); + onLibraryLoadedCallbacks.add( + () => compiler.onLibraryLoaded(element, resolvedUri)); + }); + return element; + }); + }); + }); + } + + // TODO(johnniwinther): Remove this method when 'js_helper' is handled by + // [LibraryLoaderTask]. + void importLibrary(LibraryElement importingLibrary, + LibraryElement importedLibrary, + Import tag) { + new ImportLink(tag, importedLibrary).importLibrary(compiler, + importingLibrary); + } +} + + +/** + * The fields of this class models a state machine for checking script + * tags come in the correct order. + */ +class TagState { + static const int NO_TAG_SEEN = 0; + static const int LIBRARY = 1; + static const int IMPORT_OR_EXPORT = 2; + static const int SOURCE = 3; + static const int RESOURCE = 4; + + /** Next state. */ + static const List NEXT = + const [NO_TAG_SEEN, + IMPORT_OR_EXPORT, // Only one library tag is allowed. + IMPORT_OR_EXPORT, + SOURCE, + RESOURCE]; +} + +/** + * An [import] tag and the [importedLibrary] imported through [import]. + */ +class ImportLink { + final Import import; + final LibraryElement importedLibrary; + + ImportLink(this.import, this.importedLibrary); + + /** + * Imports the library into the [importingLibrary]. + */ + void importLibrary(Compiler compiler, LibraryElement importingLibrary) { + assert(invariant(importingLibrary, + importedLibrary.exportsHandled, + message: 'Exports not handled on $importedLibrary')); + var combinatorFilter = new CombinatorFilter.fromTag(import); + if (import != null && import.prefix != null) { + String prefix = import.prefix.source; + Element existingElement = importingLibrary.find(prefix); + PrefixElement prefixElement; + if (existingElement == null || !existingElement.isPrefix()) { + prefixElement = new PrefixElementX(prefix, + importingLibrary.entryCompilationUnit, import.getBeginToken()); + } else { + prefixElement = existingElement; + } + importingLibrary.addToScope(prefixElement, compiler); + importedLibrary.forEachExport((Element element) { + if (combinatorFilter.exclude(element)) return; + prefixElement.addImport(element, import, compiler); + }); + if (import.isDeferred) { + prefixElement.addImport( + new DeferredLoaderGetterElementX(prefixElement), + import, compiler); + // TODO(sigurdm): When we remove support for the annotation based + // syntax the [PrefixElement] constructor should receive this + // information. + prefixElement.markAsDeferred(import); + } + } else { + importedLibrary.forEachExport((Element element) { + compiler.withCurrentElement(importingLibrary, () { + if (combinatorFilter.exclude(element)) return; + importingLibrary.addImport(element, import, compiler); + }); + }); + } + } +} + +/** + * The combinator filter computed from an export tag and the library dependency + * node for the library that declared the export tag. This represents an edge in + * the library dependency graph. + */ +class ExportLink { + final Export export; + final CombinatorFilter combinatorFilter; + final LibraryDependencyNode exportNode; + + ExportLink(Export export, LibraryDependencyNode this.exportNode) + : this.export = export, + this.combinatorFilter = new CombinatorFilter.fromTag(export); + + /** + * Exports [element] to the dependent library unless [element] is filtered by + * the export combinators. Returns [:true:] if the set pending exports of the + * dependent library was modified. + */ + bool exportElement(Element element) { + if (combinatorFilter.exclude(element)) return false; + return exportNode.addElementToPendingExports(element, export); + } +} + +/** + * A node in the library dependency graph. + * + * This class is used to collect the library dependencies expressed through + * import and export tags, and as the work-list entry in computations of library + * exports performed in [LibraryDependencyHandler.computeExports]. + */ +class LibraryDependencyNode { + final LibraryElement library; + + // TODO(ahe): Remove [hashCodeCounter] and [hashCode] when + // VM implementation of Object.hashCode is not slow. + final int hashCode = ++hashCodeCounter; + static int hashCodeCounter = 0; + + + /** + * A linked list of the import tags that import [library] mapped to the + * corresponding libraries. This is used to propagate exports into imports + * after the export scopes have been computed. + */ + Link imports = const Link(); + + /** + * A linked list of the export tags the dependent upon this node library. + * This is used to propagate exports during the computation of export scopes. + */ + Link dependencies = const Link(); + + /** + * The export scope for [library] which is gradually computed by the work-list + * computation in [LibraryDependencyHandler.computeExports]. + */ + Map exportScope = + new Map(); + + /// Map from exported elements to the export directives that exported them. + Map> exporters = new Map>(); + + /** + * The set of exported elements that need to be propageted to dependent + * libraries as part of the work-list computation performed in + * [LibraryDependencyHandler.computeExports]. Each export element is mapped + * to a list of exports directives that export it. + */ + Map> pendingExportMap = + new Map>(); + + LibraryDependencyNode(LibraryElement this.library); + + /** + * Registers that the library of this node imports [importLibrary] through the + * [import] tag. + */ + void registerImportDependency(Import import, + LibraryElement importedLibrary) { + imports = imports.prepend(new ImportLink(import, importedLibrary)); + } + + /** + * Registers that the library of this node is exported by + * [exportingLibraryNode] through the [export] tag. + */ + void registerExportDependency(Export export, + LibraryDependencyNode exportingLibraryNode) { + dependencies = + dependencies.prepend(new ExportLink(export, exportingLibraryNode)); + } + + /** + * Registers all non-private locally declared members of the library of this + * node to be exported. This forms the basis for the work-list computation of + * the export scopes performed in [LibraryDependencyHandler.computeExports]. + */ + void registerInitialExports() { + for (Element element in library.getNonPrivateElementsInScope()) { + pendingExportMap[element] = const Link(); + } + } + + void registerHandledExports(LibraryElement exportedLibraryElement, + Export export, + CombinatorFilter filter) { + assert(invariant(library, exportedLibraryElement.exportsHandled)); + for (Element exportedElement in exportedLibraryElement.exports) { + if (!filter.exclude(exportedElement)) { + Link exports = + pendingExportMap.putIfAbsent(exportedElement, + () => const Link()); + pendingExportMap[exportedElement] = exports.prepend(export); + } + } + } + + /** + * Registers the compute export scope with the node library. + */ + void registerExports() { + library.setExports(exportScope.values.toList()); + } + + /** + * Registers the imports of the node library. + */ + void registerImports(Compiler compiler) { + for (ImportLink link in imports) { + link.importLibrary(compiler, library); + } + } + + /** + * Copies and clears pending export set for this node. + */ + Map> pullPendingExports() { + Map> pendingExports = + new Map>.from(pendingExportMap); + pendingExportMap.clear(); + return pendingExports; + } + + /** + * Adds [element] to the export scope for this node. If the [element] name + * is a duplicate, an error element is inserted into the export scope. + */ + Element addElementToExportScope(Compiler compiler, Element element, + Link exports) { + String name = element.name; + + void reportDuplicateExport(Element duplicate, + Link duplicateExports, + {bool reportError: true}) { + assert(invariant(library, !duplicateExports.isEmpty, + message: "No export for $duplicate from ${duplicate.getLibrary()} " + "in $library.")); + compiler.withCurrentElement(library, () { + for (Export export in duplicateExports) { + if (reportError) { + compiler.reportError(export, + MessageKind.DUPLICATE_EXPORT, {'name': name}); + reportError = false; + } else { + compiler.reportInfo(export, + MessageKind.DUPLICATE_EXPORT_CONT, {'name': name}); + } + } + }); + } + + void reportDuplicateExportDecl(Element duplicate, + Link duplicateExports) { + assert(invariant(library, !duplicateExports.isEmpty, + message: "No export for $duplicate from ${duplicate.getLibrary()} " + "in $library.")); + compiler.reportInfo(duplicate, MessageKind.DUPLICATE_EXPORT_DECL, + {'name': name, 'uriString': duplicateExports.head.uri}); + } + + Element existingElement = exportScope[name]; + if (existingElement != null && existingElement != element) { + if (existingElement.isErroneous()) { + reportDuplicateExport(element, exports); + reportDuplicateExportDecl(element, exports); + element = existingElement; + } else if (existingElement.getLibrary() == library) { + // Do nothing. [existingElement] hides [element]. + } else if (element.getLibrary() == library) { + // [element] hides [existingElement]. + exportScope[name] = element; + exporters[element] = exports; + } else { + // Declared elements hide exported elements. + Link existingExports = exporters[existingElement]; + reportDuplicateExport(existingElement, existingExports); + reportDuplicateExport(element, exports, reportError: false); + reportDuplicateExportDecl(existingElement, existingExports); + reportDuplicateExportDecl(element, exports); + element = exportScope[name] = new ErroneousElementX( + MessageKind.DUPLICATE_EXPORT, {'name': name}, name, library); + } + } else { + exportScope[name] = element; + exporters[element] = exports; + } + return element; + } + + /** + * Propagates the exported [element] to all library nodes that depend upon + * this node. If the propagation updated any pending exports, [:true:] is + * returned. + */ + bool propagateElement(Element element) { + bool change = false; + for (ExportLink link in dependencies) { + if (link.exportElement(element)) { + change = true; + } + } + return change; + } + + /** + * Adds [element] to the pending exports of this node and returns [:true:] if + * the pending export set was modified. The combinators of [export] are used + * to filter the element. + */ + bool addElementToPendingExports(Element element, Export export) { + bool changed = false; + if (!identical(exportScope[element.name], element)) { + Link exports = pendingExportMap.putIfAbsent(element, () { + changed = true; + return const Link(); + }); + pendingExportMap[element] = exports.prepend(export); + } + return changed; + } +} + +/** + * Helper class used for computing the possibly cyclic import/export scopes of + * a set of libraries. + * + * This class is used by [ScannerTask.scanLibrary] to collect all newly loaded + * libraries and to compute their import/export scopes through a fixed-point + * algorithm. + */ +class LibraryDependencyHandler { + final Compiler compiler; + + /** + * Newly loaded libraries and their corresponding node in the library + * dependency graph. Libraries that have already been fully loaded are not + * part of the dependency graph of this handler since their export scopes have + * already been computed. + */ + Map nodeMap = + new Map(); + + LibraryDependencyHandler(Compiler this.compiler); + + /** + * Performs a fixed-point computation on the export scopes of all registered + * libraries and creates the import/export of the libraries based on the + * fixed-point. + */ + void computeExports() { + bool changed = true; + while (changed) { + changed = false; + Map>> tasks = + new Map>>(); + + // Locally defined elements take precedence over exported + // elements. So we must propagate local elements first. We + // ensure this by pulling the pending exports before + // propagating. This enforces that we handle exports + // breadth-first, with locally defined elements being level 0. + nodeMap.forEach((_, LibraryDependencyNode node) { + Map> pendingExports = node.pullPendingExports(); + tasks[node] = pendingExports; + }); + tasks.forEach((LibraryDependencyNode node, + Map> pendingExports) { + pendingExports.forEach((Element element, Link exports) { + element = node.addElementToExportScope(compiler, element, exports); + if (node.propagateElement(element)) { + changed = true; + } + }); + }); + } + + // Setup export scopes. These have to be set before computing the import + // scopes to avoid accessing uncomputed export scopes during handling of + // imports. + nodeMap.forEach((LibraryElement library, LibraryDependencyNode node) { + node.registerExports(); + }); + + // Setup import scopes. + nodeMap.forEach((LibraryElement library, LibraryDependencyNode node) { + node.registerImports(compiler); + }); + } + + /** + * Registers that [library] depends on [loadedLibrary] through [tag]. + */ + void registerDependency(LibraryElement library, + LibraryDependency tag, + LibraryElement loadedLibrary) { + if (tag != null) { + library.recordResolvedTag(tag, loadedLibrary); + } + if (tag is Export) { + // [loadedLibrary] is exported by [library]. + LibraryDependencyNode exportingNode = nodeMap[library]; + if (loadedLibrary.exportsHandled) { + // Export scope already computed on [loadedLibrary]. + var combinatorFilter = new CombinatorFilter.fromTag(tag); + exportingNode.registerHandledExports( + loadedLibrary, tag, combinatorFilter); + return; + } + LibraryDependencyNode exportedNode = nodeMap[loadedLibrary]; + assert(invariant(loadedLibrary, exportedNode != null, + message: "$loadedLibrary has not been registered")); + assert(invariant(library, exportingNode != null, + message: "$library has not been registered")); + exportedNode.registerExportDependency(tag, exportingNode); + } else if (tag == null || tag is Import) { + // [loadedLibrary] is imported by [library]. + LibraryDependencyNode importingNode = nodeMap[library]; + assert(invariant(library, importingNode != null, + message: "$library has not been registered")); + importingNode.registerImportDependency(tag, loadedLibrary); + } + } + + /** + * Registers [library] for the processing of its import/export scope. + */ + void registerNewLibrary(LibraryElement library) { + nodeMap[library] = new LibraryDependencyNode(library); + } + + /** + * Registers all top-level entities of [library] as starting point for the + * fixed-point computation of the import/export scopes. + */ + void registerLibraryExports(LibraryElement library) { + nodeMap[library].registerInitialExports(); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirror_renamer/mirror_renamer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirror_renamer/mirror_renamer.dart new file mode 100644 index 0000000..929479c --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirror_renamer/mirror_renamer.dart @@ -0,0 +1,14 @@ +// Copyright (c) 2013, 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. + +library mirror_renamer; + +import '../dart2jslib.dart' show Script, Compiler; +import '../tree/tree.dart'; +import '../scanner/scannerlib.dart' show Token; +import '../elements/elements.dart'; +import '../dart_backend/dart_backend.dart' show DartBackend, + PlaceholderCollector; + +part 'renamer.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirror_renamer/renamer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirror_renamer/renamer.dart new file mode 100644 index 0000000..156a970 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirror_renamer/renamer.dart @@ -0,0 +1,99 @@ +// Copyright (c) 2013, 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. + +part of mirror_renamer; + +class MirrorRenamer { + static const String MIRROR_HELPER_GET_NAME_FUNCTION = 'helperGetName'; + static const String MIRROR_HELPER_LIBRARY_NAME = '_mirror_helper'; + static const String MIRROR_HELPER_SYMBOLS_MAP_NAME = '_SYMBOLS'; + + /// Maps mangled name to original name. + Map symbols = new Map(); + /// Contains all occurrencs of MirrorSystem.getName() calls in the user code. + List mirrorSystemGetNameNodes = []; + /** + * Initialized when the placeholderCollector collects the FunctionElement + * backend.mirrorHelperGetNameFunction which represents the helperGetName + * function in _mirror_helper. + */ + FunctionExpression mirrorHelperGetNameFunctionNode; + VariableDefinitions mirrorHelperSymbolsMapNode; + Compiler compiler; + DartBackend backend; + + MirrorRenamer(this.compiler, this.backend); + + void registerStaticSend(Element element, Send node) { + if (element == compiler.mirrorSystemGetNameFunction) { + mirrorSystemGetNameNodes.add(node); + } + } + + void registerHelperElement(Element element, Node node) { + if (element == backend.mirrorHelperGetNameFunction) { + mirrorHelperGetNameFunctionNode = node; + } else if (element == backend.mirrorHelperSymbolsMap) { + mirrorHelperSymbolsMapNode = node; + } + } + + /** + * Adds a toplevel node to the output containing a map from the mangled + * to the unmangled names and replaces calls to MirrorSystem.getName() + * with calls to the corresponding wrapper from _mirror_helper which has + * been added during resolution. [renames] is assumed to map nodes in user + * code to mangled names appearing in output code, and [topLevelNodes] should + * contain all the toplevel ast nodes that will be emitted in the output. + */ + void addRenames(Map renames, List topLevelNodes, + PlaceholderCollector placeholderCollector) { + // Right now we only support instances of MirrorSystem.getName, + // hence if there are no occurence of these we don't do anything. + if (mirrorSystemGetNameNodes.isEmpty) { + return; + } + + Node parse(String text) { + Token tokens = compiler.scanner.tokenize(text); + return compiler.parser.parseCompilationUnit(tokens); + } + + // Add toplevel map containing all renames of members. + symbols = new Map(); + for (Set s in placeholderCollector.memberPlaceholders.values) { + // All members in a set have the same name so we only need to look at one. + Identifier sampleNode = s.first; + symbols.putIfAbsent(renames[sampleNode], () => sampleNode.source); + } + + Identifier symbolsMapIdentifier = + mirrorHelperSymbolsMapNode.definitions.nodes.head.asSend().selector; + assert(symbolsMapIdentifier != null); + topLevelNodes.remove(mirrorHelperSymbolsMapNode); + + StringBuffer sb = new StringBuffer( + 'const ${renames[symbolsMapIdentifier]} = const{'); + bool first = true; + for (String mangledName in symbols.keys) { + if (!first) { + sb.write(','); + } else { + first = false; + } + sb.write("'$mangledName' : '"); + sb.write(symbols[mangledName]); + sb.write("'"); + } + sb.write('};'); + sb.writeCharCode(0); // Terminate the string with '0', see [StringScanner]. + topLevelNodes.add(parse(sb.toString())); + + // Replace calls to Mirrorsystem.getName with calls to helper function. + mirrorSystemGetNameNodes.forEach((node) { + renames[node.selector] = renames[mirrorHelperGetNameFunctionNode.name]; + renames[node.receiver] = ''; + }); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/analyze.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/analyze.dart new file mode 100644 index 0000000..fe10b0f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/analyze.dart @@ -0,0 +1,66 @@ +// Copyright (c) 2012, 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. + +library dart2js.source_mirrors.analyze; + +import 'dart:async'; + +import 'source_mirrors.dart'; +import 'dart2js_mirrors.dart' show Dart2JsMirrorSystem; +import '../../compiler.dart' as api; +import '../apiimpl.dart' as apiimpl; +import '../dart2jslib.dart' show Compiler; + +//------------------------------------------------------------------------------ +// Analysis entry point. +//------------------------------------------------------------------------------ + +/** + * Analyzes set of libraries and provides a mirror system which can be used for + * static inspection of the source code. + */ +// TODO(johnniwinther): Move this to [compiler/compiler.dart]. +Future analyze(List libraries, + Uri libraryRoot, + Uri packageRoot, + api.CompilerInputProvider inputProvider, + api.DiagnosticHandler diagnosticHandler, + [List options = const []]) { + if (!libraryRoot.path.endsWith("/")) { + throw new ArgumentError("libraryRoot must end with a /"); + } + if (packageRoot != null && !packageRoot.path.endsWith("/")) { + throw new ArgumentError("packageRoot must end with a /"); + } + options = new List.from(options); + options.add('--analyze-only'); + options.add('--analyze-signatures-only'); + options.add('--analyze-all'); + options.add('--categories=Client,Server'); + + bool compilationFailed = false; + void internalDiagnosticHandler(Uri uri, int begin, int end, + String message, api.Diagnostic kind) { + if (kind == api.Diagnostic.ERROR || + kind == api.Diagnostic.CRASH) { + compilationFailed = true; + } + diagnosticHandler(uri, begin, end, message, kind); + } + + Compiler compiler = new apiimpl.Compiler(inputProvider, + null, + internalDiagnosticHandler, + libraryRoot, packageRoot, + options, + const {}); + compiler.librariesToAnalyzeWhenRun = libraries; + return compiler.run(null).then((bool success) { + if (success && !compilationFailed) { + return new Dart2JsMirrorSystem(compiler); + } else { + throw new StateError('Failed to create mirror system.'); + } + }); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_instance_mirrors.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_instance_mirrors.dart new file mode 100644 index 0000000..982aa11 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_instance_mirrors.dart @@ -0,0 +1,281 @@ +// Copyright (c) 2013, 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. + +part of dart2js.mirrors; + +abstract class ObjectMirrorMixin implements ObjectMirror { + InstanceMirror getField(Symbol fieldName) { + throw new UnsupportedError('ObjectMirror.getField unsupported.'); + } + + InstanceMirror setField(Symbol fieldName, Object value) { + throw new UnsupportedError('ObjectMirror.setField unsupported.'); + } + + InstanceMirror invoke(Symbol memberName, + List positionalArguments, + [Map namedArguments]) { + throw new UnsupportedError('ObjectMirror.invoke unsupported.'); + } +} + +abstract class InstanceMirrorMixin implements InstanceMirror { + bool get hasReflectee => false; + + get reflectee { + throw new UnsupportedError('InstanceMirror.reflectee unsupported.'); + } + + delegate(Invocation invocation) { + throw new UnsupportedError('InstanceMirror.delegate unsupported'); + } +} + +InstanceMirror _convertConstantToInstanceMirror( + Dart2JsMirrorSystem mirrorSystem, Constant constant) { + if (constant is BoolConstant) { + return new Dart2JsBoolConstantMirror(mirrorSystem, constant); + } else if (constant is NumConstant) { + return new Dart2JsNumConstantMirror(mirrorSystem, constant); + } else if (constant is StringConstant) { + return new Dart2JsStringConstantMirror(mirrorSystem, constant); + } else if (constant is ListConstant) { + return new Dart2JsListConstantMirror(mirrorSystem, constant); + } else if (constant is MapConstant) { + return new Dart2JsMapConstantMirror(mirrorSystem, constant); + } else if (constant is TypeConstant) { + return new Dart2JsTypeConstantMirror(mirrorSystem, constant); + } else if (constant is FunctionConstant) { + return new Dart2JsConstantMirror(mirrorSystem, constant); + } else if (constant is NullConstant) { + return new Dart2JsNullConstantMirror(mirrorSystem, constant); + } else if (constant is ConstructedConstant) { + return new Dart2JsConstructedConstantMirror(mirrorSystem, constant); + } + mirrorSystem.compiler.internalError(NO_LOCATION_SPANNABLE, + "Unexpected constant $constant"); + return null; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Mirrors on constant values used for metadata. +//////////////////////////////////////////////////////////////////////////////// + +class Dart2JsConstantMirror extends Object + with ObjectMirrorMixin, InstanceMirrorMixin { + final Dart2JsMirrorSystem mirrorSystem; + final Constant _constant; + + Dart2JsConstantMirror(this.mirrorSystem, this._constant); + + // TODO(johnniwinther): Improve the quality of this method. + String toString() => '$_constant'; + + ClassMirror get type { + return mirrorSystem._getTypeDeclarationMirror( + _constant.computeType(mirrorSystem.compiler).element); + } + + int get hashCode => 13 * _constant.hashCode; + + bool operator ==(var other) { + if (other is! Dart2JsConstantMirror) return false; + return _constant == other._constant; + } +} + +class Dart2JsNullConstantMirror extends Dart2JsConstantMirror { + Dart2JsNullConstantMirror(Dart2JsMirrorSystem mirrorSystem, + NullConstant constant) + : super(mirrorSystem, constant); + + NullConstant get _constant => super._constant; + + bool get hasReflectee => true; + + get reflectee => null; +} + +class Dart2JsBoolConstantMirror extends Dart2JsConstantMirror { + Dart2JsBoolConstantMirror(Dart2JsMirrorSystem mirrorSystem, + BoolConstant constant) + : super(mirrorSystem, constant); + + Dart2JsBoolConstantMirror.fromBool(Dart2JsMirrorSystem mirrorSystem, + bool value) + : super(mirrorSystem, value ? new TrueConstant() : new FalseConstant()); + + BoolConstant get _constant => super._constant; + + bool get hasReflectee => true; + + get reflectee => _constant is TrueConstant; +} + +class Dart2JsStringConstantMirror extends Dart2JsConstantMirror { + Dart2JsStringConstantMirror(Dart2JsMirrorSystem mirrorSystem, + StringConstant constant) + : super(mirrorSystem, constant); + + Dart2JsStringConstantMirror.fromString(Dart2JsMirrorSystem mirrorSystem, + String text) + : super(mirrorSystem, new StringConstant(new DartString.literal(text))); + + StringConstant get _constant => super._constant; + + bool get hasReflectee => true; + + get reflectee => _constant.value.slowToString(); +} + +class Dart2JsNumConstantMirror extends Dart2JsConstantMirror { + Dart2JsNumConstantMirror(Dart2JsMirrorSystem mirrorSystem, + NumConstant constant) + : super(mirrorSystem, constant); + + NumConstant get _constant => super._constant; + + bool get hasReflectee => true; + + get reflectee => _constant.value; +} + +class Dart2JsListConstantMirror extends Dart2JsConstantMirror + implements ListInstanceMirror { + Dart2JsListConstantMirror(Dart2JsMirrorSystem mirrorSystem, + ListConstant constant) + : super(mirrorSystem, constant); + + ListConstant get _constant => super._constant; + + int get length => _constant.length; + + InstanceMirror getElement(int index) { + if (index < 0) throw new RangeError('Negative index'); + if (index >= _constant.length) throw new RangeError('Index out of bounds'); + return _convertConstantToInstanceMirror(mirrorSystem, + _constant.entries[index]); + } +} + +class Dart2JsMapConstantMirror extends Dart2JsConstantMirror + implements MapInstanceMirror { + List _listCache; + + Dart2JsMapConstantMirror(Dart2JsMirrorSystem mirrorSystem, + MapConstant constant) + : super(mirrorSystem, constant); + + MapConstant get _constant => super._constant; + + List get _list { + if (_listCache == null) { + _listCache = new List(_constant.keys.entries.length); + int index = 0; + for (StringConstant keyConstant in _constant.keys.entries) { + _listCache[index] = keyConstant.value.slowToString(); + index++; + } + _listCache = new UnmodifiableListView(_listCache); + } + return _listCache; + } + + int get length => _constant.length; + + Iterable get keys { + return _list; + } + + InstanceMirror getValue(String key) { + int index = _list.indexOf(key); + if (index == -1) return null; + return _convertConstantToInstanceMirror(mirrorSystem, + _constant.values[index]); + } +} + +class Dart2JsTypeConstantMirror extends Dart2JsConstantMirror + implements TypeInstanceMirror { + + Dart2JsTypeConstantMirror(Dart2JsMirrorSystem mirrorSystem, + TypeConstant constant) + : super(mirrorSystem, constant); + + TypeConstant get _constant => super._constant; + + TypeMirror get representedType => + mirrorSystem._convertTypeToTypeMirror(_constant.representedType); +} + +class Dart2JsConstructedConstantMirror extends Dart2JsConstantMirror { + Map _fieldMapCache; + + Dart2JsConstructedConstantMirror(Dart2JsMirrorSystem mirrorSystem, + ConstructedConstant constant) + : super(mirrorSystem, constant); + + ConstructedConstant get _constant => super._constant; + + Map get _fieldMap { + if (_fieldMapCache == null) { + _fieldMapCache = new Map(); + if (identical(_constant.type.element.kind, ElementKind.CLASS)) { + var index = 0; + ClassElement element = _constant.type.element; + element.forEachInstanceField((_, Element field) { + String fieldName = field.name; + _fieldMapCache.putIfAbsent(fieldName, () => _constant.fields[index]); + index++; + }, includeSuperAndInjectedMembers: true); + } + } + return _fieldMapCache; + } + + InstanceMirror getField(Symbol fieldName) { + Constant fieldConstant = _fieldMap[MirrorSystem.getName(fieldName)]; + if (fieldConstant != null) { + return _convertConstantToInstanceMirror(mirrorSystem, fieldConstant); + } + return super.getField(fieldName); + } +} + +class Dart2JsCommentInstanceMirror extends Object + with ObjectMirrorMixin, InstanceMirrorMixin + implements CommentInstanceMirror { + final Dart2JsMirrorSystem mirrorSystem; + final String text; + String _trimmedText; + + Dart2JsCommentInstanceMirror(this.mirrorSystem, this.text); + + ClassMirror get type { + return mirrorSystem._getTypeDeclarationMirror( + mirrorSystem.compiler.documentClass); + } + + bool get isDocComment => text.startsWith('/**') || text.startsWith('///'); + + String get trimmedText { + if (_trimmedText == null) { + _trimmedText = stripComment(text); + } + return _trimmedText; + } + + InstanceMirror getField(Symbol fieldName) { + if (fieldName == #isDocComment) { + return new Dart2JsBoolConstantMirror.fromBool(mirrorSystem, isDocComment); + } else if (fieldName == #text) { + return new Dart2JsStringConstantMirror.fromString(mirrorSystem, text); + } else if (fieldName == #trimmedText) { + return new Dart2JsStringConstantMirror.fromString(mirrorSystem, + trimmedText); + } + return super.getField(fieldName); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_library_mirror.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_library_mirror.dart new file mode 100644 index 0000000..82e593d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_library_mirror.dart @@ -0,0 +1,236 @@ +// Copyright (c) 2013, 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. + +part of dart2js.mirrors; + + +class Dart2JsLibraryMirror + extends Dart2JsElementMirror + with ObjectMirrorMixin, ContainerMixin + implements LibrarySourceMirror { + List _libraryDependencies; + + Dart2JsLibraryMirror(Dart2JsMirrorSystem system, LibraryElement library) + : super(system, library); + + Function operator [](Symbol name) { + throw new UnsupportedError('LibraryMirror.operator [] unsupported.'); + } + + LibraryElement get _element => super._element; + + Uri get uri => _element.canonicalUri; + + DeclarationMirror get owner => null; + + bool get isPrivate => false; + + LibraryMirror library() => this; + + /** + * Returns the library name (for libraries with a library tag) or the script + * file name (for scripts without a library tag). The latter case is used to + * provide a 'library name' for scripts, to use for instance in dartdoc. + */ + String get _simpleNameString { + if (_element.libraryTag != null) { + return _element.libraryTag.name.toString(); + } else { + // Use the file name as script name. + String path = _element.canonicalUri.path; + return path.substring(path.lastIndexOf('/') + 1); + } + } + + Symbol get qualifiedName => simpleName; + + void _forEachElement(f(Element element)) => _element.forEachLocalMember(f); + + Iterable _getDeclarationMirrors(Element element) { + if (element.isClass() || element.isTypedef()) { + return [mirrorSystem._getTypeDeclarationMirror(element)]; + } else { + return super._getDeclarationMirrors(element); + } + } + + Map get topLevelMembers => null; + + /** + * Computes the first token of this library using the first library tag as + * indicator. + */ + Token getBeginToken() { + if (_element.libraryTag != null) { + return _element.libraryTag.getBeginToken(); + } else if (!_element.tags.isEmpty) { + return _element.tags.reverse().head.getBeginToken(); + } + return null; + } + + /** + * Computes the first token of this library using the last library tag as + * indicator. + */ + Token getEndToken() { + if (!_element.tags.isEmpty) { + return _element.tags.head.getEndToken(); + } + return null; + } + + void _ensureLibraryDependenciesAnalyzed() { + if (_libraryDependencies == null) { + _libraryDependencies = []; + for (LibraryTag node in _element.tags.reverse()) { + LibraryDependency libraryDependency = node.asLibraryDependency(); + if (libraryDependency != null) { + LibraryElement targetLibraryElement = + _element.getLibraryFromTag(libraryDependency); + assert(targetLibraryElement != null); + LibraryMirror targetLibrary = + mirrorSystem._getLibrary(targetLibraryElement); + _libraryDependencies.add(new Dart2JsLibraryDependencyMirror( + libraryDependency, this, targetLibrary)); + } + } + } + } + + List get libraryDependencies { + _ensureLibraryDependenciesAnalyzed(); + return _libraryDependencies; + } +} + +class Dart2JsLibraryDependencyMirror implements LibraryDependencySourceMirror { + final LibraryDependency _node; + final Dart2JsLibraryMirror _sourceLibrary; + final Dart2JsLibraryMirror _targetLibrary; + List _combinators; + + Dart2JsLibraryDependencyMirror(this._node, + this._sourceLibrary, + this._targetLibrary); + + SourceLocation get location { + return new Dart2JsSourceLocation( + _sourceLibrary._element.entryCompilationUnit.script, + _sourceLibrary.mirrorSystem.compiler.spanFromNode(_node)); + } + + List get combinators { + if (_combinators == null) { + _combinators = []; + if (_node.combinators != null) { + for (Combinator combinator in _node.combinators.nodes) { + List identifiers = []; + for (Identifier identifier in combinator.identifiers.nodes) { + identifiers.add(identifier.source); + } + _combinators.add(new Dart2JsCombinatorMirror( + identifiers, isShow: combinator.isShow)); + } + } + } + return _combinators; + } + + LibraryMirror get sourceLibrary => _sourceLibrary; + + LibraryMirror get targetLibrary => _targetLibrary; + + /*String*/ get prefix { + Import import = _node.asImport(); + if (import != null && import.prefix != null) { + return import.prefix.source; + } + return null; + } + + bool get isImport => _node.asImport() != null; + + bool get isExport => _node.asExport() != null; + + List get metadata => const []; +} + +class Dart2JsCombinatorMirror implements CombinatorSourceMirror { + final List/**/ identifiers; + final bool isShow; + + Dart2JsCombinatorMirror(this.identifiers, {bool isShow: true}) + : this.isShow = isShow; + + bool get isHide => !isShow; +} + +class Dart2JsSourceLocation implements SourceLocation { + final Script _script; + final SourceSpan _span; + int _line; + int _column; + + Dart2JsSourceLocation(this._script, this._span); + + int _computeLine() { + var sourceFile = _script.file; + if (sourceFile != null) { + return sourceFile.getLine(offset) + 1; + } + var index = 0; + var lineNumber = 0; + while (index <= offset && index < sourceText.length) { + index = sourceText.indexOf('\n', index) + 1; + if (index <= 0) break; + lineNumber++; + } + return lineNumber; + } + + int get line { + if (_line == null) { + _line = _computeLine(); + } + return _line; + } + + int _computeColumn() { + if (length == 0) return 0; + + var sourceFile = _script.file; + if (sourceFile != null) { + return sourceFile.getColumn(sourceFile.getLine(offset), offset) + 1; + } + int index = offset - 1; + var columnNumber = 0; + while (0 <= index && index < sourceText.length) { + columnNumber++; + var codeUnit = sourceText.codeUnitAt(index); + if (codeUnit == $CR || codeUnit == $LF) { + break; + } + index--; + } + return columnNumber; + } + + int get column { + if (_column == null) { + _column = _computeColumn(); + } + return _column; + } + + int get offset => _span.begin; + + int get length => _span.end - _span.begin; + + String get text => _script.text.substring(_span.begin, _span.end); + + Uri get sourceUri => _script.resourceUri; + + String get sourceText => _script.text; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_member_mirrors.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_member_mirrors.dart new file mode 100644 index 0000000..2012604 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_member_mirrors.dart @@ -0,0 +1,272 @@ +// Copyright (c) 2013, 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. + +part of dart2js.mirrors; + +//------------------------------------------------------------------------------ +// Member mirrors implementation. +//------------------------------------------------------------------------------ + +abstract class Dart2JsMemberMirror extends Dart2JsElementMirror { + + Dart2JsMemberMirror(Dart2JsMirrorSystem system, Element element) + : super(system, element); + + bool get isStatic => false; +} + + +class Dart2JsMethodKind { + static const Dart2JsMethodKind REGULAR = const Dart2JsMethodKind("regular"); + static const Dart2JsMethodKind GENERATIVE = + const Dart2JsMethodKind("generative"); + static const Dart2JsMethodKind REDIRECTING = + const Dart2JsMethodKind("redirecting"); + static const Dart2JsMethodKind CONST = const Dart2JsMethodKind("const"); + static const Dart2JsMethodKind FACTORY = const Dart2JsMethodKind("factory"); + static const Dart2JsMethodKind GETTER = const Dart2JsMethodKind("getter"); + static const Dart2JsMethodKind SETTER = const Dart2JsMethodKind("setter"); + static const Dart2JsMethodKind OPERATOR = const Dart2JsMethodKind("operator"); + + final String text; + + const Dart2JsMethodKind(this.text); + + String toString() => text; +} + + +String _getOperatorFromOperatorName(String name) { + Map mapping = const { + 'eq': '==', + 'not': '~', + 'index': '[]', + 'indexSet': '[]=', + 'mul': '*', + 'div': '/', + 'mod': '%', + 'tdiv': '~/', + 'add': '+', + 'sub': '-', + 'shl': '<<', + 'shr': '>>', + 'ge': '>=', + 'gt': '>', + 'le': '<=', + 'lt': '<', + 'and': '&', + 'xor': '^', + 'or': '|', + }; + String newName = mapping[name]; + if (newName == null) { + throw new Exception('Unhandled operator name: $name'); + } + return newName; +} + +class Dart2JsMethodMirror extends Dart2JsMemberMirror + implements MethodMirror { + final Dart2JsDeclarationMirror owner; + final String _simpleNameString; + final Dart2JsMethodKind _kind; + + Dart2JsMethodMirror._internal(Dart2JsDeclarationMirror owner, + FunctionElement function, + String this._simpleNameString, + Dart2JsMethodKind this._kind) + : this.owner = owner, + super(owner.mirrorSystem, function); + + factory Dart2JsMethodMirror(Dart2JsDeclarationMirror owner, + FunctionElement function) { + String realName = function.name; + // TODO(ahe): This method should not be calling + // Elements.operatorNameToIdentifier. + String simpleName = + Elements.operatorNameToIdentifier(function.name); + Dart2JsMethodKind kind; + if (function.kind == ElementKind.GETTER) { + kind = Dart2JsMethodKind.GETTER; + } else if (function.kind == ElementKind.SETTER) { + kind = Dart2JsMethodKind.SETTER; + simpleName = '$simpleName='; + } else if (function.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { + // TODO(johnniwinther): Support detection of redirecting constructors. + if (function.modifiers.isConst()) { + kind = Dart2JsMethodKind.CONST; + } else { + kind = Dart2JsMethodKind.GENERATIVE; + } + } else if (function.modifiers.isFactory()) { + // TODO(johnniwinther): Support detection of redirecting constructors. + kind = Dart2JsMethodKind.FACTORY; + } else if (realName == 'unary-') { + // TODO(johnniwinther): Use a constant for 'unary-'. + kind = Dart2JsMethodKind.OPERATOR; + // Simple name is 'unary-'. + simpleName = 'unary-'; + } else if (simpleName.startsWith('operator\$')) { + String str = simpleName.substring(9); + simpleName = 'operator'; + kind = Dart2JsMethodKind.OPERATOR; + simpleName = _getOperatorFromOperatorName(str); + } else { + kind = Dart2JsMethodKind.REGULAR; + } + return new Dart2JsMethodMirror._internal(owner, function, + simpleName, kind); + } + + FunctionElement get _function => _element; + + bool get isTopLevel => owner is LibraryMirror; + + // TODO(johnniwinther): This seems stale and broken. + Symbol get constructorName => isConstructor ? simpleName : const Symbol(''); + + bool get isConstructor + => isGenerativeConstructor || isConstConstructor || + isFactoryConstructor || isRedirectingConstructor; + + bool get isSynthetic => false; + + bool get isStatic => _function.modifiers.isStatic(); + + List get parameters { + return _parametersFromFunctionSignature(this, + _function.functionSignature); + } + + TypeMirror get returnType => owner._getTypeMirror( + _function.functionSignature.type.returnType); + + bool get isAbstract => _function.isAbstract; + + bool get isRegularMethod => !(isGetter || isSetter || isConstructor); + + bool get isConstConstructor => _kind == Dart2JsMethodKind.CONST; + + bool get isGenerativeConstructor => _kind == Dart2JsMethodKind.GENERATIVE; + + bool get isRedirectingConstructor => _kind == Dart2JsMethodKind.REDIRECTING; + + bool get isFactoryConstructor => _kind == Dart2JsMethodKind.FACTORY; + + bool get isGetter => _kind == Dart2JsMethodKind.GETTER; + + bool get isSetter => _kind == Dart2JsMethodKind.SETTER; + + bool get isOperator => _kind == Dart2JsMethodKind.OPERATOR; + + DeclarationMirror lookupInScope(String name) { + for (Dart2JsParameterMirror parameter in parameters) { + if (parameter._element.name == name) { + return parameter; + } + } + return super.lookupInScope(name); + } + + // TODO(johnniwinther): Should this really be in the interface of + // [MethodMirror]? + String get source => location.sourceText; + + String toString() => 'Mirror on method ${_element.name}'; +} + +class Dart2JsFieldMirror extends Dart2JsMemberMirror implements VariableMirror { + final Dart2JsDeclarationMirror owner; + VariableElement _variable; + + Dart2JsFieldMirror(Dart2JsDeclarationMirror owner, + VariableElement variable) + : this.owner = owner, + this._variable = variable, + super(owner.mirrorSystem, variable); + + bool get isTopLevel => owner is LibraryMirror; + + bool get isStatic => _variable.modifiers.isStatic(); + + bool get isFinal => _variable.modifiers.isFinal(); + + bool get isConst => _variable.modifiers.isConst(); + + TypeMirror get type => owner._getTypeMirror(_variable.type); + + +} + +class Dart2JsParameterMirror extends Dart2JsMemberMirror + implements ParameterMirror { + final Dart2JsDeclarationMirror owner; + final bool isOptional; + final bool isNamed; + + factory Dart2JsParameterMirror(Dart2JsDeclarationMirror owner, + ParameterElement element, + {bool isOptional: false, + bool isNamed: false}) { + if (element is FieldParameterElement) { + return new Dart2JsFieldParameterMirror( + owner, element, isOptional, isNamed); + } + return new Dart2JsParameterMirror._normal( + owner, element, isOptional, isNamed); + } + + Dart2JsParameterMirror._normal(Dart2JsDeclarationMirror owner, + ParameterElement element, + this.isOptional, + this.isNamed) + : this.owner = owner, + super(owner.mirrorSystem, element); + + ParameterElement get _element => super._element; + + TypeMirror get type => owner._getTypeMirror(_element.type, + _element.functionSignature); + + + bool get isFinal => false; + + bool get isConst => false; + + InstanceMirror get defaultValue { + if (hasDefaultValue) { + Constant constant = mirrorSystem.compiler.constantHandler + .getConstantForVariable(_element); + assert(invariant(_element, constant != null, + message: "Missing constant for parameter " + "$_element with default value.")); + return _convertConstantToInstanceMirror(mirrorSystem, constant); + } + return null; + } + + bool get hasDefaultValue { + return _element.initializer != null; + } + + bool get isInitializingFormal => false; + + VariableMirror get initializedField => null; +} + +class Dart2JsFieldParameterMirror extends Dart2JsParameterMirror { + + Dart2JsFieldParameterMirror(Dart2JsDeclarationMirror method, + FieldParameterElement element, + bool isOptional, + bool isNamed) + : super._normal(method, element, isOptional, isNamed); + + FieldParameterElement get _fieldParameterElement => _element; + + bool get isInitializingFormal => true; + + VariableMirror get initializedField => new Dart2JsFieldMirror( + owner.owner, _fieldParameterElement.fieldElement); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirrors.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirrors.dart new file mode 100644 index 0000000..09703a6 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirrors.dart @@ -0,0 +1,467 @@ +// Copyright (c) 2012, 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. + +library dart2js.mirrors; + +import 'dart:collection' show UnmodifiableListView; + +import '../elements/elements.dart'; +import '../scanner/scannerlib.dart'; +import '../resolution/resolution.dart' show Scope; +import '../dart2jslib.dart'; +import '../dart_types.dart'; +import '../tree/tree.dart'; +import '../util/util.dart' + show Spannable, + Link, + LinkBuilder, + NO_LOCATION_SPANNABLE; +import '../util/characters.dart' show $CR, $LF; + +import 'source_mirrors.dart'; +import 'mirrors_util.dart'; +import 'util.dart'; + +part 'dart2js_library_mirror.dart'; +part 'dart2js_type_mirrors.dart'; +part 'dart2js_member_mirrors.dart'; +part 'dart2js_instance_mirrors.dart'; + +//------------------------------------------------------------------------------ +// Utility types and functions for the dart2js mirror system +//------------------------------------------------------------------------------ + +bool _isPrivate(String name) { + return name.startsWith('_'); +} + +List _parametersFromFunctionSignature( + Dart2JsDeclarationMirror owner, + FunctionSignature signature) { + var parameters = []; + Link link = signature.requiredParameters; + while (!link.isEmpty) { + parameters.add(new Dart2JsParameterMirror( + owner, link.head, isOptional: false, isNamed: false)); + link = link.tail; + } + link = signature.optionalParameters; + bool isNamed = signature.optionalParametersAreNamed; + while (!link.isEmpty) { + parameters.add(new Dart2JsParameterMirror( + owner, link.head, isOptional: true, isNamed: isNamed)); + link = link.tail; + } + return parameters; +} + +MethodMirror _convertElementMethodToMethodMirror( + Dart2JsDeclarationMirror library, Element element) { + if (element is FunctionElement) { + return new Dart2JsMethodMirror(library, element); + } else { + return null; + } +} + +//------------------------------------------------------------------------------ +// Dart2Js specific extensions of mirror interfaces +//------------------------------------------------------------------------------ + +abstract class Dart2JsMirror implements Mirror { + Dart2JsMirrorSystem get mirrorSystem; +} + +abstract class Dart2JsDeclarationMirror extends Dart2JsMirror + implements DeclarationSourceMirror { + + bool get isTopLevel => owner != null && owner is LibraryMirror; + + bool get isPrivate => _isPrivate(_simpleNameString); + + String get _simpleNameString; + + String get _qualifiedNameString { + var parent = owner; + if (parent is Dart2JsDeclarationMirror) { + return '${parent._qualifiedNameString}.${_simpleNameString}'; + } + assert(parent == null); + return _simpleNameString; + } + + Symbol get simpleName => symbolOf(_simpleNameString, getLibrary(this)); + + Symbol get qualifiedName => symbolOf(_qualifiedNameString, getLibrary(this)); + + /** + * Returns the first token for the source of this declaration, not including + * metadata annotations. + */ + Token getBeginToken(); + + /** + * Returns the last token for the source of this declaration. + */ + Token getEndToken(); + + /** + * Returns the script for the source of this declaration. + */ + Script getScript(); + + /// Returns the type mirror for [type] in the context of this declaration. + TypeMirror _getTypeMirror(DartType type, [FunctionSignature signature]) { + return mirrorSystem._convertTypeToTypeMirror(type, signature); + } + + /// Returns a list of the declaration mirrorSystem for [element]. + Iterable _getDeclarationMirrors(Element element) { + if (element.isSynthesized) { + return const []; + } else if (element is VariableElement) { + return [new Dart2JsFieldMirror(this, element)]; + } else if (element is FunctionElement) { + return [new Dart2JsMethodMirror(this, element)]; + } else if (element is AbstractFieldElement) { + var members = []; + AbstractFieldElement field = element; + if (field.getter != null) { + members.add(new Dart2JsMethodMirror(this, field.getter)); + } + if (field.setter != null) { + members.add(new Dart2JsMethodMirror(this, field.setter)); + } + return members; + } + mirrorSystem.compiler.internalError(element, + "Unexpected member type $element ${element.kind}."); + return null; + } + +} + +abstract class Dart2JsElementMirror extends Dart2JsDeclarationMirror { + final Dart2JsMirrorSystem mirrorSystem; + final Element _element; + List _metadata; + + Dart2JsElementMirror(this.mirrorSystem, this._element) { + assert (mirrorSystem != null); + assert (_element != null); + } + + String get _simpleNameString => _element.name; + + bool get isNameSynthetic => false; + + /** + * Computes the first token for this declaration using the begin token of the + * element node or element position as indicator. + */ + Token getBeginToken() { + // TODO(johnniwinther): Avoid calling [parseNode]. + Node node = _element.parseNode(mirrorSystem.compiler); + if (node == null) { + return _element.position(); + } + return node.getBeginToken(); + } + + /** + * Computes the last token for this declaration using the end token of the + * element node or element position as indicator. + */ + Token getEndToken() { + // TODO(johnniwinther): Avoid calling [parseNode]. + Node node = _element.parseNode(mirrorSystem.compiler); + if (node == null) { + return _element.position(); + } + return node.getEndToken(); + } + + /** + * Returns the first token for the source of this declaration, including + * metadata annotations. + */ + Token getFirstToken() { + if (!_element.metadata.isEmpty) { + for (MetadataAnnotation metadata in _element.metadata) { + if (metadata.beginToken != null) { + return metadata.beginToken; + } + } + } + return getBeginToken(); + } + + Script getScript() => _element.getCompilationUnit().script; + + SourceLocation get location { + Token beginToken = getFirstToken(); + Script script = getScript(); + SourceSpan span; + if (beginToken == null) { + span = new SourceSpan(script.readableUri, 0, 0); + } else { + Token endToken = getEndToken(); + span = mirrorSystem.compiler.spanFromTokens( + beginToken, endToken, script.readableUri); + } + return new Dart2JsSourceLocation(script, span); + } + + String toString() => _element.toString(); + + void _appendCommentTokens(Token commentToken) { + while (commentToken != null && commentToken.kind == COMMENT_TOKEN) { + _metadata.add(new Dart2JsCommentInstanceMirror( + mirrorSystem, commentToken.value)); + commentToken = commentToken.next; + } + } + + List get metadata { + if (_metadata == null) { + _metadata = []; + for (MetadataAnnotation metadata in _element.metadata) { + _appendCommentTokens( + mirrorSystem.compiler.commentMap[metadata.beginToken]); + metadata.ensureResolved(mirrorSystem.compiler); + _metadata.add( + _convertConstantToInstanceMirror(mirrorSystem, metadata.value)); + } + _appendCommentTokens(mirrorSystem.compiler.commentMap[getBeginToken()]); + } + // TODO(johnniwinther): Return an unmodifiable list instead. + return new List.from(_metadata); + } + + DeclarationMirror lookupInScope(String name) { + // TODO(11653): Support lookup of constructors. + Scope scope = _element.buildScope(); + Element result; + int index = name.indexOf('.'); + if (index != -1) { + // Lookup [: prefix.id :]. + String prefix = name.substring(0, index); + String id = name.substring(index+1); + result = scope.lookup(prefix); + if (result != null && result.isPrefix()) { + PrefixElement prefix = result; + result = prefix.lookupLocalMember(id); + } else { + result = null; + } + } else { + // Lookup [: id :]. + result = scope.lookup(name); + } + if (result == null || result.isPrefix()) return null; + return _convertElementToDeclarationMirror(mirrorSystem, result); + } + + bool operator ==(var other) { + if (identical(this, other)) return true; + if (other == null) return false; + if (other is! Dart2JsElementMirror) return false; + return _element == other._element && + owner == other.owner; + } + + int get hashCode { + return 13 * _element.hashCode + 17 * owner.hashCode; + } +} + +//------------------------------------------------------------------------------ +// Mirror system implementation. +//------------------------------------------------------------------------------ + +class Dart2JsMirrorSystem extends MirrorSystem { + final Compiler compiler; + Map _libraries; + Map _libraryMap; + + Dart2JsMirrorSystem(this.compiler) + : _libraryMap = new Map(); + + IsolateMirror get isolate => null; + + void _ensureLibraries() { + if (_libraries == null) { + _libraries = new Map(); + compiler.libraries.forEach((_, LibraryElement v) { + var mirror = new Dart2JsLibraryMirror(mirrorSystem, v); + _libraries[mirror.uri] = mirror; + _libraryMap[v] = mirror; + }); + } + } + + Map get libraries { + _ensureLibraries(); + return new FilteredImmutableMap(_libraries, + (library) => const bool.fromEnvironment("list_all_libraries") || + !library._element.isInternalLibrary); + } + + Dart2JsLibraryMirror _getLibrary(LibraryElement element) => + _libraryMap[element]; + + Dart2JsMirrorSystem get mirrorSystem => this; + + TypeMirror get dynamicType => + _convertTypeToTypeMirror(compiler.types.dynamicType); + + TypeMirror get voidType => + _convertTypeToTypeMirror(compiler.types.voidType); + + TypeMirror _convertTypeToTypeMirror(DartType type, + [FunctionSignature signature]) { + assert(type != null); + if (type.treatAsDynamic) { + return new Dart2JsDynamicMirror(this, type); + } else if (type is InterfaceType) { + if (type.typeArguments.isEmpty) { + return _getTypeDeclarationMirror(type.element); + } else { + return new Dart2JsInterfaceTypeMirror(this, type); + } + } else if (type is TypeVariableType) { + return new Dart2JsTypeVariableMirror(this, type); + } else if (type is FunctionType) { + return new Dart2JsFunctionTypeMirror(this, type, signature); + } else if (type is VoidType) { + return new Dart2JsVoidMirror(this, type); + } else if (type is TypedefType) { + if (type.typeArguments.isEmpty) { + return _getTypeDeclarationMirror(type.element); + } else { + return new Dart2JsTypedefMirror(this, type); + } + } + compiler.internalError(type.element, + "Unexpected type $type of kind ${type.kind}."); + return null; + } + + DeclarationMirror _getTypeDeclarationMirror(TypeDeclarationElement element) { + if (element.isClass()) { + return new Dart2JsClassDeclarationMirror(this, element.thisType); + } else if (element.isTypedef()) { + return new Dart2JsTypedefDeclarationMirror(this, element.thisType); + } + compiler.internalError(element, "Unexpected element $element."); + return null; + } +} + +abstract class ContainerMixin { + Map _declarations; + + void _ensureDeclarations() { + if (_declarations == null) { + _declarations = {}; + _forEachElement((Element element) { + for (DeclarationMirror mirror in _getDeclarationMirrors(element)) { + assert(invariant(_element, + !_declarations.containsKey(mirror.simpleName), + message: "Declaration name '${nameOf(mirror)}' " + "is not unique in $_element.")); + _declarations[mirror.simpleName] = mirror; + } + }); + } + } + + Element get _element; + + void _forEachElement(f(Element element)); + + Iterable _getDeclarationMirrors(Element element); + + Map get declarations { + _ensureDeclarations(); + return new ImmutableMapWrapper(_declarations); + } +} + +/** + * Converts [element] into its corresponding [DeclarationMirror], if any. + * + * If [element] is an [AbstractFieldElement] the mirror for its getter is + * returned or, if not present, the mirror for its setter. + */ +DeclarationMirror _convertElementToDeclarationMirror(Dart2JsMirrorSystem system, + Element element) { + if (element.isTypeVariable()) { + TypeVariableElement typeVariable = element; + return new Dart2JsTypeVariableMirror(system, typeVariable.type); + } + + Dart2JsLibraryMirror library = system._libraryMap[element.getLibrary()]; + if (element.isLibrary()) return library; + if (element.isTypedef()) { + TypedefElement typedefElement = element; + return new Dart2JsTypedefMirror.fromLibrary( + library, typedefElement.thisType); + } + + Dart2JsDeclarationMirror container = library; + if (element.getEnclosingClass() != null) { + container = system._getTypeDeclarationMirror(element.getEnclosingClass()); + } + if (element.isClass()) return container; + if (element.isParameter()) { + Dart2JsMethodMirror method = _convertElementMethodToMethodMirror( + container, element.getOutermostEnclosingMemberOrTopLevel()); + // TODO(johnniwinther): Find the right info for [isOptional] and [isNamed]. + return new Dart2JsParameterMirror( + method, element, isOptional: false, isNamed: false); + } + Iterable members = + container._getDeclarationMirrors(element); + if (members.isEmpty) return null; + return members.first; +} + +/** + * Experimental API for accessing compilation units defined in a + * library. + */ +// TODO(ahe): Superclasses? Is this really a mirror? +class Dart2JsCompilationUnitMirror extends Dart2JsMirror + with ContainerMixin { + final Dart2JsLibraryMirror _library; + final CompilationUnitElement _element; + + Dart2JsCompilationUnitMirror(this._element, this._library); + + Dart2JsMirrorSystem get mirrorSystem => _library.mirrorSystem; + + // TODO(johnniwinther): make sure that these are returned in declaration + // order. + void _forEachElement(f(Element element)) => _element.forEachLocalMember(f); + + Iterable _getDeclarationMirrors(Element element) => + _library._getDeclarationMirrors(element); + + Uri get uri => _element.script.resourceUri; +} + +/** + * Transitional class that allows access to features that have not yet + * made it to the mirror API. + * + * All API in this class is experimental. + */ +class BackDoor { + /// Return the compilation units comprising [library]. + static List compilationUnitsOf(Dart2JsLibraryMirror library) { + return library._element.compilationUnits.toList().map( + (cu) => new Dart2JsCompilationUnitMirror(cu, library)).toList(); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_type_mirrors.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_type_mirrors.dart new file mode 100644 index 0000000..1ac23d0 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/dart2js_type_mirrors.dart @@ -0,0 +1,497 @@ +// Copyright (c) 2013, 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. + +part of dart2js.mirrors; + +abstract class ClassMirrorMixin implements ClassSourceMirror { + bool get hasReflectedType => false; + Type get reflectedType { + throw new UnsupportedError("ClassMirror.reflectedType is not supported."); + } + InstanceMirror newInstance(Symbol constructorName, + List positionalArguments, + [Map namedArguments]) { + throw new UnsupportedError("ClassMirror.newInstance is not supported."); + } +} + +abstract class Dart2JsTypeMirror + extends Dart2JsElementMirror + implements TypeSourceMirror { + final DartType _type; + + Dart2JsTypeMirror(Dart2JsMirrorSystem system, DartType type) + : super(system, type.element), + this._type = type; + + String get _simpleNameString => _type.name; + + Dart2JsDeclarationMirror get owner => library; + + Dart2JsLibraryMirror get library { + return mirrorSystem._getLibrary(_type.element.getLibrary()); + } + + bool get hasReflectedType => throw new UnimplementedError(); + + Type get reflectedType => throw new UnimplementedError(); + + bool get isOriginalDeclaration => true; + + TypeMirror get originalDeclaration => this; + + List get typeArguments => const []; + + List get typeVariables => const []; + + TypeMirror createInstantiation(List typeArguments) { + if (typeArguments.isEmpty) return this; + throw new ArgumentError('Cannot create generic instantiation of $_type.'); + } + + bool get isVoid => false; + + bool get isDynamic => false; + + bool isSubtypeOf(TypeMirror other) { + if (other is Dart2JsTypeMirror) { + return mirrorSystem.compiler.types.isSubtype(this._type, other._type); + } else { + throw new ArgumentError(other); + } + } + + bool isAssignableTo(TypeMirror other) { + if (other is Dart2JsTypeMirror) { + return mirrorSystem.compiler.types.isAssignable(this._type, other._type); + } else { + throw new ArgumentError(other); + } + } + + String toString() => _type.toString(); +} + +abstract class DeclarationMixin implements TypeMirror { + + bool get isOriginalDeclaration => true; + + TypeMirror get originalDeclaration => this; + + List get typeArguments => const []; +} + +abstract class Dart2JsGenericTypeMirror extends Dart2JsTypeMirror { + List _typeArguments; + List _typeVariables; + + Dart2JsGenericTypeMirror(Dart2JsMirrorSystem system, GenericType type) + : super(system, type); + + TypeDeclarationElement get _element => super._element; + + GenericType get _type => super._type; + + bool get isOriginalDeclaration => false; + + TypeMirror get originalDeclaration => + mirrorSystem._getTypeDeclarationMirror(_element); + + List get typeArguments { + if (_typeArguments == null) { + _typeArguments = []; + if (!_type.isRaw) { + Link type = _type.typeArguments; + while (type != null && type.head != null) { + _typeArguments.add(_getTypeMirror(type.head)); + type = type.tail; + } + } + } + return _typeArguments; + } + + List get typeVariables { + if (_typeVariables == null) { + _typeVariables = []; + for (TypeVariableType typeVariable in _element.typeVariables) { + _typeVariables.add( + new Dart2JsTypeVariableMirror(mirrorSystem, typeVariable)); + } + } + return _typeVariables; + } + + Iterable _getDeclarationMirrors(Element element) { + if (element.isTypeVariable()) { + assert(invariant(_element, _element == element.enclosingElement, + message: 'Foreigned type variable element $element.')); + for (Dart2JsTypeVariableMirror mirror in typeVariables) { + if (mirror._element == element) return [mirror]; + } + } + return super._getDeclarationMirrors(element); + } + + TypeMirror _getTypeMirror(DartType type, [FunctionSignature signature]) { + return super._getTypeMirror( + type.subst(_type.typeArguments, _type.element.typeVariables), + signature); + } + + TypeSourceMirror createInstantiation( + List newTypeArguments) { + if (newTypeArguments.isEmpty) return owner._getTypeMirror(_type.asRaw()); + if (newTypeArguments.length != typeVariables.length) { + throw new ArgumentError('Cannot create generic instantiation of $_type ' + 'with ${newTypeArguments.length} arguments, ' + 'expect ${typeVariables.length} arguments.'); + } + LinkBuilder builder = new LinkBuilder(); + for (TypeSourceMirror newTypeArgument in newTypeArguments) { + if (newTypeArgument.isVoid) { + throw new ArgumentError('Cannot use void as type argument.'); + } + if (newTypeArgument is Dart2JsTypeMirror) { + builder.addLast(newTypeArgument._type); + } else { + throw new UnsupportedError( + 'Cannot create instantiation using a type ' + 'mirror from a different mirrorSystem implementation.'); + } + } + return owner._getTypeMirror(_type.createInstantiation(builder.toLink())); + } +} + +class Dart2JsInterfaceTypeMirror + extends Dart2JsGenericTypeMirror + with ObjectMirrorMixin, ClassMirrorMixin, ContainerMixin + implements ClassMirror { + Dart2JsInterfaceTypeMirror(Dart2JsMirrorSystem system, + InterfaceType interfaceType) + : super(system, interfaceType); + + ClassElement get _element => super._element; + + InterfaceType get _type => super._type; + + bool get isNameSynthetic { + if (_element.isMixinApplication) { + MixinApplicationElement mixinApplication = _element; + return mixinApplication.isUnnamedMixinApplication; + } + return false; + } + + void _forEachElement(f(Element element)) { + _element.forEachMember((_, element) => f(element)); + } + + ClassMirror get superclass { + if (_element.supertype != null) { + return _getTypeMirror(_element.supertype); + } + return null; + } + + bool isSubclassOf(Mirror other) { + if (other is Dart2JsTypeMirror) { + return _element.isSubclassOf(other._type.element); + } else { + throw new ArgumentError(other); + } + } + + ClassMirror get mixin { + if (_element.isMixinApplication) { + MixinApplicationElement mixinApplication = _element; + return _getTypeMirror(mixinApplication.mixinType); + } + return this; + } + + List get superinterfaces { + var list = []; + Link link = _element.interfaces; + while (!link.isEmpty) { + var type = _getTypeMirror(link.head); + list.add(type); + link = link.tail; + } + return list; + } + + Map get instanceMembers => null; + Map get staticMembers => null; + + bool get isAbstract => _element.modifiers.isAbstract(); + + bool operator ==(other) { + if (identical(this, other)) { + return true; + } + if (other is! ClassMirror) { + return false; + } + return _type == other._type; + } + + String toString() => 'Mirror on interface type $_type'; +} + +class Dart2JsClassDeclarationMirror + extends Dart2JsInterfaceTypeMirror + with DeclarationMixin { + + Dart2JsClassDeclarationMirror(Dart2JsMirrorSystem system, + InterfaceType type) + : super(system, type); + + bool isSubclassOf(ClassMirror other) { + if (other is Dart2JsClassDeclarationMirror) { + Dart2JsClassDeclarationMirror otherDeclaration = + other.originalDeclaration; + return _element.isSubclassOf(otherDeclaration._element); + } else if (other is FunctionTypeMirror) { + return false; + } + throw new ArgumentError(other); + } + + String toString() => 'Mirror on class ${_type.name}'; +} + +class Dart2JsTypedefMirror + extends Dart2JsGenericTypeMirror + implements TypedefMirror { + final Dart2JsLibraryMirror _library; + List _typeVariables; + var _definition; + + Dart2JsTypedefMirror(Dart2JsMirrorSystem system, TypedefType _typedef) + : this._library = system._getLibrary(_typedef.element.getLibrary()), + super(system, _typedef); + + Dart2JsTypedefMirror.fromLibrary(Dart2JsLibraryMirror library, + TypedefType _typedef) + : this._library = library, + super(library.mirrorSystem, _typedef); + + TypedefType get _typedef => _type; + + LibraryMirror get library => _library; + + bool get isTypedef => true; + + FunctionTypeMirror get referent { + if (_definition == null) { + _definition = _getTypeMirror( + _typedef.element.alias, + _typedef.element.functionSignature); + } + return _definition; + } + + bool get isClass => false; + + bool get isAbstract => false; + + String toString() => 'Mirror on typedef $_type'; +} + +class Dart2JsTypedefDeclarationMirror + extends Dart2JsTypedefMirror + with DeclarationMixin { + Dart2JsTypedefDeclarationMirror(Dart2JsMirrorSystem system, + TypedefType type) + : super(system, type); + + String toString() => 'Mirror on typedef ${_type.name}'; +} + +class Dart2JsTypeVariableMirror extends Dart2JsTypeMirror + implements TypeVariableMirror { + Dart2JsDeclarationMirror _owner; + + Dart2JsTypeVariableMirror(Dart2JsMirrorSystem system, + TypeVariableType typeVariableType) + : super(system, typeVariableType); + + TypeVariableType get _type => super._type; + + Dart2JsDeclarationMirror get owner { + if (_owner == null) { + _owner = mirrorSystem._getTypeDeclarationMirror( + _type.element.enclosingElement); + } + return _owner; + } + + bool get isStatic => false; + + TypeMirror get upperBound => owner._getTypeMirror(_type.element.bound); + + bool operator ==(var other) { + if (identical(this, other)) { + return true; + } + if (other is! TypeVariableMirror) { + return false; + } + if (owner != other.owner) { + return false; + } + return qualifiedName == other.qualifiedName; + } + + String toString() => 'Mirror on type variable $_type'; +} + +class Dart2JsFunctionTypeMirror extends Dart2JsTypeMirror + with ObjectMirrorMixin, ClassMirrorMixin, DeclarationMixin + implements FunctionTypeMirror { + final FunctionSignature _functionSignature; + List _parameters; + + Dart2JsFunctionTypeMirror(Dart2JsMirrorSystem system, + FunctionType functionType, this._functionSignature) + : super(system, functionType) { + assert (_functionSignature != null); + } + + FunctionType get _type => super._type; + + // TODO(johnniwinther): Is this the qualified name of a function type? + Symbol get qualifiedName => originalDeclaration.qualifiedName; + + // TODO(johnniwinther): Substitute type arguments for type variables. + Map get declarations { + var method = callMethod; + if (method != null) { + var map = new Map.from( + originalDeclaration.declarations); + var name = method.qualifiedName; + assert(!map.containsKey(name)); + map[name] = method; + return new ImmutableMapWrapper(map); + } + return originalDeclaration.declarations; + } + + bool get isFunction => true; + + MethodMirror get callMethod => _convertElementMethodToMethodMirror( + mirrorSystem._getLibrary(_type.element.getLibrary()), + _type.element); + + ClassMirror get originalDeclaration => + mirrorSystem._getTypeDeclarationMirror( + mirrorSystem.compiler.functionClass); + + // TODO(johnniwinther): Substitute type arguments for type variables. + ClassMirror get superclass => originalDeclaration.superclass; + + // TODO(johnniwinther): Substitute type arguments for type variables. + List get superinterfaces => originalDeclaration.superinterfaces; + + Map get instanceMembers => null; + Map get staticMembers => null; + + ClassMirror get mixin => this; + + bool get isPrivate => false; + + bool get isAbstract => false; + + List get typeVariables => + originalDeclaration.typeVariables; + + TypeMirror get returnType => owner._getTypeMirror(_type.returnType); + + List get parameters { + if (_parameters == null) { + _parameters = _parametersFromFunctionSignature(owner, + _functionSignature); + } + return _parameters; + } + + String toString() => 'Mirror on function type $_type'; + + bool isSubclassOf(ClassMirror other) => false; +} + +class Dart2JsVoidMirror extends Dart2JsTypeMirror { + + Dart2JsVoidMirror(Dart2JsMirrorSystem system, VoidType voidType) + : super(system, voidType); + + VoidType get _voidType => _type; + + Symbol get qualifiedName => simpleName; + + /** + * The void type has no location. + */ + SourceLocation get location => null; + + /** + * The void type has no library. + */ + LibraryMirror get library => null; + + List get metadata => const []; + + bool get isVoid => true; + + bool operator ==(other) { + if (identical(this, other)) { + return true; + } + if (other is! TypeMirror) { + return false; + } + return other.isVoid; + } + + int get hashCode => 13 * _element.hashCode; + + String toString() => 'Mirror on void'; +} + +class Dart2JsDynamicMirror extends Dart2JsTypeMirror { + Dart2JsDynamicMirror(Dart2JsMirrorSystem system, InterfaceType voidType) + : super(system, voidType); + + InterfaceType get _dynamicType => _type; + + Symbol get qualifiedName => simpleName; + + /** + * The dynamic type has no location. + */ + SourceLocation get location => null; + + /** + * The dynamic type has no library. + */ + LibraryMirror get library => null; + + bool get isDynamic => true; + + bool operator ==(other) { + if (identical(this, other)) { + return true; + } + if (other is! TypeMirror) { + return false; + } + return other.isDynamic; + } + + int get hashCode => 13 * _element.hashCode; + + String toString() => 'Mirror on dynamic'; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart new file mode 100644 index 0000000..fca92c4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart @@ -0,0 +1,405 @@ +// Copyright (c) 2012, 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. + +library mirrors_util; + +import 'dart:collection' show Queue, IterableBase; + +import 'source_mirrors.dart'; + +//------------------------------------------------------------------------------ +// Utility functions for using the Mirror API +//------------------------------------------------------------------------------ + +String nameOf(DeclarationMirror mirror) => + MirrorSystem.getName(mirror.simpleName); + +String qualifiedNameOf(DeclarationMirror mirror) => + MirrorSystem.getName(mirror.qualifiedName); + +// TODO(johnniwinther): Handle private names. +Symbol symbolOf(String name, [LibraryMirror library]) => new Symbol(name); + +/** + * Return the display name for [mirror]. + * + * The display name is the normal representation of the entity name. In most + * cases the display name is the simple name, but for a setter 'foo=' the + * display name is simply 'foo' and for the unary minus operator the display + * name is 'operator -'. For 'dart:' libraries the display name is the URI and + * not the library name, for instance 'dart:core' instead of 'dart.core'. + * + * The display name is not unique. + */ +String displayName(DeclarationMirror mirror) { + if (mirror is LibraryMirror) { + LibraryMirror library = mirror; + if (library.uri.scheme == 'dart') { + return library.uri.toString(); + } + } else if (mirror is MethodMirror) { + String simpleName = nameOf(mirror); + if (mirror.isSetter) { + // Remove trailing '='. + return simpleName.substring(0, simpleName.length-1); + } else if (mirror.isOperator) { + return 'operator ${operatorName(mirror)}'; + } else if (mirror.isConstructor) { + String className = displayName(mirror.owner); + if (simpleName == '') { + return className; + } else { + return '$className.$simpleName'; + } + } + } + return MirrorSystem.getName(mirror.simpleName); +} + +/** + * Returns the operator name if [methodMirror] is an operator method, + * for instance [:'<':] for [:operator <:] and [:'-':] for the unary minus + * operator. Return [:null:] if [methodMirror] is not an operator method. + */ +String operatorName(MethodMirror methodMirror) { + if (methodMirror.isOperator) { + if (methodMirror.simpleName == const Symbol('unary-')) { + return '-'; + } else { + return nameOf(methodMirror); + } + } + return null; +} + +/** + * Returns an iterable over the type declarations directly inheriting from + * the declaration of [type] within [mirrors]. + */ +Iterable computeSubdeclarations(MirrorSystem mirrors, + ClassMirror type) { + type = type.originalDeclaration; + var subtypes = []; + mirrors.libraries.forEach((_, library) { + library.declarations.values + .where((mirror) => mirror is ClassMirror) + .forEach((ClassMirror otherType) { + var superClass = otherType.superclass; + if (superClass != null) { + superClass = superClass.originalDeclaration; + if (superClass == type) { + subtypes.add(otherType); + } + } + final superInterfaces = otherType.superinterfaces; + for (ClassMirror superInterface in superInterfaces) { + superInterface = superInterface.originalDeclaration; + if (superInterface == type) { + subtypes.add(otherType); + } + } + }); + }); + return subtypes; +} + +class HierarchyIterable extends IterableBase { + final bool includeType; + final ClassMirror type; + + HierarchyIterable(this.type, {bool includeType}) + : this.includeType = includeType; + + Iterator get iterator => + new HierarchyIterator(type, includeType: includeType); +} + +/** + * [HierarchyIterator] iterates through the class hierarchy of the provided + * type. + * + * First the superclass relation is traversed, skipping [Object], next the + * superinterface relation and finally is [Object] visited. The supertypes are + * visited in breadth first order and a superinterface is visited more than once + * if implemented through multiple supertypes. + */ +class HierarchyIterator implements Iterator { + final Queue queue = new Queue(); + ClassMirror object; + ClassMirror _current; + + HierarchyIterator(ClassMirror type, {bool includeType}) { + if (includeType) { + queue.add(type); + } else { + push(type); + } + } + + ClassMirror push(ClassMirror type) { + if (type.superclass != null) { + if (isObject(type.superclass)) { + object = type.superclass; + } else { + queue.addFirst(type.superclass); + } + } + queue.addAll(type.superinterfaces); + return type; + } + + ClassMirror get current => _current; + + bool moveNext() { + _current = null; + if (queue.isEmpty) { + if (object == null) return false; + _current = object; + object = null; + return true; + } else { + _current = push(queue.removeFirst()); + return true; + } + } +} + +LibraryMirror getLibrary(DeclarationMirror declaration) { + while (declaration != null && declaration is! LibraryMirror) { + declaration = declaration.owner; + } + return declaration; +} + +Iterable membersOf( + Map declarations) { + return declarations.values.where( + (mirror) => mirror is MethodMirror || mirror is VariableMirror); +} + +Iterable classesOf( + Map declarations) { + return declarations.values.where((mirror) => mirror is ClassMirror); +} + +Iterable typesOf( + Map declarations) { + return declarations.values.where((mirror) => mirror is TypeMirror); +} + +Iterable methodsOf( + Map declarations) { + return declarations.values.where( + (mirror) => mirror is MethodMirror && mirror.isRegularMethod); +} + +Iterable constructorsOf( + Map declarations) { + return declarations.values.where( + (mirror) => mirror is MethodMirror && mirror.isConstructor); +} + +Iterable settersOf( + Map declarations) { + return declarations.values.where( + (mirror) => mirror is MethodMirror && mirror.isSetter); +} + +Iterable gettersOf( + Map declarations) { + return declarations.values.where( + (mirror) => mirror is MethodMirror && mirror.isGetter); +} + +Iterable variablesOf( + Map declarations) { + return declarations.values.where((mirror) => mirror is VariableMirror); +} + + + +bool isObject(TypeMirror mirror) => + mirror is ClassMirror && mirror.superclass == null; + +/// Returns `true` if [cls] is declared in a private dart library. +bool isFromPrivateDartLibrary(ClassMirror cls) { + if (isMixinApplication(cls)) cls = cls.mixin; + var uri = getLibrary(cls).uri; + return uri.scheme == 'dart' && uri.path.startsWith('_'); +} + +/// Returns `true` if [mirror] reflects a mixin application. +bool isMixinApplication(Mirror mirror) { + return mirror is ClassMirror && mirror.mixin != mirror; +} + +/** + * Returns the superclass of [cls] skipping unnamed mixin applications. + * + * For instance, for all of the following definitions this method returns [:B:]. + * + * class A extends B {} + * class A extends B with C1, C2 {} + * class A extends B implements D1, D2 {} + * class A extends B with C1, C2 implements D1, D2 {} + * class A = B with C1, C2; + * abstract class A = B with C1, C2 implements D1, D2; + */ +ClassSourceMirror getSuperclass(ClassSourceMirror cls) { + ClassSourceMirror superclass = cls.superclass; + while (isMixinApplication(superclass) && superclass.isNameSynthetic) { + superclass = superclass.superclass; + } + return superclass; +} + +/** + * Returns the mixins directly applied to [cls]. + * + * For instance, for all of the following definitions this method returns + * [:C1, C2:]. + * + * class A extends B with C1, C2 {} + * class A extends B with C1, C2 implements D1, D2 {} + * class A = B with C1, C2; + * abstract class A = B with C1, C2 implements D1, D2; + */ +Iterable getAppliedMixins(ClassSourceMirror cls) { + List mixins = []; + ClassSourceMirror superclass = cls.superclass; + while (isMixinApplication(superclass) && superclass.isNameSynthetic) { + mixins.add(superclass.mixin); + superclass = superclass.superclass; + } + if (mixins.length > 1) { + mixins = new List.from(mixins.reversed); + } + if (isMixinApplication(cls)) { + mixins.add(cls.mixin); + } + return mixins; +} + +/** + * Returns the superinterfaces directly and explicitly implemented by [cls]. + * + * For instance, for all of the following definitions this method returns + * [:D1, D2:]. + * + * class A extends B implements D1, D2 {} + * class A extends B with C1, C2 implements D1, D2 {} + * abstract class A = B with C1, C2 implements D1, D2; + */ +Iterable getExplicitInterfaces(ClassMirror cls) { + if (isMixinApplication(cls)) { + bool first = true; + ClassMirror mixin = cls.mixin; + bool filter(ClassMirror superinterface) { + if (first && superinterface == mixin) { + first = false; + return false; + } + return true; + } + return cls.superinterfaces.where(filter); + } + return cls.superinterfaces; +} + +final RegExp _singleLineCommentStart = new RegExp(r'^///? ?(.*)'); +final RegExp _multiLineCommentStartEnd = + new RegExp(r'^/\*\*? ?([\s\S]*)\*/$', multiLine: true); +final RegExp _multiLineCommentLineStart = new RegExp(r'^[ \t]*\* ?(.*)'); + +/** + * Pulls the raw text out of a comment (i.e. removes the comment + * characters). + */ +String stripComment(String comment) { + Match match = _singleLineCommentStart.firstMatch(comment); + if (match != null) { + return match[1]; + } + match = _multiLineCommentStartEnd.firstMatch(comment); + if (match != null) { + comment = match[1]; + var sb = new StringBuffer(); + List lines = comment.split('\n'); + for (int index = 0 ; index < lines.length ; index++) { + String line = lines[index]; + if (index == 0) { + sb.write(line); // Add the first line unprocessed. + continue; + } + sb.write('\n'); + match = _multiLineCommentLineStart.firstMatch(line); + if (match != null) { + sb.write(match[1]); + } else if (index < lines.length-1 || !line.trim().isEmpty) { + // Do not add the last line if it only contains white space. + // This interprets cases like + // /* + // * Foo + // */ + // as "\nFoo\n" and not as "\nFoo\n ". + sb.write(line); + } + } + return sb.toString(); + } + throw new ArgumentError('Invalid comment $comment'); +} + +/** + * Looks up [name] in the scope [declaration]. + * + * If [name] is of the form 'a.b.c', 'a' is looked up in the scope of + * [declaration] and if unresolved 'a.b' is looked in the scope of + * [declaration]. Each identifier of the remaining suffix, 'c' or 'b.c', is + * then looked up in the local scope of the previous result. + * + * For instance, assumming that [:Iterable:] is imported into the scope of + * [declaration] via the prefix 'col', 'col.Iterable.E' finds the type + * variable of [:Iterable:] and 'col.Iterable.contains.element' finds the + * [:element:] parameter of the [:contains:] method on [:Iterable:]. + */ +DeclarationMirror lookupQualifiedInScope(DeclarationSourceMirror declaration, + String name) { + // TODO(11653): Support lookup of constructors using the [:new Foo:] + // syntax. + int offset = 1; + List parts = name.split('.'); + DeclarationMirror result = declaration.lookupInScope(parts[0]); + if (result == null && parts.length > 1) { + // Try lookup of `prefix.id`. + result = declaration.lookupInScope('${parts[0]}.${parts[1]}'); + offset = 2; + } + if (result == null) return null; + LibraryMirror library = getLibrary(result); + while (result != null && offset < parts.length) { + result = _lookupLocal(result, symbolOf(parts[offset++], library)); + } + return result; +} + +DeclarationMirror _lookupLocal(Mirror mirror, Symbol id) { + DeclarationMirror result; + if (mirror is LibraryMirror) { + // Try member lookup. + result = mirror.declarations[id]; + } else if (mirror is ClassMirror) { + // Try member lookup. + result = mirror.declarations[id]; + if (result != null) return result; + // Try type variables. + result = mirror.typeVariables.firstWhere( + (TypeVariableMirror v) => v.simpleName == id, orElse: () => null); + } else if (mirror is MethodMirror) { + result = mirror.parameters.firstWhere( + (ParameterMirror p) => p.simpleName == id, orElse: () => null); + } + return result; + +} \ No newline at end of file diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart new file mode 100644 index 0000000..0afbd8e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart @@ -0,0 +1,251 @@ +// Copyright (c) 2012, 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. + +library mirrors; + +import 'dart:mirrors'; +import 'dart:mirrors' as api show SourceLocation; +export 'dart:mirrors'; + +abstract class DeclarationSourceMirror implements DeclarationMirror { + /// Returns `true` if the name of this declaration is generated by the + /// provider of the mirror system. + bool get isNameSynthetic; + + /** + * Looks up [name] in the scope of this declaration. + * + * [name] may be either a single identifier, like 'foo', or of the + * a prefixed identifier, like 'foo.bar', where 'foo' must be a prefix. + * For methods and constructors, the scope includes the parameters. For + * classes and typedefs, the scope includes the type variables. + * For classes and class members, the scope includes inherited members. + * + * See also: + * + * * [Lexical Scope](https://www.dartlang.org/docs/dart-up-and-running/contents/ch02.html#ch02-lexical-scope) + * in Dart Up and Running. + * * [Lexical Scoping](http://www.dartlang.org/docs/spec/latest/dart-language-specification.html#h.jb82efuudrc5) + * in the Dart Specification. + */ + DeclarationMirror lookupInScope(String name); +} + +/** + * Specialized [InstanceMirror] used for reflection on constant lists. + */ +abstract class ListInstanceMirror implements InstanceMirror { + /** + * Returns an instance mirror of the value at [index] or throws a [RangeError] + * if the [index] is out of bounds. + */ + InstanceMirror getElement(int index); + + /** + * The number of elements in the list. + */ + int get length; +} + +/** + * Specialized [InstanceMirror] used for reflection on constant maps. + */ +abstract class MapInstanceMirror implements InstanceMirror { + /** + * Returns a collection containing all the keys in the map. + */ + Iterable get keys; + + /** + * Returns an instance mirror of the value for the given key or + * null if key is not in the map. + */ + InstanceMirror getValue(String key); + + /** + * The number of {key, value} pairs in the map. + */ + int get length; +} + +/** + * Specialized [InstanceMirror] used for reflection on type constants. + */ +abstract class TypeInstanceMirror implements InstanceMirror { + /** + * Returns the type mirror for the type represented by the reflected type + * constant. + */ + TypeMirror get representedType; +} + +/** + * Specialized [InstanceMirror] used for reflection on comments as metadata. + */ +abstract class CommentInstanceMirror implements InstanceMirror { + /** + * The comment text as written in the source text. + */ + String get text; + + /** + * The comment text without the start, end, and padding text. + * + * For example, if [text] is [: /** Comment text. */ :] then the [trimmedText] + * is [: Comment text. :]. + */ + String get trimmedText; + + /** + * Is [:true:] if this comment is a documentation comment. + * + * That is, that the comment is either enclosed in [: /** ... */ :] or starts + * with [: /// :]. + */ + bool get isDocComment; +} + +/** + * A library. + */ +abstract class LibrarySourceMirror + implements DeclarationSourceMirror, LibraryMirror { + /** + * Returns a list of the imports and exports in this library; + */ + List get libraryDependencies; +} + +/// A mirror on an import or export declaration. +abstract class LibraryDependencySourceMirror + extends Mirror implements LibraryDependencyMirror { + /// Is `true` if this dependency is an import. + bool get isImport; + + /// Is `true` if this dependency is an export. + bool get isExport; + + /// Returns the library mirror of the library that imports or exports the + /// [targetLibrary]. + LibraryMirror get sourceLibrary; + + /// Returns the library mirror of the library that is imported or exported. + LibraryMirror get targetLibrary; + + /// Returns the prefix if this is a prefixed import and `null` otherwise. + /*String*/ get prefix; + + /// Returns the list of show/hide combinators on the import/export + /// declaration. + List get combinators; + + /// Returns the source location for this import/export declaration. + SourceLocation get location; +} + +/// A mirror on a show/hide combinator declared on a library dependency. +abstract class CombinatorSourceMirror + extends Mirror implements CombinatorMirror { + /// The list of identifiers on the combinator. + List/**/ get identifiers; + + /// Is `true` if this is a 'show' combinator. + bool get isShow; + + /// Is `true` if this is a 'hide' combinator. + bool get isHide; +} + +/** + * Common interface for classes, interfaces, typedefs and type variables. + */ +abstract class TypeSourceMirror implements DeclarationSourceMirror, TypeMirror { + /// Returns `true` is this is a mirror on the void type. + bool get isVoid; + + /// Returns `true` is this is a mirror on the dynamic type. + bool get isDynamic; + + /// Create a type mirror on the instantiation of the declaration of this type + /// with [typeArguments] as type arguments. + TypeMirror createInstantiation(List typeArguments); +} + +/** + * A class or interface type. + */ +abstract class ClassSourceMirror implements TypeSourceMirror, ClassMirror { + /** + * Is [:true:] if this class is declared abstract. + */ + bool get isAbstract; +} + +/** + * A formal parameter. + */ +abstract class ParameterSourceMirror implements ParameterMirror { + /** + * Returns [:true:] iff this parameter is an initializing formal of a + * constructor. That is, if it is of the form [:this.x:] where [:x:] is a + * field. + */ + bool get isInitializingFormal; + + /** + * Returns the initialized field, if this parameter is an initializing formal. + */ + VariableMirror get initializedField; +} + +/** + * A [SourceLocation] describes the span of an entity in Dart source code. + * A [SourceLocation] with a non-zero [length] should be the minimum span that + * encloses the declaration of the mirrored entity. + */ +abstract class SourceLocation implements api.SourceLocation { + /** + * The 1-based line number for this source location. + * + * A value of 0 means that the line number is unknown. + */ + int get line; + + /** + * The 1-based column number for this source location. + * + * A value of 0 means that the column number is unknown. + */ + int get column; + + /** + * The 0-based character offset into the [sourceText] where this source + * location begins. + * + * A value of -1 means that the offset is unknown. + */ + int get offset; + + /** + * The number of characters in this source location. + * + * A value of 0 means that the [offset] is approximate. + */ + int get length; + + /** + * The text of the location span. + */ + String get text; + + /** + * Returns the URI where the source originated. + */ + Uri get sourceUri; + + /** + * Returns the text of this source. + */ + String get sourceText; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/util.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/util.dart new file mode 100644 index 0000000..7d9ad77 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors/util.dart @@ -0,0 +1,182 @@ +// Copyright (c) 2012, 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. + +library dart2js.mirrors.util; + +import 'dart:collection' show Maps; + +/** + * An abstract map implementation. This class can be used as a superclass for + * implementing maps, requiring only the further implementation of the + * [:operator []:], [:forEach:] and [:length:] methods to provide a fully + * implemented immutable map. + */ +abstract class AbstractMap implements Map { + AbstractMap(); + + AbstractMap.from(Map other) { + other.forEach((k,v) => this[k] = v); + } + + void operator []=(K key, value) { + throw new UnsupportedError('[]= is not supported'); + } + + void clear() { + throw new UnsupportedError('clear() is not supported'); + } + + void addAll(Map other) { + throw new UnsupportedError('addAll() is not supported'); + } + + bool containsKey(K key) { + var found = false; + forEach((k,_) { + if (k == key) { + found = true; + } + }); + return found; + } + + bool containsValue(V value) { + var found = false; + forEach((_,v) { + if (v == value) { + found = true; + } + }); + return found; + } + + Iterable get keys { + var keys = []; + forEach((k,_) => keys.add(k)); + return keys; + } + + Iterable get values { + var values = []; + forEach((_,v) => values.add(v)); + return values; + } + + bool get isEmpty => length == 0; + bool get isNotEmpty => !isEmpty; + V putIfAbsent(K key, V ifAbsent()) { + if (!containsKey(key)) { + V value = this[key]; + this[key] = ifAbsent(); + return value; + } + return null; + } + + V remove(K key) { + throw new UnsupportedError('V remove(K key) is not supported'); + } + + String toString() => Maps.mapToString(this); +} + +/** + * [ImmutableMapWrapper] wraps a (mutable) map as an immutable map where all + * mutating operations throw [UnsupportedError] upon invocation. + */ +class ImmutableMapWrapper extends AbstractMap { + final Map _map; + + ImmutableMapWrapper(this._map); + + int get length => _map.length; + + V operator [](K key) { + if (key is K) { + return _map[key]; + } + return null; + } + + void forEach(void f(K key, V value)) { + _map.forEach(f); + } +} + +/** + * A [Filter] function returns [:true:] iff [value] should be included. + */ +typedef bool Filter(V value); + +/** + * An immutable map wrapper capable of filtering the input map. + */ +class FilteredImmutableMap extends ImmutableMapWrapper { + final Filter _filter; + + FilteredImmutableMap(Map map, this._filter) : super(map); + + int get length { + var count = 0; + forEach((k,v) { + count++; + }); + return count; + } + + void forEach(void f(K key, V value)) { + _map.forEach((K k, V v) { + if (_filter(v)) { + f(k, v); + } + }); + } +} + +/** + * An [AsFilter] takes a [value] of type [V1] and returns [value] iff it is of + * type [V2] or [:null:] otherwise. An [AsFilter] therefore behaves like the + * [:as:] expression. + */ +typedef V2 AsFilter(V1 value); + +/** + * An immutable map wrapper capable of filtering the input map based on types. + * It takes an [AsFilter] function which converts the original values of type + * [Vin] into values of type [Vout], or returns [:null:] if the value should + * not be included in the filtered map. + */ +class AsFilteredImmutableMap extends AbstractMap { + final Map _map; + final AsFilter _filter; + + AsFilteredImmutableMap(this._map, this._filter); + + int get length { + var count = 0; + forEach((k,v) { + count++; + }); + return count; + } + + Vout operator [](K key) { + if (key is K) { + Vin value = _map[key]; + if (value != null) { + return _filter(value); + } + } + return null; + } + + void forEach(void f(K key, Vout value)) { + _map.forEach((K k, Vin v) { + var value = _filter(v); + if (value != null) { + f(k, value); + } + }); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors_used.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors_used.dart new file mode 100644 index 0000000..034d209 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/mirrors_used.dart @@ -0,0 +1,592 @@ +// Copyright (c) 2013, 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. + +library dart2js.mirrors_used; + +import 'dart2jslib.dart' show + Compiler, + CompilerTask, + Constant, + ConstructedConstant, + ListConstant, + MessageKind, + StringConstant, + TreeElements, + TypeConstant, + invariant; + +import 'elements/elements.dart' show + ClassElement, + Element, + LibraryElement, + MetadataAnnotation, + ScopeContainerElement, + VariableElement; + +import 'util/util.dart' show + Link, + Spannable; + +import 'dart_types.dart' show + DartType, + InterfaceType, + TypeKind; + +import 'tree/tree.dart' show + Import, + LibraryTag, + NamedArgument, + NewExpression, + Node; + +import 'resolution/resolution.dart' show + ConstantMapper; + +/** + * Compiler task that analyzes MirrorsUsed annotations. + * + * When importing 'dart:mirrors', it is possible to annotate the import with + * MirrorsUsed annotation. This is a way to declare what elements will be + * reflected on at runtime. Such elements, even they would normally be + * discarded by the implicit tree-shaking algorithm must be preserved in the + * final output. + * + * Since some libraries cannot tell exactly what they will be reflecting on, it + * is possible for one library to specify a MirrorsUsed annotation that applies + * to another library. For example: + * + * Mirror utility library that cannot tell what it is reflecting on: + * library mirror_utils; + * import 'dart:mirrors'; + * ... + * + * The main app which knows how it use the mirror utility library: + * library main_app; + * @MirrorsUsed(override='mirror_utils') + * import 'dart:mirrors'; + * import 'mirror_utils.dart'; + * ... + * + * In this case, we say that @MirrorsUsed in main_app overrides @MirrorsUsed in + * mirror_utils. + * + * It is possible to override all libraries using override='*'. If multiple + * catch-all overrides like this, they are merged together. + * + * It is possible for library "a" to declare that it overrides library "b", and + * vice versa. In this case, both annotations will be discarded and the + * compiler will emit a hint (that is, a warning that is not specified by the + * language specification). + * + * After applying all the overrides, we can iterate over libraries that import + * 'dart:mirrors'. If a library does not have an associated MirrorsUsed + * annotation, then we have to discard all MirrorsUsed annotations and assume + * everything can be reflected on. + * + * On the other hand, if all libraries importing dart:mirrors have a + * MirrorsUsed annotation, these annotations are merged. + * + * MERGING MIRRORSUSED + * + * TBD. + */ +class MirrorUsageAnalyzerTask extends CompilerTask { + Set librariesWithUsage; + MirrorUsageAnalyzer analyzer; + + MirrorUsageAnalyzerTask(Compiler compiler) + : super(compiler) { + analyzer = new MirrorUsageAnalyzer(compiler, this); + } + + /// Collect @MirrorsUsed annotations in all libraries. Called by the + /// compiler after all libraries are loaded, but before resolution. + void analyzeUsage(LibraryElement mainApp) { + if (compiler.mirrorsLibrary == null) return; + measure(analyzer.run); + List symbols = analyzer.mergedMirrorUsage.symbols; + List targets = analyzer.mergedMirrorUsage.targets; + List metaTargets = analyzer.mergedMirrorUsage.metaTargets; + compiler.backend.registerMirrorUsage( + symbols == null ? null : new Set.from(symbols), + targets == null ? null : new Set.from(targets), + metaTargets == null ? null : new Set.from(metaTargets)); + librariesWithUsage = analyzer.librariesWithUsage; + } + + /// Is there a @MirrorsUsed annotation in the library of [element]? Used by + /// the resolver to suppress hints about using new Symbol or + /// MirrorSystem.getName. + bool hasMirrorUsage(Element element) { + LibraryElement library = element.getLibrary(); + // Internal libraries always have implicit mirror usage. + return library.isInternalLibrary + || (librariesWithUsage != null + && librariesWithUsage.contains(library)); + } + + /// Call-back from the resolver to analyze MirorsUsed annotations. The result + /// is stored in [analyzer] and later used to compute + /// [:analyzer.mergedMirrorUsage:]. + void validate(NewExpression node, TreeElements mapping) { + for (Node argument in node.send.arguments) { + NamedArgument named = argument.asNamedArgument(); + if (named == null) continue; + Constant value = compiler.constantHandler.compileNodeWithDefinitions( + named.expression, mapping, isConst: true); + + ConstantMapper mapper = + new ConstantMapper(compiler.constantHandler, mapping, compiler); + named.expression.accept(mapper); + + MirrorUsageBuilder builder = + new MirrorUsageBuilder( + analyzer, mapping.currentElement.getLibrary(), named.expression, + value, mapper.constantToNodeMap); + + if (named.name.source == 'symbols') { + analyzer.cachedStrings[value] = + builder.convertConstantToUsageList(value, onlyStrings: true); + } else if (named.name.source == 'targets') { + analyzer.cachedElements[value] = + builder.resolveUsageList(builder.convertConstantToUsageList(value)); + } else if (named.name.source == 'metaTargets') { + analyzer.cachedElements[value] = + builder.resolveUsageList(builder.convertConstantToUsageList(value)); + } else if (named.name.source == 'override') { + analyzer.cachedElements[value] = + builder.resolveUsageList(builder.convertConstantToUsageList(value)); + } + } + } +} + +class MirrorUsageAnalyzer { + final Compiler compiler; + final MirrorUsageAnalyzerTask task; + List wildcard; + final Set librariesWithUsage; + final Map> cachedStrings; + final Map> cachedElements; + MirrorUsage mergedMirrorUsage; + + MirrorUsageAnalyzer(Compiler compiler, this.task) + : compiler = compiler, + librariesWithUsage = new Set(), + cachedStrings = new Map>(), + cachedElements = new Map>(); + + /// Collect and merge all @MirrorsUsed annotations. As a side-effect, also + /// compute which libraries have the annotation (which is used by + /// [MirrorUsageAnalyzerTask.hasMirrorUsage]). + void run() { + wildcard = compiler.libraries.values.toList(); + Map> usageMap = + collectMirrorsUsedAnnotation(); + propagateOverrides(usageMap); + Set librariesWithoutUsage = new Set(); + usageMap.forEach((LibraryElement library, List usage) { + if (usage.isEmpty) librariesWithoutUsage.add(library); + }); + if (librariesWithoutUsage.isEmpty) { + mergedMirrorUsage = mergeUsages(usageMap); + } else { + mergedMirrorUsage = new MirrorUsage(null, null, null, null); + } + } + + /// Collect all @MirrorsUsed from all libraries and represent them as + /// [MirrorUsage]. + Map> collectMirrorsUsedAnnotation() { + Map> result = + new Map>(); + for (LibraryElement library in compiler.libraries.values) { + if (library.isInternalLibrary) continue; + for (LibraryTag tag in library.tags) { + Import importTag = tag.asImport(); + if (importTag == null) continue; + compiler.withCurrentElement(library, () { + List usages = + mirrorsUsedOnLibraryTag(library, importTag); + if (usages != null) { + List existing = result[library]; + if (existing != null) { + existing.addAll(usages); + } else { + result[library] = usages; + } + } + }); + } + } + return result; + } + + /// Apply [MirrorUsage] with 'override' to libraries they override. + void propagateOverrides(Map> usageMap) { + Map> propagatedOverrides = + new Map>(); + usageMap.forEach((LibraryElement library, List usages) { + for (MirrorUsage usage in usages) { + List override = usage.override; + if (override == null) continue; + if (override == wildcard) { + for (LibraryElement overridden in wildcard) { + if (overridden != library) { + List overriddenUsages = propagatedOverrides + .putIfAbsent(overridden, () => []); + overriddenUsages.add(usage); + } + } + } else { + for (Element overridden in override) { + List overriddenUsages = propagatedOverrides + .putIfAbsent(overridden, () => []); + overriddenUsages.add(usage); + } + } + } + }); + propagatedOverrides.forEach((LibraryElement overridden, + List overriddenUsages) { + List usages = + usageMap.putIfAbsent(overridden, () => []); + usages.addAll(overriddenUsages); + }); + } + + /// Find @MirrorsUsed annotations on the given import [tag] in [library]. The + /// annotations are represented as [MirrorUsage]. + List mirrorsUsedOnLibraryTag(LibraryElement library, + Import tag) { + LibraryElement importedLibrary = library.getLibraryFromTag(tag); + if (importedLibrary != compiler.mirrorsLibrary) { + return null; + } + List result = []; + for (MetadataAnnotation metadata in tag.metadata) { + metadata.ensureResolved(compiler); + Element element = metadata.value.computeType(compiler).element; + if (element == compiler.mirrorsUsedClass) { + result.add(buildUsage(metadata.value)); + } + } + return result; + } + + /// Merge all [MirrorUsage] instances accross all libraries. + MirrorUsage mergeUsages(Map> usageMap) { + Set usagesToMerge = new Set(); + usageMap.forEach((LibraryElement library, List usages) { + librariesWithUsage.add(library); + usagesToMerge.addAll(usages); + }); + if (usagesToMerge.isEmpty) { + return new MirrorUsage(null, wildcard, null, null); + } else { + MirrorUsage result = new MirrorUsage(null, null, null, null); + for (MirrorUsage usage in usagesToMerge) { + result = merge(result, usage); + } + return result; + } + } + + /// Merge [a] with [b]. The resulting [MirrorUsage] simply has the symbols, + /// targets, and metaTargets of [a] and [b] concatenated. 'override' is + /// ignored. + MirrorUsage merge(MirrorUsage a, MirrorUsage b) { + // TOOO(ahe): Should be an instance method on MirrorUsage. + if (a.symbols == null && a.targets == null && a.metaTargets == null) { + return b; + } else if ( + b.symbols == null && b.targets == null && b.metaTargets == null) { + return a; + } + // TODO(ahe): Test the following cases. + List symbols = a.symbols; + if (symbols == null) { + symbols = b.symbols; + } else if (b.symbols != null) { + symbols.addAll(b.symbols); + } + List targets = a.targets; + if (targets == null) { + targets = b.targets; + } else if (targets != wildcard && b.targets != null) { + targets.addAll(b.targets); + } + List metaTargets = a.metaTargets; + if (metaTargets == null) { + metaTargets = b.metaTargets; + } else if (metaTargets != wildcard && b.metaTargets != null) { + metaTargets.addAll(b.metaTargets); + } + return new MirrorUsage(symbols, targets, metaTargets, null); + } + + /// Convert a [constant] to an instance of [MirrorUsage] using information + /// that was resolved during [MirrorUsageAnalyzerTask.validate]. + MirrorUsage buildUsage(ConstructedConstant constant) { + Map fields = constant.fieldElements; + VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember( + 'symbols'); + VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember( + 'targets'); + VariableElement metaTargetsField = + compiler.mirrorsUsedClass.lookupLocalMember( + 'metaTargets'); + VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember( + 'override'); + + return new MirrorUsage( + cachedStrings[fields[symbolsField]], + cachedElements[fields[targetsField]], + cachedElements[fields[metaTargetsField]], + cachedElements[fields[overrideField]]); + } +} + +/// Used to represent a resolved MirrorsUsed constant. +class MirrorUsage { + final List symbols; + final List targets; + final List metaTargets; + final List override; + + MirrorUsage(this.symbols, this.targets, this.metaTargets, this.override); + + String toString() { + return + 'MirrorUsage(' + 'symbols = $symbols, ' + 'targets = $targets, ' + 'metaTargets = $metaTargets, ' + 'override = $override' + ')'; + + } +} + +class MirrorUsageBuilder { + final MirrorUsageAnalyzer analyzer; + final LibraryElement enclosingLibrary; + final Spannable spannable; + final Constant constant; + final Map constantToNodeMap; + + MirrorUsageBuilder( + this.analyzer, + this.enclosingLibrary, + this.spannable, + this.constant, + this.constantToNodeMap); + + Compiler get compiler => analyzer.compiler; + + /// Convert a constant to a list of [String] and [Type] values. If the + /// constant is a single [String], it is assumed to be a comma-separated list + /// of qualified names. If the constant is a [Type] t, the result is [:[t]:]. + /// Otherwise, the constant is assumed to represent a list of strings (each a + /// qualified name) and types, and such a list is constructed. If + /// [onlyStrings] is true, the returned list is a [:List:] and any + /// [Type] values are treated as an error (meaning that the value is ignored + /// and a hint is emitted). + List convertConstantToUsageList( + Constant constant, { bool onlyStrings: false }) { + if (constant.isNull) { + return null; + } else if (constant.isList) { + ListConstant list = constant; + List result = onlyStrings ? [] : []; + for (Constant entry in list.entries) { + if (entry.isString) { + StringConstant string = entry; + result.add(string.value.slowToString()); + } else if (!onlyStrings && entry.isType) { + TypeConstant type = entry; + result.add(type.representedType); + } else { + Spannable node = positionOf(entry); + MessageKind kind = onlyStrings + ? MessageKind.MIRRORS_EXPECTED_STRING + : MessageKind.MIRRORS_EXPECTED_STRING_OR_TYPE; + compiler.reportHint( + node, + kind, {'name': node, 'type': apiTypeOf(entry)}); + } + } + return result; + } else if (!onlyStrings && constant.isType) { + TypeConstant type = constant; + return [type.representedType]; + } else if (constant.isString) { + StringConstant string = constant; + var iterable = + string.value.slowToString().split(',').map((e) => e.trim()); + return onlyStrings ? new List.from(iterable) : iterable.toList(); + } else { + Spannable node = positionOf(constant); + MessageKind kind = onlyStrings + ? MessageKind.MIRRORS_EXPECTED_STRING_OR_LIST + : MessageKind.MIRRORS_EXPECTED_STRING_TYPE_OR_LIST; + compiler.reportHint( + node, + kind, {'name': node, 'type': apiTypeOf(constant)}); + return null; + } + } + + /// Find the first non-implementation interface of constant. + DartType apiTypeOf(Constant constant) { + DartType type = constant.computeType(compiler); + LibraryElement library = type.element.getLibrary(); + if (type.kind == TypeKind.INTERFACE && library.isInternalLibrary) { + InterfaceType interface = type; + ClassElement cls = type.element; + cls.ensureResolved(compiler); + for (DartType supertype in cls.allSupertypes) { + if (supertype.kind == TypeKind.INTERFACE + && !supertype.element.getLibrary().isInternalLibrary) { + return interface.asInstanceOf(supertype.element); + } + } + } + return type; + } + + /// Convert a list of strings and types to a list of elements. Types are + /// converted to their corresponding element, and strings are resolved as + /// follows: + /// + /// First find the longest library name that is a prefix of the string, if + /// there are none, resolve using [resolveExpression]. Otherwise, resolve the + /// rest of the string using [resolveLocalExpression]. + List resolveUsageList(List list) { + if (list == null) return null; + if (list.length == 1 && list[0] == '*') { + return analyzer.wildcard; + } + List result = []; + for (var entry in list) { + if (entry is DartType) { + DartType type = entry; + result.add(type.element); + } else { + String string = entry; + LibraryElement libraryCandiate; + String libraryNameCandiate; + for (LibraryElement l in compiler.libraries.values) { + if (l.hasLibraryName()) { + String libraryName = l.getLibraryOrScriptName(); + if (string == libraryName) { + // Found an exact match. + libraryCandiate = l; + libraryNameCandiate = libraryName; + break; + } else if (string.startsWith('$libraryName.')) { + if (libraryNameCandiate == null + || libraryNameCandiate.length < libraryName.length) { + // Found a better candiate + libraryCandiate = l; + libraryNameCandiate = libraryName; + } + } + } + } + Element e; + if (libraryNameCandiate == string) { + e = libraryCandiate; + } else if (libraryNameCandiate != null) { + e = resolveLocalExpression( + libraryCandiate, + string.substring(libraryNameCandiate.length + 1).split('.')); + } else { + e = resolveExpression(string); + } + if (e != null) result.add(e); + } + } + return result; + } + + /// Resolve [expression] in [enclosingLibrary]'s import scope. + Element resolveExpression(String expression) { + List identifiers = expression.split('.'); + Element element = enclosingLibrary.find(identifiers[0]); + if (element == null) { + compiler.reportHint( + spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_CURRENT_LIBRARY, + {'name': expression}); + return null; + } else { + if (identifiers.length == 1) return element; + return resolveLocalExpression(element, identifiers.sublist(1)); + } + } + + /// Resolve [identifiers] in [element]'s local members. + Element resolveLocalExpression(Element element, List identifiers) { + Element current = element; + for (String identifier in identifiers) { + Element e = findLocalMemberIn(current, identifier); + if (e == null) { + if (current.isLibrary()) { + LibraryElement library = current; + compiler.reportHint( + spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_LIBRARY, + {'name': identifiers[0], + 'library': library.getLibraryOrScriptName()}); + } else { + compiler.reportHint( + spannable, MessageKind.MIRRORS_CANNOT_FIND_IN_ELEMENT, + {'name': identifier, 'element': current.name}); + } + return current; + } + current = e; + } + return current; + } + + /// Helper method to lookup members in a [ScopeContainerElement]. If + /// [element] is not a ScopeContainerElement, return null. + Element findLocalMemberIn(Element element, String name) { + if (element is ScopeContainerElement) { + ScopeContainerElement scope = element; + if (element.isClass()) { + ClassElement cls = element; + cls.ensureResolved(compiler); + } + return scope.localLookup(name); + } + return null; + } + + /// Attempt to find a [Spannable] corresponding to constant. + Spannable positionOf(Constant constant) { + Node node = constantToNodeMap[constant]; + if (node == null) { + // TODO(ahe): Returning [spannable] here leads to confusing error + // messages. For example, consider: + // @MirrorsUsed(targets: fisk) + // import 'dart:mirrors'; + // + // const fisk = const [main]; + // + // main() {} + // + // The message is: + // example.dart:1:23: Hint: Can't use 'fisk' here because ... + // Did you forget to add quotes? + // @MirrorsUsed(targets: fisk) + // ^^^^ + // + // Instead of saying 'fisk' should pretty print the problematic constant + // value. + return spannable; + } + return node; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/native_handler.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/native_handler.dart new file mode 100644 index 0000000..38577c9 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/native_handler.dart @@ -0,0 +1,1147 @@ +// Copyright (c) 2012, 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. + +library native; + +import 'dart:collection' show Queue; +import 'dart2jslib.dart'; +import 'dart_types.dart'; +import 'elements/elements.dart'; +import 'js_backend/js_backend.dart'; +import 'resolution/resolution.dart' show ResolverVisitor; +import 'scanner/scannerlib.dart'; +import 'ssa/ssa.dart'; +import 'tree/tree.dart'; +import 'universe/universe.dart' show SideEffects; +import 'util/util.dart'; +import 'js/js.dart' as js; +import 'js_emitter/js_emitter.dart' show CodeEmitterTask; + +/// This class is a temporary work-around until we get a more powerful DartType. +class SpecialType { + final String name; + const SpecialType._(this.name); + + /// The type Object, but no subtypes: + static const JsObject = const SpecialType._('=Object'); + + int get hashCode => name.hashCode; +} + +/** + * This could be an abstract class but we use it as a stub for the dart_backend. + */ +class NativeEnqueuer { + /// Initial entry point to native enqueuer. + void processNativeClasses(Iterable libraries) {} + + /// Notification of a main Enqueuer worklist element. For methods, adds + /// information from metadata attributes, and computes types instantiated due + /// to calling the method. + void registerElement(Element element) {} + + /// Notification of native field. Adds information from metadata attributes. + void handleFieldAnnotations(Element field) {} + + /// Computes types instantiated due to getting a native field. + void registerFieldLoad(Element field) {} + + /// Computes types instantiated due to setting a native field. + void registerFieldStore(Element field) {} + + NativeBehavior getNativeBehaviorOf(Send node) => NativeBehavior.NONE; + + /// Returns whether native classes are being used. + bool hasInstantiatedNativeClasses() => false; + + /** + * Handles JS-calls, which can be an instantiation point for types. + * + * For example, the following code instantiates and returns native classes + * that are `_DOMWindowImpl` or a subtype. + * + * JS('_DOMWindowImpl', 'window') + * + */ + // TODO(sra): The entry from codegen will not have a resolver. + void registerJsCall(Send node, ResolverVisitor resolver) {} + + /// Emits a summary information using the [log] function. + void logSummary(log(message)) {} + + // Do not use annotations in dart2dart. + ClassElement get annotationCreatesClass => null; + ClassElement get annotationReturnsClass => null; + ClassElement get annotationJsNameClass => null; +} + + +abstract class NativeEnqueuerBase implements NativeEnqueuer { + + /** + * The set of all native classes. Each native class is in [nativeClasses] and + * exactly one of [unusedClasses], [pendingClasses] and [registeredClasses]. + */ + final Set nativeClasses = new Set(); + + final Set registeredClasses = new Set(); + final Set pendingClasses = new Set(); + final Set unusedClasses = new Set(); + + bool hasInstantiatedNativeClasses() => !registeredClasses.isEmpty; + + final Set nativeClassesAndSubclasses = new Set(); + + final Map> nonNativeSubclasses = + new Map>(); + + /** + * Records matched constraints ([SpecialType] or [DartType]). Once a type + * constraint has been matched, there is no need to match it again. + */ + final Set matchedTypeConstraints = new Set(); + + /// Pending actions. Classes in [pendingClasses] have action thunks in + /// [queue] to register the class. + final queue = new Queue(); + bool flushing = false; + + /// Maps JS foreign calls to their computed native behavior. + final Map nativeBehaviors = + new Map(); + + final Enqueuer world; + final Compiler compiler; + final bool enableLiveTypeAnalysis; + + ClassElement _annotationCreatesClass; + ClassElement _annotationReturnsClass; + ClassElement _annotationJsNameClass; + + /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. + NativeEnqueuerBase(this.world, this.compiler, this.enableLiveTypeAnalysis); + + void processNativeClasses(Iterable libraries) { + libraries.forEach(processNativeClassesInLibrary); + if (compiler.isolateHelperLibrary != null) { + processNativeClassesInLibrary(compiler.isolateHelperLibrary); + } + processSubclassesOfNativeClasses(libraries); + if (!enableLiveTypeAnalysis) { + nativeClasses.forEach((c) => enqueueClass(c, 'forced')); + flushQueue(); + } + } + + void processNativeClassesInLibrary(LibraryElement library) { + // Use implementation to ensure the inclusion of injected members. + library.implementation.forEachLocalMember((Element element) { + if (element.isClass() && element.isNative()) { + processNativeClass(element); + } + }); + } + + void processNativeClass(ClassElement classElement) { + nativeClasses.add(classElement); + unusedClasses.add(classElement); + // Resolve class to ensure the class has valid inheritance info. + classElement.ensureResolved(compiler); + } + + void processSubclassesOfNativeClasses(Iterable libraries) { + // Collect potential subclasses, e.g. + // + // class B extends foo.A {} + // + // String "A" has a potential subclass B. + + var potentialExtends = new Map>(); + + libraries.forEach((library) { + library.implementation.forEachLocalMember((element) { + if (element.isClass()) { + String name = element.name; + String extendsName = findExtendsNameOfClass(element); + if (extendsName != null) { + Set potentialSubclasses = + potentialExtends.putIfAbsent( + extendsName, + () => new Set()); + potentialSubclasses.add(element); + } + } + }); + }); + + // Resolve all the native classes and any classes that might extend them in + // [potentialExtends], and then check that the properly resolved class is in + // fact a subclass of a native class. + + ClassElement nativeSuperclassOf(ClassElement classElement) { + if (classElement.isNative()) return classElement; + if (classElement.superclass == null) return null; + return nativeSuperclassOf(classElement.superclass); + } + + void walkPotentialSubclasses(ClassElement element) { + if (nativeClassesAndSubclasses.contains(element)) return; + element.ensureResolved(compiler); + ClassElement nativeSuperclass = nativeSuperclassOf(element); + if (nativeSuperclass != null) { + nativeClassesAndSubclasses.add(element); + if (!element.isNative()) { + nonNativeSubclasses.putIfAbsent(nativeSuperclass, + () => new Set()) + .add(element); + } + Set potentialSubclasses = potentialExtends[element.name]; + if (potentialSubclasses != null) { + potentialSubclasses.forEach(walkPotentialSubclasses); + } + } + } + + nativeClasses.forEach(walkPotentialSubclasses); + + nativeClasses.addAll(nativeClassesAndSubclasses); + unusedClasses.addAll(nativeClassesAndSubclasses); + } + + /** + * Returns the source string of the class named in the extends clause, or + * `null` if there is no extends clause. + */ + String findExtendsNameOfClass(ClassElement classElement) { + // "class B extends A ... {}" --> "A" + // "class B extends foo.A ... {}" --> "A" + // "class B extends foo.A with M1, M2 ... {}" --> "A" + + // We want to avoid calling classElement.parseNode on every class. Doing so + // will slightly increase parse time and size and cause compiler errors and + // warnings to me emitted in more unused code. + + // An alternative to this code is to extend the API of ClassElement to + // expose the name of the extended element. + + // Pattern match the above cases in the token stream. + // [abstract] class X extends [id.]* id + + Token skipTypeParameters(Token token) { + BeginGroupToken beginGroupToken = token; + Token endToken = beginGroupToken.endGroup; + return endToken.next; + //for (;;) { + // token = token.next; + // if (token.stringValue == '>') return token.next; + // if (token.stringValue == '<') return skipTypeParameters(token); + //} + } + + String scanForExtendsName(Token token) { + if (token.stringValue == 'abstract') token = token.next; + if (token.stringValue != 'class') return null; + token = token.next; + if (!token.isIdentifier()) return null; + token = token.next; + // class F> extends ... + if (token.stringValue == '<') { + token = skipTypeParameters(token); + } + if (token.stringValue != 'extends') return null; + token = token.next; + Token id = token; + while (token.kind != EOF_TOKEN) { + token = token.next; + if (token.stringValue != '.') break; + token = token.next; + if (!token.isIdentifier()) return null; + id = token; + } + // Should be at '{', 'with', 'implements', '<' or 'native'. + return id.value; + } + + return compiler.withCurrentElement(classElement, () { + return scanForExtendsName(classElement.position()); + }); + } + + ClassElement get annotationCreatesClass { + findAnnotationClasses(); + return _annotationCreatesClass; + } + + ClassElement get annotationReturnsClass { + findAnnotationClasses(); + return _annotationReturnsClass; + } + + ClassElement get annotationJsNameClass { + findAnnotationClasses(); + return _annotationJsNameClass; + } + + void findAnnotationClasses() { + if (_annotationCreatesClass != null) return; + ClassElement find(name) { + Element e = compiler.findHelper(name); + if (e == null || e is! ClassElement) { + compiler.internalError(NO_LOCATION_SPANNABLE, + "Could not find implementation class '${name}'."); + } + return e; + } + _annotationCreatesClass = find('Creates'); + _annotationReturnsClass = find('Returns'); + _annotationJsNameClass = find('JSName'); + } + + /// Returns the JSName annotation string or `null` if no JSName annotation is + /// present. + String findJsNameFromAnnotation(Element element) { + String name = null; + ClassElement annotationClass = annotationJsNameClass; + for (Link link = element.metadata; + !link.isEmpty; + link = link.tail) { + MetadataAnnotation annotation = link.head.ensureResolved(compiler); + var value = annotation.value; + if (value is! ConstructedConstant) continue; + if (value.type is! InterfaceType) continue; + if (!identical(value.type.element, annotationClass)) continue; + + var fields = value.fields; + // TODO(sra): Better validation of the constant. + if (fields.length != 1 || fields[0] is! StringConstant) { + PartialMetadataAnnotation partial = annotation; + compiler.internalError(annotation, + 'Annotations needs one string: ${partial.parseNode(compiler)}'); + } + String specString = fields[0].toDartString().slowToString(); + if (name == null) { + name = specString; + } else { + PartialMetadataAnnotation partial = annotation; + compiler.internalError(annotation, + 'Too many JSName annotations: ${partial.parseNode(compiler)}'); + } + } + return name; + } + + enqueueClass(ClassElement classElement, cause) { + assert(unusedClasses.contains(classElement)); + unusedClasses.remove(classElement); + pendingClasses.add(classElement); + queue.add(() { processClass(classElement, cause); }); + } + + void flushQueue() { + if (flushing) return; + flushing = true; + while (!queue.isEmpty) { + (queue.removeFirst())(); + } + flushing = false; + } + + processClass(ClassElement classElement, cause) { + assert(!registeredClasses.contains(classElement)); + + bool firstTime = registeredClasses.isEmpty; + pendingClasses.remove(classElement); + registeredClasses.add(classElement); + + // TODO(ahe): Is this really a global dependency? + world.registerInstantiatedClass(classElement, compiler.globalDependencies); + + // Also parse the node to know all its methods because otherwise it will + // only be parsed if there is a call to one of its constructors. + classElement.parseNode(compiler); + + if (firstTime) { + queue.add(onFirstNativeClass); + } + } + + registerElement(Element element) { + compiler.withCurrentElement(element, () { + if (element.isFunction() || element.isGetter() || element.isSetter()) { + handleMethodAnnotations(element); + if (element.isNative()) { + registerMethodUsed(element); + } + } else if (element.isField()) { + handleFieldAnnotations(element); + if (element.isNative()) { + registerFieldLoad(element); + registerFieldStore(element); + } + } + }); + } + + handleFieldAnnotations(Element element) { + if (element.enclosingElement.isNative()) { + // Exclude non-instance (static) fields - they not really native and are + // compiled as isolate globals. Access of a property of a constructor + // function or a non-method property in the prototype chain, must be coded + // using a JS-call. + if (element.isInstanceMember()) { + setNativeName(element); + } + } + } + + handleMethodAnnotations(Element method) { + if (isNativeMethod(method)) { + setNativeName(method); + } + } + + /// Sets the native name of [element], either from an annotation, or + /// defaulting to the Dart name. + void setNativeName(Element element) { + String name = findJsNameFromAnnotation(element); + if (name == null) name = element.name; + element.setNative(name); + } + + bool isNativeMethod(Element element) { + if (!element.getLibrary().canUseNative) return false; + // Native method? + return compiler.withCurrentElement(element, () { + Node node = element.parseNode(compiler); + if (node is! FunctionExpression) return false; + FunctionExpression functionExpression = node; + node = functionExpression.body; + Token token = node.getBeginToken(); + if (identical(token.stringValue, 'native')) return true; + return false; + }); + } + + void registerMethodUsed(Element method) { + processNativeBehavior( + NativeBehavior.ofMethod(method, compiler), + method); + flushQueue(); + } + + void registerFieldLoad(Element field) { + processNativeBehavior( + NativeBehavior.ofFieldLoad(field, compiler), + field); + flushQueue(); + } + + void registerFieldStore(Element field) { + processNativeBehavior( + NativeBehavior.ofFieldStore(field, compiler), + field); + flushQueue(); + } + + void registerJsCall(Send node, ResolverVisitor resolver) { + NativeBehavior behavior = NativeBehavior.ofJsCall(node, compiler, resolver); + processNativeBehavior(behavior, node); + nativeBehaviors[node] = behavior; + flushQueue(); + } + + NativeBehavior getNativeBehaviorOf(Send node) => nativeBehaviors[node]; + + processNativeBehavior(NativeBehavior behavior, cause) { + // TODO(ahe): Is this really a global dependency? + TreeElements elements = compiler.globalDependencies; + bool allUsedBefore = unusedClasses.isEmpty; + for (var type in behavior.typesInstantiated) { + if (matchedTypeConstraints.contains(type)) continue; + matchedTypeConstraints.add(type); + if (type is SpecialType) { + if (type == SpecialType.JsObject) { + world.registerInstantiatedClass(compiler.objectClass, elements); + } + continue; + } + if (type is InterfaceType) { + if (type.element == compiler.intClass) { + world.registerInstantiatedClass(compiler.intClass, elements); + } else if (type.element == compiler.doubleClass) { + world.registerInstantiatedClass(compiler.doubleClass, elements); + } else if (type.element == compiler.numClass) { + world.registerInstantiatedClass(compiler.doubleClass, elements); + world.registerInstantiatedClass(compiler.intClass, elements); + } else if (type.element == compiler.stringClass) { + world.registerInstantiatedClass(compiler.stringClass, elements); + } else if (type.element == compiler.nullClass) { + world.registerInstantiatedClass(compiler.nullClass, elements); + } else if (type.element == compiler.boolClass) { + world.registerInstantiatedClass(compiler.boolClass, elements); + } else if (compiler.types.isSubtype( + type, compiler.backend.listImplementation.rawType)) { + world.registerInstantiatedClass(type.element, elements); + } + } + assert(type is DartType); + enqueueUnusedClassesMatching( + (nativeClass) => compiler.types.isSubtype(nativeClass.thisType, type), + cause, + 'subtypeof($type)'); + } + + // Give an info so that library developers can compile with -v to find why + // all the native classes are included. + if (unusedClasses.isEmpty && !allUsedBefore) { + compiler.log('All native types marked as used due to $cause.'); + } + } + + enqueueUnusedClassesMatching(bool predicate(classElement), + cause, + [String reason]) { + Iterable matches = unusedClasses.where(predicate); + matches.toList().forEach((c) => enqueueClass(c, cause)); + } + + onFirstNativeClass() { + staticUse(name) { + JavaScriptBackend backend = compiler.backend; + backend.enqueue( + world, compiler.findHelper(name), compiler.globalDependencies); + } + + staticUse('dynamicFunction'); + staticUse('dynamicSetMetadata'); + staticUse('defineProperty'); + staticUse('toStringForNativeObject'); + staticUse('hashCodeForNativeObject'); + staticUse('convertDartClosureToJS'); + addNativeExceptions(); + } + + addNativeExceptions() { + enqueueUnusedClassesMatching((classElement) { + // TODO(sra): Annotate exception classes in dart:html. + String name = classElement.name; + if (name.contains('Exception')) return true; + if (name.contains('Error')) return true; + return false; + }, + 'native exception'); + } +} + + +class NativeResolutionEnqueuer extends NativeEnqueuerBase { + + NativeResolutionEnqueuer(Enqueuer world, Compiler compiler) + : super(world, compiler, compiler.enableNativeLiveTypeAnalysis); + + void logSummary(log(message)) { + log('Resolved ${registeredClasses.length} native elements used, ' + '${unusedClasses.length} native elements dead.'); + } +} + + +class NativeCodegenEnqueuer extends NativeEnqueuerBase { + + final CodeEmitterTask emitter; + + final Set doneAddSubtypes = new Set(); + + NativeCodegenEnqueuer(Enqueuer world, Compiler compiler, this.emitter) + : super(world, compiler, compiler.enableNativeLiveTypeAnalysis); + + void processNativeClasses(Iterable libraries) { + super.processNativeClasses(libraries); + + // HACK HACK - add all the resolved classes. + NativeEnqueuerBase enqueuer = compiler.enqueuer.resolution.nativeEnqueuer; + for (final classElement in enqueuer.registeredClasses) { + if (unusedClasses.contains(classElement)) { + enqueueClass(classElement, 'was resolved'); + } + } + flushQueue(); + } + + processClass(ClassElement classElement, cause) { + super.processClass(classElement, cause); + // Add the information that this class is a subtype of its supertypes. The + // code emitter and the ssa builder use that information. + addSubtypes(classElement, emitter.nativeEmitter); + } + + void addSubtypes(ClassElement cls, NativeEmitter emitter) { + if (!cls.isNative()) return; + if (doneAddSubtypes.contains(cls)) return; + doneAddSubtypes.add(cls); + + // Walk the superclass chain since classes on the superclass chain might not + // be instantiated (abstract or simply unused). + addSubtypes(cls.superclass, emitter); + + for (DartType type in cls.allSupertypes) { + List subtypes = emitter.subtypes.putIfAbsent( + type.element, + () => []); + subtypes.add(cls); + } + + // Skip through all the mixin applications in the super class + // chain. That way, the direct subtypes set only contain the + // natives classes. + ClassElement superclass = cls.superclass; + while (superclass != null && superclass.isMixinApplication) { + assert(!superclass.isNative()); + superclass = superclass.superclass; + } + + List directSubtypes = emitter.directSubtypes.putIfAbsent( + superclass, + () => []); + directSubtypes.add(cls); + } + + void logSummary(log(message)) { + log('Compiled ${registeredClasses.length} native classes, ' + '${unusedClasses.length} native classes omitted.'); + } +} + +void maybeEnableNative(Compiler compiler, + LibraryElement library) { + String libraryName = library.canonicalUri.toString(); + if (library.entryCompilationUnit.script.name.contains( + 'dart/tests/compiler/dart2js_native') + || libraryName == 'dart:async' + || libraryName == 'dart:html' + || libraryName == 'dart:html_common' + || libraryName == 'dart:indexed_db' + || libraryName == 'dart:js' + || libraryName == 'dart:svg' + || libraryName == 'dart:_native_typed_data' + || libraryName == 'dart:web_audio' + || libraryName == 'dart:web_gl' + || libraryName == 'dart:web_sql') { + library.canUseNative = true; + } +} + +class SideEffectsVisitor extends js.BaseVisitor { + final SideEffects sideEffects; + SideEffectsVisitor(this.sideEffects); + + void visit(js.Node node) { + node.accept(this); + } + + void visitLiteralExpression(js.LiteralExpression node) { + sideEffects.setAllSideEffects(); + sideEffects.setDependsOnSomething(); + node.visitChildren(this); + } + + void visitLiteralStatement(js.LiteralStatement node) { + sideEffects.setAllSideEffects(); + sideEffects.setDependsOnSomething(); + node.visitChildren(this); + } + + void visitAssignment(js.Assignment node) { + sideEffects.setChangesStaticProperty(); + sideEffects.setChangesInstanceProperty(); + sideEffects.setChangesIndex(); + node.visitChildren(this); + } + + void visitVariableInitialization(js.VariableInitialization node) { + node.visitChildren(this); + } + + void visitCall(js.Call node) { + sideEffects.setAllSideEffects(); + sideEffects.setDependsOnSomething(); + node.visitChildren(this); + } + + void visitBinary(js.Binary node) { + node.visitChildren(this); + } + + void visitThrow(js.Throw node) { + // TODO(ngeoffray): Incorporate a mayThrow flag in the + // [SideEffects] class. + sideEffects.setAllSideEffects(); + } + + void visitNew(js.New node) { + sideEffects.setAllSideEffects(); + sideEffects.setDependsOnSomething(); + node.visitChildren(this); + } + + void visitPrefix(js.Prefix node) { + if (node.op == 'delete') { + sideEffects.setChangesStaticProperty(); + sideEffects.setChangesInstanceProperty(); + sideEffects.setChangesIndex(); + } + node.visitChildren(this); + } + + void visitVariableUse(js.VariableUse node) { + sideEffects.setDependsOnStaticPropertyStore(); + } + + void visitPostfix(js.Postfix node) { + node.visitChildren(this); + } + + void visitAccess(js.PropertyAccess node) { + sideEffects.setDependsOnIndexStore(); + sideEffects.setDependsOnInstancePropertyStore(); + sideEffects.setDependsOnStaticPropertyStore(); + node.visitChildren(this); + } +} + +/** + * A summary of the behavior of a native element. + * + * Native code can return values of one type and cause native subtypes of + * another type to be instantiated. By default, we compute both from the + * declared type. + * + * A field might yield any native type that 'is' the field type. + * + * A method might create and return instances of native subclasses of its + * declared return type, and a callback argument may be called with instances of + * the callback parameter type (e.g. Event). + * + * If there is one or more `@Creates` annotations, the union of the named types + * replaces the inferred instantiated type, and the return type is ignored for + * the purpose of inferring instantiated types. + * + * @Creates('IDBCursor') // Created asynchronously. + * @Creates('IDBRequest') // Created synchronously (for return value). + * IDBRequest openCursor(); + * + * If there is one or more `@Returns` annotations, the union of the named types + * replaces the declared return type. + * + * @Returns('IDBRequest') + * IDBRequest openCursor(); + * + * Types in annotations are non-nullable, so include `@Returns('Null')` if + * `null` may be returned. + */ +class NativeBehavior { + + /// [DartType]s or [SpecialType]s returned or yielded by the native element. + final List typesReturned = []; + + /// [DartType]s or [SpecialType]s instantiated by the native element. + final List typesInstantiated = []; + + // If this behavior is for a JS expression, [codeAst] contains the + // parsed tree. + js.Expression codeAst; + + final SideEffects sideEffects = new SideEffects.empty(); + + static NativeBehavior NONE = new NativeBehavior(); + + static NativeBehavior ofJsCall(Send jsCall, Compiler compiler, resolver) { + // The first argument of a JS-call is a string encoding various attributes + // of the code. + // + // 'Type1|Type2'. A union type. + // '=Object'. A JavaScript Object, no subtype. + + var argNodes = jsCall.arguments; + if (argNodes.isEmpty) { + compiler.internalError(jsCall, "JS expression has no type."); + } + + var code = argNodes.tail.head; + if (code is !StringNode || code.isInterpolation) { + compiler.internalError(code, 'JS code must be a string literal.'); + } + + LiteralString specLiteral = argNodes.head.asLiteralString(); + if (specLiteral == null) { + // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It + // is not very satisfactory because it does not work for void, dynamic. + compiler.internalError(argNodes.head, "Unexpected JS first argument."); + } + + var behavior = new NativeBehavior(); + behavior.codeAst = js.js.parseForeignJS(code.dartString.slowToString()); + new SideEffectsVisitor(behavior.sideEffects).visit(behavior.codeAst); + + String specString = specLiteral.dartString.slowToString(); + // Various things that are not in fact types. + if (specString == 'void') return behavior; + if (specString == '' || specString == 'var') { + behavior.typesReturned.add(compiler.objectClass.computeType(compiler)); + behavior.typesReturned.add(compiler.nullClass.computeType(compiler)); + return behavior; + } + for (final typeString in specString.split('|')) { + var type = _parseType(typeString, compiler, + (name) => resolver.resolveTypeFromString(specLiteral, name), + jsCall); + behavior.typesInstantiated.add(type); + behavior.typesReturned.add(type); + } + + return behavior; + } + + static NativeBehavior ofMethod(FunctionElement method, Compiler compiler) { + FunctionType type = method.computeType(compiler); + var behavior = new NativeBehavior(); + behavior.typesReturned.add(type.returnType); + if (!type.returnType.isVoid) { + // Declared types are nullable. + behavior.typesReturned.add(compiler.nullClass.computeType(compiler)); + } + behavior._capture(type, compiler); + + // TODO(sra): Optional arguments are currently missing from the + // DartType. This should be fixed so the following work-around can be + // removed. + method.functionSignature.forEachOptionalParameter( + (ParameterElement parameter) { + behavior._escape(parameter.type, compiler); + }); + + behavior._overrideWithAnnotations(method, compiler); + return behavior; + } + + static NativeBehavior ofFieldLoad(Element field, Compiler compiler) { + DartType type = field.computeType(compiler); + var behavior = new NativeBehavior(); + behavior.typesReturned.add(type); + // Declared types are nullable. + behavior.typesReturned.add(compiler.nullClass.computeType(compiler)); + behavior._capture(type, compiler); + behavior._overrideWithAnnotations(field, compiler); + return behavior; + } + + static NativeBehavior ofFieldStore(Element field, Compiler compiler) { + DartType type = field.computeType(compiler); + var behavior = new NativeBehavior(); + behavior._escape(type, compiler); + // We don't override the default behaviour - the annotations apply to + // loading the field. + return behavior; + } + + void _overrideWithAnnotations(Element element, Compiler compiler) { + if (element.metadata.isEmpty) return; + + DartType lookup(String name) { + Element e = element.buildScope().lookup(name); + if (e == null) return null; + if (e is! ClassElement) return null; + ClassElement cls = e; + cls.ensureResolved(compiler); + return cls.thisType; + } + + NativeEnqueuer enqueuer = compiler.enqueuer.resolution.nativeEnqueuer; + var creates = _collect(element, compiler, enqueuer.annotationCreatesClass, + lookup); + var returns = _collect(element, compiler, enqueuer.annotationReturnsClass, + lookup); + + if (creates != null) { + typesInstantiated..clear()..addAll(creates); + } + if (returns != null) { + typesReturned..clear()..addAll(returns); + } + } + + /** + * Returns a list of type constraints from the annotations of + * [annotationClass]. + * Returns `null` if no constraints. + */ + static _collect(Element element, Compiler compiler, Element annotationClass, + lookup(str)) { + var types = null; + for (Link link = element.metadata; + !link.isEmpty; + link = link.tail) { + MetadataAnnotation annotation = link.head.ensureResolved(compiler); + var value = annotation.value; + if (value is! ConstructedConstant) continue; + if (value.type is! InterfaceType) continue; + if (!identical(value.type.element, annotationClass)) continue; + + var fields = value.fields; + // TODO(sra): Better validation of the constant. + if (fields.length != 1 || fields[0] is! StringConstant) { + PartialMetadataAnnotation partial = annotation; + compiler.internalError(annotation, + 'Annotations needs one string: ${partial.parseNode(compiler)}'); + } + String specString = fields[0].toDartString().slowToString(); + for (final typeString in specString.split('|')) { + var type = _parseType(typeString, compiler, lookup, annotation); + if (types == null) types = []; + types.add(type); + } + } + return types; + } + + /// Models the behavior of having intances of [type] escape from Dart code + /// into native code. + void _escape(DartType type, Compiler compiler) { + type = type.unalias(compiler); + if (type is FunctionType) { + FunctionType functionType = type; + // A function might be called from native code, passing us novel + // parameters. + _escape(functionType.returnType, compiler); + for (Link parameters = functionType.parameterTypes; + !parameters.isEmpty; + parameters = parameters.tail) { + _capture(parameters.head, compiler); + } + } + } + + /// Models the behavior of Dart code receiving instances and methods of [type] + /// from native code. We usually start the analysis by capturing a native + /// method that has been used. + void _capture(DartType type, Compiler compiler) { + type = type.unalias(compiler); + if (type is FunctionType) { + FunctionType functionType = type; + _capture(functionType.returnType, compiler); + for (Link parameters = functionType.parameterTypes; + !parameters.isEmpty; + parameters = parameters.tail) { + _escape(parameters.head, compiler); + } + } else { + typesInstantiated.add(type); + } + } + + static _parseType(String typeString, Compiler compiler, + lookup(name), locationNodeOrElement) { + if (typeString == '=Object') return SpecialType.JsObject; + if (typeString == 'dynamic') { + return compiler.types.dynamicType; + } + DartType type = lookup(typeString); + if (type != null) return type; + + int index = typeString.indexOf('<'); + if (index < 1) { + compiler.internalError( + _errorNode(locationNodeOrElement, compiler), + "Type '$typeString' not found."); + } + type = lookup(typeString.substring(0, index)); + if (type != null) { + // TODO(sra): Parse type parameters. + return type; + } + compiler.internalError( + _errorNode(locationNodeOrElement, compiler), + "Type '$typeString' not found."); + } + + static _errorNode(locationNodeOrElement, compiler) { + if (locationNodeOrElement is Node) return locationNodeOrElement; + return locationNodeOrElement.parseNode(compiler); + } +} + +void checkAllowedLibrary(ElementListener listener, Token token) { + LibraryElement currentLibrary = listener.compilationUnitElement.getLibrary(); + if (!currentLibrary.canUseNative) { + listener.recoverableError(token, "Unexpected token"); + } +} + +Token handleNativeBlockToSkip(Listener listener, Token token) { + checkAllowedLibrary(listener, token); + token = token.next; + if (identical(token.kind, STRING_TOKEN)) { + token = token.next; + } + if (identical(token.stringValue, '{')) { + BeginGroupToken beginGroupToken = token; + token = beginGroupToken.endGroup; + } + return token; +} + +Token handleNativeClassBodyToSkip(Listener listener, Token token) { + checkAllowedLibrary(listener, token); + listener.handleIdentifier(token); + token = token.next; + if (!identical(token.kind, STRING_TOKEN)) { + return listener.unexpected(token); + } + token = token.next; + if (!identical(token.stringValue, '{')) { + return listener.unexpected(token); + } + BeginGroupToken beginGroupToken = token; + token = beginGroupToken.endGroup; + return token; +} + +Token handleNativeClassBody(Listener listener, Token token) { + checkAllowedLibrary(listener, token); + token = token.next; + if (!identical(token.kind, STRING_TOKEN)) { + listener.unexpected(token); + } else { + token = token.next; + } + return token; +} + +Token handleNativeFunctionBody(ElementListener listener, Token token) { + checkAllowedLibrary(listener, token); + Token begin = token; + listener.beginReturnStatement(token); + token = token.next; + bool hasExpression = false; + if (identical(token.kind, STRING_TOKEN)) { + hasExpression = true; + listener.beginLiteralString(token); + listener.endLiteralString(0); + token = token.next; + } + listener.endReturnStatement(hasExpression, begin, token); + // TODO(ngeoffray): expect a ';'. + // Currently there are method with both native marker and Dart body. + return token.next; +} + +String checkForNativeClass(ElementListener listener) { + String nativeTagInfo; + Node node = listener.nodes.head; + if (node != null + && node.asIdentifier() != null + && node.asIdentifier().source == 'native') { + nativeTagInfo = node.asIdentifier().token.next.value; + listener.popNode(); + } + return nativeTagInfo; +} + +final RegExp nativeRedirectionRegExp = new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$'); + +void handleSsaNative(SsaBuilder builder, Expression nativeBody) { + Compiler compiler = builder.compiler; + FunctionElement element = builder.work.element; + NativeEmitter nativeEmitter = builder.nativeEmitter; + JavaScriptBackend backend = builder.backend; + + HInstruction convertDartClosure(Element parameter, FunctionType type) { + HInstruction local = builder.localsHandler.readLocal(parameter); + Constant arityConstant = + builder.constantSystem.createInt(type.computeArity()); + HInstruction arity = builder.graph.addConstant(arityConstant, compiler); + // TODO(ngeoffray): For static methods, we could pass a method with a + // defined arity. + Element helper = backend.getClosureConverter(); + builder.pushInvokeStatic(nativeBody, helper, [local, arity]); + HInstruction closure = builder.pop(); + return closure; + } + + // Check which pattern this native method follows: + // 1) foo() native; + // hasBody = false + // 2) foo() native "bar"; + // No longer supported, this is now done with @JSName('foo') and case 1. + // 3) foo() native "return 42"; + // hasBody = true + bool hasBody = false; + assert(element.isNative()); + String nativeMethodName = element.fixedBackendName(); + if (nativeBody != null) { + LiteralString jsCode = nativeBody.asLiteralString(); + String str = jsCode.dartString.slowToString(); + if (nativeRedirectionRegExp.hasMatch(str)) { + compiler.internalError( + nativeBody, "Deprecated syntax, use @JSName('name') instead."); + } + hasBody = true; + } + + if (!hasBody) { + nativeEmitter.nativeMethods.add(element); + } + + FunctionSignature parameters = element.functionSignature; + if (!hasBody) { + List arguments = []; + List inputs = []; + String receiver = ''; + if (element.isInstanceMember()) { + receiver = '#.'; + inputs.add(builder.localsHandler.readThis()); + } + parameters.forEachParameter((ParameterElement parameter) { + DartType type = parameter.type.unalias(compiler); + HInstruction input = builder.localsHandler.readLocal(parameter); + if (type is FunctionType) { + // The parameter type is a function type either directly or through + // typedef(s). + input = convertDartClosure(parameter, type); + } + inputs.add(input); + arguments.add('#'); + }); + + String foreignParameters = arguments.join(','); + String nativeMethodCall; + if (element.kind == ElementKind.FUNCTION) { + nativeMethodCall = '$receiver$nativeMethodName($foreignParameters)'; + } else if (element.kind == ElementKind.GETTER) { + nativeMethodCall = '$receiver$nativeMethodName'; + } else if (element.kind == ElementKind.SETTER) { + nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters'; + } else { + builder.compiler.internalError(element, + 'Unexpected kind: "${element.kind}".'); + } + + builder.push(new HForeign(js.js(nativeMethodCall), backend.dynamicType, + inputs, effects: new SideEffects())); + builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit); + } else { + if (parameters.parameterCount != 0) { + compiler.internalError(nativeBody, + 'native "..." syntax is restricted to ' + 'functions with zero parameters.'); + } + LiteralString jsCode = nativeBody.asLiteralString(); + builder.push(new HForeign.statement( + new js.LiteralStatement(jsCode.dartString.slowToString()), + [], + new SideEffects(), + null, + backend.dynamicType)); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ordered_typeset.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ordered_typeset.dart new file mode 100644 index 0000000..98cc60a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ordered_typeset.dart @@ -0,0 +1,205 @@ +// Copyright (c) 2013, 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. + +library ordered_typeset; + +import 'dart2jslib.dart' show Compiler, MessageKind, invariant; +import 'dart_types.dart'; +import 'elements/elements.dart' show ClassElement; +import 'util/util.dart' show Link, LinkBuilder; +import 'util/util_implementation.dart' show LinkEntry; + +/** + * An ordered set of the supertypes of a class. The supertypes of a class are + * ordered by decreasing hierarchy depth and by the order they are extended, + * mixed in, or implemented. + * + * For these classes + * + * class A {} // Depth = 1. + * class B {} // Depth = 1. + * class C extends B implements A {} // Depth 2. + * + * the ordered supertypes are + * + * A: [A, Object] + * B: [B, Object] + * C: [C, B, A, Object] + */ +class OrderedTypeSet { + final List> _levels; + final Link types; + final Link _supertypes; + + OrderedTypeSet._internal(List> this._levels, + Link this.types, + Link this._supertypes); + + factory OrderedTypeSet.singleton(DartType type) { + Link types = + new LinkEntry(type, const Link()); + List> list = new List>(1); + list[0] = types; + return new OrderedTypeSet._internal(list, types, const Link()); + } + + /// Creates a new [OrderedTypeSet] for [type] when it directly extends the + /// class which this set represents. This is for instance used to create the + /// type set for [ClosureClassElement] which extends [Closure]. + OrderedTypeSet extendClass(InterfaceType type) { + assert(invariant(type.element, types.head.treatAsRaw, + message: 'Cannot extend generic class ${types.head} using ' + 'OrderedTypeSet.extendClass')); + Link extendedTypes = + new LinkEntry(type, types); + List> list = new List>(levels + 1); + for (int i = 0; i < levels; i++) { + list[i] = _levels[i]; + } + list[levels] = extendedTypes; + return new OrderedTypeSet._internal( + list, extendedTypes, _supertypes.prepend(types.head)); + } + + Link get supertypes => _supertypes; + + int get levels => _levels.length; + + int get maxDepth => levels - 1; + + Link operator [](int index) { + if (index < levels) { + return _levels[index]; + } + return const Link(); + } + + void forEach(int level, void f(DartType type)) { + if (level < levels) { + Link pointer = _levels[level]; + Link end = + level > 0 ? _levels[level - 1] : const Link(); + while (!identical(pointer, end)) { + f(pointer.head); + pointer = pointer.tail; + } + } + } + + String toString() => types.toString(); +} + +/** + * Builder for creation an ordered set of the supertypes of a class. The + * supertypes are ordered by decreasing hierarchy depth and by the order they + * are extended, mixed in, or implemented. + * + * For these classes + * + * class A {} // Depth = 1. + * class B {} // Depth = 1. + * class C extends B implements A {} // Depth 2. + * + * the ordered supertypes are + * + * A: [A, Object] + * B: [B, Object] + * C: [C, B, A, Object] + */ +class OrderedTypeSetBuilder { + Map> map = new Map>(); + // TODO(15296): Avoid computing this order on the side when member + // lookup handles multiply inherited members correctly. + LinkBuilder allSupertypes = new LinkBuilder(); + int maxDepth = -1; + + final ClassElement cls; + + OrderedTypeSetBuilder(this.cls); + + void add(Compiler compiler, InterfaceType type) { + if (type.element == cls) { + if (type.element != compiler.objectClass) { + allSupertypes.addLast(compiler.objectClass.rawType); + } + _addAtDepth(compiler, type, maxDepth + 1); + } else { + if (type.element != compiler.objectClass) { + allSupertypes.addLast(type); + } + _addAtDepth(compiler, type, type.element.hierarchyDepth); + } + } + + void _addAtDepth(Compiler compiler, InterfaceType type, int depth) { + LinkEntry prev = null; + LinkEntry link = map[depth]; + while (link != null) { + DartType existingType = link.head; + if (existingType == type) return; + if (existingType.element == type.element) { + compiler.reportError(cls, + MessageKind.MULTI_INHERITANCE, + {'thisType': cls.thisType, + 'firstType': existingType, + 'secondType': type}); + return; + } + prev = link; + link = link.tail; + } + LinkEntry next = new LinkEntry(type); + next.tail = null; + if (prev == null) { + map[depth] = next; + } else { + prev.tail = next; + } + if (depth > maxDepth) { + maxDepth = depth; + } + } + + OrderedTypeSet toTypeSet() { + List> levels = new List>(maxDepth + 1); + if (maxDepth < 0) { + return new OrderedTypeSet._internal( + levels, const Link(), const Link()); + } + Link next = const Link(); + for (int depth = 0; depth <= maxDepth; depth++) { + LinkEntry first = map[depth]; + if (first == null) { + levels[depth] = next; + } else { + levels[depth] = first; + LinkEntry last = first; + while (last.tail != null) { + last = last.tail; + } + last.tail = next; + next = first; + } + } + return new OrderedTypeSet._internal( + levels, levels.last, allSupertypes.toLink()); + } + + String toString() { + StringBuffer sb = new StringBuffer(); + for (int depth = 0; depth <= maxDepth; depth++) { + sb.write('$depth: '); + LinkEntry first = map[depth]; + if (first != null) { + sb.write('${first.head}'); + while (first.tail != null) { + sb.write(', ${first.tail.head}'); + first = first.tail; + } + } + sb.write('\n'); + } + return sb.toString(); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/patch_parser.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/patch_parser.dart new file mode 100644 index 0000000..2a5a6c1 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/patch_parser.dart @@ -0,0 +1,564 @@ +// Copyright (c) 2012, 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. + +/** + * This library contains the infrastructure to parse and integrate patch files. + * + * Three types of elements can be patched: [LibraryElement], [ClassElement], + * [FunctionElement]. Patches are introduced in patch libraries which are loaded + * together with the corresponding origin library. Which libraries that are + * patched is determined by the dart2jsPatchPath field of LibraryInfo found + * in [:lib/_internal/libraries.dart:]. + * + * Patch libraries are parsed like regular library and thus provided with their + * own elements. These elements which are distinct from the elements from the + * patched library and the relation between patched and patch elements is + * established through the [:patch:] and [:origin:] fields found on + * [LibraryElement], [ClassElement] and [FunctionElement]. The [:patch:] fields + * are set on the patched elements to point to their corresponding patch + * element, and the [:origin:] elements are set on the patch elements to point + * their corresponding patched elements. + * + * The fields [Element.isPatched] and [Element.isPatch] can be used to determine + * whether the [:patch:] or [:origin:] field, respectively, has been set on an + * element, regardless of whether the element is one of the three patchable + * element types or not. + * + * ## Variants of classes and functions ## + * + * With patches there are four variants of classes and function: + * + * Regular: A class or function which is not declared in a patch library and + * which has no corresponding patch. + * Origin: A class or function which is not declared in a patch library and + * which has a corresponding patch. Origin functions must use the [:external:] + * modifier and can have no body. Origin classes and functions are also + * called 'patched'. + * Patch: A class or function which is declared in a patch library and which + * has a corresponding origin. Both patch classes and patch functions must use + * the [:patch:] modifier. + * Injected: A class or function (or even field) which is declared in a + * patch library and which has no corresponding origin. An injected element + * cannot use the [:patch:] modifier. Injected elements are never visible from + * outside the patch library in which they have been declared. For this + * reason, injected elements are often declared private and therefore called + * also called 'patch private'. + * + * Examples of the variants is shown in the code below: + * + * // In the origin library: + * class RegularClass { // A regular class. + * void regularMethod() {} // A regular method. + * } + * class PatchedClass { // An origin class. + * int regularField; // A regular field. + * void regularMethod() {} // A regular method. + * external void patchedMethod(); // An origin method. + * } + * + * // In the patch library: + * class _InjectedClass { // An injected class. + * void _injectedMethod() {} // An injected method. + * } + * patch class PatchedClass { // A patch class. + * int _injectedField; { // An injected field. + * patch void patchedMethod() {} // A patch method. + * } + * + * + * ## Declaration and implementation ## + * + * With patches we have two views on elements: as the 'declaration' which + * introduces the entity and defines its interface, and as the 'implementation' + * which defines the actual implementation of the entity. + * + * Every element has a 'declaration' and an 'implementation' element. For + * regular and injected elements these are the same. For origin elements the + * declaration is the element itself and the implementation is the patch element + * found through its [:patch:] field. For patch elements the implementation is + * the element itself and the declaration is the origin element found through + * its [:origin:] field. The declaration and implementation of any element is + * conveniently available through the [Element.declaration] and + * [Element.implementation] getters. + * + * Most patch-related invariants enforced through-out the compiler are defined + * in terms of 'declaration' and 'implementation', and tested through the + * predicate getters [Element.isDeclaration] and [Element.isImplementation]. + * Patch invariants are stated both in comments and as assertions. + * + * + * ## General invariant guidelines ## + * + * For [LibraryElement] we always use declarations. This means the + * [Element.getLibrary] method will only return library declarations. Patch + * library implementations are only accessed through calls to + * [Element.getImplementationLibrary] which is used to setup the correct + * [Element.enclosingElement] relation between patch/injected elements and the + * patch library. + * + * For [ClassElement] and [FunctionElement] we use declarations for determining + * identity and implementations for work based on the AST nodes, such as + * resolution, type-checking, type inference, building SSA graphs, etc. + * - Worklist only contain declaration elements. + * - Most maps and sets use declarations exclusively, and their individual + * invariants are stated in the field comments. + * - [tree.TreeElements] only map to patch elements from inside a patch library. + * TODO(johnniwinther): Simplify this invariant to use only declarations in + * [tree.TreeElements]. + * - Builders shift between declaration and implementation depending on usages. + * - Compile-time constants use constructor implementation exclusively. + * - Work on function parameters is performed on the declaration of the function + * element. + */ + +library patchparser; + +import 'dart:async'; + +import "tree/tree.dart" as tree; +import "dart2jslib.dart" as leg; // CompilerTask, Compiler. +import "scanner/scannerlib.dart"; // Scanner, Parsers, Listeners +import "elements/elements.dart"; +import "elements/modelx.dart" + show LibraryElementX, + MetadataAnnotationX, + FunctionElementX; +import 'util/util.dart'; + +class PatchParserTask extends leg.CompilerTask { + PatchParserTask(leg.Compiler compiler): super(compiler); + final String name = "Patching Parser"; + + /** + * Scans a library patch file, applies the method patches and + * injections to the library, and returns a list of class + * patches. + */ + Future patchLibrary(leg.LibraryDependencyHandler handler, + Uri patchUri, LibraryElement originLibrary) { + return compiler.readScript(originLibrary, patchUri) + .then((leg.Script script) { + var patchLibrary = new LibraryElementX(script, null, originLibrary); + return compiler.withCurrentElement(patchLibrary, () { + handler.registerNewLibrary(patchLibrary); + var imports = new LinkBuilder(); + compiler.withCurrentElement(patchLibrary.entryCompilationUnit, () { + // This patches the elements of the patch library into [library]. + // Injected elements are added directly under the compilation unit. + // Patch elements are stored on the patched functions or classes. + scanLibraryElements(patchLibrary.entryCompilationUnit, imports); + }); + // TODO(rnystrom): Remove .toList() here if #11523 is fixed. + return Future.forEach(imports.toLink().toList(), (tag) { + return compiler.withCurrentElement(patchLibrary, () { + return compiler.libraryLoader.registerLibraryFromTag( + handler, patchLibrary, tag); + }); + }); + }); + }); + } + + void scanLibraryElements( + CompilationUnitElement compilationUnit, + LinkBuilder imports) { + measure(() { + // TODO(lrn): Possibly recursively handle 'part' directives in patch. + leg.Script script = compilationUnit.script; + Token tokens = new Scanner(script.file).tokenize(); + Function idGenerator = compiler.getNextFreeClassId; + PatchListener patchListener = + new PatchElementListener(compiler, + compilationUnit, + idGenerator, + imports); + new PatchParser(patchListener).parseUnit(tokens); + }); + } + + void parsePatchClassNode(PartialClassElement element) { + // Parse [PartialClassElement] using a "patch"-aware parser instead + // of calling its [parseNode] method. + if (element.cachedNode != null) return; + + measure(() => compiler.withCurrentElement(element, () { + PatchMemberListener listener = new PatchMemberListener(compiler, element); + Parser parser = new PatchClassElementParser(listener); + Token token = parser.parseTopLevelDeclaration(element.beginToken); + assert(identical(token, element.endToken.next)); + element.cachedNode = listener.popNode(); + assert(listener.nodes.isEmpty); + + Link patches = element.localMembers; + applyContainerPatch(element.origin, patches); + })); + } + + void applyContainerPatch(ClassElement originClass, + Link patches) { + for (Element patch in patches) { + if (!isPatchElement(patch)) continue; + + Element origin = originClass.localLookup(patch.name); + patchElement(compiler, origin, patch); + } + } +} + +/** + * Extension of the [Listener] interface to handle the extra "patch" pseudo- + * keyword in patch files. + * Patch files shouldn't have a type named "patch". + */ +abstract class PatchListener extends Listener { + void beginPatch(Token patch); + void endPatch(Token patch); +} + +/** + * Partial parser that extends the top-level and class grammars to allow the + * word "patch" in front of some declarations. + */ +class PatchParser extends PartialParser { + PatchParser(PatchListener listener) : super(listener); + + PatchListener get patchListener => listener; + + bool isPatch(Token token) => token.value == 'patch'; + + /** + * Parse top-level declarations, and allow "patch" in front of functions + * and classes. + */ + Token parseTopLevelDeclaration(Token token) { + if (!isPatch(token)) { + return super.parseTopLevelDeclaration(token); + } + Token patch = token; + token = token.next; + String value = token.stringValue; + if (identical(value, 'interface') + || identical(value, 'typedef') + || identical(value, '#') + || identical(value, 'abstract')) { + // At the top level, you can only patch functions and classes. + // Patch classes and functions can't be marked abstract. + return listener.unexpected(patch); + } + patchListener.beginPatch(patch); + token = super.parseTopLevelDeclaration(token); + patchListener.endPatch(patch); + return token; + } + + /** + * Parse a class member. + * If the member starts with "patch", it's a member override. + * Only methods can be overridden, including constructors, getters and + * setters, but not fields. If "patch" occurs in front of a field, the error + * is caught elsewhere. + */ + Token parseMember(Token token) { + if (!isPatch(token)) { + return super.parseMember(token); + } + Token patch = token; + patchListener.beginPatch(patch); + token = super.parseMember(token.next); + patchListener.endPatch(patch); + return token; + } +} + +/** + * Partial parser for patch files that also handles the members of class + * declarations. + */ +class PatchClassElementParser extends PatchParser { + PatchClassElementParser(PatchListener listener) : super(listener); + + Token parseClassBody(Token token) => fullParseClassBody(token); +} + +/** + * Extension of [ElementListener] for parsing patch files. + */ +class PatchElementListener extends ElementListener implements PatchListener { + final LinkBuilder imports; + bool isMemberPatch = false; + bool isClassPatch = false; + + PatchElementListener(leg.DiagnosticListener listener, + CompilationUnitElement patchElement, + int idGenerator(), + this.imports) + : super(listener, patchElement, idGenerator); + + MetadataAnnotation popMetadataHack() { + // TODO(ahe): Remove this method. + popNode(); // Discard null. + return new PatchMetadataAnnotation(); + } + + void beginPatch(Token token) { + if (identical(token.next.stringValue, "class")) { + isClassPatch = true; + } else { + isMemberPatch = true; + } + handleIdentifier(token); + } + + void endPatch(Token token) { + if (identical(token.next.stringValue, "class")) { + isClassPatch = false; + } else { + isMemberPatch = false; + } + } + + /** + * Allow script tags (import only, the parser rejects the rest for now) in + * patch files. The import tags will be added to the library. + */ + bool allowLibraryTags() => true; + + void addLibraryTag(tree.LibraryTag tag) { + super.addLibraryTag(tag); + imports.addLast(tag); + } + + void pushElement(Element patch) { + if (isMemberPatch || (isClassPatch && patch is ClassElement)) { + // Apply patch. + patch.addMetadata(popMetadataHack()); + LibraryElement originLibrary = compilationUnitElement.getLibrary(); + assert(originLibrary.isPatched); + Element origin = originLibrary.localLookup(patch.name); + patchElement(listener, origin, patch); + } + super.pushElement(patch); + } +} + +/** + * Extension of [MemberListener] for parsing patch class bodies. + */ +class PatchMemberListener extends MemberListener implements PatchListener { + bool isMemberPatch = false; + bool isClassPatch = false; + PatchMemberListener(leg.DiagnosticListener listener, + Element enclosingElement) + : super(listener, enclosingElement); + + MetadataAnnotation popMetadataHack() { + // TODO(ahe): Remove this method. + popNode(); // Discard null. + return new PatchMetadataAnnotation(); + } + + void beginPatch(Token token) { + if (identical(token.next.stringValue, "class")) { + isClassPatch = true; + } else { + isMemberPatch = true; + } + handleIdentifier(token); + } + + void endPatch(Token token) { + if (identical(token.next.stringValue, "class")) { + isClassPatch = false; + } else { + isMemberPatch = false; + } + } + + void addMember(Element element) { + if (isMemberPatch || (isClassPatch && element is ClassElement)) { + element.addMetadata(popMetadataHack()); + } + super.addMember(element); + } +} + +// TODO(ahe): Get rid of this class. +class PatchMetadataAnnotation extends MetadataAnnotationX { + final leg.Constant value = null; + + PatchMetadataAnnotation() : super(STATE_DONE); + + tree.Node parseNode(leg.DiagnosticListener listener) => null; + + Token get beginToken => null; + Token get endToken => null; +} + +void patchElement(leg.DiagnosticListener listener, + Element origin, + Element patch) { + if (origin == null) { + listener.reportError( + patch, leg.MessageKind.PATCH_NON_EXISTING, {'name': patch.name}); + return; + } + if (!(origin.isClass() || + origin.isConstructor() || + origin.isFunction() || + origin.isAbstractField())) { + // TODO(ahe): Remove this error when the parser rejects all bad modifiers. + listener.reportError(origin, leg.MessageKind.PATCH_NONPATCHABLE); + return; + } + if (patch.isClass()) { + tryPatchClass(listener, origin, patch); + } else if (patch.isGetter()) { + tryPatchGetter(listener, origin, patch); + } else if (patch.isSetter()) { + tryPatchSetter(listener, origin, patch); + } else if (patch.isConstructor()) { + tryPatchConstructor(listener, origin, patch); + } else if(patch.isFunction()) { + tryPatchFunction(listener, origin, patch); + } else { + // TODO(ahe): Remove this error when the parser rejects all bad modifiers. + listener.reportError(patch, leg.MessageKind.PATCH_NONPATCHABLE); + } +} + +void tryPatchClass(leg.DiagnosticListener listener, + Element origin, + ClassElement patch) { + if (!origin.isClass()) { + listener.reportError( + origin, leg.MessageKind.PATCH_NON_CLASS, {'className': patch.name}); + listener.reportInfo( + patch, leg.MessageKind.PATCH_POINT_TO_CLASS, {'className': patch.name}); + return; + } + patchClass(listener, origin, patch); +} + +void patchClass(leg.DiagnosticListener listener, + ClassElement origin, + ClassElement patch) { + if (origin.isPatched) { + listener.internalError(origin, + "Patching the same class more than once."); + } + // TODO(johnniwinther): Change to functions on the ElementX class. + origin.patch = patch; + patch.origin = origin; +} + +void tryPatchGetter(leg.DiagnosticListener listener, + Element origin, + FunctionElement patch) { + if (!origin.isAbstractField()) { + listener.reportError( + origin, leg.MessageKind.PATCH_NON_GETTER, {'name': origin.name}); + listener.reportInfo( + patch, + leg.MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name}); + return; + } + AbstractFieldElement originField = origin; + if (originField.getter == null) { + listener.reportError( + origin, leg.MessageKind.PATCH_NO_GETTER, {'getterName': patch.name}); + listener.reportInfo( + patch, + leg.MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name}); + return; + } + patchFunction(listener, originField.getter, patch); +} + +void tryPatchSetter(leg.DiagnosticListener listener, + Element origin, + FunctionElement patch) { + if (!origin.isAbstractField()) { + listener.reportError( + origin, leg.MessageKind.PATCH_NON_SETTER, {'name': origin.name}); + listener.reportInfo( + patch, + leg.MessageKind.PATCH_POINT_TO_SETTER, {'setterName': patch.name}); + return; + } + AbstractFieldElement originField = origin; + if (originField.setter == null) { + listener.reportError( + origin, leg.MessageKind.PATCH_NO_SETTER, {'setterName': patch.name}); + listener.reportInfo( + patch, + leg.MessageKind.PATCH_POINT_TO_SETTER, {'setterName': patch.name}); + return; + } + patchFunction(listener, originField.setter, patch); +} + +void tryPatchConstructor(leg.DiagnosticListener listener, + Element origin, + FunctionElement patch) { + if (!origin.isConstructor()) { + listener.reportError( + origin, + leg.MessageKind.PATCH_NON_CONSTRUCTOR, {'constructorName': patch.name}); + listener.reportInfo( + patch, + leg.MessageKind.PATCH_POINT_TO_CONSTRUCTOR, + {'constructorName': patch.name}); + return; + } + patchFunction(listener, origin, patch); +} + +void tryPatchFunction(leg.DiagnosticListener listener, + Element origin, + FunctionElement patch) { + if (!origin.isFunction()) { + listener.reportError( + origin, + leg.MessageKind.PATCH_NON_FUNCTION, {'functionName': patch.name}); + listener.reportInfo( + patch, + leg.MessageKind.PATCH_POINT_TO_FUNCTION, {'functionName': patch.name}); + return; + } + patchFunction(listener, origin, patch); +} + +void patchFunction(leg.DiagnosticListener listener, + FunctionElementX origin, + FunctionElementX patch) { + if (!origin.modifiers.isExternal()) { + listener.reportError(origin, leg.MessageKind.PATCH_NON_EXTERNAL); + listener.reportInfo( + patch, + leg.MessageKind.PATCH_POINT_TO_FUNCTION, {'functionName': patch.name}); + return; + } + if (origin.isPatched) { + listener.internalError(origin, + "Trying to patch a function more than once."); + } + if (origin.cachedNode != null) { + listener.internalError(origin, + "Trying to patch an already compiled function."); + } + // Don't just assign the patch field. This also updates the cachedNode. + // TODO(johnniwinther): Change to functions on the ElementX class. + origin.setPatch(patch); + patch.origin = origin; +} + +// TODO(johnniwinther): Add unittest when patch is (real) metadata. +bool isPatchElement(Element element) { + // TODO(lrn): More checks needed if we introduce metadata for real. + // In that case, it must have the identifier "native" as metadata. + for (Link link = element.metadata; !link.isEmpty; link = link.tail) { + if (link.head is PatchMetadataAnnotation) return true; + } + return false; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/class_members.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/class_members.dart new file mode 100644 index 0000000..8e218ae --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/class_members.dart @@ -0,0 +1,569 @@ +// Copyright (c) 2012, 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. + +library resolution.compute_members; + +import '../elements/elements.dart' + show Element, + Name, + PublicName, + Member, + MemberSignature, + LibraryElement, + ClassElement, + MixinApplicationElement; +import '../elements/modelx.dart' + show BaseClassElementX; +import '../dart_types.dart'; +import '../dart2jslib.dart' + show Compiler, + MessageKind, + invariant; +import '../util/util.dart'; + +part 'member_impl.dart'; + +class MembersCreator { + final ClassElement cls; + final Compiler compiler; + + Map classMembers = new Map(); + Map interfaceMembers = + new Map(); + + Map> reportedMessages = + new Map>(); + + MembersCreator(Compiler this.compiler, ClassElement this.cls) { + assert(invariant(cls, cls.isDeclaration, + message: "Members may only be computed on declarations.")); + } + + void reportMessage(var marker, MessageKind kind, report()) { + Set messages = + reportedMessages.putIfAbsent(marker, + () => new Set()); + if (messages.add(kind)) { + report(); + } + } + + void computeMembers() { + Map> inheritedInterfaceMembers = + _computeSuperMembers(); + Map declaredMembers = _computeClassMembers(); + _computeInterfaceMembers(inheritedInterfaceMembers, declaredMembers); + + if (!cls.modifiers.isAbstract() && + !declaredMembers.containsKey(const PublicName('noSuchMethod'))) { + // Check for unimplemented members on concrete classes that neither have + // a `@proxy` annotation nor declare a `noSuchMethod` method. + checkInterfaceImplementation(); + } + } + + Map> _computeSuperMembers() { + Map> inheritedInterfaceMembers = + new Map>(); + + void inheritInterfaceMembers(InterfaceType supertype) { + supertype.element.forEachInterfaceMember((MemberSignature member) { + Set members = + inheritedInterfaceMembers.putIfAbsent( + member.name, () => new Set()); + for (DeclaredMember declaredMember in member.declarations) { + members.add(declaredMember.inheritFrom(supertype)); + } + }); + } + + // Inherit class and interface members from superclass. + InterfaceType superclass = cls.supertype; + if (superclass != null) { + computeClassMembers(compiler, superclass.element); + superclass.element.forEachClassMember((DeclaredMember member) { + if (!member.isStatic) { + DeclaredMember inherited = member.inheritFrom(superclass); + classMembers[member.name] = inherited; + } + }); + inheritInterfaceMembers(superclass); + } + + // Inherit interface members from superinterfaces. + for (Link link = cls.interfaces; + !link.isEmpty; + link = link.tail) { + InterfaceType superinterface = link.head; + computeClassMembers(compiler, superinterface.element); + inheritInterfaceMembers(superinterface); + } + + return inheritedInterfaceMembers; + } + + Map _computeClassMembers() { + Map declaredMembers = new Map(); + + void overrideMember(DeclaredMember declared) { + DeclaredMember inherited = classMembers[declared.name]; + classMembers[declared.name] = declared; + checkValidOverride(declared, inherited); + } + + if (cls.isMixinApplication) { + MixinApplicationElement mixinApplication = cls; + if (mixinApplication.mixin != null) { + // Only mix in class members when the mixin type is not malformed. + computeClassMembers(compiler, mixinApplication.mixin); + + mixinApplication.mixin.forEachClassMember((DeclaredMember member) { + if (!member.isStatic) { + // Abstract and static members are not mixed in. + DeclaredMember mixedInMember = + member.inheritFrom(mixinApplication.mixinType); + overrideMember(mixedInMember); + } + }); + } + } else { + LibraryElement library = cls.getLibrary(); + InterfaceType thisType = cls.thisType; + + void createMember(Element element) { + if (element.isConstructor()) return; + + Name name = new Name(element.name, library); + if (element.isField()) { + DartType type = element.computeType(compiler); + declaredMembers[name] = new DeclaredMember( + name, element, thisType, type, + new FunctionType(compiler.functionClass, type)); + if (!element.modifiers.isConst() && + !element.modifiers.isFinal()) { + name = name.setter; + declaredMembers[name] = new DeclaredMember( + name, element, thisType, type, + new FunctionType(compiler.functionClass, + compiler.types.voidType, + const Link().prepend(type))); + } + } else if (element.isGetter()) { + FunctionType functionType = element.computeType(compiler); + DartType type = functionType.returnType; + declaredMembers[name] = + new DeclaredMember(name, element, thisType, type, functionType); + } else if (element.isSetter()) { + FunctionType functionType = element.computeType(compiler); + DartType type; + if (!functionType.parameterTypes.isEmpty) { + type = functionType.parameterTypes.head; + } else { + type = compiler.types.dynamicType; + } + name = name.setter; + declaredMembers[name] = new DeclaredMember( + name, element, thisType, type, functionType); + } else { + assert(invariant(element, element.isFunction())); + FunctionType type = element.computeType(compiler); + declaredMembers[name] = new DeclaredMember( + name, element, thisType, type, type); + } + }; + + cls.forEachLocalMember(createMember); + if (cls.isPatched) { + cls.implementation.forEachLocalMember((Element element) { + if (element.isDeclaration) { + createMember(element); + } + }); + } + } + + declaredMembers.values.forEach((Member member) { + if (!member.element.isAbstract) { + overrideMember(member); + } + }); + + return declaredMembers; + } + + void _computeInterfaceMembers( + Map> inheritedInterfaceMembers, + Map declaredMembers) { + InterfaceType thisType = cls.thisType; + // Compute the interface members by overriding the inherited members with + // a declared member or by computing a single, possibly synthesized, + // inherited member. + inheritedInterfaceMembers.forEach( + (Name name, Set inheritedMembers) { + Member declared = declaredMembers[name]; + if (declared != null) { + // Check that [declaredMember] is a valid override + for (Member inherited in inheritedMembers) { + checkValidOverride(declared, inherited); + } + if (!declared.isStatic) { + interfaceMembers[name] = declared; + } + } else { + bool someAreGetters = false; + bool allAreGetters = true; + Map> subtypesOfAllInherited = + new Map>(); + outer: for (Member inherited in inheritedMembers) { + if (inherited.isGetter) { + someAreGetters = true; + if (!allAreGetters) break outer; + } else { + allAreGetters = false; + if (someAreGetters) break outer; + } + for (MemberSignature other in inheritedMembers) { + if (!compiler.types.isSubtype(inherited.functionType, + other.functionType)) { + continue outer; + } + } + subtypesOfAllInherited.putIfAbsent(inherited.functionType, + () => new Set()).add(inherited); + } + if (someAreGetters && !allAreGetters) { + compiler.reportWarning(cls, + MessageKind.INHERIT_GETTER_AND_METHOD, + {'class': thisType, 'name': name.text }); + for (Member inherited in inheritedMembers) { + MessageKind kind; + if (inherited.isMethod) { + kind = MessageKind.INHERITED_METHOD; + } else { + assert(invariant(cls, inherited.isGetter, + message: 'Conflicting member is neither a method nor a ' + 'getter.')); + if (inherited.isDeclaredByField) { + kind = MessageKind.INHERITED_IMPLICIT_GETTER; + } else { + kind = MessageKind.INHERITED_EXPLICIT_GETTER; + } + } + compiler.reportInfo(inherited.element, kind, + {'class': inherited.declarer, 'name': name.text }); + } + interfaceMembers[name] = new ErroneousMember(inheritedMembers); + } else if (subtypesOfAllInherited.length == 1) { + // All signatures have the same type. + Set members = subtypesOfAllInherited.values.first; + MemberSignature inherited = members.first; + if (members.length != 1) { + // Multiple signatures with the same type => return a + // synthesized signature. + inherited = new SyntheticMember( + members, inherited.type, inherited.functionType); + } + interfaceMembers[name] = inherited; + } else { + _inheritedSynthesizedMember(name, inheritedMembers); + } + } + }); + + // Add the non-overriding instance methods to the interface members. + declaredMembers.forEach((Name name, Member member) { + if (!member.isStatic) { + interfaceMembers.putIfAbsent(name, () => member); + } + }); + } + + /// Create and inherit a synthesized member for [inheritedMembers]. + void _inheritedSynthesizedMember(Name name, + Set inheritedMembers) { + // Multiple signatures with different types => create the synthesized + // version. + int minRequiredParameters; + int maxPositionalParameters; + Set names = new Set(); + for (MemberSignature member in inheritedMembers) { + int requiredParameters = 0; + int optionalParameters = 0; + if (member.isSetter) { + requiredParameters = 1; + } + if (member.type.kind == TypeKind.FUNCTION) { + FunctionType type = member.type; + type.namedParameters.forEach( + (String name) => names.add(name)); + requiredParameters = type.parameterTypes.slowLength(); + optionalParameters = type.optionalParameterTypes.slowLength(); + } + int positionalParameters = requiredParameters + optionalParameters; + if (minRequiredParameters == null || + minRequiredParameters > requiredParameters) { + minRequiredParameters = requiredParameters; + } + if (maxPositionalParameters == null || + maxPositionalParameters < positionalParameters) { + maxPositionalParameters = positionalParameters; + } + } + int optionalParameters = + maxPositionalParameters - minRequiredParameters; + // TODO(johnniwinther): Support function types with both optional + // and named parameters? + if (optionalParameters == 0 || names.isEmpty) { + Link requiredParameterTypes = const Link(); + while (--minRequiredParameters >= 0) { + requiredParameterTypes = + requiredParameterTypes.prepend(compiler.types.dynamicType); + } + Link optionalParameterTypes = const Link(); + while (--optionalParameters >= 0) { + optionalParameterTypes = + optionalParameterTypes.prepend(compiler.types.dynamicType); + } + Link namedParameters = const Link(); + Link namedParameterTypes = const Link(); + List namesReversed = + names.toList()..sort((a, b) => -a.compareTo(b)); + for (String name in namesReversed) { + namedParameters = namedParameters.prepend(name); + namedParameterTypes = + namedParameterTypes.prepend(compiler.types.dynamicType); + } + FunctionType memberType = new FunctionType( + compiler.functionClass, + compiler.types.dynamicType, + requiredParameterTypes, + optionalParameterTypes, + namedParameters, namedParameterTypes); + DartType type = memberType; + if (inheritedMembers.first.isGetter || + inheritedMembers.first.isSetter) { + type = compiler.types.dynamicType; + } + interfaceMembers[name] = new SyntheticMember( + inheritedMembers, type, memberType); + } + } + + /// Checks that a class member exists for every interface member. + void checkInterfaceImplementation() { + LibraryElement library = cls.getLibrary(); + + interfaceMembers.forEach((Name name, MemberSignature interfaceMember) { + if (!name.isAccessibleFrom(library)) return; + Member classMember = classMembers[name]; + if (classMember != null) return; + if (interfaceMember is DeclaredMember && + interfaceMember.declarer.element == cls) { + // Abstract method declared in [cls]. + MessageKind kind = MessageKind.ABSTRACT_METHOD; + if (interfaceMember.isSetter) { + kind = MessageKind.ABSTRACT_SETTER; + } else if (interfaceMember.isGetter) { + kind = MessageKind.ABSTRACT_GETTER; + } + reportMessage( + interfaceMember.element, MessageKind.ABSTRACT_METHOD, () { + compiler.reportWarning( + interfaceMember.element, kind, + {'class': cls.name, 'name': name.text}); + }); + } else { + reportWarning(MessageKind singleKind, + MessageKind multipleKind, + MessageKind explicitlyDeclaredKind, + [MessageKind implicitlyDeclaredKind]) { + Member inherited = interfaceMember.declarations.first; + reportMessage( + interfaceMember, MessageKind.UNIMPLEMENTED_METHOD, () { + compiler.reportWarning(cls, + interfaceMember.declarations.length == 1 + ? singleKind : multipleKind, + {'class': cls.name, + 'name': name.text, + 'method': interfaceMember, + 'declarer': inherited.declarer}); + for (Member inherited in interfaceMember.declarations) { + compiler.reportInfo(inherited.element, + inherited.isDeclaredByField ? + implicitlyDeclaredKind : explicitlyDeclaredKind, + {'class': inherited.declarer.name, + 'name': name.text}); + } + }); + } + if (interfaceMember.isSetter) { + reportWarning(MessageKind.UNIMPLEMENTED_SETTER_ONE, + MessageKind.UNIMPLEMENTED_SETTER, + MessageKind.UNIMPLEMENTED_EXPLICIT_SETTER, + MessageKind.UNIMPLEMENTED_IMPLICIT_SETTER); + } else if (interfaceMember.isGetter) { + reportWarning(MessageKind.UNIMPLEMENTED_GETTER_ONE, + MessageKind.UNIMPLEMENTED_GETTER, + MessageKind.UNIMPLEMENTED_EXPLICIT_GETTER, + MessageKind.UNIMPLEMENTED_IMPLICIT_GETTER); + } else if (interfaceMember.isMethod) { + reportWarning(MessageKind.UNIMPLEMENTED_METHOD_ONE, + MessageKind.UNIMPLEMENTED_METHOD, + MessageKind.UNIMPLEMENTED_METHOD_CONT); + } + } + // TODO(johnniwinther): If [cls] is not abstract, check that for all + // interface members, there is a class member whose type is a subtype of + // the interface member. + }); + } + + void checkValidOverride(Member declared, MemberSignature superMember) { + if (superMember == null) { + // No override. + if (!declared.isStatic) { + ClassElement superclass = cls.superclass; + while (superclass != null) { + Member superMember = + superclass.lookupClassMember(declared.name); + if (superMember != null && superMember.isStatic) { + reportMessage(superMember, MessageKind.INSTANCE_STATIC_SAME_NAME, + () { + compiler.reportWarning( + declared.element, + MessageKind.INSTANCE_STATIC_SAME_NAME, + {'memberName': declared.name, + 'className': superclass.name}); + compiler.reportInfo(superMember.element, + MessageKind.INSTANCE_STATIC_SAME_NAME_CONT); + }); + break; + } + superclass = superclass.superclass; + } + } + } else { + assert(declared.name == superMember.name); + if (declared.isStatic) { + for (Member inherited in superMember.declarations) { + reportMessage( + inherited.element, MessageKind.NO_STATIC_OVERRIDE, () { + reportErrorWithContext( + declared.element, MessageKind.NO_STATIC_OVERRIDE, + inherited.element, MessageKind.NO_STATIC_OVERRIDE_CONT); + }); + } + } + + DartType declaredType = declared.functionType; + for (Member inherited in superMember.declarations) { + + void reportError(MessageKind errorKind, MessageKind infoKind) { + reportMessage( + inherited.element, MessageKind.INVALID_OVERRIDE_METHOD, () { + compiler.reportError(declared.element, errorKind, + {'name': declared.name.text, + 'class': cls.thisType, + 'inheritedClass': inherited.declarer}); + compiler.reportInfo(inherited.element, infoKind, + {'name': declared.name.text, + 'class': inherited.declarer}); + }); + } + + if (declared.isDeclaredByField && inherited.isMethod) { + reportError(MessageKind.CANNOT_OVERRIDE_METHOD_WITH_FIELD, + MessageKind.CANNOT_OVERRIDE_METHOD_WITH_FIELD_CONT); + } else if (declared.isMethod && inherited.isDeclaredByField) { + reportError(MessageKind.CANNOT_OVERRIDE_FIELD_WITH_METHOD, + MessageKind.CANNOT_OVERRIDE_FIELD_WITH_METHOD_CONT); + } else if (declared.isGetter && inherited.isMethod) { + reportError(MessageKind.CANNOT_OVERRIDE_METHOD_WITH_GETTER, + MessageKind.CANNOT_OVERRIDE_METHOD_WITH_GETTER_CONT); + } else if (declared.isMethod && inherited.isGetter) { + reportError(MessageKind.CANNOT_OVERRIDE_GETTER_WITH_METHOD, + MessageKind.CANNOT_OVERRIDE_GETTER_WITH_METHOD_CONT); + } else { + DartType inheritedType = inherited.functionType; + if (!compiler.types.isSubtype(declaredType, inheritedType)) { + void reportWarning(var marker, + MessageKind warningKind, + MessageKind infoKind) { + reportMessage(marker, MessageKind.INVALID_OVERRIDE_METHOD, () { + compiler.reportWarning(declared.element, warningKind, + {'declaredType': declared.type, + 'name': declared.name.text, + 'class': cls.thisType, + 'inheritedType': inherited.type, + 'inheritedClass': inherited.declarer}); + compiler.reportInfo(inherited.element, infoKind, + {'name': declared.name.text, + 'class': inherited.declarer}); + }); + } + if (declared.isDeclaredByField) { + if (inherited.isDeclaredByField) { + reportWarning(inherited.element, + MessageKind.INVALID_OVERRIDE_FIELD, + MessageKind.INVALID_OVERRIDDEN_FIELD); + } else if (inherited.isGetter) { + reportWarning(inherited, + MessageKind.INVALID_OVERRIDE_GETTER_WITH_FIELD, + MessageKind.INVALID_OVERRIDDEN_GETTER); + } else if (inherited.isSetter) { + reportWarning(inherited, + MessageKind.INVALID_OVERRIDE_SETTER_WITH_FIELD, + MessageKind.INVALID_OVERRIDDEN_SETTER); + } + } else if (declared.isGetter) { + if (inherited.isDeclaredByField) { + reportWarning(inherited, + MessageKind.INVALID_OVERRIDE_FIELD_WITH_GETTER, + MessageKind.INVALID_OVERRIDDEN_FIELD); + } else { + reportWarning(inherited, + MessageKind.INVALID_OVERRIDE_GETTER, + MessageKind.INVALID_OVERRIDDEN_GETTER); + } + } else if (declared.isSetter) { + if (inherited.isDeclaredByField) { + reportWarning(inherited, + MessageKind.INVALID_OVERRIDE_FIELD_WITH_SETTER, + MessageKind.INVALID_OVERRIDDEN_FIELD); + } else { + reportWarning(inherited, + MessageKind.INVALID_OVERRIDE_SETTER, + MessageKind.INVALID_OVERRIDDEN_SETTER); + } + } else { + reportWarning(inherited, + MessageKind.INVALID_OVERRIDE_METHOD, + MessageKind.INVALID_OVERRIDDEN_METHOD); + } + } + } + } + } + } + + void reportErrorWithContext(Element errorneousElement, + MessageKind errorMessage, + Element contextElement, + MessageKind contextMessage) { + compiler.reportError( + errorneousElement, + errorMessage, + {'memberName': contextElement.name, + 'className': contextElement.getEnclosingClass().name}); + compiler.reportInfo(contextElement, contextMessage); + } + + static void computeClassMembers(Compiler compiler, BaseClassElementX cls) { + if (cls.classMembers != null) return; + MembersCreator creator = new MembersCreator(compiler, cls); + creator.computeMembers(); + cls.classMembers = creator.classMembers; + cls.interfaceMembers = creator.interfaceMembers; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/member_impl.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/member_impl.dart new file mode 100644 index 0000000..400b2d5 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/member_impl.dart @@ -0,0 +1,199 @@ +// Copyright (c) 2012, 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. + +part of resolution.compute_members; + +class DeclaredMember implements Member { + final Name name; + final Element element; + final InterfaceType declarer; + final DartType type; + final FunctionType functionType; + + DeclaredMember(this.name, this.element, + this.declarer, + this.type, this.functionType); + + bool get isStatic => !element.isInstanceMember(); + + bool get isGetter => element.isGetter() || (!isSetter && element.isField()); + + bool get isSetter => name.isSetter; + + bool get isMethod => element.isFunction(); + + bool get isDeclaredByField => element.isField(); + + /// Returns this member as inherited from [instance]. + /// + /// For instance: + /// class A { T m() {} } + /// class B extends A {} + /// class C extends B {} + /// The member `T m()` is declared in `A` and inherited from `A` into + /// `B` as `S m()`, and further from `B` into `C` as `U m()`. + DeclaredMember inheritFrom(InterfaceType instance) { + // If the member is declared in a non-generic class its type cannot change + // as a result of inheritance. + if (!declarer.isGeneric) return this; + assert(declarer.element == instance.element); + return new InheritedMember(this, instance); + } + + Iterable get declarations => [this]; + + int get hashCode => element.hashCode + 13 * isSetter.hashCode; + + bool operator ==(other) { + if (other is! Member) return false; + return element == other.element && + isSetter == other.isSetter; + } + + String toString() { + StringBuffer sb = new StringBuffer(); + printOn(sb, type); + return sb.toString(); + } + + void printOn(StringBuffer sb, DartType type) { + if (isStatic) { + sb.write('static '); + } + if (isGetter) { + sb.write(type); + sb.write(' get '); + sb.write(name); + } else if (isSetter) { + sb.write('void set '); + sb.write(name.getter); + sb.write('('); + sb.write(type); + sb.write(' _)'); + } else { + sb.write(type.getStringAsDeclared('$name')); + } + } +} + +class InheritedMember implements DeclaredMember { + final DeclaredMember declaration; + final InterfaceType instance; + + InheritedMember(DeclaredMember this.declaration, + InterfaceType this.instance) { + assert(instance.isGeneric); + assert(!declaration.isStatic); + } + + Element get element => declaration.element; + + Name get name => declaration.name; + + InterfaceType get declarer => instance; + + bool get isStatic => false; + + bool get isSetter => declaration.isSetter; + + bool get isGetter => declaration.isGetter; + + bool get isMethod => declaration.isMethod; + + bool get isDeclaredByField => declaration.isDeclaredByField; + + DartType get type => declaration.type.substByContext(instance); + + FunctionType get functionType { + return declaration.functionType.substByContext(instance); + } + + DeclaredMember inheritFrom(InterfaceType newInstance) { + assert(() { + // Assert that if [instance] contains type variables, then these are + // defined in the declaration of [newInstance] and will therefore be + // substituted into the context of [newInstance] in the created member. + ClassElement contextClass = Types.getClassContext(instance); + return contextClass == null || contextClass == newInstance.element; + }); + return new InheritedMember(declaration, + instance.substByContext(newInstance)); + } + + Iterable get declarations => [this]; + + int get hashCode => declaration.hashCode + 17 * instance.hashCode; + + bool operator ==(other) { + if (other is! InheritedMember) return false; + return declaration == other.declaration && + instance == other.instance; + } + + void printOn(StringBuffer sb, DartType type) { + declaration.printOn(sb, type); + sb.write(' inherited from $instance'); + } + + String toString() { + StringBuffer sb = new StringBuffer(); + return sb.toString(); + } +} + +abstract class AbstractSyntheticMember implements MemberSignature { + final Set inheritedMembers; + + AbstractSyntheticMember(this.inheritedMembers); + + Member get member => inheritedMembers.first; + + Iterable get declarations => inheritedMembers; + + Name get name => member.name; +} + + +class SyntheticMember extends AbstractSyntheticMember { + final DartType type; + final FunctionType functionType; + + SyntheticMember(Set inheritedMembers, + this.type, + this.functionType) + : super(inheritedMembers); + + bool get isSetter => member.isSetter; + + bool get isGetter => member.isGetter; + + bool get isMethod => member.isMethod; + + bool get isErroneous => false; + + String toString() => '${type.getStringAsDeclared('$name')} synthesized ' + 'from ${inheritedMembers}'; +} + +class ErroneousMember extends AbstractSyntheticMember { + ErroneousMember(Set inheritedMembers) : super(inheritedMembers); + + DartType get type => functionType; + + FunctionType get functionType { + throw new UnsupportedError('Erroneous members have no type.'); + } + + bool get isSetter => false; + + bool get isGetter => false; + + bool get isMethod => false; + + bool get isErroneous => true; + + String toString() => "erroneous member '$name' synthesized " + "from ${inheritedMembers}"; +} + diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/members.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/members.dart new file mode 100644 index 0000000..51a4689 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/members.dart @@ -0,0 +1,4667 @@ +// Copyright (c) 2012, 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. + +part of resolution; + +abstract class TreeElements { + Element get currentElement; + Setlet get superUses; + + /// Iterables of the dependencies that this [TreeElement] records of + /// [currentElement]. + Iterable get allElements; + Iterable get allConstants; + + /// A set of additional dependencies. See [registerDependency] below. + Setlet get otherDependencies; + + Element operator[](Node node); + Selector getSelector(Send send); + Selector getGetterSelectorInComplexSendSet(SendSet node); + Selector getOperatorSelectorInComplexSendSet(SendSet node); + DartType getType(Node node); + void setSelector(Node node, Selector selector); + void setGetterSelectorInComplexSendSet(SendSet node, Selector selector); + void setOperatorSelectorInComplexSendSet(SendSet node, Selector selector); + Selector getIteratorSelector(ForIn node); + Selector getMoveNextSelector(ForIn node); + Selector getCurrentSelector(ForIn node); + void setIteratorSelector(ForIn node, Selector selector); + void setMoveNextSelector(ForIn node, Selector selector); + void setCurrentSelector(ForIn node, Selector selector); + void setConstant(Node node, Constant constant); + Constant getConstant(Node node); + + /** + * Returns [:true:] if [node] is a type literal. + * + * Resolution marks this by setting the type on the node to be the + * [:Type:] type. + */ + bool isTypeLiteral(Send node); + + /// Register additional dependencies required by [currentElement]. + /// For example, elements that are used by a backend. + void registerDependency(Element element); + + /// Returns a list of nodes that potentially mutate [element] anywhere in its + /// scope. + List getPotentialMutations(VariableElement element); + + /// Returns a list of nodes that potentially mutate [element] in [node]. + List getPotentialMutationsIn(Node node, VariableElement element); + + /// Returns a list of nodes that potentially mutate [element] in a closure. + List getPotentialMutationsInClosure(VariableElement element); + + /// Returns a list of nodes that access [element] within a closure in [node]. + List getAccessesByClosureIn(Node node, VariableElement element); +} + +class TreeElementMapping implements TreeElements { + final Element currentElement; + final Map selectors = new Map(); + final Map types = new Map(); + final Setlet superUses = new Setlet(); + final Setlet otherDependencies = new Setlet(); + final Map constants = new Map(); + final Map> potentiallyMutated = + new Map>(); + final Map>> potentiallyMutatedIn = + new Map>>(); + final Map> potentiallyMutatedInClosure = + new Map>(); + final Map>> accessedByClosureIn = + new Map>>(); + final Setlet elements = new Setlet(); + + final int hashCode = ++hashCodeCounter; + static int hashCodeCounter = 0; + + TreeElementMapping(this.currentElement); + + operator []=(Node node, Element element) { + assert(invariant(node, () { + FunctionExpression functionExpression = node.asFunctionExpression(); + if (functionExpression != null) { + return !functionExpression.modifiers.isExternal(); + } + return true; + })); + // TODO(johnniwinther): Simplify this invariant to use only declarations in + // [TreeElements]. + assert(invariant(node, () { + if (!element.isErroneous() && currentElement != null && element.isPatch) { + return currentElement.getImplementationLibrary().isPatch; + } + return true; + })); + // TODO(ahe): Investigate why the invariant below doesn't hold. + // assert(invariant(node, + // getTreeElement(node) == element || + // getTreeElement(node) == null, + // message: '${getTreeElement(node)}; $element')); + + elements.add(element); + setTreeElement(node, element); + } + + operator [](Node node) => getTreeElement(node); + + void remove(Node node) { + setTreeElement(node, null); + } + + void setType(Node node, DartType type) { + types[node] = type; + } + + DartType getType(Node node) => types[node]; + + void setSelector(Node node, Selector selector) { + selectors[node] = selector; + } + + Selector getSelector(Node node) { + return selectors[node]; + } + + void setGetterSelectorInComplexSendSet(SendSet node, Selector selector) { + selectors[node.selector] = selector; + } + + Selector getGetterSelectorInComplexSendSet(SendSet node) { + return selectors[node.selector]; + } + + void setOperatorSelectorInComplexSendSet(SendSet node, Selector selector) { + selectors[node.assignmentOperator] = selector; + } + + Selector getOperatorSelectorInComplexSendSet(SendSet node) { + return selectors[node.assignmentOperator]; + } + + // The following methods set selectors on the "for in" node. Since + // we're using three selectors, we need to use children of the node, + // and we arbitrarily choose which ones. + + void setIteratorSelector(ForIn node, Selector selector) { + selectors[node] = selector; + } + + Selector getIteratorSelector(ForIn node) { + return selectors[node]; + } + + void setMoveNextSelector(ForIn node, Selector selector) { + selectors[node.forToken] = selector; + } + + Selector getMoveNextSelector(ForIn node) { + return selectors[node.forToken]; + } + + void setCurrentSelector(ForIn node, Selector selector) { + selectors[node.inToken] = selector; + } + + Selector getCurrentSelector(ForIn node) { + return selectors[node.inToken]; + } + + void setConstant(Node node, Constant constant) { + constants[node] = constant; + } + + Constant getConstant(Node node) { + return constants[node]; + } + + bool isTypeLiteral(Send node) { + return getType(node) != null; + } + + void registerDependency(Element element) { + otherDependencies.add(element.implementation); + } + + List getPotentialMutations(VariableElement element) { + List mutations = potentiallyMutated[element]; + if (mutations == null) return const []; + return mutations; + } + + void setPotentiallyMutated(VariableElement element, Node mutationNode) { + potentiallyMutated.putIfAbsent(element, () => []).add(mutationNode); + } + + List getPotentialMutationsIn(Node node, VariableElement element) { + Map> mutationsIn = potentiallyMutatedIn[node]; + if (mutationsIn == null) return const []; + List mutations = mutationsIn[element]; + if (mutations == null) return const []; + return mutations; + } + + void registerPotentiallyMutatedIn(Node contextNode, VariableElement element, + Node mutationNode) { + Map> mutationMap = + potentiallyMutatedIn.putIfAbsent(contextNode, + () => new Map>()); + mutationMap.putIfAbsent(element, () => []).add(mutationNode); + } + + List getPotentialMutationsInClosure(VariableElement element) { + List mutations = potentiallyMutatedInClosure[element]; + if (mutations == null) return const []; + return mutations; + } + + void registerPotentiallyMutatedInClosure(VariableElement element, + Node mutationNode) { + potentiallyMutatedInClosure.putIfAbsent( + element, () => []).add(mutationNode); + } + + List getAccessesByClosureIn(Node node, VariableElement element) { + Map> accessesIn = accessedByClosureIn[node]; + if (accessesIn == null) return const []; + List accesses = accessesIn[element]; + if (accesses == null) return const []; + return accesses; + } + + void setAccessedByClosureIn(Node contextNode, VariableElement element, + Node accessNode) { + Map> accessMap = + accessedByClosureIn.putIfAbsent(contextNode, + () => new Map>()); + accessMap.putIfAbsent(element, () => []).add(accessNode); + } + + String toString() => 'TreeElementMapping($currentElement)'; + + Iterable get allElements => elements; + + Iterable get allConstants => constants.values; +} + +class ResolverTask extends CompilerTask { + ResolverTask(Compiler compiler) : super(compiler); + + String get name => 'Resolver'; + + TreeElements resolve(Element element) { + return measure(() { + if (Elements.isErroneousElement(element)) return null; + + for (MetadataAnnotation metadata in element.metadata) { + metadata.ensureResolved(compiler); + } + + ElementKind kind = element.kind; + if (identical(kind, ElementKind.GENERATIVE_CONSTRUCTOR) || + identical(kind, ElementKind.FUNCTION) || + identical(kind, ElementKind.GETTER) || + identical(kind, ElementKind.SETTER)) { + return resolveMethodElement(element); + } + + if (identical(kind, ElementKind.FIELD)) return resolveField(element); + + if (element.isClass()) { + ClassElement cls = element; + cls.ensureResolved(compiler); + return null; + } else if (element.isTypedef()) { + TypedefElement typdef = element; + return resolveTypedef(typdef); + } + + compiler.unimplemented(element, "resolve($element)"); + }); + } + + String constructorNameForDiagnostics(String className, + String constructorName) { + String classNameString = className; + String constructorNameString = constructorName; + return (constructorName == '') + ? classNameString + : "$classNameString.$constructorNameString"; + } + + void resolveRedirectingConstructor(InitializerResolver resolver, + Node node, + FunctionElement constructor, + FunctionElement redirection) { + assert(invariant(node, constructor.isImplementation, + message: 'Redirecting constructors must be resolved on implementation ' + 'elements.')); + Setlet seen = new Setlet(); + seen.add(constructor); + while (redirection != null) { + // Ensure that we follow redirections through implementation elements. + redirection = redirection.implementation; + if (seen.contains(redirection)) { + resolver.visitor.error(node, MessageKind.REDIRECTING_CONSTRUCTOR_CYCLE); + return; + } + seen.add(redirection); + redirection = resolver.visitor.resolveConstructorRedirection(redirection); + } + } + + void checkMatchingPatchParameters(FunctionElement origin, + Link originParameters, + Link patchParameters) { + while (!originParameters.isEmpty) { + ParameterElementX originParameter = originParameters.head; + ParameterElementX patchParameter = patchParameters.head; + // TODO(johnniwinther): Remove the case for reassignment of + // [patch]/[origin] when resolution is ensure to be done only once. + assert(invariant(originParameter, originParameter.origin == null)); + assert(invariant(originParameter, + originParameter.patch == null || + originParameter.patch == patchParameter)); + originParameter.patch = patchParameter; + assert(invariant(patchParameter, + patchParameter.origin == null || + patchParameter.origin == originParameter)); + assert(invariant(patchParameter, patchParameter.patch == null)); + patchParameter.origin = originParameter; + DartType originParameterType = originParameter.computeType(compiler); + DartType patchParameterType = patchParameter.computeType(compiler); + if (originParameterType != patchParameterType) { + compiler.reportError( + originParameter.parseNode(compiler), + MessageKind.PATCH_PARAMETER_TYPE_MISMATCH, + {'methodName': origin.name, + 'parameterName': originParameter.name, + 'originParameterType': originParameterType, + 'patchParameterType': patchParameterType}); + compiler.reportInfo(patchParameter, + MessageKind.PATCH_POINT_TO_PARAMETER, + {'parameterName': patchParameter.name}); + } else { + // Hack: Use unparser to test parameter equality. This only works + // because we are restricting patch uses and the approach cannot be used + // elsewhere. + + // The node contains the type, so there is a potential overlap. + // Therefore we only check the text if the types are identical. + String originParameterText = + originParameter.parseNode(compiler).toString(); + String patchParameterText = + patchParameter.parseNode(compiler).toString(); + if (originParameterText != patchParameterText + // We special case the list constructor because of the + // optional parameter. + && origin != compiler.unnamedListConstructor) { + compiler.reportError( + originParameter.parseNode(compiler), + MessageKind.PATCH_PARAMETER_MISMATCH, + {'methodName': origin.name, + 'originParameter': originParameterText, + 'patchParameter': patchParameterText}); + compiler.reportInfo(patchParameter, + MessageKind.PATCH_POINT_TO_PARAMETER, + {'parameterName': patchParameter.name}); + } + } + + originParameters = originParameters.tail; + patchParameters = patchParameters.tail; + } + } + + void checkMatchingPatchSignatures(FunctionElement origin, + FunctionElement patch) { + // TODO(johnniwinther): Show both origin and patch locations on errors. + FunctionExpression originTree = origin.node; + FunctionSignature originSignature = origin.functionSignature; + FunctionExpression patchTree = patch.node; + FunctionSignature patchSignature = patch.functionSignature; + + if (originSignature.type.returnType != patchSignature.type.returnType) { + compiler.withCurrentElement(patch, () { + Node errorNode = + patchTree.returnType != null ? patchTree.returnType : patchTree; + error(errorNode, MessageKind.PATCH_RETURN_TYPE_MISMATCH, + {'methodName': origin.name, + 'originReturnType': originSignature.type.returnType, + 'patchReturnType': patchSignature.type.returnType}); + }); + } + if (originSignature.requiredParameterCount != + patchSignature.requiredParameterCount) { + compiler.withCurrentElement(patch, () { + error(patchTree, + MessageKind.PATCH_REQUIRED_PARAMETER_COUNT_MISMATCH, + {'methodName': origin.name, + 'originParameterCount': originSignature.requiredParameterCount, + 'patchParameterCount': patchSignature.requiredParameterCount}); + }); + } else { + checkMatchingPatchParameters(origin, + originSignature.requiredParameters, + patchSignature.requiredParameters); + } + if (originSignature.optionalParameterCount != 0 && + patchSignature.optionalParameterCount != 0) { + if (originSignature.optionalParametersAreNamed != + patchSignature.optionalParametersAreNamed) { + compiler.withCurrentElement(patch, () { + error(patchTree, + MessageKind.PATCH_OPTIONAL_PARAMETER_NAMED_MISMATCH, + {'methodName': origin.name}); + }); + } + } + if (originSignature.optionalParameterCount != + patchSignature.optionalParameterCount) { + compiler.withCurrentElement(patch, () { + error(patchTree, + MessageKind.PATCH_OPTIONAL_PARAMETER_COUNT_MISMATCH, + {'methodName': origin.name, + 'originParameterCount': originSignature.optionalParameterCount, + 'patchParameterCount': patchSignature.optionalParameterCount}); + }); + } else { + checkMatchingPatchParameters(origin, + originSignature.optionalParameters, + patchSignature.optionalParameters); + } + } + + TreeElements resolveMethodElement(FunctionElementX element) { + assert(invariant(element, element.isDeclaration)); + return compiler.withCurrentElement(element, () { + bool isConstructor = + identical(element.kind, ElementKind.GENERATIVE_CONSTRUCTOR); + TreeElements elements = + compiler.enqueuer.resolution.getCachedElements(element); + if (elements != null) { + // TODO(karlklose): Remove the check for [isConstructor]. [elememts] + // should never be non-null, not even for constructors. + assert(invariant(element, element.isConstructor(), + message: 'Non-constructor element $element ' + 'has already been analyzed.')); + return elements; + } + if (element.isSynthesized) { + if (isConstructor) { + Element target = element.targetConstructor; + // Ensure the signature of the synthesized element is + // resolved. This is the only place where the resolver is + // seeing this element. + element.computeSignature(compiler); + if (!target.isErroneous()) { + compiler.enqueuer.resolution.registerStaticUse(target); + } + } else { + assert(element.isDeferredLoaderGetter()); + } + return _ensureTreeElements(element); + } + element.parseNode(compiler); + element.computeSignature(compiler); + if (element.isPatched) { + FunctionElementX patch = element.patch; + compiler.withCurrentElement(patch, () { + patch.parseNode(compiler); + patch.computeSignature(compiler); + }); + checkMatchingPatchSignatures(element, patch); + element = patch; + } + return compiler.withCurrentElement(element, () { + FunctionExpression tree = element.node; + if (tree.modifiers.isExternal()) { + error(tree, MessageKind.PATCH_EXTERNAL_WITHOUT_IMPLEMENTATION); + return null; + } + if (isConstructor || element.isFactoryConstructor()) { + if (tree.returnType != null) { + error(tree, MessageKind.CONSTRUCTOR_WITH_RETURN_TYPE); + } + if (element.modifiers.isConst() && + tree.hasBody() && + !tree.isRedirectingFactory) { + compiler.reportError(tree, MessageKind.CONST_CONSTRUCTOR_HAS_BODY); + } + } + + ResolverVisitor visitor = visitorFor(element); + visitor.useElement(tree, element); + visitor.setupFunction(tree, element); + + if (isConstructor && !element.isForwardingConstructor) { + // Even if there is no initializer list we still have to do the + // resolution in case there is an implicit super constructor call. + InitializerResolver resolver = new InitializerResolver(visitor); + FunctionElement redirection = + resolver.resolveInitializers(element, tree); + if (redirection != null) { + resolveRedirectingConstructor(resolver, tree, element, redirection); + } + } else if (element.isForwardingConstructor) { + // Initializers will be checked on the original constructor. + } else if (tree.initializers != null) { + error(tree, MessageKind.FUNCTION_WITH_INITIALIZER); + } + + if (!compiler.analyzeSignaturesOnly || tree.isRedirectingFactory) { + // We need to analyze the redirecting factory bodies to ensure that + // we can analyze compile-time constants. + visitor.visit(tree.body); + } + + // Get the resolution tree and check that the resolved + // function doesn't use 'super' if it is mixed into another + // class. This is the part of the 'super' mixin check that + // happens when a function is resolved after the mixin + // application has been performed. + TreeElements resolutionTree = visitor.mapping; + ClassElement enclosingClass = element.getEnclosingClass(); + if (enclosingClass != null) { + Set mixinUses = + compiler.world.mixinUses[enclosingClass]; + if (mixinUses != null) { + ClassElement mixin = enclosingClass; + for (MixinApplicationElement mixinApplication in mixinUses) { + checkMixinSuperUses(resolutionTree, mixinApplication, mixin); + } + } + } + return resolutionTree; + }); + }); + } + + /// This method should only be used by this library (or tests of + /// this library). + ResolverVisitor visitorFor(Element element) { + return new ResolverVisitor(compiler, element, _ensureTreeElements(element)); + } + + TreeElements resolveField(VariableElementX element) { + VariableDefinitions tree = element.parseNode(compiler); + if(element.modifiers.isStatic() && element.isTopLevel()) { + error(element.modifiers.getStatic(), + MessageKind.TOP_LEVEL_VARIABLE_DECLARED_STATIC); + } + ResolverVisitor visitor = visitorFor(element); + // TODO(johnniwinther): Share the resolved type between all variables + // declared in the same declaration. + if (tree.type != null) { + element.variables.type = visitor.resolveTypeAnnotation(tree.type); + } else { + element.variables.type = compiler.types.dynamicType; + } + visitor.useElement(tree, element); + + Expression initializer = element.initializer; + Modifiers modifiers = element.modifiers; + if (initializer != null) { + // TODO(johnniwinther): Avoid analyzing initializers if + // [Compiler.analyzeSignaturesOnly] is set. + visitor.visit(initializer); + } else if (modifiers.isConst()) { + compiler.reportError(element, MessageKind.CONST_WITHOUT_INITIALIZER); + } else if (modifiers.isFinal() && !element.isInstanceMember()) { + compiler.reportError(element, MessageKind.FINAL_WITHOUT_INITIALIZER); + } else { + compiler.enqueuer.resolution.registerInstantiatedClass( + compiler.nullClass, visitor.mapping); + } + + if (Elements.isStaticOrTopLevelField(element)) { + visitor.addDeferredAction(element, () { + compiler.constantHandler.compileVariable( + element, isConst: element.modifiers.isConst()); + }); + if (initializer != null) { + if (!element.modifiers.isConst()) { + // TODO(johnniwinther): Determine the const-ness eagerly to avoid + // unnecessary registrations. + compiler.backend.registerLazyField(visitor.mapping); + } + } + } + + // Perform various checks as side effect of "computing" the type. + element.computeType(compiler); + + return visitor.mapping; + } + + DartType resolveTypeAnnotation(Element element, TypeAnnotation annotation) { + DartType type = resolveReturnType(element, annotation); + if (type == compiler.types.voidType) { + error(annotation, MessageKind.VOID_NOT_ALLOWED); + } + return type; + } + + DartType resolveReturnType(Element element, TypeAnnotation annotation) { + if (annotation == null) return compiler.types.dynamicType; + DartType result = visitorFor(element).resolveTypeAnnotation(annotation); + if (result == null) { + // TODO(karklose): warning. + return compiler.types.dynamicType; + } + return result; + } + + void resolveRedirectionChain(FunctionElement constructor, Spannable node) { + FunctionElementX target = constructor; + InterfaceType targetType; + List seen = new List(); + // Follow the chain of redirections and check for cycles. + while (target != target.defaultImplementation) { + if (target.internalRedirectionTarget != null) { + // We found a constructor that already has been processed. + targetType = target.redirectionTargetType; + assert(invariant(target, targetType != null, + message: 'Redirection target type has not been computed for ' + '$target')); + target = target.internalRedirectionTarget; + break; + } + + Element nextTarget = target.defaultImplementation; + if (seen.contains(nextTarget)) { + error(node, MessageKind.CYCLIC_REDIRECTING_FACTORY); + break; + } + seen.add(target); + target = nextTarget; + } + + if (targetType == null) { + assert(!target.isRedirectingFactory); + targetType = target.getEnclosingClass().thisType; + } + + // [target] is now the actual target of the redirections. Run through + // the constructors again and set their [redirectionTarget], so that we + // do not have to run the loop for these constructors again. Furthermore, + // compute [redirectionTargetType] for each factory by computing the + // substitution of the target type with respect to the factory type. + while (!seen.isEmpty) { + FunctionElementX factory = seen.removeLast(); + + TreeElements treeElements = + compiler.enqueuer.resolution.getCachedElements(factory); + FunctionExpression functionNode = factory.parseNode(compiler); + Return redirectionNode = functionNode.body; + InterfaceType factoryType = + treeElements.getType(redirectionNode.expression); + + targetType = targetType.substByContext(factoryType); + factory.redirectionTarget = target; + factory.redirectionTargetType = targetType; + } + } + + /** + * Load and resolve the supertypes of [cls]. + * + * Warning: do not call this method directly. It should only be + * called by [resolveClass] and [ClassSupertypeResolver]. + */ + void loadSupertypes(BaseClassElementX cls, Spannable from) { + compiler.withCurrentElement(cls, () => measure(() { + if (cls.supertypeLoadState == STATE_DONE) return; + if (cls.supertypeLoadState == STATE_STARTED) { + compiler.reportError(from, MessageKind.CYCLIC_CLASS_HIERARCHY, + {'className': cls.name}); + cls.supertypeLoadState = STATE_DONE; + cls.hasIncompleteHierarchy = true; + cls.allSupertypesAndSelf = + compiler.objectClass.allSupertypesAndSelf.extendClass( + cls.computeType(compiler)); + cls.supertype = cls.allSupertypes.head; + assert(invariant(from, cls.supertype != null, + message: 'Missing supertype on cyclic class $cls.')); + cls.interfaces = const Link(); + return; + } + cls.supertypeLoadState = STATE_STARTED; + compiler.withCurrentElement(cls, () { + // TODO(ahe): Cache the node in cls. + cls.parseNode(compiler).accept( + new ClassSupertypeResolver(compiler, cls)); + if (cls.supertypeLoadState != STATE_DONE) { + cls.supertypeLoadState = STATE_DONE; + } + }); + })); + } + + // TODO(johnniwinther): Remove this queue when resolution has been split into + // syntax and semantic resolution. + TypeDeclarationElement currentlyResolvedTypeDeclaration; + Queue pendingClassesToBeResolved = new Queue(); + Queue pendingClassesToBePostProcessed = + new Queue(); + + /// Resolve [element] using [resolveTypeDeclaration]. + /// + /// This methods ensure that class declarations encountered through type + /// annotations during the resolution of [element] are resolved after + /// [element] has been resolved. + // TODO(johnniwinther): Encapsulate this functionality in a + // 'TypeDeclarationResolver'. + _resolveTypeDeclaration(TypeDeclarationElement element, + resolveTypeDeclaration()) { + return compiler.withCurrentElement(element, () { + return measure(() { + TypeDeclarationElement previousResolvedTypeDeclaration = + currentlyResolvedTypeDeclaration; + currentlyResolvedTypeDeclaration = element; + var result = resolveTypeDeclaration(); + if (previousResolvedTypeDeclaration == null) { + do { + while (!pendingClassesToBeResolved.isEmpty) { + pendingClassesToBeResolved.removeFirst().ensureResolved(compiler); + } + while (!pendingClassesToBePostProcessed.isEmpty) { + _postProcessClassElement( + pendingClassesToBePostProcessed.removeFirst()); + } + } while (!pendingClassesToBeResolved.isEmpty); + assert(pendingClassesToBeResolved.isEmpty); + assert(pendingClassesToBePostProcessed.isEmpty); + } + currentlyResolvedTypeDeclaration = previousResolvedTypeDeclaration; + return result; + }); + }); + } + + /** + * Resolve the class [element]. + * + * Before calling this method, [element] was constructed by the + * scanner and most fields are null or empty. This method fills in + * these fields and also ensure that the supertypes of [element] are + * resolved. + * + * Warning: Do not call this method directly. Instead use + * [:element.ensureResolved(compiler):]. + */ + TreeElements resolveClass(BaseClassElementX element) { + return _resolveTypeDeclaration(element, () { + // TODO(johnniwinther): Store the mapping in the resolution enqueuer. + resolveClassInternal(element, _ensureTreeElements(element)); + return element.treeElements; + }); + } + + void _ensureClassWillBeResolved(ClassElement element) { + if (currentlyResolvedTypeDeclaration == null) { + element.ensureResolved(compiler); + } else { + pendingClassesToBeResolved.add(element); + } + } + + void resolveClassInternal(BaseClassElementX element, + TreeElementMapping mapping) { + if (!element.isPatch) { + compiler.withCurrentElement(element, () => measure(() { + assert(element.resolutionState == STATE_NOT_STARTED); + element.resolutionState = STATE_STARTED; + Node tree = element.parseNode(compiler); + loadSupertypes(element, tree); + + ClassResolverVisitor visitor = + new ClassResolverVisitor(compiler, element, mapping); + visitor.visit(tree); + element.resolutionState = STATE_DONE; + compiler.onClassResolved(element); + pendingClassesToBePostProcessed.add(element); + })); + if (element.isPatched) { + // Ensure handling patch after origin. + element.patch.ensureResolved(compiler); + } + } else { // Handle patch classes: + element.resolutionState = STATE_STARTED; + // Ensure handling origin before patch. + element.origin.ensureResolved(compiler); + // Ensure that the type is computed. + element.computeType(compiler); + // Copy class hierarchy from origin. + element.supertype = element.origin.supertype; + element.interfaces = element.origin.interfaces; + element.allSupertypesAndSelf = element.origin.allSupertypesAndSelf; + // Stepwise assignment to ensure invariant. + element.supertypeLoadState = STATE_STARTED; + element.supertypeLoadState = STATE_DONE; + element.resolutionState = STATE_DONE; + // TODO(johnniwinther): Check matching type variables and + // empty extends/implements clauses. + } + } + + void _postProcessClassElement(BaseClassElementX element) { + for (MetadataAnnotation metadata in element.metadata) { + metadata.ensureResolved(compiler); + if (!element.isProxy && metadata.value == compiler.proxyConstant) { + element.isProxy = true; + } + } + + // Force resolution of metadata on non-instance members since they may be + // inspected by the backend while emitting. Metadata on instance members is + // handled as a result of processing instantiated class members in the + // enqueuer. + // TODO(ahe): Avoid this eager resolution. + element.forEachMember((_, Element member) { + if (!member.isInstanceMember()) { + compiler.withCurrentElement(member, () { + for (MetadataAnnotation metadata in member.metadata) { + metadata.ensureResolved(compiler); + } + }); + } + }); + + computeClassMembers(element); + } + + void computeClassMembers(ClassElement element) { + MembersCreator.computeClassMembers(compiler, element); + } + + void checkClass(ClassElement element) { + if (element.isMixinApplication) { + checkMixinApplication(element); + } else { + checkClassMembers(element); + } + } + + void checkMixinApplication(MixinApplicationElement mixinApplication) { + Modifiers modifiers = mixinApplication.modifiers; + int illegalFlags = modifiers.flags & ~Modifiers.FLAG_ABSTRACT; + if (illegalFlags != 0) { + Modifiers illegalModifiers = new Modifiers.withFlags(null, illegalFlags); + compiler.reportError( + modifiers, + MessageKind.ILLEGAL_MIXIN_APPLICATION_MODIFIERS, + {'modifiers': illegalModifiers}); + } + + // In case of cyclic mixin applications, the mixin chain will have + // been cut. If so, we have already reported the error to the + // user so we just return from here. + ClassElement mixin = mixinApplication.mixin; + if (mixin == null) return; + + // Check that we're not trying to use Object as a mixin. + if (mixin.superclass == null) { + compiler.reportError(mixinApplication, + MessageKind.ILLEGAL_MIXIN_OBJECT); + // Avoid reporting additional errors for the Object class. + return; + } + + // Check that the mixed in class has Object as its superclass. + if (!mixin.superclass.isObject(compiler)) { + compiler.reportError(mixin, MessageKind.ILLEGAL_MIXIN_SUPERCLASS); + } + + // Check that the mixed in class doesn't have any constructors and + // make sure we aren't mixing in methods that use 'super'. + mixin.forEachLocalMember((Element member) { + if (member.isGenerativeConstructor() && !member.isSynthesized) { + compiler.reportError(member, MessageKind.ILLEGAL_MIXIN_CONSTRUCTOR); + } else { + // Get the resolution tree and check that the resolved member + // doesn't use 'super'. This is the part of the 'super' mixin + // check that happens when a function is resolved before the + // mixin application has been performed. + checkMixinSuperUses( + compiler.enqueuer.resolution.resolvedElements[member], + mixinApplication, + mixin); + } + }); + } + + void checkMixinSuperUses(TreeElements resolutionTree, + MixinApplicationElement mixinApplication, + ClassElement mixin) { + if (resolutionTree == null) return; + Setlet superUses = resolutionTree.superUses; + if (superUses.isEmpty) return; + compiler.reportError(mixinApplication, + MessageKind.ILLEGAL_MIXIN_WITH_SUPER, + {'className': mixin.name}); + // Show the user the problematic uses of 'super' in the mixin. + for (Node use in superUses) { + compiler.reportInfo( + use, + MessageKind.ILLEGAL_MIXIN_SUPER_USE); + } + } + + void checkClassMembers(ClassElement cls) { + assert(invariant(cls, cls.isDeclaration)); + if (cls.isObject(compiler)) return; + // TODO(johnniwinther): Should this be done on the implementation element as + // well? + List constConstructors = []; + List nonFinalInstanceFields = []; + cls.forEachMember((holder, member) { + compiler.withCurrentElement(member, () { + // Perform various checks as side effect of "computing" the type. + member.computeType(compiler); + + // Check modifiers. + if (member.isFunction() && member.modifiers.isFinal()) { + compiler.reportError( + member, MessageKind.ILLEGAL_FINAL_METHOD_MODIFIER); + } + if (member.isConstructor()) { + final mismatchedFlagsBits = + member.modifiers.flags & + (Modifiers.FLAG_STATIC | Modifiers.FLAG_ABSTRACT); + if (mismatchedFlagsBits != 0) { + final mismatchedFlags = + new Modifiers.withFlags(null, mismatchedFlagsBits); + compiler.reportError( + member, + MessageKind.ILLEGAL_CONSTRUCTOR_MODIFIERS, + {'modifiers': mismatchedFlags}); + } + if (member.modifiers.isConst()) { + constConstructors.add(member); + } + } + if (member.isField()) { + if (!member.modifiers.isStatic() && + !member.modifiers.isFinal()) { + nonFinalInstanceFields.add(member); + } + } + checkAbstractField(member); + checkUserDefinableOperator(member); + }); + }); + if (!constConstructors.isEmpty && !nonFinalInstanceFields.isEmpty) { + Spannable span = constConstructors.length > 1 + ? cls : constConstructors[0]; + compiler.reportError(span, + MessageKind.CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS, + {'className': cls.name}); + if (constConstructors.length > 1) { + for (Element constructor in constConstructors) { + compiler.reportInfo(constructor, + MessageKind.CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS_CONSTRUCTOR); + } + } + for (Element field in nonFinalInstanceFields) { + compiler.reportInfo(field, + MessageKind.CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS_FIELD); + } + } + } + + void checkAbstractField(Element member) { + // Only check for getters. The test can only fail if there is both a setter + // and a getter with the same name, and we only need to check each abstract + // field once, so we just ignore setters. + if (!member.isGetter()) return; + + // Find the associated abstract field. + ClassElement classElement = member.getEnclosingClass(); + Element lookupElement = classElement.lookupLocalMember(member.name); + if (lookupElement == null) { + compiler.internalError(member, + "No abstract field for accessor"); + } else if (!identical(lookupElement.kind, ElementKind.ABSTRACT_FIELD)) { + compiler.internalError(member, + "Inaccessible abstract field for accessor"); + } + AbstractFieldElement field = lookupElement; + + if (field.getter == null) return; + if (field.setter == null) return; + int getterFlags = field.getter.modifiers.flags | Modifiers.FLAG_ABSTRACT; + int setterFlags = field.setter.modifiers.flags | Modifiers.FLAG_ABSTRACT; + if (!identical(getterFlags, setterFlags)) { + final mismatchedFlags = + new Modifiers.withFlags(null, getterFlags ^ setterFlags); + compiler.reportError( + field.getter, + MessageKind.GETTER_MISMATCH, + {'modifiers': mismatchedFlags}); + compiler.reportError( + field.setter, + MessageKind.SETTER_MISMATCH, + {'modifiers': mismatchedFlags}); + } + } + + void checkUserDefinableOperator(Element member) { + FunctionElement function = member.asFunctionElement(); + if (function == null) return; + String value = member.name; + if (value == null) return; + if (!(isUserDefinableOperator(value) || identical(value, 'unary-'))) return; + + bool isMinus = false; + int requiredParameterCount; + MessageKind messageKind; + if (identical(value, 'unary-')) { + isMinus = true; + messageKind = MessageKind.MINUS_OPERATOR_BAD_ARITY; + requiredParameterCount = 0; + } else if (isMinusOperator(value)) { + isMinus = true; + messageKind = MessageKind.MINUS_OPERATOR_BAD_ARITY; + requiredParameterCount = 1; + } else if (isUnaryOperator(value)) { + messageKind = MessageKind.UNARY_OPERATOR_BAD_ARITY; + requiredParameterCount = 0; + } else if (isBinaryOperator(value)) { + messageKind = MessageKind.BINARY_OPERATOR_BAD_ARITY; + requiredParameterCount = 1; + if (identical(value, '==')) checkOverrideHashCode(member); + } else if (isTernaryOperator(value)) { + messageKind = MessageKind.TERNARY_OPERATOR_BAD_ARITY; + requiredParameterCount = 2; + } else { + compiler.internalError(function, + 'Unexpected user defined operator $value'); + } + checkArity(function, requiredParameterCount, messageKind, isMinus); + } + + void checkOverrideHashCode(FunctionElement operatorEquals) { + if (operatorEquals.isAbstract) return; + ClassElement cls = operatorEquals.getEnclosingClass(); + Element hashCodeImplementation = + cls.lookupLocalMember('hashCode'); + if (hashCodeImplementation != null) return; + compiler.reportHint( + operatorEquals, MessageKind.OVERRIDE_EQUALS_NOT_HASH_CODE, + {'class': cls.name}); + } + + void checkArity(FunctionElement function, + int requiredParameterCount, MessageKind messageKind, + bool isMinus) { + FunctionExpression node = function.node; + FunctionSignature signature = function.functionSignature; + if (signature.requiredParameterCount != requiredParameterCount) { + Node errorNode = node; + if (node.parameters != null) { + if (isMinus || + signature.requiredParameterCount < requiredParameterCount) { + // If there are too few parameters, point to the whole parameter list. + // For instance + // + // int operator +() {} + // ^^ + // + // int operator []=(value) {} + // ^^^^^^^ + // + // For operator -, always point the whole parameter list, like + // + // int operator -(a, b) {} + // ^^^^^^ + // + // instead of + // + // int operator -(a, b) {} + // ^ + // + // since the correction might not be to remove 'b' but instead to + // remove 'a, b'. + errorNode = node.parameters; + } else { + errorNode = node.parameters.nodes.skip(requiredParameterCount).head; + } + } + compiler.reportError( + errorNode, messageKind, {'operatorName': function.name}); + } + if (signature.optionalParameterCount != 0) { + Node errorNode = + node.parameters.nodes.skip(signature.requiredParameterCount).head; + if (signature.optionalParametersAreNamed) { + compiler.reportError( + errorNode, + MessageKind.OPERATOR_NAMED_PARAMETERS, + {'operatorName': function.name}); + } else { + compiler.reportError( + errorNode, + MessageKind.OPERATOR_OPTIONAL_PARAMETERS, + {'operatorName': function.name}); + } + } + } + + reportErrorWithContext(Element errorneousElement, + MessageKind errorMessage, + Element contextElement, + MessageKind contextMessage) { + compiler.reportError( + errorneousElement, + errorMessage, + {'memberName': contextElement.name, + 'className': contextElement.getEnclosingClass().name}); + compiler.reportInfo(contextElement, contextMessage); + } + + + FunctionSignature resolveSignature(FunctionElementX element) { + MessageKind defaultValuesError = null; + if (element.isFactoryConstructor()) { + FunctionExpression body = element.parseNode(compiler); + if (body.isRedirectingFactory) { + defaultValuesError = MessageKind.REDIRECTING_FACTORY_WITH_DEFAULT; + } + } + return compiler.withCurrentElement(element, () { + FunctionExpression node = + compiler.parser.measure(() => element.parseNode(compiler)); + return measure(() => SignatureResolver.analyze( + compiler, node.parameters, node.returnType, element, + _ensureTreeElements(element), + defaultValuesError: defaultValuesError)); + }); + } + + TreeElements resolveTypedef(TypedefElementX element) { + if (element.isResolved) return element.treeElements; + return _resolveTypeDeclaration(element, () { + TreeElementMapping mapping = _ensureTreeElements(element); + return compiler.withCurrentElement(element, () { + return measure(() { + Typedef node = + compiler.parser.measure(() => element.parseNode(compiler)); + TypedefResolverVisitor visitor = + new TypedefResolverVisitor(compiler, element, mapping); + visitor.visit(node); + + return mapping; + }); + }); + }); + } + + void resolveMetadataAnnotation(MetadataAnnotationX annotation) { + compiler.withCurrentElement(annotation.annotatedElement, () => measure(() { + assert(annotation.resolutionState == STATE_NOT_STARTED); + annotation.resolutionState = STATE_STARTED; + + Node node = annotation.parseNode(compiler); + // TODO(johnniwinther): Find the right analyzable element to hold the + // [TreeElements] for the annotation. + Element annotatedElement = annotation.annotatedElement; + Element context = annotatedElement.enclosingElement; + if (context == null) { + context = annotatedElement; + } + ResolverVisitor visitor = visitorFor(context); + node.accept(visitor); + annotation.value = compiler.constantHandler.compileNodeWithDefinitions( + node, visitor.mapping, isConst: true); + compiler.backend.registerMetadataConstant(annotation.value, + visitor.mapping); + + annotation.resolutionState = STATE_DONE; + })); + } + + error(Spannable node, MessageKind kind, [arguments = const {}]) { + // TODO(ahe): Make non-fatal. + compiler.reportFatalError(node, kind, arguments); + } + + Link resolveMetadata(Element element, + VariableDefinitions node) { + LinkBuilder metadata = + new LinkBuilder(); + for (Metadata annotation in node.metadata.nodes) { + ParameterMetadataAnnotation metadataAnnotation = + new ParameterMetadataAnnotation(annotation); + metadataAnnotation.annotatedElement = element; + metadata.addLast(metadataAnnotation.ensureResolved(compiler)); + } + return metadata.toLink(); + } +} + +class ConstantMapper extends Visitor { + final Map constantToNodeMap = new Map(); + final CompileTimeConstantEvaluator evaluator; + + ConstantMapper(ConstantHandler handler, + TreeElements elements, + Compiler compiler) + : evaluator = new CompileTimeConstantEvaluator( + handler, elements, compiler, isConst: false); + + visitNode(Node node) { + Constant constant = evaluator.evaluate(node); + if (constant != null) constantToNodeMap[constant] = node; + node.visitChildren(this); + } +} + +class InitializerResolver { + final ResolverVisitor visitor; + final Map initialized; + Link initializers; + bool hasSuper; + + InitializerResolver(this.visitor) + : initialized = new Map(), hasSuper = false; + + error(Node node, MessageKind kind, [arguments = const {}]) { + visitor.error(node, kind, arguments); + } + + warning(Node node, MessageKind kind, [arguments = const {}]) { + visitor.warning(node, kind, arguments); + } + + bool isFieldInitializer(SendSet node) { + if (node.selector.asIdentifier() == null) return false; + if (node.receiver == null) return true; + if (node.receiver.asIdentifier() == null) return false; + return node.receiver.asIdentifier().isThis(); + } + + reportDuplicateInitializerError(Element field, Node init, Node existing) { + visitor.compiler.reportError( + init, + MessageKind.DUPLICATE_INITIALIZER, {'fieldName': field.name}); + visitor.compiler.reportInfo( + existing, + MessageKind.ALREADY_INITIALIZED, {'fieldName': field.name}); + } + + void checkForDuplicateInitializers(VariableElement field, Node init) { + // [field] can be null if it could not be resolved. + if (field == null) return; + String name = field.name; + if (initialized.containsKey(field)) { + reportDuplicateInitializerError(field, init, initialized[field]); + } else if (field.modifiers.isFinal()) { + field.parseNode(visitor.compiler); + Expression initializer = field.initializer; + if (initializer != null) { + reportDuplicateInitializerError(field, init, initializer); + } + } + initialized[field] = init; + } + + void resolveFieldInitializer(FunctionElement constructor, SendSet init) { + // init is of the form [this.]field = value. + final Node selector = init.selector; + final String name = selector.asIdentifier().source; + // Lookup target field. + Element target; + if (isFieldInitializer(init)) { + target = constructor.getEnclosingClass().lookupLocalMember(name); + if (target == null) { + error(selector, MessageKind.CANNOT_RESOLVE, {'name': name}); + } else if (target.kind != ElementKind.FIELD) { + error(selector, MessageKind.NOT_A_FIELD, {'fieldName': name}); + } else if (!target.isInstanceMember()) { + error(selector, MessageKind.INIT_STATIC_FIELD, {'fieldName': name}); + } + } else { + error(init, MessageKind.INVALID_RECEIVER_IN_INITIALIZER); + } + visitor.useElement(init, target); + visitor.world.registerStaticUse(target); + checkForDuplicateInitializers(target, init); + // Resolve initializing value. + visitor.visitInStaticContext(init.arguments.head); + } + + ClassElement getSuperOrThisLookupTarget(FunctionElement constructor, + bool isSuperCall, + Node diagnosticNode) { + ClassElement lookupTarget = constructor.getEnclosingClass(); + if (isSuperCall) { + // Calculate correct lookup target and constructor name. + if (identical(lookupTarget, visitor.compiler.objectClass)) { + error(diagnosticNode, MessageKind.SUPER_INITIALIZER_IN_OBJECT); + } else { + return lookupTarget.supertype.element; + } + } + return lookupTarget; + } + + Element resolveSuperOrThisForSend(FunctionElement constructor, + FunctionExpression functionNode, + Send call) { + // Resolve the selector and the arguments. + ResolverTask resolver = visitor.compiler.resolver; + visitor.inStaticContext(() { + visitor.resolveSelector(call, null); + visitor.resolveArguments(call.argumentsNode); + }); + Selector selector = visitor.mapping.getSelector(call); + bool isSuperCall = Initializers.isSuperConstructorCall(call); + + ClassElement lookupTarget = getSuperOrThisLookupTarget(constructor, + isSuperCall, + call); + Selector constructorSelector = + visitor.getRedirectingThisOrSuperConstructorSelector(call); + FunctionElement calledConstructor = + lookupTarget.lookupConstructor(constructorSelector); + + final bool isImplicitSuperCall = false; + final String className = lookupTarget.name; + verifyThatConstructorMatchesCall(constructor, + calledConstructor, + selector, + isImplicitSuperCall, + call, + className, + constructorSelector); + + visitor.useElement(call, calledConstructor); + visitor.world.registerStaticUse(calledConstructor); + return calledConstructor; + } + + void resolveImplicitSuperConstructorSend(FunctionElement constructor, + FunctionExpression functionNode) { + // If the class has a super resolve the implicit super call. + ClassElement classElement = constructor.getEnclosingClass(); + ClassElement superClass = classElement.superclass; + if (classElement != visitor.compiler.objectClass) { + assert(superClass != null); + assert(superClass.resolutionState == STATE_DONE); + String constructorName = ''; + Selector callToMatch = new Selector.call( + constructorName, + classElement.getLibrary(), + 0); + + final bool isSuperCall = true; + ClassElement lookupTarget = getSuperOrThisLookupTarget(constructor, + isSuperCall, + functionNode); + Selector constructorSelector = new Selector.callDefaultConstructor( + visitor.enclosingElement.getLibrary()); + Element calledConstructor = lookupTarget.lookupConstructor( + constructorSelector); + + final String className = lookupTarget.name; + final bool isImplicitSuperCall = true; + verifyThatConstructorMatchesCall(constructor, + calledConstructor, + callToMatch, + isImplicitSuperCall, + functionNode, + className, + constructorSelector); + + visitor.world.registerStaticUse(calledConstructor); + } + } + + void verifyThatConstructorMatchesCall( + FunctionElement caller, + FunctionElement lookedupConstructor, + Selector call, + bool isImplicitSuperCall, + Node diagnosticNode, + String className, + Selector constructorSelector) { + if (lookedupConstructor == null + || !lookedupConstructor.isGenerativeConstructor()) { + var fullConstructorName = + visitor.compiler.resolver.constructorNameForDiagnostics( + className, + constructorSelector.name); + MessageKind kind = isImplicitSuperCall + ? MessageKind.CANNOT_RESOLVE_CONSTRUCTOR_FOR_IMPLICIT + : MessageKind.CANNOT_RESOLVE_CONSTRUCTOR; + visitor.compiler.reportError( + diagnosticNode, kind, {'constructorName': fullConstructorName}); + } else { + if (!call.applies(lookedupConstructor, visitor.compiler)) { + MessageKind kind = isImplicitSuperCall + ? MessageKind.NO_MATCHING_CONSTRUCTOR_FOR_IMPLICIT + : MessageKind.NO_MATCHING_CONSTRUCTOR; + visitor.compiler.reportError(diagnosticNode, kind); + } else if (caller.modifiers.isConst() + && !lookedupConstructor.modifiers.isConst()) { + visitor.compiler.reportError( + diagnosticNode, MessageKind.CONST_CALLS_NON_CONST); + } + } + } + + /** + * Resolve all initializers of this constructor. In the case of a redirecting + * constructor, the resolved constructor's function element is returned. + */ + FunctionElement resolveInitializers(FunctionElement constructor, + FunctionExpression functionNode) { + // Keep track of all "this.param" parameters specified for constructor so + // that we can ensure that fields are initialized only once. + FunctionSignature functionParameters = constructor.functionSignature; + functionParameters.forEachParameter((ParameterElement element) { + if (identical(element.kind, ElementKind.FIELD_PARAMETER)) { + FieldParameterElement fieldParameter = element; + checkForDuplicateInitializers(fieldParameter.fieldElement, + element.initializer); + } + }); + + if (functionNode.initializers == null) { + initializers = const Link(); + } else { + initializers = functionNode.initializers.nodes; + } + FunctionElement result; + bool resolvedSuper = false; + for (Link link = initializers; !link.isEmpty; link = link.tail) { + if (link.head.asSendSet() != null) { + final SendSet init = link.head.asSendSet(); + resolveFieldInitializer(constructor, init); + } else if (link.head.asSend() != null) { + final Send call = link.head.asSend(); + if (call.argumentsNode == null) { + error(link.head, MessageKind.INVALID_INITIALIZER); + continue; + } + if (Initializers.isSuperConstructorCall(call)) { + if (resolvedSuper) { + error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER); + } + resolveSuperOrThisForSend(constructor, functionNode, call); + resolvedSuper = true; + } else if (Initializers.isConstructorRedirect(call)) { + // Check that there is no body (Language specification 7.5.1). If the + // constructor is also const, we already reported an error in + // [resolveMethodElement]. + if (functionNode.hasBody() && !constructor.modifiers.isConst()) { + error(functionNode, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_BODY); + } + // Check that there are no other initializers. + if (!initializers.tail.isEmpty) { + error(call, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_INITIALIZER); + } + // Check that there are no field initializing parameters. + Compiler compiler = visitor.compiler; + FunctionSignature signature = constructor.functionSignature; + signature.forEachParameter((ParameterElement parameter) { + if (parameter.isFieldParameter()) { + Node node = parameter.node; + error(node, MessageKind.INITIALIZING_FORMAL_NOT_ALLOWED); + } + }); + return resolveSuperOrThisForSend(constructor, functionNode, call); + } else { + visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED); + return null; + } + } else { + error(link.head, MessageKind.INVALID_INITIALIZER); + } + } + if (!resolvedSuper) { + resolveImplicitSuperConstructorSend(constructor, functionNode); + } + return null; // If there was no redirection always return null. + } +} + +class CommonResolverVisitor extends Visitor { + final Compiler compiler; + + CommonResolverVisitor(Compiler this.compiler); + + R visitNode(Node node) { + internalError(node, + 'internal error: Unhandled node: ${node.getObjectDescription()}'); + return null; + } + + R visitEmptyStatement(Node node) => null; + + /** Convenience method for visiting nodes that may be null. */ + R visit(Node node) => (node == null) ? null : node.accept(this); + + void error(Spannable node, MessageKind kind, [Map arguments = const {}]) { + compiler.reportFatalError(node, kind, arguments); + } + + void warning(Spannable node, MessageKind kind, [Map arguments = const {}]) { + compiler.reportWarning(node, kind, arguments); + } + + void internalError(Spannable node, message) { + compiler.internalError(node, message); + } + + void addDeferredAction(Element element, DeferredAction action) { + compiler.enqueuer.resolution.addDeferredAction(element, action); + } +} + +abstract class LabelScope { + LabelScope get outer; + LabelElement lookup(String label); +} + +class LabeledStatementLabelScope implements LabelScope { + final LabelScope outer; + final Map labels; + LabeledStatementLabelScope(this.outer, this.labels); + LabelElement lookup(String labelName) { + LabelElement label = labels[labelName]; + if (label != null) return label; + return outer.lookup(labelName); + } +} + +class SwitchLabelScope implements LabelScope { + final LabelScope outer; + final Map caseLabels; + + SwitchLabelScope(this.outer, this.caseLabels); + + LabelElement lookup(String labelName) { + LabelElement result = caseLabels[labelName]; + if (result != null) return result; + return outer.lookup(labelName); + } +} + +class EmptyLabelScope implements LabelScope { + const EmptyLabelScope(); + LabelElement lookup(String label) => null; + LabelScope get outer { + throw 'internal error: empty label scope has no outer'; + } +} + +class StatementScope { + LabelScope labels; + Link breakTargetStack; + Link continueTargetStack; + // Used to provide different numbers to statements if one is inside the other. + // Can be used to make otherwise duplicate labels unique. + int nestingLevel = 0; + + StatementScope() + : labels = const EmptyLabelScope(), + breakTargetStack = const Link(), + continueTargetStack = const Link(); + + LabelElement lookupLabel(String label) { + return labels.lookup(label); + } + + TargetElement currentBreakTarget() => + breakTargetStack.isEmpty ? null : breakTargetStack.head; + + TargetElement currentContinueTarget() => + continueTargetStack.isEmpty ? null : continueTargetStack.head; + + void enterLabelScope(Map elements) { + labels = new LabeledStatementLabelScope(labels, elements); + nestingLevel++; + } + + void exitLabelScope() { + nestingLevel--; + labels = labels.outer; + } + + void enterLoop(TargetElement element) { + breakTargetStack = breakTargetStack.prepend(element); + continueTargetStack = continueTargetStack.prepend(element); + nestingLevel++; + } + + void exitLoop() { + nestingLevel--; + breakTargetStack = breakTargetStack.tail; + continueTargetStack = continueTargetStack.tail; + } + + void enterSwitch(TargetElement breakElement, + Map continueElements) { + breakTargetStack = breakTargetStack.prepend(breakElement); + labels = new SwitchLabelScope(labels, continueElements); + nestingLevel++; + } + + void exitSwitch() { + nestingLevel--; + breakTargetStack = breakTargetStack.tail; + labels = labels.outer; + } +} + +class TypeResolver { + final Compiler compiler; + + TypeResolver(this.compiler); + + /// Tries to resolve the type name as an element. + Element resolveTypeName(Identifier prefixName, + Identifier typeName, + Scope scope, + {bool deferredIsMalformed: true}) { + Element element; + bool deferredTypeAnnotation = false; + if (prefixName != null) { + Element prefixElement = + lookupInScope(compiler, prefixName, scope, prefixName.source); + if (prefixElement != null && prefixElement.isPrefix()) { + // The receiver is a prefix. Lookup in the imported members. + PrefixElement prefix = prefixElement; + element = prefix.lookupLocalMember(typeName.source); + // TODO(17260, sigurdm): The test for DartBackend is there because + // dart2dart outputs malformed types with prefix. + if (element != null && + prefix.isDeferred && + deferredIsMalformed && + compiler.backend is! DartBackend) { + element = new ErroneousElementX(MessageKind.DEFERRED_TYPE_ANNOTATION, + {'node': typeName}, + element.name, + element); + } + } else { + // The caller of this method will create the ErroneousElement for + // the MalformedType. + element = null; + } + } else { + String stringValue = typeName.source; + if (identical(stringValue, 'void')) { + element = compiler.types.voidType.element; + } else if (identical(stringValue, 'dynamic')) { + element = compiler.dynamicClass; + } else { + element = lookupInScope(compiler, typeName, scope, typeName.source); + } + } + return element; + } + + DartType resolveTypeAnnotation(MappingVisitor visitor, TypeAnnotation node, + {bool malformedIsError: false, + bool deferredIsMalformed: true}) { + Identifier typeName; + Identifier prefixName; + Send send = node.typeName.asSend(); + if (send != null) { + // The type name is of the form [: prefix . identifier :]. + prefixName = send.receiver.asIdentifier(); + typeName = send.selector.asIdentifier(); + } else { + typeName = node.typeName.asIdentifier(); + } + + Element element = resolveTypeName(prefixName, typeName, visitor.scope, + deferredIsMalformed: deferredIsMalformed); + + DartType reportFailureAndCreateType(MessageKind messageKind, + Map messageArguments, + {DartType userProvidedBadType, + Element erroneousElement}) { + if (malformedIsError) { + visitor.error(node, messageKind, messageArguments); + } else { + compiler.backend.registerThrowRuntimeError(visitor.mapping); + visitor.warning(node, messageKind, messageArguments); + } + if (erroneousElement == null) { + erroneousElement = new ErroneousElementX( + messageKind, messageArguments, typeName.source, + visitor.enclosingElement); + } + LinkBuilder arguments = new LinkBuilder(); + resolveTypeArguments(visitor, node, null, arguments); + return new MalformedType(erroneousElement, + userProvidedBadType, arguments.toLink()); + } + + DartType checkNoTypeArguments(DartType type) { + LinkBuilder arguments = new LinkBuilder(); + bool hasTypeArgumentMismatch = resolveTypeArguments( + visitor, node, const Link(), arguments); + if (hasTypeArgumentMismatch) { + return new MalformedType( + new ErroneousElementX(MessageKind.TYPE_ARGUMENT_COUNT_MISMATCH, + {'type': node}, typeName.source, visitor.enclosingElement), + type, arguments.toLink()); + } + return type; + } + + // Try to construct the type from the element. + DartType type; + if (element == null) { + type = reportFailureAndCreateType( + MessageKind.CANNOT_RESOLVE_TYPE, {'typeName': node.typeName}); + } else if (element.isAmbiguous()) { + AmbiguousElement ambiguous = element; + type = reportFailureAndCreateType( + ambiguous.messageKind, ambiguous.messageArguments); + ambiguous.diagnose(visitor.mapping.currentElement, compiler); + } else if (element.isErroneous()) { + ErroneousElement erroneousElement = element; + type = reportFailureAndCreateType( + erroneousElement.messageKind, erroneousElement.messageArguments, + erroneousElement: erroneousElement); + } else if (!element.impliesType()) { + type = reportFailureAndCreateType( + MessageKind.NOT_A_TYPE, {'node': node.typeName}); + } else { + bool addTypeVariableBoundsCheck = false; + if (identical(element, compiler.types.voidType.element) || + identical(element, compiler.dynamicClass)) { + type = checkNoTypeArguments(element.computeType(compiler)); + } else if (element.isClass()) { + ClassElement cls = element; + compiler.resolver._ensureClassWillBeResolved(cls); + element.computeType(compiler); + var arguments = new LinkBuilder(); + bool hasTypeArgumentMismatch = resolveTypeArguments( + visitor, node, cls.typeVariables, arguments); + if (hasTypeArgumentMismatch) { + type = new BadInterfaceType(cls.declaration, + new InterfaceType.forUserProvidedBadType(cls.declaration, + arguments.toLink())); + } else { + if (arguments.isEmpty) { + type = cls.rawType; + } else { + type = new InterfaceType(cls.declaration, arguments.toLink()); + addTypeVariableBoundsCheck = true; + } + } + } else if (element.isTypedef()) { + TypedefElement typdef = element; + // TODO(ahe): Should be [ensureResolved]. + compiler.resolveTypedef(typdef); + var arguments = new LinkBuilder(); + bool hasTypeArgumentMismatch = resolveTypeArguments( + visitor, node, typdef.typeVariables, arguments); + if (hasTypeArgumentMismatch) { + type = new BadTypedefType(typdef, + new TypedefType.forUserProvidedBadType(typdef, + arguments.toLink())); + } else { + if (arguments.isEmpty) { + type = typdef.rawType; + } else { + type = new TypedefType(typdef, arguments.toLink()); + addTypeVariableBoundsCheck = true; + } + } + } else if (element.isTypeVariable()) { + Element outer = + visitor.enclosingElement.getOutermostEnclosingMemberOrTopLevel(); + bool isInFactoryConstructor = + outer != null && outer.isFactoryConstructor(); + if (!outer.isClass() && + !outer.isTypedef() && + !isInFactoryConstructor && + Elements.isInStaticContext(visitor.enclosingElement)) { + compiler.backend.registerThrowRuntimeError(visitor.mapping); + type = reportFailureAndCreateType( + MessageKind.TYPE_VARIABLE_WITHIN_STATIC_MEMBER, + {'typeVariableName': node}, + userProvidedBadType: element.computeType(compiler)); + } else { + type = element.computeType(compiler); + } + type = checkNoTypeArguments(type); + } else { + compiler.internalError(node, + "Unexpected element kind ${element.kind}."); + } + // TODO(johnniwinther): We should not resolve type annotations after the + // resolution queue has been closed. Currently the dart backend does so. + // Remove the guarded when this is fixed. + if (!compiler.enqueuer.resolution.queueIsClosed && + addTypeVariableBoundsCheck) { + visitor.addDeferredAction( + visitor.enclosingElement, + () => checkTypeVariableBounds(visitor.mapping, node, type)); + } + } + visitor.useType(node, type); + return type; + } + + /// Checks the type arguments of [type] against the type variable bounds. + void checkTypeVariableBounds(TreeElements elements, + TypeAnnotation node, GenericType type) { + void checkTypeVariableBound(_, DartType typeArgument, + TypeVariableType typeVariable, + DartType bound) { + compiler.backend.registerTypeVariableBoundCheck(elements); + if (!compiler.types.isSubtype(typeArgument, bound)) { + compiler.reportWarning(node, + MessageKind.INVALID_TYPE_VARIABLE_BOUND, + {'typeVariable': typeVariable, + 'bound': bound, + 'typeArgument': typeArgument, + 'thisType': type.element.thisType}); + } + }; + + compiler.types.checkTypeVariableBounds(type, checkTypeVariableBound); + } + + /** + * Resolves the type arguments of [node] and adds these to [arguments]. + * + * Returns [: true :] if the number of type arguments did not match the + * number of type variables. + */ + bool resolveTypeArguments( + MappingVisitor visitor, + TypeAnnotation node, + Link typeVariables, + LinkBuilder arguments) { + if (node.typeArguments == null) { + return false; + } + bool typeArgumentCountMismatch = false; + for (Link typeArguments = node.typeArguments.nodes; + !typeArguments.isEmpty; + typeArguments = typeArguments.tail) { + if (typeVariables != null && typeVariables.isEmpty) { + visitor.warning( + typeArguments.head, MessageKind.ADDITIONAL_TYPE_ARGUMENT); + typeArgumentCountMismatch = true; + } + DartType argType = resolveTypeAnnotation(visitor, typeArguments.head); + arguments.addLast(argType); + if (typeVariables != null && !typeVariables.isEmpty) { + typeVariables = typeVariables.tail; + } + } + if (typeVariables != null && !typeVariables.isEmpty) { + visitor.warning(node.typeArguments, + MessageKind.MISSING_TYPE_ARGUMENT); + typeArgumentCountMismatch = true; + } + return typeArgumentCountMismatch; + } +} + +/** + * Common supertype for resolver visitors that record resolutions in a + * [TreeElements] mapping. + */ +abstract class MappingVisitor extends CommonResolverVisitor { + final TreeElementMapping mapping; + final TypeResolver typeResolver; + /// The current enclosing element for the visited AST nodes. + Element get enclosingElement; + /// The current scope of the visitor. + Scope get scope; + + MappingVisitor(Compiler compiler, TreeElementMapping this.mapping) + : typeResolver = new TypeResolver(compiler), + super(compiler); + + Element useElement(Node node, Element element) { + if (element == null) return null; + return mapping[node] = element; + } + + DartType useType(Node annotation, DartType type) { + if (type != null) { + mapping.setType(annotation, type); + useElement(annotation, type.element); + } + return type; + } + + Element defineElement(Node node, Element element, + {bool doAddToScope: true}) { + invariant(node, element != null); + mapping[node] = element; + if (doAddToScope) { + Element existing = scope.add(element); + if (existing != element) { + reportDuplicateDefinition(node, element, existing); + } + } + return element; + } + + void reportDuplicateDefinition(/*Node|String*/ name, + Spannable definition, + Spannable existing) { + compiler.reportError(definition, + MessageKind.DUPLICATE_DEFINITION, {'name': name}); + compiler.reportInfo(existing, + MessageKind.EXISTING_DEFINITION, {'name': name}); + } +} + +/** + * Core implementation of resolution. + * + * Do not subclass or instantiate this class outside this library + * except for testing. + */ +class ResolverVisitor extends MappingVisitor { + /** + * The current enclosing element for the visited AST nodes. + * + * This field is updated when nested closures are visited. + */ + Element enclosingElement; + bool inInstanceContext; + bool inCheckContext; + bool inCatchBlock; + Scope scope; + ClassElement currentClass; + ExpressionStatement currentExpressionStatement; + bool sendIsMemberAccess = false; + StatementScope statementScope; + int allowedCategory = ElementCategory.VARIABLE | ElementCategory.FUNCTION + | ElementCategory.IMPLIES_TYPE; + + /** + * Record of argument nodes to JS_INTERCEPTOR_CONSTANT for deferred + * processing. + */ + Set argumentsToJsInterceptorConstant = null; + + /// When visiting the type declaration of the variable in a [ForIn] loop, + /// the initializer of the variable is implicit and we should not emit an + /// error when verifying that all final variables are initialized. + bool allowFinalWithoutInitializer = false; + + /// The nodes for which variable access and mutation must be registered in + /// order to determine when the static type of variables types is promoted. + Link promotionScope = const Link(); + + bool isPotentiallyMutableTarget(Element target) { + if (target == null) return false; + return (target.isVariable() || target.isParameter()) && + !(target.modifiers.isFinal() || target.modifiers.isConst()); + } + + // TODO(ahe): Find a way to share this with runtime implementation. + static final RegExp symbolValidationPattern = + new RegExp(r'^(?:[a-zA-Z$][a-zA-Z$0-9_]*\.)*(?:[a-zA-Z$][a-zA-Z$0-9_]*=?|' + r'-|' + r'unary-|' + r'\[\]=|' + r'~|' + r'==|' + r'\[\]|' + r'\*|' + r'/|' + r'%|' + r'~/|' + r'\+|' + r'<<|' + r'>>|' + r'>=|' + r'>|' + r'<=|' + r'<|' + r'&|' + r'\^|' + r'\|' + r')$'); + + ResolverVisitor(Compiler compiler, + Element element, + TreeElementMapping mapping) + : this.enclosingElement = element, + // When the element is a field, we are actually resolving its + // initial value, which should not have access to instance + // fields. + inInstanceContext = (element.isInstanceMember() && !element.isField()) + || element.isGenerativeConstructor(), + this.currentClass = element.isMember() ? element.getEnclosingClass() + : null, + this.statementScope = new StatementScope(), + scope = element.buildScope(), + // The type annotations on a typedef do not imply type checks. + // TODO(karlklose): clean this up (dartbug.com/8870). + inCheckContext = compiler.enableTypeAssertions && + !element.isLibrary() && + !element.isTypedef() && + !element.enclosingElement.isTypedef(), + inCatchBlock = false, + super(compiler, mapping); + + ResolutionEnqueuer get world => compiler.enqueuer.resolution; + + Element reportLookupErrorIfAny(Element result, Node node, String name) { + if (!Elements.isUnresolved(result)) { + if (!inInstanceContext && result.isInstanceMember()) { + compiler.reportError( + node, MessageKind.NO_INSTANCE_AVAILABLE, {'name': name}); + return new ErroneousElementX(MessageKind.NO_INSTANCE_AVAILABLE, + {'name': name}, + name, enclosingElement); + } else if (result.isAmbiguous()) { + AmbiguousElement ambiguous = result; + compiler.reportError( + node, ambiguous.messageKind, ambiguous.messageArguments); + ambiguous.diagnose(enclosingElement, compiler); + return new ErroneousElementX(ambiguous.messageKind, + ambiguous.messageArguments, + name, enclosingElement); + } + } + return result; + } + + // Create, or reuse an already created, statement element for a statement. + TargetElement getOrCreateTargetElement(Node statement) { + TargetElement element = mapping[statement]; + if (element == null) { + element = new TargetElementX(statement, + statementScope.nestingLevel, + enclosingElement); + mapping[statement] = element; + } + return element; + } + + doInCheckContext(action()) { + bool wasInCheckContext = inCheckContext; + inCheckContext = true; + var result = action(); + inCheckContext = wasInCheckContext; + return result; + } + + inStaticContext(action()) { + bool wasInstanceContext = inInstanceContext; + inInstanceContext = false; + var result = action(); + inInstanceContext = wasInstanceContext; + return result; + } + + doInPromotionScope(Node node, action()) { + promotionScope = promotionScope.prepend(node); + var result = action(); + promotionScope = promotionScope.tail; + return result; + } + + visitInStaticContext(Node node) { + inStaticContext(() => visit(node)); + } + + ErroneousElement warnAndCreateErroneousElement(Node node, + String name, + MessageKind kind, + [Map arguments = const {}]) { + compiler.reportWarning(node, kind, arguments); + return new ErroneousElementX(kind, arguments, name, enclosingElement); + } + + Element visitIdentifier(Identifier node) { + if (node.isThis()) { + if (!inInstanceContext) { + error(node, MessageKind.NO_INSTANCE_AVAILABLE, {'name': node}); + } + return null; + } else if (node.isSuper()) { + if (!inInstanceContext) error(node, MessageKind.NO_SUPER_IN_STATIC); + if ((ElementCategory.SUPER & allowedCategory) == 0) { + error(node, MessageKind.INVALID_USE_OF_SUPER); + } + return null; + } else { + String name = node.source; + Element element = lookupInScope(compiler, node, scope, name); + if (Elements.isUnresolved(element) && name == 'dynamic') { + element = compiler.dynamicClass; + } + element = reportLookupErrorIfAny(element, node, node.source); + if (element == null) { + if (!inInstanceContext) { + element = warnAndCreateErroneousElement( + node, node.source, MessageKind.CANNOT_RESOLVE, + {'name': node}); + compiler.backend.registerThrowNoSuchMethod(mapping); + } + } else if (element.isErroneous()) { + // Use the erroneous element. + } else { + if ((element.kind.category & allowedCategory) == 0) { + // TODO(ahe): Improve error message. Need UX input. + error(node, MessageKind.GENERIC, + {'text': "is not an expression $element"}); + } + } + if (!Elements.isUnresolved(element) && element.isClass()) { + ClassElement classElement = element; + classElement.ensureResolved(compiler); + } + return useElement(node, element); + } + } + + Element visitTypeAnnotation(TypeAnnotation node) { + DartType type = resolveTypeAnnotation(node); + if (type != null) { + if (inCheckContext) { + compiler.enqueuer.resolution.registerIsCheck(type, mapping); + } + return type.element; + } + return null; + } + + bool isNamedConstructor(Send node) => node.receiver != null; + + Selector getRedirectingThisOrSuperConstructorSelector(Send node) { + if (isNamedConstructor(node)) { + String constructorName = node.selector.asIdentifier().source; + return new Selector.callConstructor( + constructorName, + enclosingElement.getLibrary()); + } else { + return new Selector.callDefaultConstructor( + enclosingElement.getLibrary()); + } + } + + FunctionElement resolveConstructorRedirection(FunctionElement constructor) { + FunctionExpression node = constructor.parseNode(compiler); + + // A synthetic constructor does not have a node. + if (node == null) return null; + if (node.initializers == null) return null; + Link initializers = node.initializers.nodes; + if (!initializers.isEmpty && + Initializers.isConstructorRedirect(initializers.head)) { + Selector selector = + getRedirectingThisOrSuperConstructorSelector(initializers.head); + final ClassElement classElement = constructor.getEnclosingClass(); + return classElement.lookupConstructor(selector); + } + return null; + } + + void setupFunction(FunctionExpression node, FunctionElement function) { + Element enclosingElement = function.enclosingElement; + if (node.modifiers.isStatic() && + enclosingElement.kind != ElementKind.CLASS) { + compiler.reportError(node, MessageKind.ILLEGAL_STATIC); + } + + scope = new MethodScope(scope, function); + // Put the parameters in scope. + FunctionSignature functionParameters = function.functionSignature; + Link parameterNodes = (node.parameters == null) + ? const Link() : node.parameters.nodes; + functionParameters.forEachParameter((ParameterElement element) { + if (element == functionParameters.optionalParameters.head) { + NodeList nodes = parameterNodes.head; + parameterNodes = nodes.nodes; + } + visit(element.initializer); + VariableDefinitions variableDefinitions = parameterNodes.head; + Node parameterNode = variableDefinitions.definitions.nodes.head; + // Field parameters (this.x) are not visible inside the constructor. The + // fields they reference are visible, but must be resolved independently. + if (element.kind == ElementKind.FIELD_PARAMETER) { + useElement(parameterNode, element); + } else { + defineElement(parameterNode, element); + } + parameterNodes = parameterNodes.tail; + }); + addDeferredAction(enclosingElement, () { + functionParameters.forEachOptionalParameter((Element parameter) { + compiler.constantHandler.compileConstant(parameter); + }); + }); + if (inCheckContext) { + functionParameters.forEachParameter((Element element) { + compiler.enqueuer.resolution.registerIsCheck( + element.computeType(compiler), mapping); + }); + } + } + + visitCascade(Cascade node) { + visit(node.expression); + } + + visitCascadeReceiver(CascadeReceiver node) { + visit(node.expression); + } + + visitClassNode(ClassNode node) { + internalError(node, "shouldn't be called"); + } + + visitIn(Node node, Scope nestedScope) { + Scope oldScope = scope; + scope = nestedScope; + Element element = visit(node); + scope = oldScope; + return element; + } + + /** + * Introduces new default targets for break and continue + * before visiting the body of the loop + */ + visitLoopBodyIn(Node loop, Node body, Scope bodyScope) { + TargetElement element = getOrCreateTargetElement(loop); + statementScope.enterLoop(element); + visitIn(body, bodyScope); + statementScope.exitLoop(); + if (!element.isTarget) { + mapping.remove(loop); + } + } + + visitBlock(Block node) { + visitIn(node.statements, new BlockScope(scope)); + } + + visitDoWhile(DoWhile node) { + visitLoopBodyIn(node, node.body, new BlockScope(scope)); + visit(node.condition); + } + + visitEmptyStatement(EmptyStatement node) { } + + visitExpressionStatement(ExpressionStatement node) { + ExpressionStatement oldExpressionStatement = currentExpressionStatement; + currentExpressionStatement = node; + visit(node.expression); + currentExpressionStatement = oldExpressionStatement; + } + + visitFor(For node) { + Scope blockScope = new BlockScope(scope); + visitIn(node.initializer, blockScope); + visitIn(node.condition, blockScope); + visitIn(node.update, blockScope); + visitLoopBodyIn(node, node.body, blockScope); + } + + visitFunctionDeclaration(FunctionDeclaration node) { + assert(node.function.name != null); + visit(node.function); + FunctionElement functionElement = mapping[node.function]; + // TODO(floitsch): this might lead to two errors complaining about + // shadowing. + defineElement(node, functionElement); + } + + visitFunctionExpression(FunctionExpression node) { + visit(node.returnType); + String name; + if (node.name == null) { + name = ""; + } else { + name = node.name.asIdentifier().source; + } + FunctionElementX function = new FunctionElementX.fromNode( + name, node, ElementKind.FUNCTION, Modifiers.EMPTY, + enclosingElement); + function.functionSignatureCache = + SignatureResolver.analyze(compiler, node.parameters, node.returnType, + function, mapping); + Scope oldScope = scope; // The scope is modified by [setupFunction]. + setupFunction(node, function); + defineElement(node, function, doAddToScope: node.name != null); + + Element previousEnclosingElement = enclosingElement; + enclosingElement = function; + // Run the body in a fresh statement scope. + StatementScope oldStatementScope = statementScope; + statementScope = new StatementScope(); + visit(node.body); + statementScope = oldStatementScope; + + scope = oldScope; + enclosingElement = previousEnclosingElement; + + world.registerClosure(function, mapping); + world.registerInstantiatedClass(compiler.functionClass, mapping); + } + + visitIf(If node) { + doInPromotionScope(node.condition.expression, () => visit(node.condition)); + doInPromotionScope(node.thenPart, + () => visitIn(node.thenPart, new BlockScope(scope))); + visitIn(node.elsePart, new BlockScope(scope)); + } + + Element resolveSend(Send node) { + Selector selector = resolveSelector(node, null); + if (node.isSuperCall) mapping.superUses.add(node); + + if (node.receiver == null) { + // If this send is of the form "assert(expr);", then + // this is an assertion. + if (selector.isAssert()) { + if (selector.argumentCount != 1) { + error(node.selector, + MessageKind.WRONG_NUMBER_OF_ARGUMENTS_FOR_ASSERT, + {'argumentCount': selector.argumentCount}); + } else if (selector.namedArgumentCount != 0) { + error(node.selector, + MessageKind.ASSERT_IS_GIVEN_NAMED_ARGUMENTS, + {'argumentCount': selector.namedArgumentCount}); + } + return compiler.assertMethod; + } + + return node.selector.accept(this); + } + + var oldCategory = allowedCategory; + allowedCategory |= ElementCategory.PREFIX | ElementCategory.SUPER; + Element resolvedReceiver = visit(node.receiver); + allowedCategory = oldCategory; + + Element target; + String name = node.selector.asIdentifier().source; + if (identical(name, 'this')) { + // TODO(ahe): Why is this using GENERIC? + error(node.selector, MessageKind.GENERIC, + {'text': "expected an identifier"}); + } else if (node.isSuperCall) { + if (node.isOperator) { + if (isUserDefinableOperator(name)) { + name = selector.name; + } else { + error(node.selector, MessageKind.ILLEGAL_SUPER_SEND, {'name': name}); + } + } + if (!inInstanceContext) { + error(node.receiver, MessageKind.NO_INSTANCE_AVAILABLE, {'name': name}); + return null; + } + if (currentClass.supertype == null) { + // This is just to guard against internal errors, so no need + // for a real error message. + error(node.receiver, MessageKind.GENERIC, + {'text': "Object has no superclass"}); + } + // TODO(johnniwinther): Ensure correct behavior if currentClass is a + // patch. + target = currentClass.lookupSuperSelector(selector, compiler); + // [target] may be null which means invoking noSuchMethod on + // super. + if (target == null) { + target = warnAndCreateErroneousElement( + node, name, MessageKind.NO_SUCH_SUPER_MEMBER, + {'className': currentClass, 'memberName': name}); + // We still need to register the invocation, because we might + // call [:super.noSuchMethod:] which calls + // [JSInvocationMirror._invokeOn]. + world.registerDynamicInvocation(selector); + compiler.backend.registerSuperNoSuchMethod(mapping); + } + } else if (Elements.isUnresolved(resolvedReceiver)) { + return null; + } else if (resolvedReceiver.isClass()) { + ClassElement receiverClass = resolvedReceiver; + receiverClass.ensureResolved(compiler); + if (node.isOperator) { + // When the resolved receiver is a class, we can have two cases: + // 1) a static send: C.foo, or + // 2) an operator send, where the receiver is a class literal: 'C + 1'. + // The following code that looks up the selector on the resolved + // receiver will treat the second as the invocation of a static operator + // if the resolved receiver is not null. + return null; + } + target = receiverClass.lookupLocalMember(name); + if (target == null || target.isInstanceMember()) { + compiler.backend.registerThrowNoSuchMethod(mapping); + // TODO(johnniwinther): With the simplified [TreeElements] invariant, + // try to resolve injected elements if [currentClass] is in the patch + // library of [receiverClass]. + + // TODO(karlklose): this should be reported by the caller of + // [resolveSend] to select better warning messages for getters and + // setters. + MessageKind kind = (target == null) + ? MessageKind.MEMBER_NOT_FOUND + : MessageKind.MEMBER_NOT_STATIC; + return warnAndCreateErroneousElement(node, name, kind, + {'className': receiverClass.name, + 'memberName': name}); + } + } else if (identical(resolvedReceiver.kind, ElementKind.PREFIX)) { + PrefixElement prefix = resolvedReceiver; + target = prefix.lookupLocalMember(name); + if (Elements.isUnresolved(target)) { + compiler.backend.registerThrowNoSuchMethod(mapping); + return warnAndCreateErroneousElement( + node, name, MessageKind.NO_SUCH_LIBRARY_MEMBER, + {'libraryName': prefix.name, 'memberName': name}); + } else if (target.kind == ElementKind.CLASS) { + ClassElement classElement = target; + classElement.ensureResolved(compiler); + } + } + return target; + } + + static Selector computeSendSelector(Send node, + LibraryElement library, + Element element) { + // First determine if this is part of an assignment. + bool isSet = node.asSendSet() != null; + + if (node.isIndex) { + return isSet ? new Selector.indexSet() : new Selector.index(); + } + + if (node.isOperator) { + String source = node.selector.asOperator().source; + String string = source; + if (identical(string, '!') || + identical(string, '&&') || identical(string, '||') || + identical(string, 'is') || identical(string, 'as') || + identical(string, '?') || + identical(string, '>>>')) { + return null; + } + String op = source; + if (!isUserDefinableOperator(source)) { + op = Elements.mapToUserOperatorOrNull(source); + } + if (op == null) { + // Unsupported operator. An error has been reported during parsing. + return new Selector.call( + source, library, node.argumentsNode.slowLength(), []); + } + return node.arguments.isEmpty + ? new Selector.unaryOperator(op) + : new Selector.binaryOperator(op); + } + + Identifier identifier = node.selector.asIdentifier(); + if (node.isPropertyAccess) { + assert(!isSet); + return new Selector.getter(identifier.source, library); + } else if (isSet) { + return new Selector.setter(identifier.source, library); + } + + // Compute the arity and the list of named arguments. + int arity = 0; + List named = []; + for (Link link = node.argumentsNode.nodes; + !link.isEmpty; + link = link.tail) { + Expression argument = link.head; + NamedArgument namedArgument = argument.asNamedArgument(); + if (namedArgument != null) { + named.add(namedArgument.name.source); + } + arity++; + } + + if (element != null && element.isConstructor()) { + return new Selector.callConstructor( + element.name, library, arity, named); + } + + // If we're invoking a closure, we do not have an identifier. + return (identifier == null) + ? new Selector.callClosure(arity, named) + : new Selector.call(identifier.source, library, arity, named); + } + + Selector resolveSelector(Send node, Element element) { + LibraryElement library = enclosingElement.getLibrary(); + Selector selector = computeSendSelector(node, library, element); + if (selector != null) mapping.setSelector(node, selector); + return selector; + } + + void resolveArguments(NodeList list) { + if (list == null) return; + bool oldSendIsMemberAccess = sendIsMemberAccess; + sendIsMemberAccess = false; + Map seenNamedArguments = new Map(); + for (Link link = list.nodes; !link.isEmpty; link = link.tail) { + Expression argument = link.head; + visit(argument); + NamedArgument namedArgument = argument.asNamedArgument(); + if (namedArgument != null) { + String source = namedArgument.name.source; + if (seenNamedArguments.containsKey(source)) { + reportDuplicateDefinition( + source, + argument, + seenNamedArguments[source]); + } else { + seenNamedArguments[source] = namedArgument; + } + } else if (!seenNamedArguments.isEmpty) { + error(argument, MessageKind.INVALID_ARGUMENT_AFTER_NAMED); + } + } + sendIsMemberAccess = oldSendIsMemberAccess; + } + + visitSend(Send node) { + bool oldSendIsMemberAccess = sendIsMemberAccess; + sendIsMemberAccess = node.isPropertyAccess || node.isCall; + Element target; + if (node.isLogicalAnd) { + target = doInPromotionScope(node.receiver, () => resolveSend(node)); + } else { + target = resolveSend(node); + } + sendIsMemberAccess = oldSendIsMemberAccess; + + if (target != null + && target == compiler.mirrorSystemGetNameFunction + && !compiler.mirrorUsageAnalyzerTask.hasMirrorUsage(enclosingElement)) { + compiler.reportHint( + node.selector, MessageKind.STATIC_FUNCTION_BLOAT, + {'class': compiler.mirrorSystemClass.name, + 'name': compiler.mirrorSystemGetNameFunction.name}); + } + + if (!Elements.isUnresolved(target)) { + if (target.isAbstractField()) { + AbstractFieldElement field = target; + target = field.getter; + if (target == null && !inInstanceContext) { + compiler.backend.registerThrowNoSuchMethod(mapping); + target = + warnAndCreateErroneousElement(node.selector, field.name, + MessageKind.CANNOT_RESOLVE_GETTER); + } + } else if (target.isTypeVariable()) { + ClassElement cls = target.getEnclosingClass(); + assert(enclosingElement.getEnclosingClass() == cls); + compiler.backend.registerClassUsingVariableExpression(cls); + compiler.backend.registerTypeVariableExpression(mapping); + // Set the type of the node to [Type] to mark this send as a + // type variable expression. + mapping.setType(node, compiler.typeClass.computeType(compiler)); + world.registerTypeLiteral(target, mapping); + } else if (target.impliesType() && (!sendIsMemberAccess || node.isCall)) { + // Set the type of the node to [Type] to mark this send as a + // type literal. + mapping.setType(node, compiler.typeClass.computeType(compiler)); + world.registerTypeLiteral(target, mapping); + + // Don't try to make constants of calls to type literals. + analyzeConstant(node, isConst: !node.isCall); + } + if (isPotentiallyMutableTarget(target)) { + if (enclosingElement != target.enclosingElement) { + for (Node scope in promotionScope) { + mapping.setAccessedByClosureIn(scope, target, node); + } + } + } + } + + bool resolvedArguments = false; + if (node.isOperator) { + String operatorString = node.selector.asOperator().source; + if (identical(operatorString, 'is')) { + // TODO(johnniwinther): Use seen type tests to avoid registration of + // mutation/access to unpromoted variables. + DartType type = + resolveTypeAnnotation(node.typeAnnotationFromIsCheckOrCast); + if (type != null) { + compiler.enqueuer.resolution.registerIsCheck(type, mapping); + } + resolvedArguments = true; + } else if (identical(operatorString, 'as')) { + DartType type = resolveTypeAnnotation(node.arguments.head); + if (type != null) { + compiler.enqueuer.resolution.registerAsCheck(type, mapping); + } + resolvedArguments = true; + } else if (identical(operatorString, '&&')) { + doInPromotionScope(node.arguments.head, + () => resolveArguments(node.argumentsNode)); + resolvedArguments = true; + } + } + + if (!resolvedArguments) { + resolveArguments(node.argumentsNode); + } + + // If the selector is null, it means that we will not be generating + // code for this as a send. + Selector selector = mapping.getSelector(node); + if (selector == null) return null; + + if (node.isCall) { + if (Elements.isUnresolved(target) || + target.isGetter() || + target.isField() || + Elements.isClosureSend(node, target)) { + // If we don't know what we're calling or if we are calling a getter, + // we need to register that fact that we may be calling a closure + // with the same arguments. + Selector call = new Selector.callClosureFrom(selector); + world.registerDynamicInvocation(call); + } else if (target.impliesType()) { + // We call 'call()' on a Type instance returned from the reference to a + // class or typedef literal. We do not need to register this call as a + // dynamic invocation, because we statically know what the target is. + } else if (!selector.applies(target, compiler)) { + warnArgumentMismatch(node, target); + if (node.isSuperCall) { + // Similar to what we do when we can't find super via selector + // in [resolveSend] above, we still need to register the invocation, + // because we might call [:super.noSuchMethod:] which calls + // [JSInvocationMirror._invokeOn]. + world.registerDynamicInvocation(selector); + compiler.backend.registerSuperNoSuchMethod(mapping); + } + } + + if (target != null && target.isForeign(compiler)) { + if (selector.name == 'JS') { + world.registerJsCall(node, this); + } else if (selector.name == 'JS_INTERCEPTOR_CONSTANT') { + if (!node.argumentsNode.isEmpty) { + Node argument = node.argumentsNode.nodes.head; + if (argumentsToJsInterceptorConstant == null) { + argumentsToJsInterceptorConstant = new Set(); + } + argumentsToJsInterceptorConstant.add(argument); + } + } + } + } + + useElement(node, target); + registerSend(selector, target); + if (node.isPropertyAccess && Elements.isStaticOrTopLevelFunction(target)) { + world.registerGetOfStaticFunction(target.declaration); + } + return node.isPropertyAccess ? target : null; + } + + void warnArgumentMismatch(Send node, Element target) { + compiler.backend.registerThrowNoSuchMethod(mapping); + // TODO(karlklose): we can be more precise about the reason of the + // mismatch. + warning(node.argumentsNode, MessageKind.INVALID_ARGUMENTS, + {'methodName': target.name}); + } + + /// Callback for native enqueuer to parse a type. Returns [:null:] on error. + DartType resolveTypeFromString(Node node, String typeName) { + Element element = lookupInScope(compiler, node, + scope, typeName); + if (element == null) return null; + if (element is! ClassElement) return null; + ClassElement cls = element; + cls.ensureResolved(compiler); + return cls.computeType(compiler); + } + + visitSendSet(SendSet node) { + bool oldSendIsMemberAccess = sendIsMemberAccess; + sendIsMemberAccess = node.isPropertyAccess || node.isCall; + Element target = resolveSend(node); + sendIsMemberAccess = oldSendIsMemberAccess; + Element setter = target; + Element getter = target; + String operatorName = node.assignmentOperator.source; + String source = operatorName; + bool isComplex = !identical(source, '='); + if (!Elements.isUnresolved(target)) { + if (target.isAbstractField()) { + AbstractFieldElement field = target; + setter = field.setter; + getter = field.getter; + if (setter == null && !inInstanceContext) { + setter = warnAndCreateErroneousElement( + node.selector, field.name, MessageKind.CANNOT_RESOLVE_SETTER); + compiler.backend.registerThrowNoSuchMethod(mapping); + } + if (isComplex && getter == null && !inInstanceContext) { + getter = warnAndCreateErroneousElement( + node.selector, field.name, MessageKind.CANNOT_RESOLVE_GETTER); + compiler.backend.registerThrowNoSuchMethod(mapping); + } + } else if (target.impliesType()) { + setter = warnAndCreateErroneousElement( + node.selector, target.name, MessageKind.ASSIGNING_TYPE); + compiler.backend.registerThrowNoSuchMethod(mapping); + } else if (target.modifiers.isFinal() || + target.modifiers.isConst() || + (target.isFunction() && + Elements.isStaticOrTopLevelFunction(target) && + !target.isSetter())) { + if (target.isFunction()) { + setter = warnAndCreateErroneousElement( + node.selector, target.name, MessageKind.ASSIGNING_METHOD); + } else { + setter = warnAndCreateErroneousElement( + node.selector, target.name, MessageKind.CANNOT_RESOLVE_SETTER); + } + compiler.backend.registerThrowNoSuchMethod(mapping); + } + if (isPotentiallyMutableTarget(target)) { + mapping.setPotentiallyMutated(target, node); + if (enclosingElement != target.enclosingElement) { + mapping.registerPotentiallyMutatedInClosure(target, node); + } + for (Node scope in promotionScope) { + mapping.registerPotentiallyMutatedIn(scope, target, node); + } + } + } + + resolveArguments(node.argumentsNode); + + Selector selector = mapping.getSelector(node); + if (isComplex) { + Selector getterSelector; + if (selector.isSetter()) { + getterSelector = new Selector.getterFrom(selector); + } else { + assert(selector.isIndexSet()); + getterSelector = new Selector.index(); + } + registerSend(getterSelector, getter); + mapping.setGetterSelectorInComplexSendSet(node, getterSelector); + if (node.isSuperCall) { + getter = currentClass.lookupSuperSelector(getterSelector, compiler); + if (getter == null) { + target = warnAndCreateErroneousElement( + node, selector.name, MessageKind.NO_SUCH_SUPER_MEMBER, + {'className': currentClass, 'memberName': selector.name}); + compiler.backend.registerSuperNoSuchMethod(mapping); + } + } + useElement(node.selector, getter); + + // Make sure we include the + and - operators if we are using + // the ++ and -- ones. Also, if op= form is used, include op itself. + void registerBinaryOperator(String name) { + Selector binop = new Selector.binaryOperator(name); + world.registerDynamicInvocation(binop); + mapping.setOperatorSelectorInComplexSendSet(node, binop); + } + if (identical(source, '++')) { + registerBinaryOperator('+'); + world.registerInstantiatedClass(compiler.intClass, mapping); + } else if (identical(source, '--')) { + registerBinaryOperator('-'); + world.registerInstantiatedClass(compiler.intClass, mapping); + } else if (source.endsWith('=')) { + registerBinaryOperator(Elements.mapToUserOperator(operatorName)); + } + } + + registerSend(selector, setter); + return useElement(node, setter); + } + + void registerSend(Selector selector, Element target) { + if (target == null || target.isInstanceMember()) { + if (selector.isGetter()) { + world.registerDynamicGetter(selector); + } else if (selector.isSetter()) { + world.registerDynamicSetter(selector); + } else { + world.registerDynamicInvocation(selector); + } + } else if (Elements.isStaticOrTopLevel(target)) { + // Avoid registration of type variables since they are not analyzable but + // instead resolved through their enclosing type declaration. + if (!target.isTypeVariable()) { + // [target] might be the implementation element and only declaration + // elements may be registered. + world.registerStaticUse(target.declaration); + } + } + } + + visitLiteralInt(LiteralInt node) { + world.registerInstantiatedClass(compiler.intClass, mapping); + } + + visitLiteralDouble(LiteralDouble node) { + world.registerInstantiatedClass(compiler.doubleClass, mapping); + } + + visitLiteralBool(LiteralBool node) { + world.registerInstantiatedClass(compiler.boolClass, mapping); + } + + visitLiteralString(LiteralString node) { + world.registerInstantiatedClass(compiler.stringClass, mapping); + } + + visitLiteralNull(LiteralNull node) { + world.registerInstantiatedClass(compiler.nullClass, mapping); + } + + visitLiteralSymbol(LiteralSymbol node) { + world.registerInstantiatedClass(compiler.symbolClass, mapping); + world.registerStaticUse(compiler.symbolConstructor.declaration); + world.registerConstSymbol(node.slowNameString, mapping); + if (!validateSymbol(node, node.slowNameString, reportError: false)) { + compiler.reportError(node, + MessageKind.UNSUPPORTED_LITERAL_SYMBOL, + {'value': node.slowNameString}); + } + analyzeConstant(node); + } + + visitStringJuxtaposition(StringJuxtaposition node) { + world.registerInstantiatedClass(compiler.stringClass, mapping); + node.visitChildren(this); + } + + visitNodeList(NodeList node) { + for (Link link = node.nodes; !link.isEmpty; link = link.tail) { + visit(link.head); + } + } + + visitOperator(Operator node) { + internalError(node, 'operator'); + } + + visitRethrow(Rethrow node) { + if (!inCatchBlock) { + error(node, MessageKind.THROW_WITHOUT_EXPRESSION); + } + } + + visitReturn(Return node) { + if (node.isRedirectingFactoryBody) { + handleRedirectingFactoryBody(node); + } else { + Node expression = node.expression; + if (expression != null && + enclosingElement.isGenerativeConstructor()) { + // It is a compile-time error if a return statement of the form + // `return e;` appears in a generative constructor. (Dart Language + // Specification 13.12.) + compiler.reportError(expression, + MessageKind.CANNOT_RETURN_FROM_CONSTRUCTOR); + } + visit(node.expression); + } + } + + void handleRedirectingFactoryBody(Return node) { + final isSymbolConstructor = enclosingElement == compiler.symbolConstructor; + if (!enclosingElement.isFactoryConstructor()) { + compiler.reportError( + node, MessageKind.FACTORY_REDIRECTION_IN_NON_FACTORY); + compiler.reportHint( + enclosingElement, MessageKind.MISSING_FACTORY_KEYWORD); + } + FunctionElement constructor = enclosingElement; + bool isConstConstructor = constructor.modifiers.isConst(); + FunctionElement redirectionTarget = resolveRedirectingFactory( + node, inConstContext: isConstConstructor); + constructor.defaultImplementation = redirectionTarget; + useElement(node.expression, redirectionTarget); + if (Elements.isUnresolved(redirectionTarget)) { + compiler.backend.registerThrowNoSuchMethod(mapping); + return; + } else { + if (isConstConstructor && + !redirectionTarget.modifiers.isConst()) { + compiler.reportError(node, MessageKind.CONSTRUCTOR_IS_NOT_CONST); + } + if (redirectionTarget == constructor) { + compiler.reportError(node, MessageKind.CYCLIC_REDIRECTING_FACTORY); + return; + } + } + + // Check that the target constructor is type compatible with the + // redirecting constructor. + ClassElement targetClass = redirectionTarget.getEnclosingClass(); + InterfaceType type = mapping.getType(node.expression); + FunctionType targetType = redirectionTarget.computeType(compiler) + .subst(type.typeArguments, targetClass.typeVariables); + FunctionType constructorType = constructor.computeType(compiler); + bool isSubtype = compiler.types.isSubtype(targetType, constructorType); + if (!isSubtype) { + warning(node, MessageKind.NOT_ASSIGNABLE, + {'fromType': targetType, 'toType': constructorType}); + } + + FunctionSignature targetSignature = + redirectionTarget.computeSignature(compiler); + FunctionSignature constructorSignature = + constructor.computeSignature(compiler); + if (!targetSignature.isCompatibleWith(constructorSignature)) { + assert(!isSubtype); + compiler.backend.registerThrowNoSuchMethod(mapping); + } + + // Register a post process to check for cycles in the redirection chain and + // set the actual generative constructor at the end of the chain. + addDeferredAction(constructor, () { + compiler.resolver.resolveRedirectionChain(constructor, node); + }); + + world.registerStaticUse(redirectionTarget); + world.registerInstantiatedClass( + redirectionTarget.enclosingElement.declaration, mapping); + if (isSymbolConstructor) { + compiler.backend.registerSymbolConstructor(mapping); + } + } + + visitThrow(Throw node) { + compiler.backend.registerThrowExpression(mapping); + visit(node.expression); + } + + visitVariableDefinitions(VariableDefinitions node) { + DartType type; + if (node.type != null) { + type = resolveTypeAnnotation(node.type); + } else { + type = compiler.types.dynamicType; + } + VariableList variables = new VariableList.node(node, type); + VariableDefinitionsVisitor visitor = + new VariableDefinitionsVisitor(compiler, node, this, + ElementKind.VARIABLE, + variables); + + Modifiers modifiers = node.modifiers; + void reportExtraModifier(String modifier) { + Node modifierNode; + for (Link nodes = modifiers.nodes.nodes; + !nodes.isEmpty; + nodes = nodes.tail) { + if (modifier == nodes.head.asIdentifier().source) { + modifierNode = nodes.head; + break; + } + } + assert(modifierNode != null); + compiler.reportError(modifierNode, MessageKind.EXTRANEOUS_MODIFIER, + {'modifier': modifier}); + } + if (modifiers.isFinal() && (modifiers.isConst() || modifiers.isVar())) { + reportExtraModifier('final'); + } + if (modifiers.isVar() && (modifiers.isConst() || node.type != null)) { + reportExtraModifier('var'); + } + if (enclosingElement.isFunction()) { + if (modifiers.isAbstract()) { + reportExtraModifier('abstract'); + } + if (modifiers.isStatic()) { + reportExtraModifier('static'); + } + } + if (node.metadata != null) { + variables.metadata = + compiler.resolver.resolveMetadata(enclosingElement, node); + } + visitor.visit(node.definitions); + } + + visitWhile(While node) { + visit(node.condition); + visitLoopBodyIn(node, node.body, new BlockScope(scope)); + } + + visitParenthesizedExpression(ParenthesizedExpression node) { + bool oldSendIsMemberAccess = sendIsMemberAccess; + sendIsMemberAccess = false; + visit(node.expression); + sendIsMemberAccess = oldSendIsMemberAccess; + } + + visitNewExpression(NewExpression node) { + Node selector = node.send.selector; + FunctionElement constructor = resolveConstructor(node); + final bool isSymbolConstructor = constructor == compiler.symbolConstructor; + final bool isMirrorsUsedConstant = + node.isConst() && (constructor == compiler.mirrorsUsedConstructor); + resolveSelector(node.send, constructor); + resolveArguments(node.send.argumentsNode); + useElement(node.send, constructor); + if (Elements.isUnresolved(constructor)) return constructor; + Selector callSelector = mapping.getSelector(node.send); + if (!callSelector.applies(constructor, compiler)) { + warnArgumentMismatch(node.send, constructor); + compiler.backend.registerThrowNoSuchMethod(mapping); + } + + // [constructor] might be the implementation element + // and only declaration elements may be registered. + world.registerStaticUse(constructor.declaration); + ClassElement cls = constructor.getEnclosingClass(); + InterfaceType type = mapping.getType(node); + if (node.isConst() && type.containsTypeVariables) { + compiler.reportError(node.send.selector, + MessageKind.TYPE_VARIABLE_IN_CONSTANT); + } + world.registerInstantiatedType(type, mapping); + if (constructor.isFactoryConstructor() && !type.typeArguments.isEmpty) { + world.registerFactoryWithTypeArguments(mapping); + } + if (constructor.isGenerativeConstructor() && cls.isAbstract) { + warning(node, MessageKind.ABSTRACT_CLASS_INSTANTIATION); + compiler.backend.registerAbstractClassInstantiation(mapping); + } + + if (isSymbolConstructor) { + if (node.isConst()) { + Node argumentNode = node.send.arguments.head; + Constant name = compiler.constantHandler.compileNodeWithDefinitions( + argumentNode, mapping, isConst: true); + if (!name.isString) { + DartType type = name.computeType(compiler); + compiler.reportError(argumentNode, MessageKind.STRING_EXPECTED, + {'type': type}); + } else { + StringConstant stringConstant = name; + String nameString = stringConstant.toDartString().slowToString(); + if (validateSymbol(argumentNode, nameString)) { + world.registerConstSymbol(nameString, mapping); + } + } + } else { + if (!compiler.mirrorUsageAnalyzerTask.hasMirrorUsage( + enclosingElement)) { + compiler.reportHint( + node.newToken, MessageKind.NON_CONST_BLOAT, + {'name': compiler.symbolClass.name}); + } + world.registerNewSymbol(mapping); + } + } else if (isMirrorsUsedConstant) { + compiler.mirrorUsageAnalyzerTask.validate(node, mapping); + } + if (node.isConst()) { + analyzeConstant(node); + } + + return null; + } + + void checkConstMapKeysDontOverrideEquals(Spannable spannable, + MapConstant map) { + for (Constant key in map.keys.entries) { + if (!key.isObject) continue; + ObjectConstant objectConstant = key; + DartType keyType = objectConstant.type; + ClassElement cls = keyType.element; + if (cls == compiler.stringClass) continue; + Element equals = cls.lookupMember('=='); + if (equals.getEnclosingClass() != compiler.objectClass) { + compiler.reportError(spannable, + MessageKind.CONST_MAP_KEY_OVERRIDES_EQUALS, + {'type': keyType}); + } + } + } + + void analyzeConstant(Node node, {bool isConst: true}) { + addDeferredAction(enclosingElement, () { + Constant constant = compiler.constantHandler.compileNodeWithDefinitions( + node, mapping, isConst: isConst); + + if (isConst && constant != null && constant.isMap) { + checkConstMapKeysDontOverrideEquals(node, constant); + } + + // The type constant that is an argument to JS_INTERCEPTOR_CONSTANT names + // a class that will be instantiated outside the program by attaching a + // native class dispatch record referencing the interceptor. + if (argumentsToJsInterceptorConstant != null && + argumentsToJsInterceptorConstant.contains(node)) { + if (constant.isType) { + TypeConstant typeConstant = constant; + if (typeConstant.representedType is InterfaceType) { + world.registerInstantiatedType(typeConstant.representedType, + mapping); + } else { + compiler.reportError(node, + MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT); + } + } else { + compiler.reportError(node, + MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT); + } + } + }); + } + + bool validateSymbol(Node node, String name, {bool reportError: true}) { + if (name.isEmpty) return true; + if (name.startsWith('_')) { + if (reportError) { + compiler.reportError(node, MessageKind.PRIVATE_IDENTIFIER, + {'value': name}); + } + return false; + } + if (!symbolValidationPattern.hasMatch(name)) { + if (reportError) { + compiler.reportError(node, MessageKind.INVALID_SYMBOL, + {'value': name}); + } + return false; + } + return true; + } + + /** + * Try to resolve the constructor that is referred to by [node]. + * Note: this function may return an ErroneousFunctionElement instead of + * [:null:], if there is no corresponding constructor, class or library. + */ + FunctionElement resolveConstructor(NewExpression node) { + return node.accept(new ConstructorResolver(compiler, this)); + } + + FunctionElement resolveRedirectingFactory(Return node, + {bool inConstContext: false}) { + return node.accept(new ConstructorResolver(compiler, this, + inConstContext: inConstContext)); + } + + DartType resolveTypeAnnotation(TypeAnnotation node, + {bool malformedIsError: false, + bool deferredIsMalformed: true}) { + DartType type = typeResolver.resolveTypeAnnotation( + this, node, malformedIsError: malformedIsError, + deferredIsMalformed: deferredIsMalformed); + if (type == null) return null; + if (inCheckContext) { + compiler.enqueuer.resolution.registerIsCheck(type, mapping); + compiler.backend.registerRequiredType(type, enclosingElement); + } + return type; + } + + visitModifiers(Modifiers node) { + internalError(node, 'modifiers'); + } + + visitLiteralList(LiteralList node) { + bool oldSendIsMemberAccess = sendIsMemberAccess; + sendIsMemberAccess = false; + + NodeList arguments = node.typeArguments; + DartType typeArgument; + if (arguments != null) { + Link nodes = arguments.nodes; + if (nodes.isEmpty) { + // The syntax [: <>[] :] is not allowed. + error(arguments, MessageKind.MISSING_TYPE_ARGUMENT); + } else { + typeArgument = resolveTypeAnnotation(nodes.head); + for (nodes = nodes.tail; !nodes.isEmpty; nodes = nodes.tail) { + warning(nodes.head, MessageKind.ADDITIONAL_TYPE_ARGUMENT); + resolveTypeAnnotation(nodes.head); + } + } + } + DartType listType; + if (typeArgument != null) { + if (node.isConst() && typeArgument.containsTypeVariables) { + compiler.reportError(arguments.nodes.head, + MessageKind.TYPE_VARIABLE_IN_CONSTANT); + } + listType = new InterfaceType(compiler.listClass, + new Link.fromList([typeArgument])); + } else { + compiler.listClass.computeType(compiler); + listType = compiler.listClass.rawType; + } + mapping.setType(node, listType); + world.registerInstantiatedType(listType, mapping); + compiler.backend.registerRequiredType(listType, enclosingElement); + visit(node.elements); + if (node.isConst()) { + analyzeConstant(node); + } + + sendIsMemberAccess = false; + } + + visitConditional(Conditional node) { + doInPromotionScope(node.condition, () => visit(node.condition)); + doInPromotionScope(node.thenExpression, () => visit(node.thenExpression)); + visit(node.elseExpression); + } + + visitStringInterpolation(StringInterpolation node) { + world.registerInstantiatedClass(compiler.stringClass, mapping); + compiler.backend.registerStringInterpolation(mapping); + node.visitChildren(this); + } + + visitStringInterpolationPart(StringInterpolationPart node) { + registerImplicitInvocation('toString', 0); + node.visitChildren(this); + } + + visitBreakStatement(BreakStatement node) { + TargetElement target; + if (node.target == null) { + target = statementScope.currentBreakTarget(); + if (target == null) { + error(node, MessageKind.NO_BREAK_TARGET); + return; + } + target.isBreakTarget = true; + } else { + String labelName = node.target.source; + LabelElement label = statementScope.lookupLabel(labelName); + if (label == null) { + error(node.target, MessageKind.UNBOUND_LABEL, {'labelName': labelName}); + return; + } + target = label.target; + if (!target.statement.isValidBreakTarget()) { + error(node.target, MessageKind.INVALID_BREAK); + return; + } + label.setBreakTarget(); + mapping[node.target] = label; + } + if (mapping[node] != null) { + // TODO(ahe): I'm not sure why this node already has an element + // that is different from target. I will talk to Lasse and + // figure out what is going on. + mapping.remove(node); + } + mapping[node] = target; + } + + visitContinueStatement(ContinueStatement node) { + TargetElement target; + if (node.target == null) { + target = statementScope.currentContinueTarget(); + if (target == null) { + error(node, MessageKind.NO_CONTINUE_TARGET); + return; + } + target.isContinueTarget = true; + } else { + String labelName = node.target.source; + LabelElement label = statementScope.lookupLabel(labelName); + if (label == null) { + error(node.target, MessageKind.UNBOUND_LABEL, {'labelName': labelName}); + return; + } + target = label.target; + if (!target.statement.isValidContinueTarget()) { + error(node.target, MessageKind.INVALID_CONTINUE); + } + label.setContinueTarget(); + mapping[node.target] = label; + } + mapping[node] = target; + } + + registerImplicitInvocation(String name, int arity) { + Selector selector = new Selector.call(name, null, arity); + world.registerDynamicInvocation(selector); + } + + visitForIn(ForIn node) { + LibraryElement library = enclosingElement.getLibrary(); + mapping.setIteratorSelector(node, compiler.iteratorSelector); + world.registerDynamicGetter(compiler.iteratorSelector); + mapping.setCurrentSelector(node, compiler.currentSelector); + world.registerDynamicGetter(compiler.currentSelector); + mapping.setMoveNextSelector(node, compiler.moveNextSelector); + world.registerDynamicInvocation(compiler.moveNextSelector); + + visit(node.expression); + Scope blockScope = new BlockScope(scope); + Node declaration = node.declaredIdentifier; + + bool oldAllowFinalWithoutInitializer = allowFinalWithoutInitializer; + allowFinalWithoutInitializer = true; + visitIn(declaration, blockScope); + allowFinalWithoutInitializer = oldAllowFinalWithoutInitializer; + + Send send = declaration.asSend(); + VariableDefinitions variableDefinitions = + declaration.asVariableDefinitions(); + Element loopVariable; + Selector loopVariableSelector; + if (send != null) { + loopVariable = mapping[send]; + Identifier identifier = send.selector.asIdentifier(); + if (identifier == null) { + compiler.reportError(send.selector, MessageKind.INVALID_FOR_IN); + } else { + loopVariableSelector = new Selector.setter(identifier.source, library); + } + if (send.receiver != null) { + compiler.reportError(send.receiver, MessageKind.INVALID_FOR_IN); + } + } else if (variableDefinitions != null) { + Link nodes = variableDefinitions.definitions.nodes; + if (!nodes.tail.isEmpty) { + compiler.reportError(nodes.tail.head, MessageKind.INVALID_FOR_IN); + } + Node first = nodes.head; + Identifier identifier = first.asIdentifier(); + if (identifier == null) { + compiler.reportError(first, MessageKind.INVALID_FOR_IN); + } else { + loopVariableSelector = new Selector.setter(identifier.source, library); + loopVariable = mapping[identifier]; + } + } else { + compiler.reportError(declaration, MessageKind.INVALID_FOR_IN); + } + if (loopVariableSelector != null) { + mapping.setSelector(declaration, loopVariableSelector); + registerSend(loopVariableSelector, loopVariable); + } else { + // The selector may only be null if we reported an error. + assert(invariant(declaration, compiler.compilationFailed)); + } + if (loopVariable != null) { + // loopVariable may be null if it could not be resolved. + mapping[declaration] = loopVariable; + } + visitLoopBodyIn(node, node.body, blockScope); + } + + visitLabel(Label node) { + // Labels are handled by their containing statements/cases. + } + + visitLabeledStatement(LabeledStatement node) { + Statement body = node.statement; + TargetElement targetElement = getOrCreateTargetElement(body); + Map labelElements = {}; + for (Label label in node.labels) { + String labelName = label.labelName; + if (labelElements.containsKey(labelName)) continue; + LabelElement element = targetElement.addLabel(label, labelName); + labelElements[labelName] = element; + } + statementScope.enterLabelScope(labelElements); + visit(node.statement); + statementScope.exitLabelScope(); + labelElements.forEach((String labelName, LabelElement element) { + if (element.isTarget) { + mapping[element.label] = element; + } else { + warning(element.label, MessageKind.UNUSED_LABEL, + {'labelName': labelName}); + } + }); + if (!targetElement.isTarget && identical(mapping[body], targetElement)) { + // If the body is itself a break or continue for another target, it + // might have updated its mapping to the target it actually does target. + mapping.remove(body); + } + } + + visitLiteralMap(LiteralMap node) { + bool oldSendIsMemberAccess = sendIsMemberAccess; + sendIsMemberAccess = false; + + NodeList arguments = node.typeArguments; + DartType keyTypeArgument; + DartType valueTypeArgument; + if (arguments != null) { + Link nodes = arguments.nodes; + if (nodes.isEmpty) { + // The syntax [: <>{} :] is not allowed. + error(arguments, MessageKind.MISSING_TYPE_ARGUMENT); + } else { + keyTypeArgument = resolveTypeAnnotation(nodes.head); + nodes = nodes.tail; + if (nodes.isEmpty) { + warning(arguments, MessageKind.MISSING_TYPE_ARGUMENT); + } else { + valueTypeArgument = resolveTypeAnnotation(nodes.head); + for (nodes = nodes.tail; !nodes.isEmpty; nodes = nodes.tail) { + warning(nodes.head, MessageKind.ADDITIONAL_TYPE_ARGUMENT); + resolveTypeAnnotation(nodes.head); + } + } + } + } + DartType mapType; + if (valueTypeArgument != null) { + mapType = new InterfaceType(compiler.mapClass, + new Link.fromList([keyTypeArgument, valueTypeArgument])); + } else { + compiler.mapClass.computeType(compiler); + mapType = compiler.mapClass.rawType; + } + if (node.isConst() && mapType.containsTypeVariables) { + compiler.reportError(arguments, + MessageKind.TYPE_VARIABLE_IN_CONSTANT); + } + mapping.setType(node, mapType); + world.registerInstantiatedType(mapType, mapping); + if (node.isConst()) { + compiler.backend.registerConstantMap(mapping); + } + compiler.backend.registerRequiredType(mapType, enclosingElement); + node.visitChildren(this); + if (node.isConst()) { + analyzeConstant(node); + } + + sendIsMemberAccess = false; + } + + visitLiteralMapEntry(LiteralMapEntry node) { + node.visitChildren(this); + } + + visitNamedArgument(NamedArgument node) { + visit(node.expression); + } + + DartType typeOfConstant(Constant constant) { + if (constant.isInt) return compiler.intClass.rawType; + if (constant.isBool) return compiler.boolClass.rawType; + if (constant.isDouble) return compiler.doubleClass.rawType; + if (constant.isString) return compiler.stringClass.rawType; + if (constant.isNull) return compiler.nullClass.rawType; + if (constant.isFunction) return compiler.functionClass.rawType; + assert(constant.isObject); + ObjectConstant objectConstant = constant; + return objectConstant.type; + } + + bool overridesEquals(DartType type) { + ClassElement cls = type.element; + Element equals = cls.lookupMember('=='); + return equals.getEnclosingClass() != compiler.objectClass; + } + + void checkCaseExpressions(SwitchStatement node) { + TargetElement breakElement = getOrCreateTargetElement(node); + Map continueLabels = {}; + + Link cases = node.cases.nodes; + SwitchCase switchCase = cases.head; + CaseMatch firstCase = null; + DartType firstCaseType = null; + bool hasReportedProblem = false; + + for (Link cases = node.cases.nodes; + !cases.isEmpty; + cases = cases.tail) { + SwitchCase switchCase = cases.head; + + for (Node labelOrCase in switchCase.labelsAndCases) { + CaseMatch caseMatch = labelOrCase.asCaseMatch(); + if (caseMatch == null) continue; + + // Analyze the constant. + Constant constant = mapping.getConstant(caseMatch.expression); + assert(invariant(node, constant != null, + message: 'No constant computed for $node')); + + DartType caseType = typeOfConstant(constant); + + if (firstCaseType == null) { + firstCase = caseMatch; + firstCaseType = caseType; + + // We only report the bad type on the first class element. All others + // get a "type differs" error. + if (caseType.element == compiler.doubleClass) { + compiler.reportError(node, + MessageKind.SWITCH_CASE_VALUE_OVERRIDES_EQUALS, + {'type': "double"}); + } else if (caseType.element == compiler.functionClass) { + compiler.reportError(node, MessageKind.SWITCH_CASE_FORBIDDEN, + {'type': "Function"}); + } else if (constant.isObject && overridesEquals(caseType)) { + compiler.reportError(firstCase.expression, + MessageKind.SWITCH_CASE_VALUE_OVERRIDES_EQUALS, + {'type': caseType}); + } + } else { + if (caseType != firstCaseType) { + if (!hasReportedProblem) { + compiler.reportError( + node, + MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL, + {'type': firstCaseType}); + compiler.reportInfo( + firstCase.expression, + MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL_CASE, + {'type': firstCaseType}); + hasReportedProblem = true; + } + compiler.reportInfo( + caseMatch.expression, + MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL_CASE, + {'type': caseType}); + } + } + } + } + } + + visitSwitchStatement(SwitchStatement node) { + node.expression.accept(this); + + TargetElement breakElement = getOrCreateTargetElement(node); + Map continueLabels = {}; + Link cases = node.cases.nodes; + while (!cases.isEmpty) { + SwitchCase switchCase = cases.head; + for (Node labelOrCase in switchCase.labelsAndCases) { + CaseMatch caseMatch = labelOrCase.asCaseMatch(); + if (caseMatch != null) { + analyzeConstant(caseMatch.expression); + continue; + } + Label label = labelOrCase; + String labelName = label.labelName; + + LabelElement existingElement = continueLabels[labelName]; + if (existingElement != null) { + // It's an error if the same label occurs twice in the same switch. + compiler.reportError( + label, + MessageKind.DUPLICATE_LABEL, {'labelName': labelName}); + compiler.reportInfo( + existingElement.label, + MessageKind.EXISTING_LABEL, {'labelName': labelName}); + } else { + // It's only a warning if it shadows another label. + existingElement = statementScope.lookupLabel(labelName); + if (existingElement != null) { + compiler.reportWarning( + label, + MessageKind.DUPLICATE_LABEL, {'labelName': labelName}); + compiler.reportInfo( + existingElement.label, + MessageKind.EXISTING_LABEL, {'labelName': labelName}); + } + } + + TargetElement targetElement = getOrCreateTargetElement(switchCase); + LabelElement labelElement = targetElement.addLabel(label, labelName); + mapping[label] = labelElement; + continueLabels[labelName] = labelElement; + } + cases = cases.tail; + // Test that only the last case, if any, is a default case. + if (switchCase.defaultKeyword != null && !cases.isEmpty) { + error(switchCase, MessageKind.INVALID_CASE_DEFAULT); + } + } + + addDeferredAction(enclosingElement, () { + checkCaseExpressions(node); + }); + + statementScope.enterSwitch(breakElement, continueLabels); + node.cases.accept(this); + statementScope.exitSwitch(); + + // Clean-up unused labels. + continueLabels.forEach((String key, LabelElement label) { + if (!label.isContinueTarget) { + TargetElement targetElement = label.target; + SwitchCase switchCase = targetElement.statement; + mapping.remove(switchCase); + mapping.remove(label.label); + } + }); + // TODO(15575): We should warn if we can detect a fall through + // error. + compiler.backend.registerFallThroughError(mapping); + } + + visitSwitchCase(SwitchCase node) { + node.labelsAndCases.accept(this); + visitIn(node.statements, new BlockScope(scope)); + } + + visitCaseMatch(CaseMatch node) { + visit(node.expression); + } + + visitTryStatement(TryStatement node) { + visit(node.tryBlock); + if (node.catchBlocks.isEmpty && node.finallyBlock == null) { + error(node.getEndToken().next, MessageKind.NO_CATCH_NOR_FINALLY); + } + visit(node.catchBlocks); + visit(node.finallyBlock); + } + + visitCatchBlock(CatchBlock node) { + compiler.backend.registerCatchStatement(world, mapping); + // Check that if catch part is present, then + // it has one or two formal parameters. + VariableDefinitions exceptionDefinition; + VariableDefinitions stackTraceDefinition; + if (node.formals != null) { + Link formalsToProcess = node.formals.nodes; + if (formalsToProcess.isEmpty) { + error(node, MessageKind.EMPTY_CATCH_DECLARATION); + } else { + exceptionDefinition = formalsToProcess.head.asVariableDefinitions(); + formalsToProcess = formalsToProcess.tail; + if (!formalsToProcess.isEmpty) { + stackTraceDefinition = formalsToProcess.head.asVariableDefinitions(); + formalsToProcess = formalsToProcess.tail; + if (!formalsToProcess.isEmpty) { + for (Node extra in formalsToProcess) { + error(extra, MessageKind.EXTRA_CATCH_DECLARATION); + } + } + compiler.backend.registerStackTraceInCatch(mapping); + } + } + + // Check that the formals aren't optional and that they have no + // modifiers or type. + for (Link link = node.formals.nodes; + !link.isEmpty; + link = link.tail) { + // If the formal parameter is a node list, it means that it is a + // sequence of optional parameters. + NodeList nodeList = link.head.asNodeList(); + if (nodeList != null) { + error(nodeList, MessageKind.OPTIONAL_PARAMETER_IN_CATCH); + } else { + VariableDefinitions declaration = link.head; + for (Node modifier in declaration.modifiers.nodes) { + error(modifier, MessageKind.PARAMETER_WITH_MODIFIER_IN_CATCH); + } + TypeAnnotation type = declaration.type; + if (type != null) { + error(type, MessageKind.PARAMETER_WITH_TYPE_IN_CATCH); + } + } + } + } + + Scope blockScope = new BlockScope(scope); + doInCheckContext(() => visitIn(node.type, blockScope)); + visitIn(node.formals, blockScope); + var oldInCatchBlock = inCatchBlock; + inCatchBlock = true; + visitIn(node.block, blockScope); + inCatchBlock = oldInCatchBlock; + + if (node.type != null && exceptionDefinition != null) { + DartType exceptionType = mapping.getType(node.type); + Node exceptionVariable = exceptionDefinition.definitions.nodes.head; + VariableElementX exceptionElement = mapping[exceptionVariable]; + exceptionElement.variables.type = exceptionType; + } + if (stackTraceDefinition != null) { + Node stackTraceVariable = stackTraceDefinition.definitions.nodes.head; + VariableElementX stackTraceElement = mapping[stackTraceVariable]; + world.registerInstantiatedClass(compiler.stackTraceClass, mapping); + stackTraceElement.variables.type = compiler.stackTraceClass.rawType; + } + } + + visitTypedef(Typedef node) { + internalError(node, 'typedef'); + } +} + +class TypeDefinitionVisitor extends MappingVisitor { + Scope scope; + final TypeDeclarationElement enclosingElement; + TypeDeclarationElement get element => enclosingElement; + + TypeDefinitionVisitor(Compiler compiler, + TypeDeclarationElement element, + TreeElementMapping mapping) + : this.enclosingElement = element, + scope = Scope.buildEnclosingScope(element), + super(compiler, mapping); + + DartType get objectType => compiler.objectClass.rawType; + + void resolveTypeVariableBounds(NodeList node) { + if (node == null) return; + + var nameSet = new Setlet(); + // Resolve the bounds of type variables. + Link typeLink = element.typeVariables; + Link nodeLink = node.nodes; + while (!nodeLink.isEmpty) { + TypeVariableType typeVariable = typeLink.head; + String typeName = typeVariable.name; + TypeVariable typeNode = nodeLink.head; + useType(typeNode, typeVariable); + if (nameSet.contains(typeName)) { + error(typeNode, MessageKind.DUPLICATE_TYPE_VARIABLE_NAME, + {'typeVariableName': typeName}); + } + nameSet.add(typeName); + + TypeVariableElementX variableElement = typeVariable.element; + if (typeNode.bound != null) { + DartType boundType = typeResolver.resolveTypeAnnotation( + this, typeNode.bound); + variableElement.boundCache = boundType; + + void checkTypeVariableBound() { + Link seenTypeVariables = + const Link(); + seenTypeVariables = seenTypeVariables.prepend(variableElement); + DartType bound = boundType; + while (bound.element.isTypeVariable()) { + TypeVariableElement element = bound.element; + if (seenTypeVariables.contains(element)) { + if (identical(element, variableElement)) { + // Only report an error on the checked type variable to avoid + // generating multiple errors for the same cyclicity. + warning(typeNode.name, MessageKind.CYCLIC_TYPE_VARIABLE, + {'typeVariableName': variableElement.name}); + } + break; + } + seenTypeVariables = seenTypeVariables.prepend(element); + bound = element.bound; + } + } + addDeferredAction(element, checkTypeVariableBound); + } else { + variableElement.boundCache = objectType; + } + nodeLink = nodeLink.tail; + typeLink = typeLink.tail; + } + assert(typeLink.isEmpty); + } +} + +class TypedefResolverVisitor extends TypeDefinitionVisitor { + TypedefElement get element => enclosingElement; + + TypedefResolverVisitor(Compiler compiler, + TypedefElement typedefElement, + TreeElementMapping mapping) + : super(compiler, typedefElement, mapping); + + visitTypedef(Typedef node) { + TypedefType type = element.computeType(compiler); + scope = new TypeDeclarationScope(scope, element); + resolveTypeVariableBounds(node.typeParameters); + + FunctionSignature signature = SignatureResolver.analyze( + compiler, node.formals, node.returnType, element, mapping, + defaultValuesError: MessageKind.TYPEDEF_FORMAL_WITH_DEFAULT); + element.functionSignature = signature; + + scope = new MethodScope(scope, element); + signature.forEachParameter((Element element) { + defineElement(element.parseNode(compiler), element); + }); + + element.alias = signature.type; + + void checkCyclicReference() { + element.checkCyclicReference(compiler); + } + addDeferredAction(element, checkCyclicReference); + } +} + +// TODO(johnniwinther): Replace with a traversal on the AST when the type +// annotations in typedef alias are stored in a [TreeElements] mapping. +class TypedefCyclicVisitor extends DartTypeVisitor { + final Compiler compiler; + final TypedefElementX element; + bool hasCyclicReference = false; + + Link seenTypedefs = const Link(); + + int seenTypedefsCount = 0; + + Link seenTypeVariables = + const Link(); + + TypedefCyclicVisitor(Compiler this.compiler, TypedefElement this.element); + + visitType(DartType type, _) { + // Do nothing. + } + + visitTypedefType(TypedefType type, _) { + TypedefElement typedefElement = type.element; + if (seenTypedefs.contains(typedefElement)) { + if (!hasCyclicReference && identical(element, typedefElement)) { + // Only report an error on the checked typedef to avoid generating + // multiple errors for the same cyclicity. + hasCyclicReference = true; + if (seenTypedefsCount == 1) { + // Direct cyclicity. + compiler.reportError(element, + MessageKind.CYCLIC_TYPEDEF, + {'typedefName': element.name}); + } else if (seenTypedefsCount == 2) { + // Cyclicity through one other typedef. + compiler.reportError(element, + MessageKind.CYCLIC_TYPEDEF_ONE, + {'typedefName': element.name, + 'otherTypedefName': seenTypedefs.head.name}); + } else { + // Cyclicity through more than one other typedef. + for (TypedefElement cycle in seenTypedefs) { + if (!identical(typedefElement, cycle)) { + compiler.reportError(element, + MessageKind.CYCLIC_TYPEDEF_ONE, + {'typedefName': element.name, + 'otherTypedefName': cycle.name}); + } + } + } + ErroneousElementX erroneousElement = new ErroneousElementX( + MessageKind.CYCLIC_TYPEDEF, + {'typedefName': element.name}, + element.name, element); + element.alias = + new MalformedType(erroneousElement, typedefElement.alias); + element.hasBeenCheckedForCycles = true; + } + } else { + seenTypedefs = seenTypedefs.prepend(typedefElement); + seenTypedefsCount++; + type.visitChildren(this, null); + typedefElement.alias.accept(this, null); + seenTypedefs = seenTypedefs.tail; + seenTypedefsCount--; + } + } + + visitFunctionType(FunctionType type, _) { + type.visitChildren(this, null); + } + + visitInterfaceType(InterfaceType type, _) { + type.visitChildren(this, null); + } + + visitTypeVariableType(TypeVariableType type, _) { + TypeVariableElement typeVariableElement = type.element; + if (seenTypeVariables.contains(typeVariableElement)) { + // Avoid running in cycles on cyclic type variable bounds. + // Cyclicity is reported elsewhere. + return; + } + seenTypeVariables = seenTypeVariables.prepend(typeVariableElement); + typeVariableElement.bound.accept(this, null); + seenTypeVariables = seenTypeVariables.tail; + } +} + +/** + * The implementation of [ResolverTask.resolveClass]. + * + * This visitor has to be extra careful as it is building the basic + * element information, and cannot safely look at other elements as + * this may lead to cycles. + * + * This visitor can assume that the supertypes have already been + * resolved, but it cannot call [ResolverTask.resolveClass] directly + * or indirectly (through [ClassElement.ensureResolved]) for any other + * types. + */ +class ClassResolverVisitor extends TypeDefinitionVisitor { + ClassElement get element => enclosingElement; + + ClassResolverVisitor(Compiler compiler, + ClassElement classElement, + TreeElementMapping mapping) + : super(compiler, classElement, mapping); + + DartType visitClassNode(ClassNode node) { + invariant(node, element != null); + invariant(element, element.resolutionState == STATE_STARTED, + message: () => 'cyclic resolution of class $element'); + + InterfaceType type = element.computeType(compiler); + scope = new TypeDeclarationScope(scope, element); + // TODO(ahe): It is not safe to call resolveTypeVariableBounds yet. + // As a side-effect, this may get us back here trying to + // resolve this class again. + resolveTypeVariableBounds(node.typeParameters); + + // Setup the supertype for the element (if there is a cycle in the + // class hierarchy, it has already been set to Object). + if (element.supertype == null && node.superclass != null) { + MixinApplication superMixin = node.superclass.asMixinApplication(); + if (superMixin != null) { + DartType supertype = resolveSupertype(element, superMixin.superclass); + Link link = superMixin.mixins.nodes; + while (!link.isEmpty) { + supertype = applyMixin(supertype, + checkMixinType(link.head), link.head); + link = link.tail; + } + element.supertype = supertype; + } else { + element.supertype = resolveSupertype(element, node.superclass); + } + } + // If the super type isn't specified, we provide a default. The language + // specifies [Object] but the backend can pick a specific 'implementation' + // of Object - the JavaScript backend chooses between Object and + // Interceptor. + if (element.supertype == null) { + ClassElement superElement = compiler.backend.defaultSuperclass(element); + // Avoid making the superclass (usually Object) extend itself. + if (element != superElement) { + if (superElement == null) { + compiler.internalError(node, + "Cannot resolve default superclass for $element."); + } else { + superElement.ensureResolved(compiler); + } + element.supertype = superElement.computeType(compiler); + } + } + + if (element.interfaces == null) { + element.interfaces = resolveInterfaces(node.interfaces, node.superclass); + } else { + assert(invariant(element, element.hasIncompleteHierarchy)); + } + calculateAllSupertypes(element); + + if (!element.hasConstructor) { + Element superMember = element.superclass.localLookup(''); + if (superMember == null || !superMember.isGenerativeConstructor()) { + MessageKind kind = MessageKind.CANNOT_FIND_CONSTRUCTOR; + Map arguments = {'constructorName': ''}; + // TODO(ahe): Why is this a compile-time error? Or if it is an error, + // why do we bother to registerThrowNoSuchMethod below? + compiler.reportError(node, kind, arguments); + superMember = new ErroneousElementX( + kind, arguments, '', element); + compiler.backend.registerThrowNoSuchMethod(mapping); + } else { + Selector callToMatch = new Selector.call("", element.getLibrary(), 0); + if (!callToMatch.applies(superMember, compiler)) { + MessageKind kind = MessageKind.NO_MATCHING_CONSTRUCTOR_FOR_IMPLICIT; + compiler.reportError(node, kind); + superMember = new ErroneousElementX(kind, {}, '', element); + } + } + FunctionElement constructor = + new SynthesizedConstructorElementX.forDefault(superMember, element); + element.setDefaultConstructor(constructor, compiler); + } + return element.computeType(compiler); + } + + /// Resolves the mixed type for [mixinNode] and checks that the the mixin type + /// is a valid, non-blacklisted interface type. The mixin type is returned. + DartType checkMixinType(TypeAnnotation mixinNode) { + DartType mixinType = resolveType(mixinNode); + if (isBlackListed(mixinType)) { + compiler.reportError(mixinNode, + MessageKind.CANNOT_MIXIN, {'type': mixinType}); + } else if (mixinType.kind == TypeKind.TYPE_VARIABLE) { + compiler.reportError(mixinNode, MessageKind.CLASS_NAME_EXPECTED); + } else if (mixinType.kind == TypeKind.MALFORMED_TYPE) { + compiler.reportError(mixinNode, MessageKind.CANNOT_MIXIN_MALFORMED); + } + return mixinType; + } + + DartType visitNamedMixinApplication(NamedMixinApplication node) { + invariant(node, element != null); + invariant(element, element.resolutionState == STATE_STARTED, + message: () => 'cyclic resolution of class $element'); + + if (identical(node.classKeyword.stringValue, 'typedef')) { + // TODO(aprelev@gmail.com): Remove this deprecation diagnostic + // together with corresponding TODO in parser.dart. + compiler.reportWarning(node.classKeyword, + MessageKind.DEPRECATED_TYPEDEF_MIXIN_SYNTAX); + } + + InterfaceType type = element.computeType(compiler); + scope = new TypeDeclarationScope(scope, element); + resolveTypeVariableBounds(node.typeParameters); + + // Generate anonymous mixin application elements for the + // intermediate mixin applications (excluding the last). + DartType supertype = resolveSupertype(element, node.superclass); + Link link = node.mixins.nodes; + while (!link.tail.isEmpty) { + supertype = applyMixin(supertype, checkMixinType(link.head), link.head); + link = link.tail; + } + doApplyMixinTo(element, supertype, checkMixinType(link.head)); + return element.computeType(compiler); + } + + DartType applyMixin(DartType supertype, DartType mixinType, Node node) { + String superName = supertype.name; + String mixinName = mixinType.name; + MixinApplicationElementX mixinApplication = new MixinApplicationElementX( + "${superName}+${mixinName}", + element.getCompilationUnit(), + compiler.getNextFreeClassId(), + node, + new Modifiers.withFlags(new NodeList.empty(), Modifiers.FLAG_ABSTRACT)); + // Create synthetic type variables for the mixin application. + LinkBuilder typeVariablesBuilder = new LinkBuilder(); + element.typeVariables.forEach((TypeVariableType type) { + TypeVariableElementX typeVariableElement = new TypeVariableElementX( + type.name, mixinApplication, type.element.parseNode(compiler)); + TypeVariableType typeVariable = new TypeVariableType(typeVariableElement); + typeVariablesBuilder.addLast(typeVariable); + }); + Link typeVariables = typeVariablesBuilder.toLink(); + // Setup bounds on the synthetic type variables. + Link link = typeVariables; + element.typeVariables.forEach((TypeVariableType type) { + TypeVariableType typeVariable = link.head; + TypeVariableElementX typeVariableElement = typeVariable.element; + typeVariableElement.typeCache = typeVariable; + typeVariableElement.boundCache = + type.element.bound.subst(typeVariables, element.typeVariables); + link = link.tail; + }); + // Setup this and raw type for the mixin application. + mixinApplication.computeThisAndRawType(compiler, typeVariables); + // Substitute in synthetic type variables in super and mixin types. + supertype = supertype.subst(typeVariables, element.typeVariables); + mixinType = mixinType.subst(typeVariables, element.typeVariables); + + doApplyMixinTo(mixinApplication, supertype, mixinType); + mixinApplication.resolutionState = STATE_DONE; + mixinApplication.supertypeLoadState = STATE_DONE; + // Replace the synthetic type variables by the original type variables in + // the returned type (which should be the type actually extended). + InterfaceType mixinThisType = mixinApplication.computeType(compiler); + return mixinThisType.subst(element.typeVariables, + mixinThisType.typeArguments); + } + + bool isDefaultConstructor(FunctionElement constructor) { + return constructor.name == '' && + constructor.computeSignature(compiler).parameterCount == 0; + } + + FunctionElement createForwardingConstructor(FunctionElement target, + ClassElement enclosing) { + return new SynthesizedConstructorElementX( + target.name, target, enclosing, false); + } + + void doApplyMixinTo(MixinApplicationElementX mixinApplication, + DartType supertype, + DartType mixinType) { + Node node = mixinApplication.parseNode(compiler); + + if (mixinApplication.supertype != null) { + // [supertype] is not null if there was a cycle. + assert(invariant(node, compiler.compilationFailed)); + supertype = mixinApplication.supertype; + assert(invariant(node, supertype.element == compiler.objectClass)); + } else { + mixinApplication.supertype = supertype; + } + + // Named mixin application may have an 'implements' clause. + NamedMixinApplication namedMixinApplication = + node.asNamedMixinApplication(); + Link interfaces = (namedMixinApplication != null) + ? resolveInterfaces(namedMixinApplication.interfaces, + namedMixinApplication.superclass) + : const Link(); + + // The class that is the result of a mixin application implements + // the interface of the class that was mixed in so always prepend + // that to the interface list. + if (mixinApplication.interfaces == null) { + if (mixinType.kind == TypeKind.INTERFACE) { + // Avoid malformed types in the interfaces. + interfaces = interfaces.prepend(mixinType); + } + mixinApplication.interfaces = interfaces; + } else { + assert(invariant(mixinApplication, + mixinApplication.hasIncompleteHierarchy)); + } + + ClassElement superclass = supertype.element; + if (mixinType.kind != TypeKind.INTERFACE) { + mixinApplication.hasIncompleteHierarchy = true; + mixinApplication.allSupertypesAndSelf = superclass.allSupertypesAndSelf; + return; + } + + assert(mixinApplication.mixinType == null); + mixinApplication.mixinType = resolveMixinFor(mixinApplication, mixinType); + + // Create forwarding constructors for constructor defined in the superclass + // because they are now hidden by the mixin application. + superclass.forEachLocalMember((Element member) { + if (!member.isGenerativeConstructor()) return; + FunctionElement forwarder = + createForwardingConstructor(member, mixinApplication); + mixinApplication.addConstructor(forwarder); + }); + calculateAllSupertypes(mixinApplication); + } + + InterfaceType resolveMixinFor(MixinApplicationElement mixinApplication, + DartType mixinType) { + ClassElement mixin = mixinType.element; + mixin.ensureResolved(compiler); + + // Check for cycles in the mixin chain. + ClassElement previous = mixinApplication; // For better error messages. + ClassElement current = mixin; + while (current != null && current.isMixinApplication) { + MixinApplicationElement currentMixinApplication = current; + if (currentMixinApplication == mixinApplication) { + compiler.reportError( + mixinApplication, MessageKind.ILLEGAL_MIXIN_CYCLE, + {'mixinName1': current.name, 'mixinName2': previous.name}); + // We have found a cycle in the mixin chain. Return null as + // the mixin for this application to avoid getting into + // infinite recursion when traversing members. + return null; + } + previous = current; + current = currentMixinApplication.mixin; + } + compiler.world.registerMixinUse(mixinApplication, mixin); + return mixinType; + } + + DartType resolveType(TypeAnnotation node) { + return typeResolver.resolveTypeAnnotation(this, node); + } + + DartType resolveSupertype(ClassElement cls, TypeAnnotation superclass) { + DartType supertype = resolveType(superclass); + if (supertype != null) { + if (identical(supertype.kind, TypeKind.MALFORMED_TYPE)) { + compiler.reportError(superclass, MessageKind.CANNOT_EXTEND_MALFORMED); + return objectType; + } else if (!identical(supertype.kind, TypeKind.INTERFACE)) { + compiler.reportError(superclass.typeName, + MessageKind.CLASS_NAME_EXPECTED); + return objectType; + } else if (isBlackListed(supertype)) { + compiler.reportError(superclass, MessageKind.CANNOT_EXTEND, + {'type': supertype}); + return objectType; + } + } + return supertype; + } + + Link resolveInterfaces(NodeList interfaces, Node superclass) { + Link result = const Link(); + if (interfaces == null) return result; + for (Link link = interfaces.nodes; !link.isEmpty; link = link.tail) { + DartType interfaceType = resolveType(link.head); + if (interfaceType != null) { + if (identical(interfaceType.kind, TypeKind.MALFORMED_TYPE)) { + compiler.reportError(superclass, + MessageKind.CANNOT_IMPLEMENT_MALFORMED); + } else if (!identical(interfaceType.kind, TypeKind.INTERFACE)) { + // TODO(johnniwinther): Handle dynamic. + TypeAnnotation typeAnnotation = link.head; + error(typeAnnotation.typeName, MessageKind.CLASS_NAME_EXPECTED); + } else { + if (interfaceType == element.supertype) { + compiler.reportError( + superclass, + MessageKind.DUPLICATE_EXTENDS_IMPLEMENTS, + {'type': interfaceType}); + compiler.reportError( + link.head, + MessageKind.DUPLICATE_EXTENDS_IMPLEMENTS, + {'type': interfaceType}); + } + if (result.contains(interfaceType)) { + compiler.reportError( + link.head, + MessageKind.DUPLICATE_IMPLEMENTS, + {'type': interfaceType}); + } + result = result.prepend(interfaceType); + if (isBlackListed(interfaceType)) { + error(link.head, MessageKind.CANNOT_IMPLEMENT, + {'type': interfaceType}); + } + } + } + } + return result; + } + + /** + * Compute the list of all supertypes. + * + * The elements of this list are ordered as follows: first the supertype that + * the class extends, then the implemented interfaces, and then the supertypes + * of these. The class [Object] appears only once, at the end of the list. + * + * For example, for a class `class C extends S implements I1, I2`, we compute + * supertypes(C) = [S, I1, I2] ++ supertypes(S) ++ supertypes(I1) + * ++ supertypes(I2), + * where ++ stands for list concatenation. + * + * This order makes sure that if a class implements an interface twice with + * different type arguments, the type used in the most specific class comes + * first. + */ + void calculateAllSupertypes(BaseClassElementX cls) { + if (cls.allSupertypesAndSelf != null) return; + final DartType supertype = cls.supertype; + if (supertype != null) { + OrderedTypeSetBuilder allSupertypes = new OrderedTypeSetBuilder(cls); + // TODO(15296): Collapse these iterations to one when the order is not + // needed. + allSupertypes.add(compiler, supertype); + for (Link interfaces = cls.interfaces; + !interfaces.isEmpty; + interfaces = interfaces.tail) { + allSupertypes.add(compiler, interfaces.head); + } + + addAllSupertypes(allSupertypes, supertype); + for (Link interfaces = cls.interfaces; + !interfaces.isEmpty; + interfaces = interfaces.tail) { + addAllSupertypes(allSupertypes, interfaces.head); + } + allSupertypes.add(compiler, cls.computeType(compiler)); + cls.allSupertypesAndSelf = allSupertypes.toTypeSet(); + } else { + assert(identical(cls, compiler.objectClass)); + cls.allSupertypesAndSelf = + new OrderedTypeSet.singleton(cls.computeType(compiler)); + } + } + + /** + * Adds [type] and all supertypes of [type] to [allSupertypes] while + * substituting type variables. + */ + void addAllSupertypes(OrderedTypeSetBuilder allSupertypes, + InterfaceType type) { + ClassElement classElement = type.element; + Link supertypes = classElement.allSupertypes; + assert(invariant(element, supertypes != null, + message: "Supertypes not computed on $classElement " + "during resolution of $element")); + while (!supertypes.isEmpty) { + DartType supertype = supertypes.head; + allSupertypes.add(compiler, supertype.substByContext(type)); + supertypes = supertypes.tail; + } + } + + isBlackListed(DartType type) { + LibraryElement lib = element.getLibrary(); + return + !identical(lib, compiler.coreLibrary) && + !identical(lib, compiler.jsHelperLibrary) && + !identical(lib, compiler.interceptorsLibrary) && + (identical(type, compiler.types.dynamicType) || + identical(type.element, compiler.boolClass) || + identical(type.element, compiler.numClass) || + identical(type.element, compiler.intClass) || + identical(type.element, compiler.doubleClass) || + identical(type.element, compiler.stringClass) || + identical(type.element, compiler.nullClass)); + } +} + +class ClassSupertypeResolver extends CommonResolverVisitor { + Scope context; + ClassElement classElement; + + ClassSupertypeResolver(Compiler compiler, ClassElement cls) + : context = Scope.buildEnclosingScope(cls), + this.classElement = cls, + super(compiler); + + void loadSupertype(ClassElement element, Node from) { + compiler.resolver.loadSupertypes(element, from); + element.ensureResolved(compiler); + } + + void visitNodeList(NodeList node) { + if (node != null) { + for (Link link = node.nodes; !link.isEmpty; link = link.tail) { + link.head.accept(this); + } + } + } + + void visitClassNode(ClassNode node) { + if (node.superclass == null) { + if (!identical(classElement, compiler.objectClass)) { + loadSupertype(compiler.objectClass, node); + } + } else { + node.superclass.accept(this); + } + visitNodeList(node.interfaces); + } + + void visitMixinApplication(MixinApplication node) { + node.superclass.accept(this); + visitNodeList(node.mixins); + } + + void visitNamedMixinApplication(NamedMixinApplication node) { + node.superclass.accept(this); + visitNodeList(node.mixins); + visitNodeList(node.interfaces); + } + + void visitTypeAnnotation(TypeAnnotation node) { + node.typeName.accept(this); + } + + void visitIdentifier(Identifier node) { + Element element = lookupInScope(compiler, node, context, node.source); + if (element != null && element.isClass()) { + loadSupertype(element, node); + } + } + + void visitSend(Send node) { + Identifier prefix = node.receiver.asIdentifier(); + if (prefix == null) { + error(node.receiver, MessageKind.NOT_A_PREFIX, {'node': node.receiver}); + return; + } + Element element = lookupInScope(compiler, prefix, context, prefix.source); + if (element == null || !identical(element.kind, ElementKind.PREFIX)) { + error(node.receiver, MessageKind.NOT_A_PREFIX, {'node': node.receiver}); + return; + } + PrefixElement prefixElement = element; + Identifier selector = node.selector.asIdentifier(); + var e = prefixElement.lookupLocalMember(selector.source); + if (e == null || !e.impliesType()) { + error(node.selector, MessageKind.CANNOT_RESOLVE_TYPE, + {'typeName': node.selector}); + return; + } + loadSupertype(e, node); + } +} + +class VariableDefinitionsVisitor extends CommonResolverVisitor { + VariableDefinitions definitions; + ResolverVisitor resolver; + ElementKind kind; + VariableList variables; + + VariableDefinitionsVisitor(Compiler compiler, + this.definitions, + this.resolver, + this.kind, + this.variables) + : super(compiler) { + } + + Identifier visitSendSet(SendSet node) { + assert(node.arguments.tail.isEmpty); // Sanity check + Identifier identifier = node.selector; + String name = identifier.source; + VariableDefinitionScope scope = + new VariableDefinitionScope(resolver.scope, name); + resolver.visitIn(node.arguments.head, scope); + if (scope.variableReferencedInInitializer) { + resolver.error(identifier, MessageKind.REFERENCE_IN_INITIALIZATION, + {'variableName': name}); + } + return identifier; + } + + Identifier visitIdentifier(Identifier node) { + // The variable is initialized to null. + resolver.world.registerInstantiatedClass(compiler.nullClass, + resolver.mapping); + if (definitions.modifiers.isConst()) { + compiler.reportError(node, MessageKind.CONST_WITHOUT_INITIALIZER); + } + if (definitions.modifiers.isFinal() && + !resolver.allowFinalWithoutInitializer) { + compiler.reportError(node, MessageKind.FINAL_WITHOUT_INITIALIZER); + } + return node; + } + + visitNodeList(NodeList node) { + for (Link link = node.nodes; !link.isEmpty; link = link.tail) { + Identifier name = visit(link.head); + VariableElement element = new VariableElementX( + name.source, kind, resolver.enclosingElement, + variables, name.token); + resolver.defineElement(link.head, element); + if (definitions.modifiers.isConst()) { + compiler.enqueuer.resolution.addDeferredAction(element, () { + compiler.constantHandler.compileVariable(element, isConst: true); + }); + } + } + } +} + +class ConstructorResolver extends CommonResolverVisitor { + final ResolverVisitor resolver; + bool inConstContext; + DartType type; + + ConstructorResolver(Compiler compiler, this.resolver, + {bool this.inConstContext: false}) + : super(compiler); + + visitNode(Node node) { + throw 'not supported'; + } + + failOrReturnErroneousElement(Element enclosing, Node diagnosticNode, + String targetName, MessageKind kind, + Map arguments) { + if (kind == MessageKind.CANNOT_FIND_CONSTRUCTOR) { + compiler.backend.registerThrowNoSuchMethod(resolver.mapping); + } else { + compiler.backend.registerThrowRuntimeError(resolver.mapping); + } + if (inConstContext) { + compiler.reportError(diagnosticNode, kind, arguments); + } else { + compiler.reportWarning(diagnosticNode, kind, arguments); + } + return new ErroneousElementX(kind, arguments, targetName, enclosing); + } + + Selector createConstructorSelector(String constructorName) { + return constructorName == '' + ? new Selector.callDefaultConstructor( + resolver.enclosingElement.getLibrary()) + : new Selector.callConstructor( + constructorName, + resolver.enclosingElement.getLibrary()); + } + + FunctionElement resolveConstructor(ClassElement cls, + Node diagnosticNode, + String constructorName) { + cls.ensureResolved(compiler); + Selector selector = createConstructorSelector(constructorName); + Element result = cls.lookupConstructor(selector); + if (result == null) { + String fullConstructorName = + resolver.compiler.resolver.constructorNameForDiagnostics( + cls.name, + constructorName); + return failOrReturnErroneousElement( + cls, + diagnosticNode, + fullConstructorName, + MessageKind.CANNOT_FIND_CONSTRUCTOR, + {'constructorName': fullConstructorName}); + } else if (inConstContext && !result.modifiers.isConst()) { + error(diagnosticNode, MessageKind.CONSTRUCTOR_IS_NOT_CONST); + } + return result; + } + + Element visitNewExpression(NewExpression node) { + inConstContext = node.isConst(); + Node selector = node.send.selector; + Element element = visit(selector); + assert(invariant(selector, element != null, + message: 'No element return for $selector.')); + return finishConstructorReference(element, node.send.selector, node); + } + + /// Finishes resolution of a constructor reference and records the + /// type of the constructed instance on [expression]. + FunctionElement finishConstructorReference(Element element, + Node diagnosticNode, + Node expression) { + assert(invariant(diagnosticNode, element != null, + message: 'No element return for $diagnosticNode.')); + // Find the unnamed constructor if the reference resolved to a + // class. + if (!Elements.isUnresolved(element) && !element.isConstructor()) { + if (element.isClass()) { + ClassElement cls = element; + cls.ensureResolved(compiler); + // The unnamed constructor may not exist, so [e] may become unresolved. + element = resolveConstructor(cls, diagnosticNode, ''); + } else { + element = failOrReturnErroneousElement( + element, diagnosticNode, element.name, MessageKind.NOT_A_TYPE, + {'node': diagnosticNode}); + } + } + if (type == null) { + if (Elements.isUnresolved(element)) { + type = compiler.types.dynamicType; + } else { + type = element.getEnclosingClass().rawType; + } + } + resolver.mapping.setType(expression, type); + return element; + } + + Element visitTypeAnnotation(TypeAnnotation node) { + assert(invariant(node, type == null)); + // This is not really resolving a type-annotation, but the name of the + // constructor. Therefore we allow deferred types. + type = resolver.resolveTypeAnnotation(node, + malformedIsError: inConstContext, + deferredIsMalformed: false); + compiler.backend.registerRequiredType(type, resolver.enclosingElement); + return type.element; + } + + Element visitSend(Send node) { + Element element = visit(node.receiver); + assert(invariant(node.receiver, element != null, + message: 'No element return for $node.receiver.')); + if (Elements.isUnresolved(element)) return element; + Identifier name = node.selector.asIdentifier(); + if (name == null) internalError(node.selector, 'unexpected node'); + + if (element.isClass()) { + ClassElement cls = element; + cls.ensureResolved(compiler); + return resolveConstructor(cls, name, name.source); + } else if (element.isPrefix()) { + PrefixElement prefix = element; + element = prefix.lookupLocalMember(name.source); + element = Elements.unwrap(element, compiler, node); + if (element == null) { + return failOrReturnErroneousElement( + resolver.enclosingElement, name, + name.source, + MessageKind.CANNOT_RESOLVE, + {'name': name}); + } else if (!element.isClass()) { + error(node, MessageKind.NOT_A_TYPE, {'node': name}); + } + } else { + internalError(node.receiver, 'unexpected element $element'); + } + return element; + } + + Element visitIdentifier(Identifier node) { + String name = node.source; + Element element = resolver.reportLookupErrorIfAny( + lookupInScope(compiler, node, resolver.scope, name), node, name); + resolver.useElement(node, element); + // TODO(johnniwinther): Change errors to warnings, cf. 11.11.1. + if (element == null) { + return failOrReturnErroneousElement(resolver.enclosingElement, node, name, + MessageKind.CANNOT_RESOLVE, + {'name': name}); + } else if (element.isErroneous()) { + return element; + } else if (element.isTypedef()) { + error(node, MessageKind.CANNOT_INSTANTIATE_TYPEDEF, + {'typedefName': name}); + } else if (element.isTypeVariable()) { + error(node, MessageKind.CANNOT_INSTANTIATE_TYPE_VARIABLE, + {'typeVariableName': name}); + } else if (!element.isClass() && !element.isPrefix()) { + error(node, MessageKind.NOT_A_TYPE, {'node': name}); + } + return element; + } + + /// Assumed to be called by [resolveRedirectingFactory]. + Element visitReturn(Return node) { + Node expression = node.expression; + return finishConstructorReference(visit(expression), + expression, expression); + } +} + +/// Looks up [name] in [scope] and unwraps the result. +Element lookupInScope(Compiler compiler, Node node, + Scope scope, String name) { + return Elements.unwrap(scope.lookup(name), compiler, node); +} + +TreeElements _ensureTreeElements(AnalyzableElement element) { + if (element._treeElements == null) { + element._treeElements = new TreeElementMapping(element); + } + return element._treeElements; +} + +abstract class AnalyzableElement implements Element { + TreeElements _treeElements; + + bool get hasTreeElements => _treeElements != null; + + TreeElements get treeElements { + assert(invariant(this, _treeElements !=null, + message: "TreeElements have not been computed for $this.")); + return _treeElements; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/resolution.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/resolution.dart new file mode 100644 index 0000000..0b59263 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/resolution.dart @@ -0,0 +1,40 @@ +// Copyright (c) 2012, 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. + +library resolution; + +import 'dart:collection' show Queue; + +import '../dart2jslib.dart'; +import '../dart_types.dart'; +import '../tree/tree.dart'; +import '../elements/elements.dart'; +import '../elements/modelx.dart' + show BaseClassElementX, + FunctionElementX, + ErroneousElementX, + VariableElementX, + FieldParameterElementX, + VariableList, + FunctionSignatureX, + LabelElementX, + TargetElementX, + MixinApplicationElementX, + ParameterElementX, + TypeVariableElementX, + TypedefElementX, + SynthesizedConstructorElementX, + MetadataAnnotationX, + ParameterMetadataAnnotation, + MetadataContainer; +import '../util/util.dart'; + +import 'secret_tree_element.dart' show getTreeElement, setTreeElement; +import '../ordered_typeset.dart' show OrderedTypeSet, OrderedTypeSetBuilder; +import 'class_members.dart' show MembersCreator; +import '../dart_backend/dart_backend.dart' show DartBackend; + +part 'members.dart'; +part 'scope.dart'; +part 'signatures.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/scope.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/scope.dart new file mode 100644 index 0000000..ebc83d6 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/scope.dart @@ -0,0 +1,176 @@ +// Copyright (c) 2012, 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. + +part of resolution; + +abstract class Scope { + /** + * Adds [element] to this scope. This operation is only allowed on mutable + * scopes such as [MethodScope] and [BlockScope]. + */ + Element add(Element element); + + /** + * Looks up the [Element] for [name] in this scope. + */ + Element lookup(String name); + + static Scope buildEnclosingScope(Element element) { + return element.enclosingElement != null + ? element.enclosingElement.buildScope() : element.buildScope(); + } +} + +abstract class NestedScope extends Scope { + final Scope parent; + + NestedScope(this.parent); + + Element lookup(String name) { + Element result = localLookup(name); + if (result != null) return result; + return parent.lookup(name); + } + + Element localLookup(String name); +} + +class VariableDefinitionScope extends NestedScope { + final String variableName; + bool variableReferencedInInitializer = false; + + VariableDefinitionScope(Scope parent, this.variableName) : super(parent); + + Element localLookup(String name) { + if (name == variableName) { + variableReferencedInInitializer = true; + } + return null; + } + + Element add(Element newElement) { + throw "Cannot add element to VariableDefinitionScope"; + } +} + +/** + * [TypeDeclarationScope] defines the outer scope of a type declaration in + * which the declared type variables and the entities in the enclosing scope are + * available but where declared and inherited members are not available. This + * scope is only used for class declarations during resolution of the + * class hierarchy. In all other cases [ClassScope] is used. + */ +class TypeDeclarationScope extends NestedScope { + final TypeDeclarationElement element; + + TypeDeclarationScope(parent, this.element) + : super(parent) { + assert(parent != null); + } + + Element add(Element newElement) { + throw "Cannot add element to TypeDeclarationScope"; + } + + Element lookupTypeVariable(String name) { + Link typeVariableLink = element.typeVariables; + while (!typeVariableLink.isEmpty) { + TypeVariableType typeVariable = typeVariableLink.head; + if (typeVariable.name == name) { + return typeVariable.element; + } + typeVariableLink = typeVariableLink.tail; + } + return null; + } + + Element localLookup(String name) => lookupTypeVariable(name); + + String toString() => + 'TypeDeclarationScope($element)'; +} + +abstract class MutableScope extends NestedScope { + final Map elements; + + MutableScope(Scope parent) + : super(parent), + this.elements = new Map() { + assert(parent != null); + } + + Element add(Element newElement) { + if (elements.containsKey(newElement.name)) { + return elements[newElement.name]; + } + elements[newElement.name] = newElement; + return newElement; + } + + Element localLookup(String name) => elements[name]; +} + +class MethodScope extends MutableScope { + final Element element; + + MethodScope(Scope parent, this.element) + : super(parent); + + String toString() => 'MethodScope($element${elements.keys.toList()})'; +} + +class BlockScope extends MutableScope { + BlockScope(Scope parent) : super(parent); + + String toString() => 'BlockScope(${elements.keys.toList()})'; +} + +/** + * [ClassScope] defines the inner scope of a class/interface declaration in + * which declared members, declared type variables, entities in the enclosing + * scope and inherited members are available, in the given order. + */ +class ClassScope extends TypeDeclarationScope { + ClassElement get element => super.element; + + ClassScope(Scope parentScope, ClassElement element) + : super(parentScope, element) { + assert(parent != null); + } + + Element localLookup(String name) { + Element result = element.lookupLocalMember(name); + if (result != null) return result; + return super.localLookup(name); + } + + Element lookup(String name) { + Element result = localLookup(name); + if (result != null) return result; + result = parent.lookup(name); + if (result != null) return result; + return element.lookupSuperMember(name); + } + + Element add(Element newElement) { + throw "Cannot add an element in a class scope"; + } + + String toString() => 'ClassScope($element)'; +} + +class LibraryScope implements Scope { + final LibraryElement library; + + LibraryScope(LibraryElement this.library); + + Element localLookup(String name) => library.find(name); + Element lookup(String name) => localLookup(name); + + Element add(Element newElement) { + throw "Cannot add an element to a library scope"; + } + + String toString() => 'LibraryScope($library)'; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/secret_tree_element.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/secret_tree_element.dart new file mode 100644 index 0000000..665938e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/secret_tree_element.dart @@ -0,0 +1,46 @@ +// Copyright (c) 2012, 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. + +/** + * Encapsulates the field [TreeElementMixin._element]. + * + * This library is an implementation detail of dart2js, and should not + * be imported except by resolution and tree node libraries, or for + * testing. + * + * We have taken great care to ensure AST nodes can be cached between + * compiler instances. Part of this requires that we always access + * resolution results through TreeElements. + * + * So please, do not add additional elements to this library, and do + * not import it. + */ +library secret_tree_element; + +/** + * The superclass of all AST nodes. + */ +abstract class TreeElementMixin { + // Deliberately using [Object] here to thwart code completion. + // You're not really supposed to access this field anyways. + Object _element; +} + +/** + * Do not call this method directly. Instead, use an instance of + * TreeElements. + * + * Using [Object] as return type to thwart code completion. + */ +Object getTreeElement(TreeElementMixin node) { + return node._element; +} + +/** + * Do not call this method directly. Instead, use an instance of + * TreeElements. + */ +void setTreeElement(TreeElementMixin node, Object value) { + node._element = value; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/signatures.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/signatures.dart new file mode 100644 index 0000000..b9d9fd9 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolution/signatures.dart @@ -0,0 +1,367 @@ +// Copyright (c) 2014, 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. + +part of resolution; + +/** + * [SignatureResolver] resolves function signatures. + */ +class SignatureResolver extends MappingVisitor { + final ResolverVisitor resolver; + final Element enclosingElement; + final Scope scope; + final MessageKind defaultValuesError; + Link optionalParameters = const Link(); + int optionalParameterCount = 0; + bool isOptionalParameter = false; + bool optionalParametersAreNamed = false; + VariableDefinitions currentDefinitions; + + SignatureResolver(Compiler compiler, + Element enclosingElement, + TreeElementMapping treeElements, + {this.defaultValuesError}) + : this.enclosingElement = enclosingElement, + this.scope = enclosingElement.buildScope(), + this.resolver = + new ResolverVisitor(compiler, enclosingElement, treeElements), + super(compiler, treeElements); + + bool get defaultValuesAllowed => defaultValuesError == null; + + visitNodeList(NodeList node) { + // This must be a list of optional arguments. + String value = node.beginToken.stringValue; + if ((!identical(value, '[')) && (!identical(value, '{'))) { + internalError(node, "expected optional parameters"); + } + optionalParametersAreNamed = (identical(value, '{')); + isOptionalParameter = true; + LinkBuilder elements = analyzeNodes(node.nodes); + optionalParameterCount = elements.length; + optionalParameters = elements.toLink(); + } + + ParameterElementX visitVariableDefinitions(VariableDefinitions node) { + Link definitions = node.definitions.nodes; + if (definitions.isEmpty) { + internalError(node, 'no parameter definition'); + return null; + } + if (!definitions.tail.isEmpty) { + internalError(definitions.tail.head, 'extra definition'); + return null; + } + Node definition = definitions.head; + if (definition is NodeList) { + internalError(node, 'optional parameters are not implemented'); + } + if (node.modifiers.isConst()) { + compiler.reportError(node, MessageKind.FORMAL_DECLARED_CONST); + } + if (node.modifiers.isStatic()) { + compiler.reportError(node, MessageKind.FORMAL_DECLARED_STATIC); + } + + if (currentDefinitions != null) { + internalError(node, 'function type parameters not supported'); + } + currentDefinitions = node; + ParameterElementX element = definition.accept(this); + if (currentDefinitions.metadata != null) { + element.metadata = compiler.resolver.resolveMetadata(element, node); + } + currentDefinitions = null; + return element; + } + + void validateName(Identifier node) { + String name = node.source; + if (isOptionalParameter && + optionalParametersAreNamed && + isPrivateName(node.source)) { + compiler.reportError(node, MessageKind.PRIVATE_NAMED_PARAMETER); + } + } + + void computeParameterType(ParameterElementX element, + [VariableElement fieldElement]) { + void computeFunctionType(FunctionExpression functionExpression) { + FunctionSignature functionSignature = SignatureResolver.analyze( + compiler, functionExpression.parameters, + functionExpression.returnType, element, mapping, + defaultValuesError: MessageKind.FUNCTION_TYPE_FORMAL_WITH_DEFAULT); + element.functionSignatureCache = functionSignature; + element.typeCache = functionSignature.type; + } + + if (currentDefinitions.type != null) { + element.typeCache = resolveTypeAnnotation(currentDefinitions.type); + } else { + // Is node.definitions exactly one FunctionExpression? + Link link = currentDefinitions.definitions.nodes; + assert(invariant(currentDefinitions, !link.isEmpty)); + assert(invariant(currentDefinitions, link.tail.isEmpty)); + if (link.head.asFunctionExpression() != null) { + // Inline function typed parameter, like `void m(int f(String s))`. + computeFunctionType(link.head); + } else if (link.head.asSend() != null && + link.head.asSend().selector.asFunctionExpression() != null) { + // Inline function typed initializing formal or + // parameter with default value, like `C(int this.f(String s))` or + // `void m([int f(String s) = null])`. + computeFunctionType(link.head.asSend().selector.asFunctionExpression()); + } else { + assert(invariant(currentDefinitions, + link.head.asIdentifier() != null || link.head.asSend() != null)); + if (fieldElement != null) { + element.typeCache = fieldElement.computeType(compiler); + } else { + element.typeCache = compiler.types.dynamicType; + } + } + } + } + + Element visitIdentifier(Identifier node) { + return createParameter(node, null); + } + + Identifier getParameterName(Send node) { + var identifier = node.selector.asIdentifier(); + if (identifier != null) { + // Normal parameter: [:Type name:]. + return identifier; + } else { + // Function type parameter: [:void name(DartType arg):]. + var functionExpression = node.selector.asFunctionExpression(); + if (functionExpression != null && + functionExpression.name.asIdentifier() != null) { + return functionExpression.name.asIdentifier(); + } else { + internalError(node, + 'internal error: unimplemented receiver on parameter send'); + return null; + } + } + } + + // The only valid [Send] can be in constructors and must be of the form + // [:this.x:] (where [:x:] represents an instance field). + FieldParameterElementX visitSend(Send node) { + return createFieldParameter(node, null); + } + + ParameterElementX createParameter(Identifier name, Expression initializer) { + validateName(name); + ParameterElementX parameter = new ParameterElementX( + ElementKind.PARAMETER, enclosingElement, + currentDefinitions, name, initializer); + computeParameterType(parameter); + return parameter; + } + + FieldParameterElementX createFieldParameter(Send node, + Expression initializer) { + FieldParameterElementX element; + if (node.receiver.asIdentifier() == null || + !node.receiver.asIdentifier().isThis()) { + error(node, MessageKind.INVALID_PARAMETER); + } else if (!identical(enclosingElement.kind, + ElementKind.GENERATIVE_CONSTRUCTOR)) { + error(node, MessageKind.INITIALIZING_FORMAL_NOT_ALLOWED); + } else { + Identifier name = getParameterName(node); + validateName(name); + Element fieldElement = currentClass.lookupLocalMember(name.source); + if (fieldElement == null || + !identical(fieldElement.kind, ElementKind.FIELD)) { + error(node, MessageKind.NOT_A_FIELD, {'fieldName': name}); + } else if (!fieldElement.isInstanceMember()) { + error(node, MessageKind.NOT_INSTANCE_FIELD, {'fieldName': name}); + } + element = new FieldParameterElementX(enclosingElement, + currentDefinitions, name, initializer, fieldElement); + computeParameterType(element, fieldElement); + } + return element; + } + + /// A [SendSet] node is an optional parameter with a default value. + Element visitSendSet(SendSet node) { + ParameterElementX element; + if (node.receiver != null) { + element = createFieldParameter(node, node.arguments.first); + } else if (node.selector.asIdentifier() != null || + node.selector.asFunctionExpression() != null) { + element = createParameter(getParameterName(node), node.arguments.first); + } + Node defaultValue = node.arguments.head; + if (!defaultValuesAllowed) { + error(defaultValue, defaultValuesError); + } + // Visit the value. The compile time constant handler will + // make sure it's a compile time constant. + resolveExpression(defaultValue); + return element; + } + + Element visitFunctionExpression(FunctionExpression node) { + // This is a function typed parameter. + Modifiers modifiers = currentDefinitions.modifiers; + if (modifiers.isFinal()) { + compiler.reportError(modifiers, + MessageKind.FINAL_FUNCTION_TYPE_PARAMETER); + } + if (modifiers.isVar()) { + compiler.reportError(modifiers, MessageKind.VAR_FUNCTION_TYPE_PARAMETER); + } + + return createParameter(node.name, null); + } + + LinkBuilder analyzeNodes(Link link) { + LinkBuilder elements = new LinkBuilder(); + for (; !link.isEmpty; link = link.tail) { + Element element = link.head.accept(this); + if (element != null) { + elements.addLast(element); + } else { + // If parameter is null, the current node should be the last, + // and a list of optional named parameters. + if (!link.tail.isEmpty || (link.head is !NodeList)) { + internalError(link.head, "expected optional parameters"); + } + } + } + return elements; + } + + /** + * Resolves formal parameters and return type of a [FunctionExpression] + * to a [FunctionSignature]. + */ + static FunctionSignature analyze(Compiler compiler, + NodeList formalParameters, + Node returnNode, + Element element, + TreeElementMapping mapping, + {MessageKind defaultValuesError}) { + SignatureResolver visitor = new SignatureResolver(compiler, element, + mapping, defaultValuesError: defaultValuesError); + Link parameters = const Link(); + int requiredParameterCount = 0; + if (formalParameters == null) { + if (!element.isGetter()) { + compiler.reportError(element, MessageKind.MISSING_FORMALS); + } + } else { + if (element.isGetter()) { + if (!identical(formalParameters.getEndToken().next.stringValue, + // TODO(ahe): Remove the check for native keyword. + 'native')) { + compiler.reportError(formalParameters, + MessageKind.EXTRA_FORMALS); + } + } + LinkBuilder parametersBuilder = + visitor.analyzeNodes(formalParameters.nodes); + requiredParameterCount = parametersBuilder.length; + parameters = parametersBuilder.toLink(); + } + DartType returnType; + if (element.isFactoryConstructor()) { + returnType = element.getEnclosingClass().thisType; + // Because there is no type annotation for the return type of + // this element, we explicitly add one. + if (compiler.enableTypeAssertions) { + compiler.enqueuer.resolution.registerIsCheck(returnType, mapping); + } + } else { + returnType = visitor.resolveReturnType(returnNode); + } + + if (element.isSetter() && (requiredParameterCount != 1 || + visitor.optionalParameterCount != 0)) { + // If there are no formal parameters, we already reported an error above. + if (formalParameters != null) { + compiler.reportError(formalParameters, + MessageKind.ILLEGAL_SETTER_FORMALS); + } + } + LinkBuilder parameterTypes = new LinkBuilder(); + for (ParameterElement parameter in parameters) { + parameterTypes.addLast(parameter.type); + } + Link optionalParameterTypes = const Link(); + Link namedParameters = const Link(); + Link namedParameterTypes = const Link(); + List orderedOptionalParameters = + visitor.optionalParameters.toList(); + if (visitor.optionalParametersAreNamed) { + orderedOptionalParameters.sort((Element a, Element b) { + return a.name.compareTo(b.name); + }); + LinkBuilder namedParametersBuilder = new LinkBuilder(); + LinkBuilder namedParameterTypesBuilder = + new LinkBuilder(); + for (ParameterElement parameter in orderedOptionalParameters) { + namedParametersBuilder.addLast(parameter.name); + namedParameterTypesBuilder.addLast(parameter.type); + } + namedParameters = namedParametersBuilder.toLink(); + namedParameterTypes = namedParameterTypesBuilder.toLink(); + } else { + LinkBuilder optionalParameterTypesBuilder = + new LinkBuilder(); + for (ParameterElement parameter in visitor.optionalParameters) { + optionalParameterTypesBuilder.addLast(parameter.type); + } + optionalParameterTypes = optionalParameterTypesBuilder.toLink(); + } + FunctionType type = new FunctionType( + element.declaration, + returnType, + parameterTypes.toLink(), + optionalParameterTypes, + namedParameters, + namedParameterTypes); + return new FunctionSignatureX(parameters, + visitor.optionalParameters, + requiredParameterCount, + visitor.optionalParameterCount, + visitor.optionalParametersAreNamed, + orderedOptionalParameters, + type); + } + + DartType resolveTypeAnnotation(TypeAnnotation annotation) { + DartType type = resolveReturnType(annotation); + if (type == compiler.types.voidType) { + compiler.reportError(annotation, MessageKind.VOID_NOT_ALLOWED); + } + return type; + } + + DartType resolveReturnType(TypeAnnotation annotation) { + if (annotation == null) return compiler.types.dynamicType; + DartType result = resolver.resolveTypeAnnotation(annotation); + if (result == null) { + return compiler.types.dynamicType; + } + return result; + } + + // TODO(ahe): This is temporary. + void resolveExpression(Node node) { + if (node == null) return; + node.accept(resolver); + } + + // TODO(ahe): This is temporary. + ClassElement get currentClass { + return enclosingElement.isMember() + ? enclosingElement.getEnclosingClass() : null; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolved_visitor.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolved_visitor.dart new file mode 100644 index 0000000..edf4c2c --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/resolved_visitor.dart @@ -0,0 +1,75 @@ +// Copyright (c) 2012, 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. + +part of dart2js; + +abstract class ResolvedVisitor extends Visitor { + final Compiler compiler; + TreeElements elements; + + ResolvedVisitor(this.elements, this.compiler); + + R visitSend(Send node) { + Element element = elements[node]; + if (node.isSuperCall) { + return visitSuperSend(node); + } else if (node.isOperator) { + return visitOperatorSend(node); + } else if (node.isPropertyAccess) { + if (!Elements.isUnresolved(element) && element.impliesType()) { + // A reference to a class literal, typedef or type variable. + return visitTypeReferenceSend(node); + } else { + return visitGetterSend(node); + } + } else if (element != null && Initializers.isConstructorRedirect(node)) { + return visitStaticSend(node); + } else if (Elements.isClosureSend(node, element)) { + return visitClosureSend(node); + } else if (element == compiler.assertMethod) { + assert(element != null); + return visitAssert(node); + } else { + if (Elements.isUnresolved(element)) { + if (element == null) { + // Example: f() with 'f' unbound. + // This can only happen inside an instance method. + return visitDynamicSend(node); + } else { + return visitStaticSend(node); + } + } else if (element.impliesType()) { + // A reference to a class literal, typedef or type variable. + return visitTypeReferenceSend(node); + } else if (element.isInstanceMember()) { + // Example: f() with 'f' bound to instance method. + return visitDynamicSend(node); + } else if (!element.isInstanceMember()) { + // Example: A.f() or f() with 'f' bound to a static function. + // Also includes new A() or new A.named() which is treated like a + // static call to a factory. + return visitStaticSend(node); + } else { + internalError("Cannot generate code for send", node: node); + return null; + } + } + } + + R visitSuperSend(Send node); + R visitOperatorSend(Send node); + R visitGetterSend(Send node); + R visitClosureSend(Send node); + R visitDynamicSend(Send node); + R visitStaticSend(Send node); + R visitTypeReferenceSend(Send node); + R visitAssert(Send node); + + void internalError(String reason, {Node node}); + + R visitNode(Node node) { + internalError("Unhandled node", node: node); + return null; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/array_based_scanner.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/array_based_scanner.dart new file mode 100644 index 0000000..e6d3cef --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/array_based_scanner.dart @@ -0,0 +1,214 @@ +// Copyright (c) 2011, 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. + +part of scanner; + +abstract class ArrayBasedScanner extends AbstractScanner { + ArrayBasedScanner(SourceFile file, bool includeComments) + : super(file, includeComments); + + /** + * The stack of open groups, e.g [: { ... ( .. :] + * Each BeginGroupToken has a pointer to the token where the group + * ends. This field is set when scanning the end group token. + */ + Link groupingStack = const Link(); + + /** + * Appends a token whose kind is determined by [info] and content is defined + * by the String [value]. + * + * This method is invoked for class names, field names, method names, types, + * etc. + */ + void appendStringToken(PrecedenceInfo info, String value) { + tail.next = new StringToken.fromString(info, value, tokenStart, + canonicalize: true); + tail = tail.next; + } + + /** + * Appends a fixed token whose kind and content is determined by [info]. + * Appends an *operator* token from [info]. + * + * An operator token represent operators like ':', '.', ';', '&&', '==', '--', + * '=>', etc. + */ + void appendPrecedenceToken(PrecedenceInfo info) { + tail.next = new SymbolToken(info, tokenStart); + tail = tail.next; + } + + /** + * Appends a fixed token based on whether the current char is [choice] or not. + * If the current char is [choice] a fixed token whose kind and content + * is determined by [yes] is appended, otherwise a fixed token whose kind + * and content is determined by [no] is appended. + */ + int select(int choice, PrecedenceInfo yes, PrecedenceInfo no) { + int next = advance(); + if (identical(next, choice)) { + appendPrecedenceToken(yes); + return advance(); + } else { + appendPrecedenceToken(no); + return next; + } + } + + /** + * Appends a keyword token whose kind is determined by [keyword]. + */ + void appendKeywordToken(Keyword keyword) { + String syntax = keyword.syntax; + // Type parameters and arguments cannot contain 'this' or 'super'. + if (identical(syntax, 'this') || identical(syntax, 'super')) { + discardOpenLt(); + } + tail.next = new KeywordToken(keyword, tokenStart); + tail = tail.next; + } + + void appendEofToken() { + beginToken(); + tail.next = new SymbolToken(EOF_INFO, tokenStart); + tail = tail.next; + // EOF points to itself so there's always infinite look-ahead. + tail.next = tail; + discardOpenLt(); + while (!groupingStack.isEmpty) { + unmatchedBeginGroup(groupingStack.head); + groupingStack = groupingStack.tail; + } + } + + /** + * Notifies scanning a whitespace character. Note that [appendWhiteSpace] is + * not always invoked for [$SPACE] characters. + * + * This method is used by the scanners to track line breaks and create the + * [lineStarts] map. + */ + void appendWhiteSpace(int next) { + if (next == $LF && file != null) { + lineStarts.add(stringOffset + 1); // +1, the line starts after the $LF. + } + } + + /** + * Notifies on [$LF] characters in multi-line comments or strings. + * + * This method is used by the scanners to track line breaks and create the + * [lineStarts] map. + */ + void lineFeedInMultiline() { + if (file != null) { + lineStarts.add(stringOffset + 1); + } + } + + /** + * Appends a token that begins a new group, represented by [value]. + * Group begin tokens are '{', '(', '[' and '${'. + */ + void appendBeginGroup(PrecedenceInfo info) { + Token token = new BeginGroupToken(info, tokenStart); + tail.next = token; + tail = tail.next; + + // { ( [ ${ cannot appear inside a type parameters / arguments. + if (!identical(info.kind, LT_TOKEN)) discardOpenLt(); + groupingStack = groupingStack.prepend(token); + } + + /** + * Appends a token that begins an end group, represented by [value]. + * It handles the group end tokens '}', ')' and ']'. The tokens '>' and + * '>>' are handled separately bo [appendGt] and [appendGtGt]. + */ + int appendEndGroup(PrecedenceInfo info, int openKind) { + assert(!identical(openKind, LT_TOKEN)); // openKind is < for > and >> + appendPrecedenceToken(info); + // Don't report unmatched errors for <; it is also the less-than operator. + discardOpenLt(); + if (groupingStack.isEmpty) { + return advance(); + } + BeginGroupToken begin = groupingStack.head; + if (!identical(begin.kind, openKind)) { + if (!identical(openKind, OPEN_CURLY_BRACKET_TOKEN) || + !identical(begin.kind, STRING_INTERPOLATION_TOKEN)) { + // Not ending string interpolation. + unmatchedBeginGroup(begin); + return advance(); + } + // We're ending an interpolated expression. + begin.endGroup = tail; + groupingStack = groupingStack.tail; + // Using "start-of-text" to signal that we're back in string + // scanning mode. + return $STX; + } + begin.endGroup = tail; + groupingStack = groupingStack.tail; + return advance(); + } + + /** + * Appends a token for '>'. + * This method does not issue unmatched errors, because > is also the + * greater-than operator. It does not necessarily have to close a group. + */ + void appendGt(PrecedenceInfo info) { + appendPrecedenceToken(info); + if (groupingStack.isEmpty) return; + if (identical(groupingStack.head.kind, LT_TOKEN)) { + groupingStack.head.endGroup = tail; + groupingStack = groupingStack.tail; + } + } + + /** + * Appends a token for '>>'. + * This method does not issue unmatched errors, because >> is also the + * shift operator. It does not necessarily have to close a group. + */ + void appendGtGt(PrecedenceInfo info) { + appendPrecedenceToken(info); + if (groupingStack.isEmpty) return; + if (identical(groupingStack.head.kind, LT_TOKEN)) { + // Don't assign endGroup: in "T>", the '>>' token closes the outer + // '<', the inner '<' is left without endGroup. + groupingStack = groupingStack.tail; + } + if (groupingStack.isEmpty) return; + if (identical(groupingStack.head.kind, LT_TOKEN)) { + groupingStack.head.endGroup = tail; + groupingStack = groupingStack.tail; + } + } + + void appendComment(start, bool asciiOnly) { + if (!includeComments) return; + appendSubstringToken(COMMENT_INFO, start, asciiOnly); + } + + /** + * This method is called to discard '<' from the "grouping" stack. + * + * [PartialParser.skipExpression] relies on the fact that we do not + * create groups for stuff like: + * [:a = b < c, d = e > f:]. + * + * In other words, this method is called when the scanner recognizes + * something which cannot possibly be part of a type parameter/argument + * list, like the '=' in the above example. + */ + void discardOpenLt() { + while (!groupingStack.isEmpty + && identical(groupingStack.head.kind, LT_TOKEN)) { + groupingStack = groupingStack.tail; + } + } +} \ No newline at end of file diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/class_element_parser.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/class_element_parser.dart new file mode 100644 index 0000000..7e6a81b --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/class_element_parser.dart @@ -0,0 +1,203 @@ +// Copyright (c) 2011, 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. + +part of scanner; + +class ClassElementParser extends PartialParser { + ClassElementParser(Listener listener) : super(listener); + + Token parseClassBody(Token token) => fullParseClassBody(token); +} + +class PartialClassElement extends ClassElementX { + final Token beginToken; + final Token endToken; + ClassNode cachedNode; + + PartialClassElement(String name, + Token this.beginToken, + Token this.endToken, + Element enclosing, + int id) + : super(name, enclosing, id, STATE_NOT_STARTED); + + void set supertypeLoadState(int state) { + assert(state == supertypeLoadState + 1); + assert(state <= STATE_DONE); + super.supertypeLoadState = state; + } + + void set resolutionState(int state) { + assert(state == resolutionState + 1); + assert(state <= STATE_DONE); + super.resolutionState = state; + } + + ClassNode parseNode(Compiler compiler) { + if (cachedNode != null) return cachedNode; + compiler.withCurrentElement(this, () { + compiler.parser.measure(() { + MemberListener listener = new MemberListener(compiler, this); + Parser parser = new ClassElementParser(listener); + Token token = parser.parseTopLevelDeclaration(beginToken); + assert(identical(token, endToken.next)); + cachedNode = listener.popNode(); + assert(invariant(beginToken, listener.nodes.isEmpty, + message: "Non-empty listener stack: ${listener.nodes}")); + }); + compiler.patchParser.measure(() { + if (isPatched) { + // TODO(lrn): Perhaps extract functionality so it doesn't + // need compiler. + compiler.patchParser.parsePatchClassNode(patch); + } + }); + }); + return cachedNode; + } + + Token position() => beginToken; + + // TODO(johnniwinther): Ensure that modifiers are always available. + Modifiers get modifiers => + cachedNode != null ? cachedNode.modifiers : Modifiers.EMPTY; + + accept(ElementVisitor visitor) => visitor.visitClassElement(this); +} + +class MemberListener extends NodeListener { + final ClassElement enclosingElement; + + MemberListener(DiagnosticListener listener, + Element enclosingElement) + : this.enclosingElement = enclosingElement, + super(listener, enclosingElement.getCompilationUnit()); + + bool isConstructorName(Node nameNode) { + if (enclosingElement == null || + enclosingElement.kind != ElementKind.CLASS) { + return false; + } + String name; + if (nameNode.asIdentifier() != null) { + name = nameNode.asIdentifier().source; + } else { + Send send = nameNode.asSend(); + name = send.receiver.asIdentifier().source; + } + return enclosingElement.name == name; + } + + // TODO(johnniwinther): Remove this method. + String getMethodNameHack(Node methodName) { + Send send = methodName.asSend(); + if (send == null) { + if (isConstructorName(methodName)) return ''; + return methodName.asIdentifier().source; + } + Identifier receiver = send.receiver.asIdentifier(); + Identifier selector = send.selector.asIdentifier(); + Operator operator = selector.asOperator(); + if (operator != null) { + assert(identical(receiver.source, 'operator')); + // TODO(ahe): It is a hack to compare to ')', but it beats + // parsing the node. + bool isUnary = identical(operator.token.next.next.stringValue, ')'); + return Elements.constructOperatorName(operator.source, isUnary); + } else { + if (receiver == null || receiver.source != enclosingElement.name) { + listener.reportError(send.receiver, + MessageKind.INVALID_CONSTRUCTOR_NAME, + {'name': enclosingElement.name}); + } + return selector.source; + } + } + + void endMethod(Token getOrSet, Token beginToken, Token endToken) { + super.endMethod(getOrSet, beginToken, endToken); + FunctionExpression method = popNode(); + pushNode(null); + bool isConstructor = isConstructorName(method.name); + String name = getMethodNameHack(method.name); + ElementKind kind = ElementKind.FUNCTION; + if (isConstructor) { + if (getOrSet != null) { + recoverableError(getOrSet, 'illegal modifier'); + } + kind = ElementKind.GENERATIVE_CONSTRUCTOR; + } else if (getOrSet != null) { + kind = (identical(getOrSet.stringValue, 'get')) + ? ElementKind.GETTER : ElementKind.SETTER; + } + bool hasNoBody = !isConstructor && !method.hasBody(); + Element memberElement = + new PartialFunctionElement(name, beginToken, getOrSet, endToken, + kind, method.modifiers, enclosingElement, + hasNoBody); + addMember(memberElement); + } + + void endFactoryMethod(Token beginToken, Token endToken) { + super.endFactoryMethod(beginToken, endToken); + FunctionExpression method = popNode(); + pushNode(null); + String name = getMethodNameHack(method.name); + Identifier singleIdentifierName = method.name.asIdentifier(); + if (singleIdentifierName != null && singleIdentifierName.source == name) { + if (name != enclosingElement.name) { + listener.reportError(singleIdentifierName, + MessageKind.INVALID_UNNAMED_CONSTRUCTOR_NAME, + {'name': enclosingElement.name}); + } + } + ElementKind kind = ElementKind.FUNCTION; + Element memberElement = + new PartialFunctionElement(name, beginToken, null, endToken, kind, + method.modifiers, enclosingElement, false); + addMember(memberElement); + } + + void endFields(int count, Token beginToken, Token endToken) { + super.endFields(count, beginToken, endToken); + VariableDefinitions variableDefinitions = popNode(); + Modifiers modifiers = variableDefinitions.modifiers; + pushNode(null); + void buildFieldElement(Identifier name, VariableList fields) { + Element element = + new FieldElementX(name, enclosingElement, fields); + addMember(element); + } + buildFieldElements(modifiers, variableDefinitions.definitions, + enclosingElement, + buildFieldElement, beginToken, endToken); + } + + void endInitializer(Token assignmentOperator) { + pushNode(null); // Super expects an expression, but + // ClassElementParser just skips expressions. + super.endInitializer(assignmentOperator); + } + + void endInitializers(int count, Token beginToken, Token endToken) { + pushNode(null); + } + + void addMember(Element memberElement) { + for (Link link = metadata; !link.isEmpty; link = link.tail) { + memberElement.addMetadata(link.head); + } + metadata = const Link(); + enclosingElement.addMember(memberElement, listener); + } + + void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) { + popNode(); // Discard arguments. + if (periodBeforeName != null) { + popNode(); // Discard name. + } + popNode(); // Discard node (Send or Identifier). + pushMetadata(new PartialMetadataAnnotation(beginToken, endToken)); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/keyword.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/keyword.dart new file mode 100644 index 0000000..4bf9695 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/keyword.dart @@ -0,0 +1,205 @@ +// Copyright (c) 2011, 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. + +part of scanner; + +/** + * A keyword in the Dart programming language. + */ +class Keyword { + static const List values = const [ + const Keyword("assert"), + const Keyword("break"), + const Keyword("case"), + const Keyword("catch"), + const Keyword("class"), + const Keyword("const"), + const Keyword("continue"), + const Keyword("default"), + const Keyword("do"), + const Keyword("else"), + const Keyword("extends"), + const Keyword("false"), + const Keyword("final"), + const Keyword("finally"), + const Keyword("for"), + const Keyword("if"), + const Keyword("in"), + const Keyword("new"), + const Keyword("null"), + const Keyword("rethrow"), + const Keyword("return"), + const Keyword("super"), + const Keyword("switch"), + const Keyword("this"), + const Keyword("throw"), + const Keyword("true"), + const Keyword("try"), + const Keyword("var"), + const Keyword("void"), + const Keyword("while"), + const Keyword("with"), + + // TODO(ahe): Don't think this is a reserved word. + // See: http://dartbug.com/5579 + const Keyword("is", info: IS_INFO), + + const Keyword("abstract", isBuiltIn: true), + const Keyword("as", info: AS_INFO, isBuiltIn: true), + const Keyword("dynamic", isBuiltIn: true), + const Keyword("export", isBuiltIn: true), + const Keyword("external", isBuiltIn: true), + const Keyword("factory", isBuiltIn: true), + const Keyword("get", isBuiltIn: true), + const Keyword("implements", isBuiltIn: true), + const Keyword("import", isBuiltIn: true), + const Keyword("library", isBuiltIn: true), + const Keyword("operator", isBuiltIn: true), + const Keyword("part", isBuiltIn: true), + const Keyword("set", isBuiltIn: true), + const Keyword("static", isBuiltIn: true), + const Keyword("typedef", isBuiltIn: true), + + const Keyword("hide", isPseudo: true), + const Keyword("native", isPseudo: true), + const Keyword("of", isPseudo: true), + const Keyword("on", isPseudo: true), + const Keyword("show", isPseudo: true), + const Keyword("source", isPseudo: true), + const Keyword("deferred", isPseudo: true)]; + + final String syntax; + final bool isPseudo; + final bool isBuiltIn; + final PrecedenceInfo info; + + static Map _keywords; + static Map get keywords { + if (_keywords == null) { + _keywords = computeKeywordMap(); + } + return _keywords; + } + + const Keyword(this.syntax, + {this.isPseudo: false, + this.isBuiltIn: false, + this.info: KEYWORD_INFO}); + + static Map computeKeywordMap() { + Map result = new Map(); + for (Keyword keyword in values) { + result[keyword.syntax] = keyword; + } + return result; + } + + String toString() => syntax; +} + +/** + * Abstract state in a state machine for scanning keywords. + */ +abstract class KeywordState { + KeywordState(this.keyword); + + KeywordState next(int c); + final Keyword keyword; + + static KeywordState _KEYWORD_STATE; + static KeywordState get KEYWORD_STATE { + if (_KEYWORD_STATE == null) { + List strings = + new List(Keyword.values.length); + for (int i = 0; i < Keyword.values.length; i++) { + strings[i] = Keyword.values[i].syntax; + } + strings.sort((a,b) => a.compareTo(b)); + _KEYWORD_STATE = computeKeywordStateTable(0, strings, 0, strings.length); + } + return _KEYWORD_STATE; + } + + static KeywordState computeKeywordStateTable(int start, List strings, + int offset, int length) { + List result = new List(26); + assert(length != 0); + int chunk = 0; + int chunkStart = -1; + bool isLeaf = false; + for (int i = offset; i < offset + length; i++) { + if (strings[i].length == start) { + isLeaf = true; + } + if (strings[i].length > start) { + int c = strings[i].codeUnitAt(start); + if (chunk != c) { + if (chunkStart != -1) { + assert(result[chunk - $a] == null); + result[chunk - $a] = computeKeywordStateTable(start + 1, strings, + chunkStart, + i - chunkStart); + } + chunkStart = i; + chunk = c; + } + } + } + if (chunkStart != -1) { + assert(result[chunk - $a] == null); + result[chunk - $a] = + computeKeywordStateTable(start + 1, strings, chunkStart, + offset + length - chunkStart); + } else { + assert(length == 1); + return new LeafKeywordState(strings[offset]); + } + if (isLeaf) { + return new ArrayKeywordState(result, strings[offset]); + } else { + return new ArrayKeywordState(result, null); + } + } +} + +/** + * A state with multiple outgoing transitions. + */ +class ArrayKeywordState extends KeywordState { + final List table; + + ArrayKeywordState(List this.table, String syntax) + : super((syntax == null) ? null : Keyword.keywords[syntax]); + + KeywordState next(int c) => table[c - $a]; + + String toString() { + StringBuffer sb = new StringBuffer(); + sb.write("["); + if (keyword != null) { + sb.write("*"); + sb.write(keyword); + sb.write(" "); + } + List foo = table; + for (int i = 0; i < foo.length; i++) { + if (foo[i] != null) { + sb.write("${new String.fromCharCodes([i + $a])}: ${foo[i]}; "); + } + } + sb.write("]"); + return sb.toString(); + } +} + +/** + * A state that has no outgoing transitions. + */ +class LeafKeywordState extends KeywordState { + LeafKeywordState(String syntax) : super(Keyword.keywords[syntax]); + + KeywordState next(int c) => null; + + String toString() => keyword.syntax; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/listener.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/listener.dart new file mode 100644 index 0000000..96dcd2a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/listener.dart @@ -0,0 +1,2065 @@ +// Copyright (c) 2012, 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. + +part of scanner; + +const bool VERBOSE = false; + +/** + * A parser event listener that does nothing except throw exceptions + * on parser errors. + */ +class Listener { + void beginArguments(Token token) { + } + + void endArguments(int count, Token beginToken, Token endToken) { + } + + void beginBlock(Token token) { + } + + void endBlock(int count, Token beginToken, Token endToken) { + } + + void beginCascade(Token token) { + } + + void endCascade() { + } + + void beginClassBody(Token token) { + } + + void endClassBody(int memberCount, Token beginToken, Token endToken) { + } + + void beginClassDeclaration(Token token) { + } + + void endClassDeclaration(int interfacesCount, Token beginToken, + Token extendsKeyword, Token implementsKeyword, + Token endToken) { + } + + void beginCombinators(Token token) { + } + + void endCombinators(int count) { + } + + void beginCompilationUnit(Token token) { + } + + void endCompilationUnit(int count, Token token) { + } + + void beginConstructorReference(Token start) { + } + + void endConstructorReference(Token start, Token periodBeforeName, + Token endToken) { + } + + void beginDoWhileStatement(Token token) { + } + + void endDoWhileStatement(Token doKeyword, Token whileKeyword, + Token endToken) { + } + + void beginExport(Token token) { + } + + void endExport(Token exportKeyword, Token semicolon) { + } + + void beginExpressionStatement(Token token) { + } + + void endExpressionStatement(Token token) { + } + + void beginFactoryMethod(Token token) { + } + + void endFactoryMethod(Token beginToken, Token endToken) { + } + + void beginFormalParameter(Token token) { + } + + void endFormalParameter(Token thisKeyword) { + } + + void handleNoFormalParameters(Token token) { + } + + void beginFormalParameters(Token token) { + } + + void endFormalParameters(int count, Token beginToken, Token endToken) { + } + + void endFields(int count, Token beginToken, Token endToken) { + } + + void beginForStatement(Token token) { + } + + void endForStatement(int updateExpressionCount, + Token beginToken, Token endToken) { + } + + void endForIn(Token beginToken, Token inKeyword, Token endToken) { + } + + void beginFunction(Token token) { + } + + void endFunction(Token getOrSet, Token endToken) { + } + + void beginFunctionDeclaration(Token token) { + } + + void endFunctionDeclaration(Token token) { + } + + void beginFunctionBody(Token token) { + } + + void endFunctionBody(int count, Token beginToken, Token endToken) { + } + + void handleNoFunctionBody(Token token) { + } + + void skippedFunctionBody(Token token) { + } + + void beginFunctionName(Token token) { + } + + void endFunctionName(Token token) { + } + + void beginFunctionTypeAlias(Token token) { + } + + void endFunctionTypeAlias(Token typedefKeyword, Token endToken) { + } + + void beginMixinApplication(Token token) { + } + + void endMixinApplication() { + } + + void beginNamedMixinApplication(Token token) { + } + + void endNamedMixinApplication(Token classKeyword, + Token implementsKeyword, + Token endToken) { + } + + void beginHide(Token hideKeyword) { + } + + void endHide(Token hideKeyword) { + } + + void beginIdentifierList(Token token) { + } + + void endIdentifierList(int count) { + } + + void beginTypeList(Token token) { + } + + void endTypeList(int count) { + } + + void beginIfStatement(Token token) { + } + + void endIfStatement(Token ifToken, Token elseToken) { + } + + void beginImport(Token importKeyword) { + } + + void endImport(Token importKeyword, Token DeferredKeyword, + Token asKeyword, Token semicolon) { + } + + void beginInitializedIdentifier(Token token) { + } + + void endInitializedIdentifier() { + } + + void beginInitializer(Token token) { + } + + void endInitializer(Token assignmentOperator) { + } + + void beginInitializers(Token token) { + } + + void endInitializers(int count, Token beginToken, Token endToken) { + } + + void handleNoInitializers() { + } + + void handleLabel(Token token) { + } + + void beginLabeledStatement(Token token, int labelCount) { + } + + void endLabeledStatement(int labelCount) { + } + + void beginLibraryName(Token token) { + } + + void endLibraryName(Token libraryKeyword, Token semicolon) { + } + + void beginLiteralMapEntry(Token token) { + } + + void endLiteralMapEntry(Token colon, Token endToken) { + } + + void beginLiteralString(Token token) { + } + + void endLiteralString(int interpolationCount) { + } + + void handleStringJuxtaposition(int literalCount) { + } + + void beginMember(Token token) { + } + + void endMethod(Token getOrSet, Token beginToken, Token endToken) { + } + + void beginMetadataStar(Token token) { + } + + void endMetadataStar(int count, bool forParameter) { + } + + void beginMetadata(Token token) { + } + + void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) { + } + + void beginOptionalFormalParameters(Token token) { + } + + void endOptionalFormalParameters(int count, + Token beginToken, Token endToken) { + } + + void beginPart(Token token) { + } + + void endPart(Token partKeyword, Token semicolon) { + } + + void beginPartOf(Token token) { + } + + void endPartOf(Token partKeyword, Token semicolon) { + } + + void beginRedirectingFactoryBody(Token token) { + } + + void endRedirectingFactoryBody(Token beginToken, Token endToken) { + } + + void beginReturnStatement(Token token) { + } + + void endReturnStatement(bool hasExpression, + Token beginToken, Token endToken) { + } + + void beginSend(Token token) { + } + + void endSend(Token token) { + } + + void beginShow(Token showKeyword) { + } + + void endShow(Token showKeyword) { + } + + void beginSwitchStatement(Token token) { + } + + void endSwitchStatement(Token switchKeyword, Token endToken) { + } + + void beginSwitchBlock(Token token) { + } + + void endSwitchBlock(int caseCount, Token beginToken, Token endToken) { + } + + void beginLiteralSymbol(Token token) { + } + + void endLiteralSymbol(Token hashToken, int identifierCount) { + } + + void beginThrowExpression(Token token) { + } + + void endThrowExpression(Token throwToken, Token endToken) { + } + + void beginRethrowStatement(Token token) { + } + + void endRethrowStatement(Token throwToken, Token endToken) { + } + + void endTopLevelDeclaration(Token token) { + } + + void beginTopLevelMember(Token token) { + } + + void endTopLevelFields(int count, Token beginToken, Token endToken) { + } + + void endTopLevelMethod(Token beginToken, Token getOrSet, Token endToken) { + } + + void beginTryStatement(Token token) { + } + + void handleCaseMatch(Token caseKeyword, Token colon) { + } + + void handleCatchBlock(Token onKeyword, Token catchKeyword) { + } + + void handleFinallyBlock(Token finallyKeyword) { + } + + void endTryStatement(int catchCount, Token tryKeyword, Token finallyKeyword) { + } + + void endType(Token beginToken, Token endToken) { + } + + void beginTypeArguments(Token token) { + } + + void endTypeArguments(int count, Token beginToken, Token endToken) { + } + + void handleNoTypeArguments(Token token) { + } + + void beginTypeVariable(Token token) { + } + + void endTypeVariable(Token token) { + } + + void beginTypeVariables(Token token) { + } + + void endTypeVariables(int count, Token beginToken, Token endToken) { + } + + void beginUnamedFunction(Token token) { + } + + void endUnamedFunction(Token token) { + } + + void beginVariablesDeclaration(Token token) { + } + + void endVariablesDeclaration(int count, Token endToken) { + } + + void beginWhileStatement(Token token) { + } + + void endWhileStatement(Token whileKeyword, Token endToken) { + } + + void handleAsOperator(Token operathor, Token endToken) { + // TODO(ahe): Rename [operathor] to "operator" when VM bug is fixed. + } + + void handleAssignmentExpression(Token token) { + } + + void handleBinaryExpression(Token token) { + } + + void handleConditionalExpression(Token question, Token colon) { + } + + void handleConstExpression(Token token) { + } + + void handleFunctionTypedFormalParameter(Token token) { + } + + void handleIdentifier(Token token) { + } + + void handleIndexedExpression(Token openCurlyBracket, + Token closeCurlyBracket) { + } + + void handleIsOperator(Token operathor, Token not, Token endToken) { + // TODO(ahe): Rename [operathor] to "operator" when VM bug is fixed. + } + + void handleLiteralBool(Token token) { + } + + void handleBreakStatement(bool hasTarget, + Token breakKeyword, Token endToken) { + } + + void handleContinueStatement(bool hasTarget, + Token continueKeyword, Token endToken) { + } + + void handleEmptyStatement(Token token) { + } + + void handleAssertStatement(Token assertKeyword, Token semicolonToken) { + } + + /** Called with either the token containing a double literal, or + * an immediately preceding "unary plus" token. + */ + void handleLiteralDouble(Token token) { + } + + /** Called with either the token containing an integer literal, + * or an immediately preceding "unary plus" token. + */ + void handleLiteralInt(Token token) { + } + + void handleLiteralList(int count, Token beginToken, Token constKeyword, + Token endToken) { + } + + void handleLiteralMap(int count, Token beginToken, Token constKeyword, + Token endToken) { + } + + void handleLiteralNull(Token token) { + } + + void handleModifier(Token token) { + } + + void handleModifiers(int count) { + } + + void handleNamedArgument(Token colon) { + } + + void handleNewExpression(Token token) { + } + + void handleNoArguments(Token token) { + } + + void handleNoExpression(Token token) { + } + + void handleNoType(Token token) { + } + + void handleNoTypeVariables(Token token) { + } + + void handleOperator(Token token) { + } + + void handleOperatorName(Token operatorKeyword, Token token) { + } + + void handleParenthesizedExpression(BeginGroupToken token) { + } + + void handleQualified(Token period) { + } + + void handleStringPart(Token token) { + } + + void handleSuperExpression(Token token) { + } + + void handleSwitchCase(int labelCount, int expressionCount, + Token defaultKeyword, int statementCount, + Token firstToken, Token endToken) { + } + + void handleThisExpression(Token token) { + } + + void handleUnaryPostfixAssignmentExpression(Token token) { + } + + void handleUnaryPrefixExpression(Token token) { + } + + void handleUnaryPrefixAssignmentExpression(Token token) { + } + + void handleValuedFormalParameter(Token equals, Token token) { + } + + void handleVoidKeyword(Token token) { + } + + Token expected(String string, Token token) { + error("expected '$string', but got '${token.value}'", token); + return skipToEof(token); + } + + Token expectedIdentifier(Token token) { + error("expected identifier, but got '${token.value}'", token); + return skipToEof(token); + } + + Token expectedType(Token token) { + error("expected a type, but got '${token.value}'", token); + return skipToEof(token); + } + + Token expectedExpression(Token token) { + String message; + if (token.info == BAD_INPUT_INFO) { + message = token.value; + } else { + message = "expected an expression, but got '${token.value}'"; + } + error(message, token); + return skipToEof(token); + } + + Token unexpected(Token token) { + error("unexpected token '${token.value}'", token); + return skipToEof(token); + } + + Token expectedBlockToSkip(Token token) { + error("expected a block, but got '${token.value}'", token); + return skipToEof(token); + } + + Token expectedFunctionBody(Token token) { + error("expected a function body, but got '${token.value}'", token); + return skipToEof(token); + } + + Token expectedClassBody(Token token) { + error("expected a class body, but got '${token.value}'", token); + return skipToEof(token); + } + + Token expectedClassBodyToSkip(Token token) { + error("expected a class body, but got '${token.value}'", token); + return skipToEof(token); + } + + Link expectedDeclaration(Token token) { + error("expected a declaration, but got '${token.value}'", token); + return const Link(); + } + + Token unmatched(Token token) { + error("unmatched '${token.value}'", token); + return skipToEof(token); + } + + skipToEof(Token token) { + while (!identical(token.info, EOF_INFO)) { + token = token.next; + } + return token; + } + + void recoverableError(Token token, String message) { + error(message, token); + } + + void error(String message, Token token) { + throw new ParserError("$message @ ${token.charOffset}"); + } + + void reportError(Spannable spannable, + MessageKind messageKind, + [Map arguments = const {}]) { + String message = messageKind.message(arguments, true).toString(); + Token token; + Node node; + if (spannable is Token) { + token = spannable; + } else if (spannable is Node) { + token = spannable.getBeginToken(); + } else { + throw new ParserError(message); + } + recoverableError(token, message); + } +} + +class ParserError { + final String reason; + ParserError(this.reason); + toString() => reason; +} + +typedef int IdGenerator(); + +/** + * A parser event listener designed to work with [PartialParser]. It + * builds elements representing the top-level declarations found in + * the parsed compilation unit and records them in + * [compilationUnitElement]. + */ +class ElementListener extends Listener { + final IdGenerator idGenerator; + final DiagnosticListener listener; + final CompilationUnitElement compilationUnitElement; + final StringValidator stringValidator; + Link interpolationScope; + + Link nodes = const Link(); + + Link metadata = const Link(); + + ElementListener(DiagnosticListener listener, + this.compilationUnitElement, + this.idGenerator) + : this.listener = listener, + stringValidator = new StringValidator(listener), + interpolationScope = const Link(); + + void pushQuoting(StringQuoting quoting) { + interpolationScope = interpolationScope.prepend(quoting); + } + + StringQuoting popQuoting() { + StringQuoting result = interpolationScope.head; + interpolationScope = interpolationScope.tail; + return result; + } + + StringNode popLiteralString() { + StringNode node = popNode(); + // TODO(lrn): Handle interpolations in script tags. + if (node.isInterpolation) { + listener.internalError(node, + "String interpolation not supported in library tags."); + return null; + } + return node; + } + + bool allowLibraryTags() { + // Library tags are only allowed in the library file itself, not + // in sourced files. + LibraryElement library = compilationUnitElement.getLibrary(); + return !compilationUnitElement.hasMembers + && library.entryCompilationUnit == compilationUnitElement; + } + + void endLibraryName(Token libraryKeyword, Token semicolon) { + Expression name = popNode(); + addLibraryTag(new LibraryName(libraryKeyword, name, + popMetadata(compilationUnitElement))); + } + + void endImport(Token importKeyword, Token deferredKeyword, Token asKeyword, + Token semicolon) { + NodeList combinators = popNode(); + bool isDeferred = deferredKeyword != null; + Identifier prefix; + if (asKeyword != null) { + prefix = popNode(); + } + StringNode uri = popLiteralString(); + addLibraryTag(new Import(importKeyword, uri, prefix, combinators, + popMetadata(compilationUnitElement), + isDeferred: isDeferred)); + } + + void endExport(Token exportKeyword, Token semicolon) { + NodeList combinators = popNode(); + StringNode uri = popNode(); + addLibraryTag(new Export(exportKeyword, uri, combinators, + popMetadata(compilationUnitElement))); + } + + void endCombinators(int count) { + if (0 == count) { + pushNode(null); + } else { + pushNode(makeNodeList(count, null, null, " ")); + } + } + + void endHide(Token hideKeyword) => pushCombinator(hideKeyword); + + void endShow(Token showKeyword) => pushCombinator(showKeyword); + + void pushCombinator(Token keywordToken) { + NodeList identifiers = popNode(); + pushNode(new Combinator(identifiers, keywordToken)); + } + + void endIdentifierList(int count) { + pushNode(makeNodeList(count, null, null, ",")); + } + + void endTypeList(int count) { + pushNode(makeNodeList(count, null, null, ",")); + } + + void endPart(Token partKeyword, Token semicolon) { + StringNode uri = popLiteralString(); + addLibraryTag(new Part(partKeyword, uri, + popMetadata(compilationUnitElement))); + } + + void endPartOf(Token partKeyword, Token semicolon) { + Expression name = popNode(); + addPartOfTag(new PartOf(partKeyword, name, + popMetadata(compilationUnitElement))); + } + + void addPartOfTag(PartOf tag) { + compilationUnitElement.setPartOf(tag, listener); + } + + void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) { + if (periodBeforeName != null) { + popNode(); // Discard name. + } + popNode(); // Discard node (Send or Identifier). + pushMetadata(new PartialMetadataAnnotation(beginToken, endToken)); + } + + void endTopLevelDeclaration(Token token) { + if (!metadata.isEmpty) { + recoverableError(metadata.head.beginToken, + 'Metadata not supported here.'); + metadata = const Link(); + } + } + + void endClassDeclaration(int interfacesCount, Token beginToken, + Token extendsKeyword, Token implementsKeyword, + Token endToken) { + String nativeTagInfo = native.checkForNativeClass(this); + NodeList interfaces = + makeNodeList(interfacesCount, implementsKeyword, null, ","); + Node supertype = popNode(); + NodeList typeParameters = popNode(); + Identifier name = popNode(); + int id = idGenerator(); + ClassElement element = new PartialClassElement( + name.source, beginToken, endToken, compilationUnitElement, id); + element.nativeTagInfo = nativeTagInfo; + pushElement(element); + rejectBuiltInIdentifier(name); + } + + void rejectBuiltInIdentifier(Identifier name) { + if (name.token is KeywordToken) { + Keyword keyword = (name.token as KeywordToken).keyword; + if (!keyword.isPseudo) { + recoverableError(name, "Illegal name '${keyword.syntax}'."); + } + } + } + + void endFunctionTypeAlias(Token typedefKeyword, Token endToken) { + NodeList typeVariables = popNode(); // TOOD(karlklose): do not throw away. + Identifier name = popNode(); + TypeAnnotation returnType = popNode(); + pushElement(new PartialTypedefElement(name.source, compilationUnitElement, + typedefKeyword)); + rejectBuiltInIdentifier(name); + } + + void endNamedMixinApplication(Token classKeyword, + Token implementsKeyword, + Token endToken) { + NodeList interfaces = (implementsKeyword != null) ? popNode() : null; + MixinApplication mixinApplication = popNode(); + Modifiers modifiers = popNode(); + NodeList typeParameters = popNode(); + Identifier name = popNode(); + NamedMixinApplication namedMixinApplication = new NamedMixinApplication( + name, typeParameters, modifiers, mixinApplication, interfaces, + classKeyword, endToken); + + int id = idGenerator(); + Element enclosing = compilationUnitElement; + pushElement(new MixinApplicationElementX(name.source, enclosing, id, + namedMixinApplication, + modifiers)); + rejectBuiltInIdentifier(name); + } + + void endMixinApplication() { + NodeList mixins = popNode(); + TypeAnnotation superclass = popNode(); + pushNode(new MixinApplication(superclass, mixins)); + } + + void handleVoidKeyword(Token token) { + pushNode(new TypeAnnotation(new Identifier(token), null)); + } + + void endTopLevelMethod(Token beginToken, Token getOrSet, Token endToken) { + Identifier name = popNode(); + TypeAnnotation type = popNode(); + Modifiers modifiers = popNode(); + ElementKind kind; + if (getOrSet == null) { + kind = ElementKind.FUNCTION; + } else if (identical(getOrSet.stringValue, 'get')) { + kind = ElementKind.GETTER; + } else if (identical(getOrSet.stringValue, 'set')) { + kind = ElementKind.SETTER; + } + pushElement(new PartialFunctionElement(name.source, beginToken, getOrSet, + endToken, kind, modifiers, + compilationUnitElement, false)); + } + + void endTopLevelFields(int count, Token beginToken, Token endToken) { + void buildFieldElement(Identifier name, VariableList fields) { + pushElement( + new FieldElementX(name, compilationUnitElement, fields)); + } + NodeList variables = makeNodeList(count, null, null, ","); + TypeAnnotation type = popNode(); + Modifiers modifiers = popNode(); + buildFieldElements(modifiers, variables, compilationUnitElement, + buildFieldElement, + beginToken, endToken); + } + + void buildFieldElements(Modifiers modifiers, + NodeList variables, + Element enclosingElement, + void buildFieldElement(Identifier name, + VariableList fields), + Token beginToken, Token endToken) { + VariableList fields = + new PartialFieldList(beginToken, endToken, modifiers); + for (Link variableNodes = variables.nodes; + !variableNodes.isEmpty; + variableNodes = variableNodes.tail) { + Expression initializedIdentifier = variableNodes.head; + Identifier identifier = initializedIdentifier.asIdentifier(); + if (identifier == null) { + identifier = initializedIdentifier.asSendSet().selector.asIdentifier(); + } + buildFieldElement(identifier, fields); + } + } + + void handleIdentifier(Token token) { + pushNode(new Identifier(token)); + } + + void handleQualified(Token period) { + Identifier last = popNode(); + Expression first = popNode(); + pushNode(new Send(first, last)); + } + + void handleNoType(Token token) { + pushNode(null); + } + + void endTypeVariable(Token token) { + TypeAnnotation bound = popNode(); + Identifier name = popNode(); + pushNode(new TypeVariable(name, bound)); + rejectBuiltInIdentifier(name); + } + + void endTypeVariables(int count, Token beginToken, Token endToken) { + pushNode(makeNodeList(count, beginToken, endToken, ',')); + } + + void handleNoTypeVariables(token) { + pushNode(null); + } + + void endTypeArguments(int count, Token beginToken, Token endToken) { + pushNode(makeNodeList(count, beginToken, endToken, ',')); + } + + void handleNoTypeArguments(Token token) { + pushNode(null); + } + + void endType(Token beginToken, Token endToken) { + NodeList typeArguments = popNode(); + Expression typeName = popNode(); + pushNode(new TypeAnnotation(typeName, typeArguments)); + } + + void handleParenthesizedExpression(BeginGroupToken token) { + Expression expression = popNode(); + pushNode(new ParenthesizedExpression(expression, token)); + } + + void handleModifier(Token token) { + pushNode(new Identifier(token)); + } + + void handleModifiers(int count) { + if (count == 0) { + pushNode(Modifiers.EMPTY); + } else { + NodeList modifierNodes = makeNodeList(count, null, null, ' '); + pushNode(new Modifiers(modifierNodes)); + } + } + + Token expected(String string, Token token) { + reportFatalError(token, + "Expected '$string', but got '${token.value}'."); + return skipToEof(token); + } + + Token expectedIdentifier(Token token) { + if (token is KeywordToken) { + reportError( + token, MessageKind.EXPECTED_IDENTIFIER_NOT_RESERVED_WORD, + {'keyword': token.value}); + } else { + reportFatalError(token, + "Expected identifier, but got '${token.value}'."); + } + return token; + } + + Token expectedType(Token token) { + reportFatalError(token, + "Expected a type, but got '${token.value}'."); + pushNode(null); + return skipToEof(token); + } + + Token expectedExpression(Token token) { + if (token.info == BAD_INPUT_INFO) { + reportError(token, MessageKind.GENERIC, {'text': token.value}); + pushNode(new ErrorExpression(token)); + return token.next; + } else { + reportFatalError(token, + "Expected an expression, but got '${token.value}'."); + pushNode(null); + return skipToEof(token); + } + } + + Token unexpected(Token token) { + String message = "Unexpected token '${token.value}'."; + if (token.info == BAD_INPUT_INFO) { + message = token.value; + } + reportFatalError(token, message); + return skipToEof(token); + } + + Token expectedBlockToSkip(Token token) { + if (identical(token.stringValue, 'native')) { + return native.handleNativeBlockToSkip(this, token); + } else { + return unexpected(token); + } + } + + Token expectedFunctionBody(Token token) { + String printString = token.value; + reportFatalError(token, + "Expected a function body, but got '$printString'."); + return skipToEof(token); + } + + Token expectedClassBody(Token token) { + reportFatalError(token, + "Expected a class body, but got '${token.value}'."); + return skipToEof(token); + } + + Token expectedClassBodyToSkip(Token token) { + if (identical(token.stringValue, 'native')) { + return native.handleNativeClassBodyToSkip(this, token); + } else { + return unexpected(token); + } + } + + Link expectedDeclaration(Token token) { + reportFatalError(token, + "Expected a declaration, but got '${token.value}'."); + return const Link(); + } + + Token unmatched(Token token) { + reportFatalError(token, + "Unmatched '${token.value}'."); + return skipToEof(token); + } + + void recoverableError(Spannable node, String message) { + // TODO(johnniwinther): Make recoverable errors non-fatal. + reportFatalError(node, message); + } + + void pushElement(Element element) { + popMetadata(element); + compilationUnitElement.addMember(element, listener); + } + + Link popMetadata(Element element) { + var result = const Link(); + for (Link link = metadata; !link.isEmpty; link = link.tail) { + element.addMetadata(link.head); + // Reverse the list as is implicitly done by addMetadata. + result = result.prepend(link.head); + } + metadata = const Link(); + return result; + } + + void pushMetadata(MetadataAnnotation annotation) { + metadata = metadata.prepend(annotation); + } + + void addLibraryTag(LibraryTag tag) { + if (!allowLibraryTags()) { + recoverableError(tag, 'Library tags not allowed here.'); + } + compilationUnitElement.getImplementationLibrary().addTag(tag, listener); + } + + void pushNode(Node node) { + nodes = nodes.prepend(node); + if (VERBOSE) log("push $nodes"); + } + + Node popNode() { + assert(!nodes.isEmpty); + Node node = nodes.head; + nodes = nodes.tail; + if (VERBOSE) log("pop $nodes"); + return node; + } + + void log(message) { + print(message); + } + + NodeList makeNodeList(int count, Token beginToken, Token endToken, + String delimiter) { + Link poppedNodes = const Link(); + for (; count > 0; --count) { + // This effectively reverses the order of nodes so they end up + // in correct (source) order. + poppedNodes = poppedNodes.prepend(popNode()); + } + return new NodeList(beginToken, poppedNodes, endToken, delimiter); + } + + void beginLiteralString(Token token) { + String source = token.value; + StringQuoting quoting = StringValidator.quotingFromString(source); + pushQuoting(quoting); + // Just wrap the token for now. At the end of the interpolation, + // when we know how many there are, go back and validate the tokens. + pushNode(new LiteralString(token, null)); + } + + void handleStringPart(Token token) { + // Just push an unvalidated token now, and replace it when we know the + // end of the interpolation. + pushNode(new LiteralString(token, null)); + } + + void endLiteralString(int count) { + StringQuoting quoting = popQuoting(); + + Link parts = + const Link(); + // Parts of the string interpolation are popped in reverse order, + // starting with the last literal string part. + bool isLast = true; + for (int i = 0; i < count; i++) { + LiteralString string = popNode(); + DartString validation = + stringValidator.validateInterpolationPart(string.token, quoting, + isFirst: false, + isLast: isLast); + // Replace the unvalidated LiteralString with a new LiteralString + // object that has the validation result included. + string = new LiteralString(string.token, validation); + Expression expression = popNode(); + parts = parts.prepend(new StringInterpolationPart(expression, string)); + isLast = false; + } + + LiteralString string = popNode(); + DartString validation = + stringValidator.validateInterpolationPart(string.token, quoting, + isFirst: true, + isLast: isLast); + string = new LiteralString(string.token, validation); + if (isLast) { + pushNode(string); + } else { + NodeList partNodes = new NodeList(null, parts, null, ""); + pushNode(new StringInterpolation(string, partNodes)); + } + } + + void handleStringJuxtaposition(int stringCount) { + assert(stringCount != 0); + Expression accumulator = popNode(); + stringCount--; + while (stringCount > 0) { + Expression expression = popNode(); + accumulator = new StringJuxtaposition(expression, accumulator); + stringCount--; + } + pushNode(accumulator); + } + + void reportFatalError(Spannable spannable, + String message) { + listener.reportFatalError( + spannable, MessageKind.GENERIC, {'text': message}); + } + + void reportError(Spannable spannable, + MessageKind errorCode, + [Map arguments = const {}]) { + listener.reportError(spannable, errorCode, arguments); + } +} + +class NodeListener extends ElementListener { + NodeListener(DiagnosticListener listener, CompilationUnitElement element) + : super(listener, element, null); + + void addLibraryTag(LibraryTag tag) { + pushNode(tag); + } + + void addPartOfTag(PartOf tag) { + pushNode(tag); + } + + void endClassDeclaration(int interfacesCount, Token beginToken, + Token extendsKeyword, Token implementsKeyword, + Token endToken) { + NodeList body = popNode(); + NodeList interfaces = + makeNodeList(interfacesCount, implementsKeyword, null, ","); + Node supertype = popNode(); + NodeList typeParameters = popNode(); + Identifier name = popNode(); + Modifiers modifiers = popNode(); + pushNode(new ClassNode(modifiers, name, typeParameters, supertype, + interfaces, beginToken, extendsKeyword, body, + endToken)); + } + + void endCompilationUnit(int count, Token token) { + pushNode(makeNodeList(count, null, null, '\n')); + } + + void endFunctionTypeAlias(Token typedefKeyword, Token endToken) { + NodeList formals = popNode(); + NodeList typeParameters = popNode(); + Identifier name = popNode(); + TypeAnnotation returnType = popNode(); + pushNode(new Typedef(returnType, name, typeParameters, formals, + typedefKeyword, endToken)); + } + + void endNamedMixinApplication(Token classKeyword, + Token implementsKeyword, + Token endToken) { + NodeList interfaces = (implementsKeyword != null) ? popNode() : null; + Node mixinApplication = popNode(); + Modifiers modifiers = popNode(); + NodeList typeParameters = popNode(); + Identifier name = popNode(); + pushNode(new NamedMixinApplication(name, typeParameters, + modifiers, mixinApplication, + interfaces, + classKeyword, endToken)); + } + + void endClassBody(int memberCount, Token beginToken, Token endToken) { + pushNode(makeNodeList(memberCount, beginToken, endToken, null)); + } + + void endTopLevelFields(int count, Token beginToken, Token endToken) { + NodeList variables = makeNodeList(count, null, endToken, ","); + TypeAnnotation type = popNode(); + Modifiers modifiers = popNode(); + pushNode(new VariableDefinitions(type, modifiers, variables)); + } + + void endTopLevelMethod(Token beginToken, Token getOrSet, Token endToken) { + Statement body = popNode(); + NodeList formalParameters = popNode(); + Identifier name = popNode(); + TypeAnnotation type = popNode(); + Modifiers modifiers = popNode(); + ElementKind kind; + if (getOrSet == null) { + kind = ElementKind.FUNCTION; + } else if (identical(getOrSet.stringValue, 'get')) { + kind = ElementKind.GETTER; + } else if (identical(getOrSet.stringValue, 'set')) { + kind = ElementKind.SETTER; + } + pushElement(new PartialFunctionElement(name.source, beginToken, getOrSet, + endToken, kind, modifiers, + compilationUnitElement, false)); + } + + void endFormalParameter(Token thisKeyword) { + Expression name = popNode(); + if (thisKeyword != null) { + Identifier thisIdentifier = new Identifier(thisKeyword); + if (name.asSend() == null) { + name = new Send(thisIdentifier, name); + } else { + name = name.asSend().copyWithReceiver(thisIdentifier); + } + } + TypeAnnotation type = popNode(); + Modifiers modifiers = popNode(); + NodeList metadata = popNode(); + pushNode(new VariableDefinitions.forParameter( + metadata, type, modifiers, new NodeList.singleton(name))); + } + + void endFormalParameters(int count, Token beginToken, Token endToken) { + pushNode(makeNodeList(count, beginToken, endToken, ",")); + } + + void handleNoFormalParameters(Token token) { + pushNode(null); + } + + void endArguments(int count, Token beginToken, Token endToken) { + pushNode(makeNodeList(count, beginToken, endToken, ",")); + } + + void handleNoArguments(Token token) { + pushNode(null); + } + + void endConstructorReference(Token start, Token periodBeforeName, + Token endToken) { + Identifier name = null; + if (periodBeforeName != null) { + name = popNode(); + } + NodeList typeArguments = popNode(); + Node classReference = popNode(); + if (typeArguments != null) { + classReference = new TypeAnnotation(classReference, typeArguments); + } else { + Identifier identifier = classReference.asIdentifier(); + Send send = classReference.asSend(); + if (identifier != null) { + // TODO(ahe): Should be: + // classReference = new Send(null, identifier); + classReference = identifier; + } else if (send != null) { + classReference = send; + } else { + internalError(node: classReference); + } + } + Node constructor = classReference; + if (name != null) { + // Either typeName.name or x.y.name. + constructor = new Send(classReference, name); + } + pushNode(constructor); + } + + void endRedirectingFactoryBody(Token beginToken, + Token endToken) { + pushNode(new Return(beginToken, endToken, popNode())); + } + + void endReturnStatement(bool hasExpression, + Token beginToken, Token endToken) { + Expression expression = hasExpression ? popNode() : null; + pushNode(new Return(beginToken, endToken, expression)); + } + + void endExpressionStatement(Token token) { + pushNode(new ExpressionStatement(popNode(), token)); + } + + void handleOnError(Token token, var errorInformation) { + listener.internalError(token, "'${token.value}': ${errorInformation}"); + } + + Token expectedFunctionBody(Token token) { + if (identical(token.stringValue, 'native')) { + return native.handleNativeFunctionBody(this, token); + } else { + reportFatalError(token, + "Expected a function body, but got '${token.value}'."); + return skipToEof(token); + } + } + + Token expectedClassBody(Token token) { + if (identical(token.stringValue, 'native')) { + return native.handleNativeClassBody(this, token); + } else { + reportFatalError(token, + "Expected a class body, but got '${token.value}'."); + return skipToEof(token); + } + } + + void handleLiteralInt(Token token) { + pushNode(new LiteralInt(token, (t, e) => handleOnError(t, e))); + } + + void handleLiteralDouble(Token token) { + pushNode(new LiteralDouble(token, (t, e) => handleOnError(t, e))); + } + + void handleLiteralBool(Token token) { + pushNode(new LiteralBool(token, (t, e) => handleOnError(t, e))); + } + + void handleLiteralNull(Token token) { + pushNode(new LiteralNull(token)); + } + + void endLiteralSymbol(Token hashToken, int identifierCount) { + NodeList identifiers = makeNodeList(identifierCount, null, null, '.'); + pushNode(new LiteralSymbol(hashToken, identifiers)); + } + + void handleBinaryExpression(Token token) { + Node argument = popNode(); + Node receiver = popNode(); + String tokenString = token.stringValue; + if (identical(tokenString, '.') || identical(tokenString, '..')) { + Send argumentSend = argument.asSend(); + if (argumentSend == null) { + // TODO(ahe): The parser should diagnose this problem, not + // this listener. + reportFatalError(argument, + 'Expected an identifier.'); + } + if (argumentSend.receiver != null) internalError(node: argument); + if (argument is SendSet) internalError(node: argument); + pushNode(argument.asSend().copyWithReceiver(receiver)); + } else { + NodeList arguments = new NodeList.singleton(argument); + pushNode(new Send(receiver, new Operator(token), arguments)); + } + if (identical(tokenString, '===')) { + listener.reportError(token, MessageKind.UNSUPPORTED_EQ_EQ_EQ, + {'lhs': receiver, 'rhs': argument}); + } + if (identical(tokenString, '!==')) { + listener.reportError(token, MessageKind.UNSUPPORTED_BANG_EQ_EQ, + {'lhs': receiver, 'rhs': argument}); + } + } + + void beginCascade(Token token) { + pushNode(new CascadeReceiver(popNode(), token)); + } + + void endCascade() { + pushNode(new Cascade(popNode())); + } + + void handleAsOperator(Token operathor, Token endToken) { + TypeAnnotation type = popNode(); + Expression expression = popNode(); + NodeList arguments = new NodeList.singleton(type); + pushNode(new Send(expression, new Operator(operathor), arguments)); + } + + void handleAssignmentExpression(Token token) { + Node arg = popNode(); + Node node = popNode(); + Send send = node.asSend(); + if (send == null || !(send.isPropertyAccess || send.isIndex)) { + reportNotAssignable(node); + } + if (send.asSendSet() != null) internalError(node: send); + NodeList arguments; + if (send.isIndex) { + Link link = const Link().prepend(arg); + link = link.prepend(send.arguments.head); + arguments = new NodeList(null, link); + } else { + arguments = new NodeList.singleton(arg); + } + Operator op = new Operator(token); + pushNode(new SendSet(send.receiver, send.selector, op, arguments)); + } + + void reportNotAssignable(Node node) { + // TODO(ahe): The parser should diagnose this problem, not this + // listener. + reportFatalError(node, + 'Not assignable.'); + } + + void handleConditionalExpression(Token question, Token colon) { + Node elseExpression = popNode(); + Node thenExpression = popNode(); + Node condition = popNode(); + pushNode(new Conditional( + condition, thenExpression, elseExpression, question, colon)); + } + + void endSend(Token token) { + NodeList arguments = popNode(); + Node selector = popNode(); + // TODO(ahe): Handle receiver. + pushNode(new Send(null, selector, arguments)); + } + + void endFunctionBody(int count, Token beginToken, Token endToken) { + if (count == 0 && beginToken == null) { + pushNode(new EmptyStatement(endToken)); + } else { + pushNode(new Block(makeNodeList(count, beginToken, endToken, null))); + } + } + + void skippedFunctionBody(Token token) { + pushNode(new Block(new NodeList.empty())); + } + + void handleNoFunctionBody(Token token) { + pushNode(new EmptyStatement(token)); + } + + void endFunction(Token getOrSet, Token endToken) { + Statement body = popNode(); + NodeList initializers = popNode(); + NodeList formals = popNode(); + // The name can be an identifier or a send in case of named constructors. + Expression name = popNode(); + TypeAnnotation type = popNode(); + Modifiers modifiers = popNode(); + pushNode(new FunctionExpression(name, formals, body, type, + modifiers, initializers, getOrSet)); + } + + void endFunctionDeclaration(Token endToken) { + pushNode(new FunctionDeclaration(popNode())); + } + + void endVariablesDeclaration(int count, Token endToken) { + // TODO(ahe): Pick one name for this concept, either + // VariablesDeclaration or VariableDefinitions. + NodeList variables = makeNodeList(count, null, endToken, ","); + TypeAnnotation type = popNode(); + Modifiers modifiers = popNode(); + pushNode(new VariableDefinitions(type, modifiers, variables)); + } + + void endInitializer(Token assignmentOperator) { + Expression initializer = popNode(); + NodeList arguments = new NodeList.singleton(initializer); + Expression name = popNode(); + Operator op = new Operator(assignmentOperator); + pushNode(new SendSet(null, name, op, arguments)); + } + + void endIfStatement(Token ifToken, Token elseToken) { + Statement elsePart = (elseToken == null) ? null : popNode(); + Statement thenPart = popNode(); + ParenthesizedExpression condition = popNode(); + pushNode(new If(condition, thenPart, elsePart, ifToken, elseToken)); + } + + void endForStatement(int updateExpressionCount, + Token beginToken, Token endToken) { + Statement body = popNode(); + NodeList updates = makeNodeList(updateExpressionCount, null, null, ','); + Statement condition = popNode(); + Node initializer = popNode(); + pushNode(new For(initializer, condition, updates, body, beginToken)); + } + + void handleNoExpression(Token token) { + pushNode(null); + } + + void endDoWhileStatement(Token doKeyword, Token whileKeyword, + Token endToken) { + Expression condition = popNode(); + Statement body = popNode(); + pushNode(new DoWhile(body, condition, doKeyword, whileKeyword, endToken)); + } + + void endWhileStatement(Token whileKeyword, Token endToken) { + Statement body = popNode(); + Expression condition = popNode(); + pushNode(new While(condition, body, whileKeyword)); + } + + void endBlock(int count, Token beginToken, Token endToken) { + pushNode(new Block(makeNodeList(count, beginToken, endToken, null))); + } + + void endThrowExpression(Token throwToken, Token endToken) { + Expression expression = popNode(); + pushNode(new Throw(expression, throwToken, endToken)); + } + + void endRethrowStatement(Token throwToken, Token endToken) { + pushNode(new Rethrow(throwToken, endToken)); + if (identical(throwToken.stringValue, 'throw')) { + listener.reportError(throwToken, + MessageKind.UNSUPPORTED_THROW_WITHOUT_EXP); + } + } + + void handleUnaryPrefixExpression(Token token) { + pushNode(new Send.prefix(popNode(), new Operator(token))); + } + + void handleSuperExpression(Token token) { + pushNode(new Identifier(token)); + } + + void handleThisExpression(Token token) { + pushNode(new Identifier(token)); + } + + void handleUnaryAssignmentExpression(Token token, bool isPrefix) { + Node node = popNode(); + Send send = node.asSend(); + if (send == null) { + reportNotAssignable(node); + } + if (!(send.isPropertyAccess || send.isIndex)) { + reportNotAssignable(node); + } + if (send.asSendSet() != null) internalError(node: send); + Node argument = null; + if (send.isIndex) argument = send.arguments.head; + Operator op = new Operator(token); + + if (isPrefix) { + pushNode(new SendSet.prefix(send.receiver, send.selector, op, argument)); + } else { + pushNode(new SendSet.postfix(send.receiver, send.selector, op, argument)); + } + } + + void handleUnaryPostfixAssignmentExpression(Token token) { + handleUnaryAssignmentExpression(token, false); + } + + void handleUnaryPrefixAssignmentExpression(Token token) { + handleUnaryAssignmentExpression(token, true); + } + + void endInitializers(int count, Token beginToken, Token endToken) { + pushNode(makeNodeList(count, beginToken, null, ',')); + } + + void handleNoInitializers() { + pushNode(null); + } + + void endFields(int count, Token beginToken, Token endToken) { + NodeList variables = makeNodeList(count, null, endToken, ","); + TypeAnnotation type = popNode(); + Modifiers modifiers = popNode(); + pushNode(new VariableDefinitions(type, modifiers, variables)); + } + + void endMethod(Token getOrSet, Token beginToken, Token endToken) { + Statement body = popNode(); + NodeList initializers = popNode(); + NodeList formalParameters = popNode(); + Expression name = popNode(); + TypeAnnotation returnType = popNode(); + Modifiers modifiers = popNode(); + pushNode(new FunctionExpression(name, formalParameters, body, returnType, + modifiers, initializers, getOrSet)); + } + + void handleLiteralMap(int count, Token beginToken, Token constKeyword, + Token endToken) { + NodeList entries = makeNodeList(count, beginToken, endToken, ','); + NodeList typeArguments = popNode(); + pushNode(new LiteralMap(typeArguments, entries, constKeyword)); + } + + void endLiteralMapEntry(Token colon, Token endToken) { + Expression value = popNode(); + Expression key = popNode(); + pushNode(new LiteralMapEntry(key, colon, value)); + } + + void handleLiteralList(int count, Token beginToken, Token constKeyword, + Token endToken) { + NodeList elements = makeNodeList(count, beginToken, endToken, ','); + pushNode(new LiteralList(popNode(), elements, constKeyword)); + } + + void handleIndexedExpression(Token openSquareBracket, + Token closeSquareBracket) { + NodeList arguments = + makeNodeList(1, openSquareBracket, closeSquareBracket, null); + Node receiver = popNode(); + Token token = new StringToken.fromString(INDEX_INFO, '[]', + openSquareBracket.charOffset); + Node selector = new Operator(token); + pushNode(new Send(receiver, selector, arguments)); + } + + void handleNewExpression(Token token) { + NodeList arguments = popNode(); + Node name = popNode(); + pushNode(new NewExpression(token, new Send(null, name, arguments))); + } + + void handleConstExpression(Token token) { + // [token] carries the 'const' information. + handleNewExpression(token); + } + + void handleOperator(Token token) { + pushNode(new Operator(token)); + } + + void handleOperatorName(Token operatorKeyword, Token token) { + Operator op = new Operator(token); + pushNode(new Send(new Identifier(operatorKeyword), op, null)); + } + + void handleNamedArgument(Token colon) { + Expression expression = popNode(); + Identifier name = popNode(); + pushNode(new NamedArgument(name, colon, expression)); + } + + void endOptionalFormalParameters(int count, + Token beginToken, Token endToken) { + pushNode(makeNodeList(count, beginToken, endToken, ',')); + } + + void handleFunctionTypedFormalParameter(Token endToken) { + NodeList formals = popNode(); + Identifier name = popNode(); + TypeAnnotation returnType = popNode(); + pushNode(null); // Signal "no type" to endFormalParameter. + pushNode(new FunctionExpression(name, formals, null, returnType, + Modifiers.EMPTY, null, null)); + } + + void handleValuedFormalParameter(Token equals, Token token) { + Expression defaultValue = popNode(); + Expression parameterName = popNode(); + pushNode(new SendSet(null, parameterName, new Operator(equals), + new NodeList.singleton(defaultValue))); + } + + void endTryStatement(int catchCount, Token tryKeyword, Token finallyKeyword) { + Block finallyBlock = null; + if (finallyKeyword != null) { + finallyBlock = popNode(); + } + NodeList catchBlocks = makeNodeList(catchCount, null, null, null); + Block tryBlock = popNode(); + pushNode(new TryStatement(tryBlock, catchBlocks, finallyBlock, + tryKeyword, finallyKeyword)); + } + + void handleCaseMatch(Token caseKeyword, Token colon) { + pushNode(new CaseMatch(caseKeyword, popNode(), colon)); + } + + void handleCatchBlock(Token onKeyword, Token catchKeyword) { + Block block = popNode(); + NodeList formals = catchKeyword != null? popNode(): null; + TypeAnnotation type = onKeyword != null ? popNode() : null; + pushNode(new CatchBlock(type, formals, block, onKeyword, catchKeyword)); + } + + void endSwitchStatement(Token switchKeyword, Token endToken) { + NodeList cases = popNode(); + ParenthesizedExpression expression = popNode(); + pushNode(new SwitchStatement(expression, cases, switchKeyword)); + } + + void endSwitchBlock(int caseCount, Token beginToken, Token endToken) { + Link caseNodes = const Link(); + while (caseCount > 0) { + SwitchCase switchCase = popNode(); + caseNodes = caseNodes.prepend(switchCase); + caseCount--; + } + pushNode(new NodeList(beginToken, caseNodes, endToken, null)); + } + + void handleSwitchCase(int labelCount, int caseCount, + Token defaultKeyword, int statementCount, + Token firstToken, Token endToken) { + NodeList statements = makeNodeList(statementCount, null, null, null); + NodeList labelsAndCases = + makeNodeList(labelCount + caseCount, null, null, null); + pushNode(new SwitchCase(labelsAndCases, defaultKeyword, statements, + firstToken)); + } + + void handleBreakStatement(bool hasTarget, + Token breakKeyword, Token endToken) { + Identifier target = null; + if (hasTarget) { + target = popNode(); + } + pushNode(new BreakStatement(target, breakKeyword, endToken)); + } + + void handleContinueStatement(bool hasTarget, + Token continueKeyword, Token endToken) { + Identifier target = null; + if (hasTarget) { + target = popNode(); + } + pushNode(new ContinueStatement(target, continueKeyword, endToken)); + } + + void handleEmptyStatement(Token token) { + pushNode(new EmptyStatement(token)); + } + + void endFactoryMethod(Token beginToken, Token endToken) { + Statement body = popNode(); + NodeList formals = popNode(); + Node name = popNode(); + + // TODO(ahe): Move this parsing to the parser. + int modifierCount = 0; + Token modifier = beginToken; + if (modifier.stringValue == "external") { + handleModifier(modifier); + modifierCount++; + modifier = modifier.next; + } + if (modifier.stringValue == "const") { + handleModifier(modifier); + modifierCount++; + modifier = modifier.next; + } + assert(modifier.stringValue == "factory"); + handleModifier(modifier); + modifierCount++; + handleModifiers(modifierCount); + Modifiers modifiers = popNode(); + + pushNode(new FunctionExpression(name, formals, body, null, + modifiers, null, null)); + } + + void endForIn(Token beginToken, Token inKeyword, Token endToken) { + Statement body = popNode(); + Expression expression = popNode(); + Node declaredIdentifier = popNode(); + pushNode(new ForIn(declaredIdentifier, expression, body, + beginToken, inKeyword)); + } + + void endMetadataStar(int count, bool forParameter) { + // TODO(johnniwinther): Handle metadata for all node kinds. + if (forParameter) { + if (0 == count) { + pushNode(null); + } else { + pushNode(makeNodeList(count, null, null, ' ')); + } + } + } + + void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) { + NodeList arguments = popNode(); + if (arguments == null) { + // This is a constant expression. + Identifier name; + if (periodBeforeName != null) { + name = popNode(); + } + NodeList typeArguments = popNode(); + Node receiver = popNode(); + if (typeArguments != null) { + receiver = new TypeAnnotation(receiver, typeArguments); + recoverableError(typeArguments, + 'Type arguments are not allowed here.'); + } else { + Identifier identifier = receiver.asIdentifier(); + Send send = receiver.asSend(); + if (identifier != null) { + receiver = new Send(null, identifier); + } else if (send == null) { + internalError(node: receiver); + } + } + Send send = receiver; + if (name != null) { + send = new Send(receiver, name); + } + pushNode(new Metadata(beginToken, send)); + } else { + // This is a const constructor call. + endConstructorReference(beginToken, periodBeforeName, endToken); + Node constructor = popNode(); + pushNode(new Metadata(beginToken, + new NewExpression(null, + new Send(null, constructor, arguments)))); + } + } + + void handleAssertStatement(Token assertKeyword, Token semicolonToken) { + NodeList arguments = popNode(); + Node selector = new Identifier(assertKeyword); + Node send = new Send(null, selector, arguments); + pushNode(new ExpressionStatement(send, semicolonToken)); + } + + void endUnamedFunction(Token token) { + Statement body = popNode(); + NodeList formals = popNode(); + pushNode(new FunctionExpression(null, formals, body, null, + Modifiers.EMPTY, null, null)); + } + + void handleIsOperator(Token operathor, Token not, Token endToken) { + TypeAnnotation type = popNode(); + Expression expression = popNode(); + Node argument; + if (not != null) { + argument = new Send.prefix(type, new Operator(not)); + } else { + argument = type; + } + + NodeList arguments = new NodeList.singleton(argument); + pushNode(new Send(expression, new Operator(operathor), arguments)); + } + + void handleLabel(Token colon) { + Identifier name = popNode(); + pushNode(new Label(name, colon)); + } + + void endLabeledStatement(int labelCount) { + Statement statement = popNode(); + NodeList labels = makeNodeList(labelCount, null, null, null); + pushNode(new LabeledStatement(labels, statement)); + } + + void log(message) { + listener.log(message); + } + + void internalError({Token token, Node node}) { + // TODO(ahe): This should call listener.internalError. + Spannable spannable = (token == null) ? node : token; + throw new SpannableAssertionFailure(spannable, 'Internal error in parser.'); + } +} + +class PartialFunctionElement extends FunctionElementX { + final Token beginToken; + final Token getOrSet; + final Token endToken; + + /** + * The position is computed in the constructor using [findMyName]. Computing + * it on demand fails in case tokens are GC'd. + */ + final Token _position; + + PartialFunctionElement(String name, + Token beginToken, + this.getOrSet, + this.endToken, + ElementKind kind, + Modifiers modifiers, + Element enclosing, + bool hasNoBody) + : super(name, kind, modifiers, enclosing, hasNoBody), + beginToken = beginToken, + _position = ElementX.findNameToken( + beginToken, + modifiers.isFactory() || + identical(kind, ElementKind.GENERATIVE_CONSTRUCTOR), + name, enclosing.name); + + FunctionExpression parseNode(DiagnosticListener listener) { + if (cachedNode != null) return cachedNode; + parseFunction(Parser p) { + if (isMember() && modifiers.isFactory()) { + p.parseFactoryMethod(beginToken); + } else { + p.parseFunction(beginToken, getOrSet); + } + } + cachedNode = parse(listener, getCompilationUnit(), parseFunction); + return cachedNode; + } + + Token position() => _position; +} + +class PartialFieldList extends VariableList { + final Token beginToken; + final Token endToken; + + PartialFieldList(Token this.beginToken, + Token this.endToken, + Modifiers modifiers) + : super(modifiers); + + VariableDefinitions parseNode(Element element, DiagnosticListener listener) { + if (definitions != null) return definitions; + definitions = parse(listener, + element.getCompilationUnit(), + (p) => p.parseVariablesDeclaration(beginToken)); + if (!definitions.modifiers.isVar() && + !definitions.modifiers.isFinal() && + !definitions.modifiers.isConst() && + definitions.type == null) { + listener.reportError( + definitions, + MessageKind.GENERIC, + { 'text': 'A field declaration must start with var, final, ' + 'const, or a type annotation.' }); + } + return definitions; + } + + computeType(Element element, Compiler compiler) { + if (type != null) return type; + // TODO(johnniwinther): Compute this in the resolver. + compiler.withCurrentElement(element, () { + VariableDefinitions node = parseNode(element, compiler); + if (node.type != null) { + type = compiler.resolver.resolveTypeAnnotation(element, node.type); + } else { + type = compiler.types.dynamicType; + } + }); + assert(type != null); + return type; + } +} + +class PartialTypedefElement extends TypedefElementX { + final Token token; + + PartialTypedefElement(String name, Element enclosing, this.token) + : super(name, enclosing); + + Node parseNode(DiagnosticListener listener) { + if (cachedNode != null) return cachedNode; + cachedNode = parse(listener, + getCompilationUnit(), + (p) => p.parseTopLevelDeclaration(token)); + return cachedNode; + } + + Token position() => findMyName(token); +} + +/// A [MetadataAnnotation] which is constructed on demand. +class PartialMetadataAnnotation extends MetadataAnnotationX { + final Token beginToken; + final Token tokenAfterEndToken; + Expression cachedNode; + + PartialMetadataAnnotation(this.beginToken, this.tokenAfterEndToken); + + Token get endToken { + Token token = beginToken; + while (token.kind != EOF_TOKEN) { + if (identical(token.next, tokenAfterEndToken)) break; + token = token.next; + } + assert(token != null); + return token; + } + + Node parseNode(DiagnosticListener listener) { + if (cachedNode != null) return cachedNode; + Metadata metadata = parse(listener, + annotatedElement.getCompilationUnit(), + (p) => p.parseMetadata(beginToken)); + cachedNode = metadata.expression; + return cachedNode; + } +} + +Node parse(DiagnosticListener diagnosticListener, + CompilationUnitElement element, + doParse(Parser parser)) { + NodeListener listener = new NodeListener(diagnosticListener, element); + doParse(new Parser(listener)); + Node node = listener.popNode(); + assert(listener.nodes.isEmpty); + return node; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/parser.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/parser.dart new file mode 100644 index 0000000..e28b110 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/parser.dart @@ -0,0 +1,2447 @@ +// Copyright (c) 2012, 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. + +part of scanner; + +class FormalParameterType { + final String type; + const FormalParameterType(this.type); + bool get isRequired => this == REQUIRED; + bool get isPositional => this == POSITIONAL; + bool get isNamed => this == NAMED; + static final REQUIRED = const FormalParameterType('required'); + static final POSITIONAL = const FormalParameterType('positional'); + static final NAMED = const FormalParameterType('named'); +} + +/** + * An event generating parser of Dart programs. This parser expects + * all tokens in a linked list (aka a token stream). + * + * The class [Scanner] is used to generate a token stream. See the + * file scanner.dart. + * + * Subclasses of the class [Listener] are used to listen to events. + * + * Most methods of this class belong in one of two major categories: + * parse metods and peek methods. Parse methods all have the prefix + * parse, and peek methods all have the prefix peek. + * + * Parse methods generate events (by calling methods on [listener]) + * and return the next token to parse. Peek methods do not generate + * events (except for errors) and may return null. + * + * Parse methods are generally named parseGrammarProductionSuffix. The + * suffix can be one of "opt", or "star". "opt" means zero or one + * matches, "star" means zero or more matches. For example, + * [parseMetadataStar] corresponds to this grammar snippet: [: + * metadata* :], and [parseTypeOpt] corresponds to: [: type? :]. + */ +class Parser { + final Listener listener; + bool mayParseFunctionExpressions = true; + + Parser(this.listener); + + Token parseUnit(Token token) { + listener.beginCompilationUnit(token); + int count = 0; + while (!identical(token.kind, EOF_TOKEN)) { + token = parseTopLevelDeclaration(token); + listener.endTopLevelDeclaration(token); + count++; + } + listener.endCompilationUnit(count, token); + return token; + } + + Token parseTopLevelDeclaration(Token token) { + token = parseMetadataStar(token); + final String value = token.stringValue; + if ((identical(value, 'abstract') && optional('class', token.next)) + || identical(value, 'class')) { + return parseClassOrNamedMixinApplication(token); + } else if (identical(value, 'typedef')) { + return parseTypedef(token); + } else if (identical(value, 'library')) { + return parseLibraryName(token); + } else if (identical(value, 'import')) { + return parseImport(token); + } else if (identical(value, 'export')) { + return parseExport(token); + } else if (identical(value, 'part')) { + return parsePartOrPartOf(token); + } else { + return parseTopLevelMember(token); + } + } + + /// library qualified ';' + Token parseLibraryName(Token token) { + Token libraryKeyword = token; + listener.beginLibraryName(libraryKeyword); + assert(optional('library', token)); + token = parseQualified(token.next); + Token semicolon = token; + token = expect(';', token); + listener.endLibraryName(libraryKeyword, semicolon); + return token; + } + + /// import uri (as identifier)? combinator* ';' + Token parseImport(Token token) { + Token importKeyword = token; + listener.beginImport(importKeyword); + assert(optional('import', token)); + token = parseLiteralStringOrRecoverExpression(token.next); + Token deferredKeyword; + if (optional('deferred', token)) { + deferredKeyword = token; + token = token.next; + } + Token asKeyword; + if (optional('as', token)) { + asKeyword = token; + token = parseIdentifier(token.next); + } + token = parseCombinators(token); + Token semicolon = token; + token = expect(';', token); + listener.endImport(importKeyword, deferredKeyword, asKeyword, semicolon); + return token; + } + + /// export uri combinator* ';' + Token parseExport(Token token) { + Token exportKeyword = token; + listener.beginExport(exportKeyword); + assert(optional('export', token)); + token = parseLiteralStringOrRecoverExpression(token.next); + token = parseCombinators(token); + Token semicolon = token; + token = expect(';', token); + listener.endExport(exportKeyword, semicolon); + return token; + } + + Token parseCombinators(Token token) { + listener.beginCombinators(token); + int count = 0; + while (true) { + String value = token.stringValue; + if (identical('hide', value)) { + token = parseHide(token); + } else if (identical('show', value)) { + token = parseShow(token); + } else { + listener.endCombinators(count); + break; + } + count++; + } + return token; + } + + /// hide identifierList + Token parseHide(Token token) { + Token hideKeyword = token; + listener.beginHide(hideKeyword); + assert(optional('hide', token)); + token = parseIdentifierList(token.next); + listener.endHide(hideKeyword); + return token; + } + + /// show identifierList + Token parseShow(Token token) { + Token showKeyword = token; + listener.beginShow(showKeyword); + assert(optional('show', token)); + token = parseIdentifierList(token.next); + listener.endShow(showKeyword); + return token; + } + + /// identifier (, identifier)* + Token parseIdentifierList(Token token) { + listener.beginIdentifierList(token); + token = parseIdentifier(token); + int count = 1; + while (optional(',', token)) { + token = parseIdentifier(token.next); + count++; + } + listener.endIdentifierList(count); + return token; + } + + /// type (, type)* + Token parseTypeList(Token token) { + listener.beginTypeList(token); + token = parseType(token); + int count = 1; + while (optional(',', token)) { + token = parseType(token.next); + count++; + } + listener.endTypeList(count); + return token; + } + + Token parsePartOrPartOf(Token token) { + assert(optional('part', token)); + if (optional('of', token.next)) { + return parsePartOf(token); + } else { + return parsePart(token); + } + } + + Token parsePart(Token token) { + Token partKeyword = token; + listener.beginPart(token); + assert(optional('part', token)); + token = parseLiteralStringOrRecoverExpression(token.next); + Token semicolon = token; + token = expect(';', token); + listener.endPart(partKeyword, semicolon); + return token; + } + + Token parsePartOf(Token token) { + listener.beginPartOf(token); + assert(optional('part', token)); + assert(optional('of', token.next)); + Token partKeyword = token; + token = parseQualified(token.next.next); + Token semicolon = token; + token = expect(';', token); + listener.endPartOf(partKeyword, semicolon); + return token; + } + + Token parseMetadataStar(Token token, {bool forParameter: false}) { + listener.beginMetadataStar(token); + int count = 0; + while (optional('@', token)) { + token = parseMetadata(token); + count++; + } + listener.endMetadataStar(count, forParameter); + return token; + } + + /** + * Parse + * [: '@' qualified (‘.’ identifier)? (arguments)? :] + */ + Token parseMetadata(Token token) { + listener.beginMetadata(token); + Token atToken = token; + assert(optional('@', token)); + token = parseIdentifier(token.next); + token = parseQualifiedRestOpt(token); + token = parseTypeArgumentsOpt(token); + Token period = null; + if (optional('.', token)) { + period = token; + token = parseIdentifier(token.next); + } + token = parseArgumentsOpt(token); + listener.endMetadata(atToken, period, token); + return token; + } + + Token parseTypedef(Token token) { + Token typedefKeyword = token; + if (optional('=', peekAfterType(token.next))) { + // TODO(aprelev@gmail.com): Remove deprecated 'typedef' mixin application, + // remove corresponding diagnostic from members.dart. + listener.beginNamedMixinApplication(token); + token = parseIdentifier(token.next); + token = parseTypeVariablesOpt(token); + token = expect('=', token); + token = parseModifiers(token); + token = parseMixinApplication(token); + Token implementsKeyword = null; + if (optional('implements', token)) { + implementsKeyword = token; + token = parseTypeList(token.next); + } + listener.endNamedMixinApplication( + typedefKeyword, implementsKeyword, token); + } else { + listener.beginFunctionTypeAlias(token); + token = parseReturnTypeOpt(token.next); + token = parseIdentifier(token); + token = parseTypeVariablesOpt(token); + token = parseFormalParameters(token); + listener.endFunctionTypeAlias(typedefKeyword, token); + } + return expect(';', token); + } + + Token parseMixinApplication(Token token) { + listener.beginMixinApplication(token); + token = parseType(token); + token = expect('with', token); + token = parseTypeList(token); + listener.endMixinApplication(); + return token; + } + + Token parseReturnTypeOpt(Token token) { + if (identical(token.stringValue, 'void')) { + listener.handleVoidKeyword(token); + return token.next; + } else { + return parseTypeOpt(token); + } + } + + Token parseFormalParametersOpt(Token token) { + if (optional('(', token)) { + return parseFormalParameters(token); + } else { + listener.handleNoFormalParameters(token); + return token; + } + } + + Token parseFormalParameters(Token token) { + Token begin = token; + listener.beginFormalParameters(begin); + expect('(', token); + int parameterCount = 0; + if (optional(')', token.next)) { + listener.endFormalParameters(parameterCount, begin, token.next); + return token.next.next; + } + do { + ++parameterCount; + token = token.next; + String value = token.stringValue; + if (identical(value, '[')) { + token = parseOptionalFormalParameters(token, false); + break; + } else if (identical(value, '{')) { + token = parseOptionalFormalParameters(token, true); + break; + } + token = parseFormalParameter(token, FormalParameterType.REQUIRED); + } while (optional(',', token)); + listener.endFormalParameters(parameterCount, begin, token); + return expect(')', token); + } + + Token parseFormalParameter(Token token, FormalParameterType type) { + token = parseMetadataStar(token, forParameter: true); + listener.beginFormalParameter(token); + token = parseModifiers(token); + // TODO(ahe): Validate that there are formal parameters if void. + token = parseReturnTypeOpt(token); + Token thisKeyword = null; + if (optional('this', token)) { + thisKeyword = token; + // TODO(ahe): Validate field initializers are only used in + // constructors, and not for function-typed arguments. + token = expect('.', token.next); + } + token = parseIdentifier(token); + if (optional('(', token)) { + token = parseFormalParameters(token); + listener.handleFunctionTypedFormalParameter(token); + } + String value = token.stringValue; + if ((identical('=', value)) || (identical(':', value))) { + // TODO(ahe): Validate that these are only used for optional parameters. + Token equal = token; + token = parseExpression(token.next); + listener.handleValuedFormalParameter(equal, token); + if (type.isRequired) { + listener.reportError(equal, + MessageKind.REQUIRED_PARAMETER_WITH_DEFAULT); + } else if (type.isNamed && identical('=', value)) { + listener.reportError(equal, MessageKind.NAMED_PARAMETER_WITH_EQUALS); + } else if (type.isPositional && identical(':', value)) { + listener.reportError(equal, + MessageKind.POSITIONAL_PARAMETER_WITH_EQUALS); + } + } + listener.endFormalParameter(thisKeyword); + return token; + } + + Token parseOptionalFormalParameters(Token token, bool isNamed) { + Token begin = token; + listener.beginOptionalFormalParameters(begin); + assert((isNamed && optional('{', token)) || optional('[', token)); + int parameterCount = 0; + do { + token = token.next; + var type = isNamed ? FormalParameterType.NAMED + : FormalParameterType.POSITIONAL; + token = parseFormalParameter(token, type); + ++parameterCount; + } while (optional(',', token)); + listener.endOptionalFormalParameters(parameterCount, begin, token); + if (isNamed) { + return expect('}', token); + } else { + return expect(']', token); + } + } + + Token parseTypeOpt(Token token) { + String value = token.stringValue; + if (!identical(value, 'this')) { + Token peek = peekAfterExpectedType(token); + if (peek.isIdentifier() || optional('this', peek)) { + return parseType(token); + } + } + listener.handleNoType(token); + return token; + } + + bool isValidTypeReference(Token token) { + final kind = token.kind; + if (identical(kind, IDENTIFIER_TOKEN)) return true; + if (identical(kind, KEYWORD_TOKEN)) { + Keyword keyword = (token as KeywordToken).keyword; + String value = keyword.syntax; + return keyword.isPseudo + || (identical(value, 'dynamic')) + || (identical(value, 'void')); + } + return false; + } + + Token parseQualified(Token token) { + token = parseIdentifier(token); + while (optional('.', token)) { + token = parseQualifiedRest(token); + } + return token; + } + + Token parseQualifiedRestOpt(Token token) { + if (optional('.', token)) { + return parseQualifiedRest(token); + } else { + return token; + } + } + + Token parseQualifiedRest(Token token) { + assert(optional('.', token)); + Token period = token; + token = parseIdentifier(token.next); + listener.handleQualified(period); + return token; + } + + Token skipBlock(Token token) { + if (!optional('{', token)) { + return listener.expectedBlockToSkip(token); + } + BeginGroupToken beginGroupToken = token; + Token endGroup = beginGroupToken.endGroup; + if (endGroup == null) { + return listener.unmatched(beginGroupToken); + } else if (!identical(endGroup.kind, $CLOSE_CURLY_BRACKET)) { + return listener.unmatched(beginGroupToken); + } + return beginGroupToken.endGroup; + } + + Token parseClassOrNamedMixinApplication(Token token) { + Token begin = token; + Token abstractKeyword; + if (optional('abstract', token)) { + abstractKeyword = token; + token = token.next; + } + Token classKeyword = token; + var isMixinApplication = optional('=', peekAfterType(token.next)); + if (isMixinApplication) { + listener.beginNamedMixinApplication(begin); + token = parseIdentifier(token.next); + token = parseTypeVariablesOpt(token); + token = expect('=', token); + } else { + listener.beginClassDeclaration(begin); + } + + // TODO(aprelev@gmail.com): Once 'typedef' named mixin application is + // removed, move modifiers for named mixin application to the bottom of + // listener stack. This is so stacks for class declaration and named + // mixin application look similar. + int modifierCount = 0; + if (abstractKeyword != null) { + parseModifier(abstractKeyword); + modifierCount++; + } + listener.handleModifiers(modifierCount); + + if (isMixinApplication) { + return parseNamedMixinApplication(token, classKeyword); + } else { + return parseClass(begin, classKeyword); + } + } + + Token parseNamedMixinApplication(Token token, Token classKeyword) { + token = parseMixinApplication(token); + Token implementsKeyword = null; + if (optional('implements', token)) { + implementsKeyword = token; + token = parseTypeList(token.next); + } + listener.endNamedMixinApplication( + classKeyword, implementsKeyword, token); + return expect(';', token); + } + + Token parseClass(Token begin, Token classKeyword) { + Token token = parseIdentifier(classKeyword.next); + token = parseTypeVariablesOpt(token); + Token extendsKeyword; + if (optional('extends', token)) { + extendsKeyword = token; + if (optional('with', peekAfterType(token.next))) { + token = parseMixinApplication(token.next); + } else { + token = parseType(token.next); + } + } else { + extendsKeyword = null; + listener.handleNoType(token); + } + Token implementsKeyword; + int interfacesCount = 0; + if (optional('implements', token)) { + implementsKeyword = token; + do { + token = parseType(token.next); + ++interfacesCount; + } while (optional(',', token)); + } + token = parseClassBody(token); + listener.endClassDeclaration(interfacesCount, begin, extendsKeyword, + implementsKeyword, token); + return token.next; + } + + Token parseStringPart(Token token) { + if (identical(token.kind, STRING_TOKEN)) { + listener.handleStringPart(token); + return token.next; + } else { + return listener.expected('string', token); + } + } + + Token parseIdentifier(Token token) { + if (!token.isIdentifier()) { + token = listener.expectedIdentifier(token); + } + listener.handleIdentifier(token); + return token.next; + } + + Token expect(String string, Token token) { + if (!identical(string, token.stringValue)) { + return listener.expected(string, token); + } + return token.next; + } + + Token parseTypeVariable(Token token) { + listener.beginTypeVariable(token); + token = parseIdentifier(token); + if (optional('extends', token)) { + token = parseType(token.next); + } else { + listener.handleNoType(token); + } + listener.endTypeVariable(token); + return token; + } + + /** + * Returns true if the stringValue of the [token] is [value]. + */ + bool optional(String value, Token token) { + return identical(value, token.stringValue); + } + + /** + * Returns true if the stringValue of the [token] is either [value1], + * [value2], [value3], or [value4]. + */ + bool isOneOf4(Token token, + String value1, String value2, String value3, String value4) { + String stringValue = token.stringValue; + return identical(value1, stringValue) || + identical(value2, stringValue) || + identical(value3, stringValue) || + identical(value4, stringValue); + } + + bool notEofOrValue(String value, Token token) { + return !identical(token.kind, EOF_TOKEN) && + !identical(value, token.stringValue); + } + + Token parseType(Token token) { + Token begin = token; + if (isValidTypeReference(token)) { + token = parseIdentifier(token); + token = parseQualifiedRestOpt(token); + } else { + token = listener.expectedType(token); + } + token = parseTypeArgumentsOpt(token); + listener.endType(begin, token); + return token; + } + + Token parseTypeArgumentsOpt(Token token) { + return parseStuff(token, + (t) => listener.beginTypeArguments(t), + (t) => parseType(t), + (c, bt, et) => listener.endTypeArguments(c, bt, et), + (t) => listener.handleNoTypeArguments(t)); + } + + Token parseTypeVariablesOpt(Token token) { + return parseStuff(token, + (t) => listener.beginTypeVariables(t), + (t) => parseTypeVariable(t), + (c, bt, et) => listener.endTypeVariables(c, bt, et), + (t) => listener.handleNoTypeVariables(t)); + } + + // TODO(ahe): Clean this up. + Token parseStuff(Token token, Function beginStuff, Function stuffParser, + Function endStuff, Function handleNoStuff) { + if (optional('<', token)) { + Token begin = token; + beginStuff(begin); + int count = 0; + do { + token = stuffParser(token.next); + ++count; + } while (optional(',', token)); + Token next = token.next; + if (identical(token.stringValue, '>>')) { + token = new SymbolToken(GT_INFO, token.charOffset); + token.next = new SymbolToken(GT_INFO, token.charOffset + 1); + token.next.next = next; + } else if (identical(token.stringValue, '>>>')) { + token = new SymbolToken(GT_INFO, token.charOffset); + token.next = new SymbolToken(GT_GT_INFO, token.charOffset + 1); + token.next.next = next; + } + endStuff(count, begin, token); + return expect('>', token); + } + handleNoStuff(token); + return token; + } + + Token parseTopLevelMember(Token token) { + Token start = token; + listener.beginTopLevelMember(token); + + Link identifiers = findMemberName(token); + if (identifiers.isEmpty) { + return listener.unexpected(start); + } + Token name = identifiers.head; + identifiers = identifiers.tail; + Token getOrSet; + if (!identifiers.isEmpty) { + String value = identifiers.head.stringValue; + if ((identical(value, 'get')) || (identical(value, 'set'))) { + getOrSet = identifiers.head; + identifiers = identifiers.tail; + } + } + Token type; + if (!identifiers.isEmpty) { + if (isValidTypeReference(identifiers.head)) { + type = identifiers.head; + identifiers = identifiers.tail; + } + } + + token = name.next; + bool isField; + while (true) { + // Loop to allow the listener to rewrite the token stream for + // error handling. + final String value = token.stringValue; + if ((identical(value, '(')) || (identical(value, '{')) + || (identical(value, '=>'))) { + isField = false; + break; + } else if ((identical(value, '=')) || (identical(value, ','))) { + isField = true; + break; + } else if (identical(value, ';')) { + if (getOrSet != null) { + // If we found a "get" keyword, this must be an abstract + // getter. + isField = (!identical(getOrSet.stringValue, 'get')); + // TODO(ahe): This feels like a hack. + } else { + isField = true; + } + break; + } else { + token = listener.unexpected(token); + if (identical(token.kind, EOF_TOKEN)) return token; + } + } + var modifiers = identifiers.reverse(); + return isField + ? parseFields(start, modifiers, type, getOrSet, name, true) + : parseTopLevelMethod(start, modifiers, type, getOrSet, name); + } + + bool isVarFinalOrConst(Token token) { + String value = token.stringValue; + return identical('var', value) + || identical('final', value) + || identical('const', value); + } + + Token expectVarFinalOrConst(Link modifiers, + bool hasType, + bool allowStatic) { + int modifierCount = 0; + Token staticModifier; + if (allowStatic && !modifiers.isEmpty + && optional('static', modifiers.head)) { + staticModifier = modifiers.head; + modifierCount++; + parseModifier(staticModifier); + modifiers = modifiers.tail; + } + if (modifiers.isEmpty) { + listener.handleModifiers(modifierCount); + return null; + } + if (modifiers.tail.isEmpty) { + Token modifier = modifiers.head; + if (isVarFinalOrConst(modifier)) { + modifierCount++; + parseModifier(modifier); + listener.handleModifiers(modifierCount); + // TODO(ahe): The caller checks for "var Type name", perhaps we should + // check here instead. + return modifier; + } + } + + // Slow case to report errors. + List modifierList = modifiers.toList(); + Token varFinalOrConst = + modifierList.firstWhere(isVarFinalOrConst, orElse: () => null); + if (allowStatic && staticModifier == null) { + staticModifier = + modifierList.firstWhere( + (modifier) => optional('static', modifier), orElse: () => null); + if (staticModifier != null) { + modifierCount++; + parseModifier(staticModifier); + modifierList.remove(staticModifier); + } + } + bool hasTypeOrModifier = hasType; + if (varFinalOrConst != null) { + parseModifier(varFinalOrConst); + modifierCount++; + hasTypeOrModifier = true; + modifierList.remove(varFinalOrConst); + } + listener.handleModifiers(modifierCount); + var kind = hasTypeOrModifier + ? MessageKind.EXTRANEOUS_MODIFIER + : MessageKind.EXTRANEOUS_MODIFIER_REPLACE; + for (Token modifier in modifierList) { + listener.reportError(modifier, kind, {'modifier': modifier}); + } + return null; + } + + Token parseFields(Token start, + Link modifiers, + Token type, + Token getOrSet, + Token name, + bool isTopLevel) { + bool hasType = type != null; + Token varFinalOrConst = + expectVarFinalOrConst(modifiers, hasType, !isTopLevel); + bool isVar = false; + bool hasModifier = false; + if (varFinalOrConst != null) { + hasModifier = true; + isVar = optional('var', varFinalOrConst); + } + + if (getOrSet != null) { + var kind = (hasModifier || hasType) + ? MessageKind.EXTRANEOUS_MODIFIER + : MessageKind.EXTRANEOUS_MODIFIER_REPLACE; + listener.reportError(getOrSet, kind, {'modifier': getOrSet}); + } + + if (!hasType) { + listener.handleNoType(name); + } else if (optional('void', type)) { + listener.handleNoType(name); + // TODO(ahe): This error is reported twice, second time is from + // [parseVariablesDeclarationMaybeSemicolon] via + // [PartialFieldListElement.parseNode]. + listener.reportError(type, MessageKind.VOID_NOT_ALLOWED); + } else { + parseType(type); + if (isVar) { + listener.reportError( + modifiers.head, MessageKind.EXTRANEOUS_MODIFIER, + {'modifier': modifiers.head}); + } + } + + Token token = parseIdentifier(name); + + int fieldCount = 1; + token = parseVariableInitializerOpt(token); + while (optional(',', token)) { + token = parseIdentifier(token.next); + token = parseVariableInitializerOpt(token); + ++fieldCount; + } + expectSemicolon(token); + if (isTopLevel) { + listener.endTopLevelFields(fieldCount, start, token); + } else { + listener.endFields(fieldCount, start, token); + } + return token.next; + } + + Token parseTopLevelMethod(Token start, + Link modifiers, + Token type, + Token getOrSet, + Token name) { + Token externalModifier; + for (Token modifier in modifiers) { + if (externalModifier == null && optional('external', modifier)) { + externalModifier = modifier; + } else { + listener.reportError( + modifier, MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); + } + } + if (externalModifier != null) { + parseModifier(externalModifier); + listener.handleModifiers(1); + } else { + listener.handleModifiers(0); + } + + if (type == null) { + listener.handleNoType(name); + } else { + parseReturnTypeOpt(type); + } + Token token = parseIdentifier(name); + + token = parseFormalParametersOpt(token); + token = parseFunctionBody(token, false, externalModifier != null); + listener.endTopLevelMethod(start, getOrSet, token); + return token.next; + } + + Link findMemberName(Token token) { + Token start = token; + Link identifiers = const Link(); + while (!identical(token.kind, EOF_TOKEN)) { + String value = token.stringValue; + if ((identical(value, '(')) || (identical(value, '{')) + || (identical(value, '=>'))) { + // A method. + return identifiers; + } else if ((identical(value, '=')) || (identical(value, ';')) + || (identical(value, ','))) { + // A field or abstract getter. + return identifiers; + } + identifiers = identifiers.prepend(token); + if (isValidTypeReference(token)) { + // type ... + if (optional('.', token.next)) { + // type '.' ... + if (token.next.next.isIdentifier()) { + // type '.' identifier + token = token.next.next; + } + } + if (optional('<', token.next)) { + if (token.next is BeginGroupToken) { + BeginGroupToken beginGroup = token.next; + if (beginGroup.endGroup == null) { + listener.unmatched(beginGroup); + } + token = beginGroup.endGroup; + } + } + } + token = token.next; + } + return listener.expectedDeclaration(start); + } + + Token parseVariableInitializerOpt(Token token) { + if (optional('=', token)) { + Token assignment = token; + listener.beginInitializer(token); + token = parseExpression(token.next); + listener.endInitializer(assignment); + } + return token; + } + + Token parseInitializersOpt(Token token) { + if (optional(':', token)) { + return parseInitializers(token); + } else { + listener.handleNoInitializers(); + return token; + } + } + + Token parseInitializers(Token token) { + Token begin = token; + listener.beginInitializers(begin); + expect(':', token); + int count = 0; + bool old = mayParseFunctionExpressions; + mayParseFunctionExpressions = false; + do { + token = parseExpression(token.next); + ++count; + } while (optional(',', token)); + mayParseFunctionExpressions = old; + listener.endInitializers(count, begin, token); + return token; + } + + Token parseLiteralStringOrRecoverExpression(Token token) { + if (identical(token.kind, STRING_TOKEN)) { + return parseLiteralString(token); + } else { + listener.recoverableError(token, "unexpected"); + return parseExpression(token); + } + } + + Token expectSemicolon(Token token) { + return expect(';', token); + } + + bool isModifier(Token token) { + final String value = token.stringValue; + return (identical('final', value)) || + (identical('var', value)) || + (identical('const', value)) || + (identical('abstract', value)) || + (identical('static', value)) || + (identical('external', value)); + } + + Token parseModifier(Token token) { + assert(isModifier(token)); + listener.handleModifier(token); + return token.next; + } + + void parseModifierList(Link tokens) { + int count = 0; + for (; !tokens.isEmpty; tokens = tokens.tail) { + Token token = tokens.head; + if (isModifier(token)) { + parseModifier(token); + } else { + listener.unexpected(token); + } + count++; + } + listener.handleModifiers(count); + } + + Token parseModifiers(Token token) { + int count = 0; + while (identical(token.kind, KEYWORD_TOKEN)) { + if (!isModifier(token)) + break; + token = parseModifier(token); + count++; + } + listener.handleModifiers(count); + return token; + } + + Token peekAfterType(Token token) { + // TODO(ahe): Also handle var? + // We are looking at "identifier ...". + Token peek = token.next; + if (identical(peek.kind, PERIOD_TOKEN)) { + if (peek.next.isIdentifier()) { + // Look past a library prefix. + peek = peek.next.next; + } + } + // We are looking at "qualified ...". + if (identical(peek.kind, LT_TOKEN)) { + // Possibly generic type. + // We are looking at "qualified '<'". + BeginGroupToken beginGroupToken = peek; + Token gtToken = beginGroupToken.endGroup; + if (gtToken != null) { + // We are looking at "qualified '<' ... '>' ...". + return gtToken.next; + } + } + return peek; + } + + /** + * Returns the token after the type which is expected to begin at [token]. + * If [token] is not the start of a type, [Listener.expectedType] is called. + */ + Token peekAfterExpectedType(Token token) { + if (!identical('void', token.stringValue) && !token.isIdentifier()) { + return listener.expectedType(token); + } + return peekAfterType(token); + } + + Token parseClassBody(Token token) { + Token begin = token; + listener.beginClassBody(token); + if (!optional('{', token)) { + token = listener.expectedClassBody(token); + } + token = token.next; + int count = 0; + while (notEofOrValue('}', token)) { + token = parseMember(token); + ++count; + } + expect('}', token); + listener.endClassBody(count, begin, token); + return token; + } + + bool isGetOrSet(Token token) { + final String value = token.stringValue; + return (identical(value, 'get')) || (identical(value, 'set')); + } + + bool isFactoryDeclaration(Token token) { + if (optional('external', token)) token = token.next; + if (optional('const', token)) token = token.next; + return optional('factory', token); + } + + Token parseMember(Token token) { + token = parseMetadataStar(token); + String value = token.stringValue; + if (isFactoryDeclaration(token)) { + return parseFactoryMethod(token); + } + Token start = token; + listener.beginMember(token); + + Link identifiers = findMemberName(token); + if (identifiers.isEmpty) { + return listener.unexpected(start); + } + Token name = identifiers.head; + Token afterName = name.next; + identifiers = identifiers.tail; + if (!identifiers.isEmpty) { + if (optional('operator', identifiers.head)) { + name = identifiers.head; + identifiers = identifiers.tail; + } + } + Token getOrSet; + if (!identifiers.isEmpty) { + if (isGetOrSet(identifiers.head)) { + getOrSet = identifiers.head; + identifiers = identifiers.tail; + } + } + Token type; + if (!identifiers.isEmpty) { + if (isValidTypeReference(identifiers.head)) { + type = identifiers.head; + identifiers = identifiers.tail; + } + } + + token = afterName; + bool isField; + while (true) { + // Loop to allow the listener to rewrite the token stream for + // error handling. + final String value = token.stringValue; + if ((identical(value, '(')) || (identical(value, '.')) + || (identical(value, '{')) || (identical(value, '=>'))) { + isField = false; + break; + } else if (identical(value, ';')) { + if (getOrSet != null) { + // If we found a "get" keyword, this must be an abstract + // getter. + isField = (!identical(getOrSet.stringValue, 'get')); + // TODO(ahe): This feels like a hack. + } else { + isField = true; + } + break; + } else if ((identical(value, '=')) || (identical(value, ','))) { + isField = true; + break; + } else { + token = listener.unexpected(token); + if (identical(token.kind, EOF_TOKEN)) { + // TODO(ahe): This is a hack, see parseTopLevelMember. + listener.endFields(1, start, token); + return token; + } + } + } + + var modifiers = identifiers.reverse(); + return isField + ? parseFields(start, modifiers, type, getOrSet, name, false) + : parseMethod(start, modifiers, type, getOrSet, name); + + } + + Token parseMethod(Token start, + Link modifiers, + Token type, + Token getOrSet, + Token name) { + Token externalModifier; + Token staticModifier; + Token constModifier; + int modifierCount = 0; + int allowedModifierCount = 1; + for (Token modifier in modifiers) { + if (externalModifier == null && optional('external', modifier)) { + modifierCount++; + externalModifier = modifier; + if (modifierCount != allowedModifierCount) { + listener.reportError( + modifier, + MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); + } + allowedModifierCount++; + } else if (staticModifier == null && optional('static', modifier)) { + modifierCount++; + staticModifier = modifier; + if (modifierCount != allowedModifierCount) { + listener.reportError( + modifier, + MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); + } + } else if (constModifier == null && optional('const', modifier)) { + modifierCount++; + constModifier = modifier; + if (modifierCount != allowedModifierCount) { + listener.reportError( + modifier, + MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); + } + } else { + listener.reportError( + modifier, MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); + } + } + parseModifierList(modifiers); + + if (type == null) { + listener.handleNoType(name); + } else { + parseReturnTypeOpt(type); + } + Token token; + if (optional('operator', name)) { + token = parseOperatorName(name); + if (staticModifier != null) { + // TODO(ahe): Consider a more specific error message. + listener.reportError( + staticModifier, MessageKind.EXTRANEOUS_MODIFIER, + {'modifier': staticModifier}); + } + } else { + token = parseIdentifier(name); + } + + token = parseQualifiedRestOpt(token); + token = parseFormalParametersOpt(token); + token = parseInitializersOpt(token); + if (optional('=', token)) { + token = parseRedirectingFactoryBody(token); + } else { + token = parseFunctionBody( + token, false, staticModifier == null || externalModifier != null); + } + listener.endMethod(getOrSet, start, token); + return token.next; + } + + Token parseFactoryMethod(Token token) { + assert(isFactoryDeclaration(token)); + Token start = token; + Token externalModifier; + if (identical(token.stringValue, 'external')) { + externalModifier = token; + token = token.next; + } + Token constKeyword = null; + if (optional('const', token)) { + constKeyword = token; + token = token.next; + } + Token factoryKeyword = token; + listener.beginFactoryMethod(factoryKeyword); + token = token.next; // Skip 'factory'. + token = parseConstructorReference(token); + token = parseFormalParameters(token); + if (optional('=', token)) { + token = parseRedirectingFactoryBody(token); + } else { + token = parseFunctionBody(token, false, externalModifier != null); + } + listener.endFactoryMethod(start, token); + return token.next; + } + + Token parseOperatorName(Token token) { + assert(optional('operator', token)); + if (isUserDefinableOperator(token.next.stringValue)) { + Token operator = token; + token = token.next; + listener.handleOperatorName(operator, token); + return token.next; + } else { + return parseIdentifier(token); + } + } + + Token parseFunction(Token token, Token getOrSet) { + listener.beginFunction(token); + token = parseModifiers(token); + if (identical(getOrSet, token)) token = token.next; + if (optional('operator', token)) { + listener.handleNoType(token); + listener.beginFunctionName(token); + token = parseOperatorName(token); + } else { + token = parseReturnTypeOpt(token); + if (identical(getOrSet, token)) token = token.next; + listener.beginFunctionName(token); + if (optional('operator', token)) { + token = parseOperatorName(token); + } else { + token = parseIdentifier(token); + } + } + token = parseQualifiedRestOpt(token); + listener.endFunctionName(token); + token = parseFormalParametersOpt(token); + token = parseInitializersOpt(token); + if (optional('=', token)) { + token = parseRedirectingFactoryBody(token); + } else { + token = parseFunctionBody(token, false, true); + } + listener.endFunction(getOrSet, token); + return token.next; + } + + Token parseUnamedFunction(Token token) { + listener.beginUnamedFunction(token); + token = parseFormalParameters(token); + bool isBlock = optional('{', token); + token = parseFunctionBody(token, true, false); + listener.endUnamedFunction(token); + return isBlock ? token.next : token; + } + + Token parseFunctionDeclaration(Token token) { + listener.beginFunctionDeclaration(token); + token = parseFunction(token, null); + listener.endFunctionDeclaration(token); + return token; + } + + Token parseFunctionExpression(Token token) { + listener.beginFunction(token); + listener.handleModifiers(0); + token = parseReturnTypeOpt(token); + listener.beginFunctionName(token); + token = parseIdentifier(token); + listener.endFunctionName(token); + token = parseFormalParameters(token); + listener.handleNoInitializers(); + bool isBlock = optional('{', token); + token = parseFunctionBody(token, true, false); + listener.endFunction(null, token); + return isBlock ? token.next : token; + } + + Token parseConstructorReference(Token token) { + Token start = token; + listener.beginConstructorReference(start); + token = parseIdentifier(token); + token = parseQualifiedRestOpt(token); + token = parseTypeArgumentsOpt(token); + Token period = null; + if (optional('.', token)) { + period = token; + token = parseIdentifier(token.next); + } + listener.endConstructorReference(start, period, token); + return token; + } + + Token parseRedirectingFactoryBody(Token token) { + listener.beginRedirectingFactoryBody(token); + assert(optional('=', token)); + Token equals = token; + token = parseConstructorReference(token.next); + Token semicolon = token; + expectSemicolon(token); + listener.endRedirectingFactoryBody(equals, semicolon); + return token; + } + + Token parseFunctionBody(Token token, bool isExpression, bool allowAbstract) { + if (optional(';', token)) { + if (!allowAbstract) { + listener.reportError(token, MessageKind.BODY_EXPECTED); + } + listener.endFunctionBody(0, null, token); + return token; + } else if (optional('=>', token)) { + Token begin = token; + token = parseExpression(token.next); + if (!isExpression) { + expectSemicolon(token); + listener.endReturnStatement(true, begin, token); + } else { + listener.endReturnStatement(true, begin, null); + } + return token; + } + Token begin = token; + int statementCount = 0; + if (!optional('{', token)) { + return listener.expectedFunctionBody(token); + } + + listener.beginFunctionBody(begin); + token = token.next; + while (notEofOrValue('}', token)) { + token = parseStatement(token); + ++statementCount; + } + listener.endFunctionBody(statementCount, begin, token); + expect('}', token); + return token; + } + + Token parseStatement(Token token) { + final value = token.stringValue; + if (identical(token.kind, IDENTIFIER_TOKEN)) { + return parseExpressionStatementOrDeclaration(token); + } else if (identical(value, '{')) { + return parseBlock(token); + } else if (identical(value, 'return')) { + return parseReturnStatement(token); + } else if (identical(value, 'var') || identical(value, 'final')) { + return parseVariablesDeclaration(token); + } else if (identical(value, 'if')) { + return parseIfStatement(token); + } else if (identical(value, 'for')) { + return parseForStatement(token); + } else if (identical(value, 'rethrow')) { + return parseRethrowStatement(token); + } else if (identical(value, 'throw') && optional(';', token.next)) { + // TODO(kasperl): Stop dealing with throw here. + return parseRethrowStatement(token); + } else if (identical(value, 'void')) { + return parseExpressionStatementOrDeclaration(token); + } else if (identical(value, 'while')) { + return parseWhileStatement(token); + } else if (identical(value, 'do')) { + return parseDoWhileStatement(token); + } else if (identical(value, 'try')) { + return parseTryStatement(token); + } else if (identical(value, 'switch')) { + return parseSwitchStatement(token); + } else if (identical(value, 'break')) { + return parseBreakStatement(token); + } else if (identical(value, 'continue')) { + return parseContinueStatement(token); + } else if (identical(value, 'assert')) { + return parseAssertStatement(token); + } else if (identical(value, ';')) { + return parseEmptyStatement(token); + } else if (identical(value, 'const')) { + return parseExpressionStatementOrConstDeclaration(token); + } else if (token.isIdentifier()) { + return parseExpressionStatementOrDeclaration(token); + } else { + return parseExpressionStatement(token); + } + } + + Token parseReturnStatement(Token token) { + Token begin = token; + listener.beginReturnStatement(begin); + assert(identical('return', token.stringValue)); + token = token.next; + if (optional(';', token)) { + listener.endReturnStatement(false, begin, token); + } else { + token = parseExpression(token); + listener.endReturnStatement(true, begin, token); + } + return expectSemicolon(token); + } + + Token peekIdentifierAfterType(Token token) { + Token peek = peekAfterType(token); + if (peek != null && peek.isIdentifier()) { + // We are looking at "type identifier". + return peek; + } else { + return null; + } + } + + Token peekIdentifierAfterOptionalType(Token token) { + Token peek = peekIdentifierAfterType(token); + if (peek != null) { + // We are looking at "type identifier". + return peek; + } else if (token.isIdentifier()) { + // We are looking at "identifier". + return token; + } else { + return null; + } + } + + Token parseExpressionStatementOrDeclaration(Token token) { + assert(token.isIdentifier() || identical(token.stringValue, 'void')); + Token identifier = peekIdentifierAfterType(token); + if (identifier != null) { + assert(identifier.isIdentifier()); + Token afterId = identifier.next; + int afterIdKind = afterId.kind; + if (identical(afterIdKind, EQ_TOKEN) || + identical(afterIdKind, SEMICOLON_TOKEN) || + identical(afterIdKind, COMMA_TOKEN)) { + // We are looking at "type identifier" followed by '=', ';', ','. + return parseVariablesDeclaration(token); + } else if (identical(afterIdKind, OPEN_PAREN_TOKEN)) { + // We are looking at "type identifier '('". + BeginGroupToken beginParen = afterId; + Token endParen = beginParen.endGroup; + Token afterParens = endParen.next; + if (optional('{', afterParens) || optional('=>', afterParens)) { + // We are looking at "type identifier '(' ... ')'" followed + // by '=>' or '{'. + return parseFunctionDeclaration(token); + } + } + // Fall-through to expression statement. + } else { + if (optional(':', token.next)) { + return parseLabeledStatement(token); + } else if (optional('(', token.next)) { + BeginGroupToken begin = token.next; + String afterParens = begin.endGroup.next.stringValue; + if (identical(afterParens, '{') || identical(afterParens, '=>')) { + return parseFunctionDeclaration(token); + } + } + } + return parseExpressionStatement(token); + } + + Token parseExpressionStatementOrConstDeclaration(Token token) { + assert(identical(token.stringValue, 'const')); + if (isModifier(token.next)) { + return parseVariablesDeclaration(token); + } + Token identifier = peekIdentifierAfterOptionalType(token.next); + if (identifier != null) { + assert(identifier.isIdentifier()); + Token afterId = identifier.next; + int afterIdKind = afterId.kind; + if (identical(afterIdKind, EQ_TOKEN) || + identical(afterIdKind, SEMICOLON_TOKEN) || + identical(afterIdKind, COMMA_TOKEN)) { + // We are looking at "const type identifier" followed by '=', ';', or + // ','. + return parseVariablesDeclaration(token); + } + // Fall-through to expression statement. + } + + return parseExpressionStatement(token); + } + + Token parseLabel(Token token) { + token = parseIdentifier(token); + Token colon = token; + token = expect(':', token); + listener.handleLabel(colon); + return token; + } + + Token parseLabeledStatement(Token token) { + int labelCount = 0; + do { + token = parseLabel(token); + labelCount++; + } while (token.isIdentifier() && optional(':', token.next)); + listener.beginLabeledStatement(token, labelCount); + token = parseStatement(token); + listener.endLabeledStatement(labelCount); + return token; + } + + Token parseExpressionStatement(Token token) { + listener.beginExpressionStatement(token); + token = parseExpression(token); + listener.endExpressionStatement(token); + return expectSemicolon(token); + } + + Token parseExpression(Token token) { + return optional('throw', token) + ? parseThrowExpression(token, true) + : parsePrecedenceExpression(token, ASSIGNMENT_PRECEDENCE, true); + } + + Token parseExpressionWithoutCascade(Token token) { + return optional('throw', token) + ? parseThrowExpression(token, false) + : parsePrecedenceExpression(token, ASSIGNMENT_PRECEDENCE, false); + } + + Token parseConditionalExpressionRest(Token token) { + assert(optional('?', token)); + Token question = token; + token = parseExpressionWithoutCascade(token.next); + Token colon = token; + token = expect(':', token); + token = parseExpressionWithoutCascade(token); + listener.handleConditionalExpression(question, colon); + return token; + } + + Token parsePrecedenceExpression(Token token, int precedence, + bool allowCascades) { + assert(precedence >= 1); + assert(precedence <= POSTFIX_PRECEDENCE); + token = parseUnaryExpression(token, allowCascades); + PrecedenceInfo info = token.info; + int tokenLevel = info.precedence; + for (int level = tokenLevel; level >= precedence; --level) { + while (identical(tokenLevel, level)) { + Token operator = token; + if (identical(tokenLevel, CASCADE_PRECEDENCE)) { + if (!allowCascades) { + return token; + } + token = parseCascadeExpression(token); + } else if (identical(tokenLevel, ASSIGNMENT_PRECEDENCE)) { + // Right associative, so we recurse at the same precedence + // level. + token = parsePrecedenceExpression(token.next, level, allowCascades); + listener.handleAssignmentExpression(operator); + } else if (identical(tokenLevel, POSTFIX_PRECEDENCE)) { + if (identical(info, PERIOD_INFO)) { + // Left associative, so we recurse at the next higher + // precedence level. However, POSTFIX_PRECEDENCE is the + // highest level, so we just call parseUnaryExpression + // directly. + token = parseUnaryExpression(token.next, allowCascades); + listener.handleBinaryExpression(operator); + } else if ((identical(info, OPEN_PAREN_INFO)) || + (identical(info, OPEN_SQUARE_BRACKET_INFO))) { + token = parseArgumentOrIndexStar(token); + } else if ((identical(info, PLUS_PLUS_INFO)) || + (identical(info, MINUS_MINUS_INFO))) { + listener.handleUnaryPostfixAssignmentExpression(token); + token = token.next; + } else { + token = listener.unexpected(token); + } + } else if (identical(info, IS_INFO)) { + token = parseIsOperatorRest(token); + } else if (identical(info, AS_INFO)) { + token = parseAsOperatorRest(token); + } else if (identical(info, QUESTION_INFO)) { + token = parseConditionalExpressionRest(token); + } else { + // Left associative, so we recurse at the next higher + // precedence level. + token = parsePrecedenceExpression(token.next, level + 1, + allowCascades); + listener.handleBinaryExpression(operator); + } + info = token.info; + tokenLevel = info.precedence; + if (level == EQUALITY_PRECEDENCE || level == RELATIONAL_PRECEDENCE) { + // We don't allow (a == b == c) or (a < b < c). + // Continue the outer loop if we have matched one equality or + // relational operator. + break; + } + } + } + return token; + } + + Token parseCascadeExpression(Token token) { + listener.beginCascade(token); + assert(optional('..', token)); + Token cascadeOperator = token; + token = token.next; + if (optional('[', token)) { + token = parseArgumentOrIndexStar(token); + } else if (token.isIdentifier()) { + token = parseSend(token); + listener.handleBinaryExpression(cascadeOperator); + } else { + return listener.unexpected(token); + } + Token mark; + do { + mark = token; + if (optional('.', token)) { + Token period = token; + token = parseSend(token.next); + listener.handleBinaryExpression(period); + } + token = parseArgumentOrIndexStar(token); + } while (!identical(mark, token)); + + if (identical(token.info.precedence, ASSIGNMENT_PRECEDENCE)) { + Token assignment = token; + token = parseExpressionWithoutCascade(token.next); + listener.handleAssignmentExpression(assignment); + } + listener.endCascade(); + return token; + } + + Token parseUnaryExpression(Token token, bool allowCascades) { + String value = token.stringValue; + // Prefix: + if (identical(value, '+')) { + // Dart no longer allows prefix-plus. + listener.reportError(token, MessageKind.UNSUPPORTED_PREFIX_PLUS); + return parseUnaryExpression(token.next, allowCascades); + } else if ((identical(value, '!')) || + (identical(value, '-')) || + (identical(value, '~'))) { + Token operator = token; + // Right associative, so we recurse at the same precedence + // level. + token = parsePrecedenceExpression(token.next, POSTFIX_PRECEDENCE, + allowCascades); + listener.handleUnaryPrefixExpression(operator); + } else if ((identical(value, '++')) || identical(value, '--')) { + // TODO(ahe): Validate this is used correctly. + Token operator = token; + // Right associative, so we recurse at the same precedence + // level. + token = parsePrecedenceExpression(token.next, POSTFIX_PRECEDENCE, + allowCascades); + listener.handleUnaryPrefixAssignmentExpression(operator); + } else { + token = parsePrimary(token); + } + return token; + } + + Token parseArgumentOrIndexStar(Token token) { + while (true) { + if (optional('[', token)) { + Token openSquareBracket = token; + bool old = mayParseFunctionExpressions; + mayParseFunctionExpressions = true; + token = parseExpression(token.next); + mayParseFunctionExpressions = old; + listener.handleIndexedExpression(openSquareBracket, token); + token = expect(']', token); + } else if (optional('(', token)) { + token = parseArguments(token); + listener.endSend(token); + } else { + break; + } + } + return token; + } + + Token parsePrimary(Token token) { + final kind = token.kind; + if (identical(kind, IDENTIFIER_TOKEN)) { + return parseSendOrFunctionLiteral(token); + } else if (identical(kind, INT_TOKEN) + || identical(kind, HEXADECIMAL_TOKEN)) { + return parseLiteralInt(token); + } else if (identical(kind, DOUBLE_TOKEN)) { + return parseLiteralDouble(token); + } else if (identical(kind, STRING_TOKEN)) { + return parseLiteralString(token); + } else if (identical(kind, HASH_TOKEN)) { + return parseLiteralSymbol(token); + } else if (identical(kind, KEYWORD_TOKEN)) { + final value = token.stringValue; + if ((identical(value, 'true')) || (identical(value, 'false'))) { + return parseLiteralBool(token); + } else if (identical(value, 'null')) { + return parseLiteralNull(token); + } else if (identical(value, 'this')) { + return parseThisExpression(token); + } else if (identical(value, 'super')) { + return parseSuperExpression(token); + } else if (identical(value, 'new')) { + return parseNewExpression(token); + } else if (identical(value, 'const')) { + return parseConstExpression(token); + } else if (identical(value, 'void')) { + return parseFunctionExpression(token); + } else if (token.isIdentifier()) { + return parseSendOrFunctionLiteral(token); + } else { + return listener.expectedExpression(token); + } + } else if (identical(kind, OPEN_PAREN_TOKEN)) { + return parseParenthesizedExpressionOrFunctionLiteral(token); + } else if ((identical(kind, LT_TOKEN)) || + (identical(kind, OPEN_SQUARE_BRACKET_TOKEN)) || + (identical(kind, OPEN_CURLY_BRACKET_TOKEN)) || + identical(token.stringValue, '[]')) { + return parseLiteralListOrMap(token); + } else { + return listener.expectedExpression(token); + } + } + + Token parseParenthesizedExpressionOrFunctionLiteral(Token token) { + BeginGroupToken beginGroup = token; + int kind = beginGroup.endGroup.next.kind; + if (mayParseFunctionExpressions && + (identical(kind, FUNCTION_TOKEN) + || identical(kind, OPEN_CURLY_BRACKET_TOKEN))) { + return parseUnamedFunction(token); + } else { + bool old = mayParseFunctionExpressions; + mayParseFunctionExpressions = true; + token = parseParenthesizedExpression(token); + mayParseFunctionExpressions = old; + return token; + } + } + + Token parseParenthesizedExpression(Token token) { + // We expect [begin] to be of type [BeginGroupToken], but we don't know for + // sure until after calling expect. + var begin = token; + token = expect('(', token); + // [begin] is now known to have type [BeginGroupToken]. + token = parseExpression(token); + if (!identical(begin.endGroup, token)) { + listener.unexpected(token); + token = begin.endGroup; + } + listener.handleParenthesizedExpression(begin); + return expect(')', token); + } + + Token parseThisExpression(Token token) { + listener.handleThisExpression(token); + token = token.next; + if (optional('(', token)) { + // Constructor forwarding. + token = parseArguments(token); + listener.endSend(token); + } + return token; + } + + Token parseSuperExpression(Token token) { + listener.handleSuperExpression(token); + token = token.next; + if (optional('(', token)) { + // Super constructor. + token = parseArguments(token); + listener.endSend(token); + } + return token; + } + + Token parseLiteralListOrMap(Token token) { + Token constKeyword = null; + if (optional('const', token)) { + constKeyword = token; + token = token.next; + } + token = parseTypeArgumentsOpt(token); + Token beginToken = token; + int count = 0; + if (optional('{', token)) { + bool old = mayParseFunctionExpressions; + mayParseFunctionExpressions = true; + do { + if (optional('}', token.next)) { + token = token.next; + break; + } + token = parseMapLiteralEntry(token.next); + ++count; + } while (optional(',', token)); + mayParseFunctionExpressions = old; + listener.handleLiteralMap(count, beginToken, constKeyword, token); + return expect('}', token); + } else if (optional('[', token)) { + bool old = mayParseFunctionExpressions; + mayParseFunctionExpressions = true; + do { + if (optional(']', token.next)) { + token = token.next; + break; + } + token = parseExpression(token.next); + ++count; + } while (optional(',', token)); + mayParseFunctionExpressions = old; + listener.handleLiteralList(count, beginToken, constKeyword, token); + return expect(']', token); + } else if (optional('[]', token)) { + listener.handleLiteralList(0, token, constKeyword, token); + return token.next; + } else { + listener.unexpected(token); + return null; + } + } + + Token parseMapLiteralEntry(Token token) { + listener.beginLiteralMapEntry(token); + // Assume the listener rejects non-string keys. + token = parseExpression(token); + Token colon = token; + token = expect(':', token); + token = parseExpression(token); + listener.endLiteralMapEntry(colon, token); + return token; + } + + Token parseSendOrFunctionLiteral(Token token) { + if (!mayParseFunctionExpressions) return parseSend(token); + Token peek = peekAfterExpectedType(token); + if (identical(peek.kind, IDENTIFIER_TOKEN) && isFunctionDeclaration(peek.next)) { + return parseFunctionExpression(token); + } else if (isFunctionDeclaration(token.next)) { + return parseFunctionExpression(token); + } else { + return parseSend(token); + } + } + + bool isFunctionDeclaration(Token token) { + if (optional('(', token)) { + BeginGroupToken begin = token; + String afterParens = begin.endGroup.next.stringValue; + if (identical(afterParens, '{') || identical(afterParens, '=>')) { + return true; + } + } + return false; + } + + Token parseRequiredArguments(Token token) { + if (optional('(', token)) { + token = parseArguments(token); + } else { + listener.handleNoArguments(token); + token = listener.unexpected(token); + } + return token; + } + + Token parseNewExpression(Token token) { + Token newKeyword = token; + token = expect('new', token); + token = parseConstructorReference(token); + token = parseRequiredArguments(token); + listener.handleNewExpression(newKeyword); + return token; + } + + Token parseConstExpression(Token token) { + Token constKeyword = token; + token = expect('const', token); + final String value = token.stringValue; + if ((identical(value, '<')) || + (identical(value, '[')) || + (identical(value, '[]')) || + (identical(value, '{'))) { + return parseLiteralListOrMap(constKeyword); + } + token = parseConstructorReference(token); + token = parseRequiredArguments(token); + listener.handleConstExpression(constKeyword); + return token; + } + + Token parseLiteralInt(Token token) { + listener.handleLiteralInt(token); + return token.next; + } + + Token parseLiteralDouble(Token token) { + listener.handleLiteralDouble(token); + return token.next; + } + + Token parseLiteralString(Token token) { + token = parseSingleLiteralString(token); + int count = 1; + while (identical(token.kind, STRING_TOKEN)) { + token = parseSingleLiteralString(token); + count++; + } + if (count > 1) { + listener.handleStringJuxtaposition(count); + } + return token; + } + + Token parseLiteralSymbol(Token token) { + Token hashToken = token; + listener.beginLiteralSymbol(hashToken); + token = token.next; + if (isUserDefinableOperator(token.stringValue)) { + listener.handleOperator(token); + listener.endLiteralSymbol(hashToken, 1); + return token.next; + } else { + int count = 1; + token = parseIdentifier(token); + while (identical(token.stringValue, '.')) { + count++; + token = parseIdentifier(token.next); + } + listener.endLiteralSymbol(hashToken, count); + return token; + } + } + + /** + * Only called when [:token.kind === STRING_TOKEN:]. + */ + Token parseSingleLiteralString(Token token) { + listener.beginLiteralString(token); + // Parsing the prefix, for instance 'x of 'x${id}y${id}z' + token = token.next; + int interpolationCount = 0; + var kind = token.kind; + while (kind != EOF_TOKEN) { + if (identical(kind, STRING_INTERPOLATION_TOKEN)) { + // Parsing ${expression}. + token = token.next; + token = parseExpression(token); + token = expect('}', token); + } else if (identical(kind, STRING_INTERPOLATION_IDENTIFIER_TOKEN)) { + // Parsing $identifier. + token = token.next; + token = parseExpression(token); + } else { + break; + } + ++interpolationCount; + // Parsing the infix/suffix, for instance y and z' of 'x${id}y${id}z' + token = parseStringPart(token); + kind = token.kind; + } + listener.endLiteralString(interpolationCount); + return token; + } + + Token parseLiteralBool(Token token) { + listener.handleLiteralBool(token); + return token.next; + } + + Token parseLiteralNull(Token token) { + listener.handleLiteralNull(token); + return token.next; + } + + Token parseSend(Token token) { + listener.beginSend(token); + token = parseIdentifier(token); + token = parseArgumentsOpt(token); + listener.endSend(token); + return token; + } + + Token parseArgumentsOpt(Token token) { + if (!optional('(', token)) { + listener.handleNoArguments(token); + return token; + } else { + return parseArguments(token); + } + } + + Token parseArguments(Token token) { + Token begin = token; + listener.beginArguments(begin); + assert(identical('(', token.stringValue)); + int argumentCount = 0; + if (optional(')', token.next)) { + listener.endArguments(argumentCount, begin, token.next); + return token.next.next; + } + bool old = mayParseFunctionExpressions; + mayParseFunctionExpressions = true; + do { + Token colon = null; + if (optional(':', token.next.next)) { + token = parseIdentifier(token.next); + colon = token; + } + token = parseExpression(token.next); + if (colon != null) listener.handleNamedArgument(colon); + ++argumentCount; + } while (optional(',', token)); + mayParseFunctionExpressions = old; + listener.endArguments(argumentCount, begin, token); + return expect(')', token); + } + + Token parseIsOperatorRest(Token token) { + assert(optional('is', token)); + Token operator = token; + Token not = null; + if (optional('!', token.next)) { + token = token.next; + not = token; + } + token = parseType(token.next); + listener.handleIsOperator(operator, not, token); + String value = token.stringValue; + if (identical(value, 'is') || identical(value, 'as')) { + // The is- and as-operators cannot be chained, but they can take part of + // expressions like: foo is Foo || foo is Bar. + listener.unexpected(token); + } + return token; + } + + Token parseAsOperatorRest(Token token) { + assert(optional('as', token)); + Token operator = token; + token = parseType(token.next); + listener.handleAsOperator(operator, token); + String value = token.stringValue; + if (identical(value, 'is') || identical(value, 'as')) { + // The is- and as-operators cannot be chained. + listener.unexpected(token); + } + return token; + } + + Token parseVariablesDeclaration(Token token) { + return parseVariablesDeclarationMaybeSemicolon(token, true); + } + + Token parseVariablesDeclarationNoSemicolon(Token token) { + // Only called when parsing a for loop, so this is for parsing locals. + return parseVariablesDeclarationMaybeSemicolon(token, false); + } + + Token parseVariablesDeclarationMaybeSemicolon(Token token, + bool endWithSemicolon) { + int count = 1; + listener.beginVariablesDeclaration(token); + token = parseModifiers(token); + token = parseTypeOpt(token); + token = parseOptionallyInitializedIdentifier(token); + while (optional(',', token)) { + token = parseOptionallyInitializedIdentifier(token.next); + ++count; + } + if (endWithSemicolon) { + Token semicolon = token; + token = expectSemicolon(semicolon); + listener.endVariablesDeclaration(count, semicolon); + return token; + } else { + listener.endVariablesDeclaration(count, null); + return token; + } + } + + Token parseOptionallyInitializedIdentifier(Token token) { + listener.beginInitializedIdentifier(token); + token = parseIdentifier(token); + token = parseVariableInitializerOpt(token); + listener.endInitializedIdentifier(); + return token; + } + + Token parseIfStatement(Token token) { + Token ifToken = token; + listener.beginIfStatement(ifToken); + token = expect('if', token); + token = parseParenthesizedExpression(token); + token = parseStatement(token); + Token elseToken = null; + if (optional('else', token)) { + elseToken = token; + token = parseStatement(token.next); + } + listener.endIfStatement(ifToken, elseToken); + return token; + } + + Token parseForStatement(Token token) { + Token forToken = token; + listener.beginForStatement(forToken); + token = expect('for', token); + token = expect('(', token); + token = parseVariablesDeclarationOrExpressionOpt(token); + if (optional('in', token)) { + return parseForInRest(forToken, token); + } else { + return parseForRest(forToken, token); + } + } + + Token parseVariablesDeclarationOrExpressionOpt(Token token) { + final String value = token.stringValue; + if (identical(value, ';')) { + listener.handleNoExpression(token); + return token; + } else if ((identical(value, 'var')) || (identical(value, 'final'))) { + return parseVariablesDeclarationNoSemicolon(token); + } + Token identifier = peekIdentifierAfterType(token); + if (identifier != null) { + assert(identifier.isIdentifier()); + if (isOneOf4(identifier.next, '=', ';', ',', 'in')) { + return parseVariablesDeclarationNoSemicolon(token); + } + } + return parseExpression(token); + } + + Token parseForRest(Token forToken, Token token) { + token = expectSemicolon(token); + if (optional(';', token)) { + token = parseEmptyStatement(token); + } else { + token = parseExpressionStatement(token); + } + int expressionCount = 0; + while (true) { + if (optional(')', token)) break; + token = parseExpression(token); + ++expressionCount; + if (optional(',', token)) { + token = token.next; + } else { + break; + } + } + token = expect(')', token); + token = parseStatement(token); + listener.endForStatement(expressionCount, forToken, token); + return token; + } + + Token parseForInRest(Token forToken, Token token) { + assert(optional('in', token)); + Token inKeyword = token; + token = parseExpression(token.next); + token = expect(')', token); + token = parseStatement(token); + listener.endForIn(forToken, inKeyword, token); + return token; + } + + Token parseWhileStatement(Token token) { + Token whileToken = token; + listener.beginWhileStatement(whileToken); + token = expect('while', token); + token = parseParenthesizedExpression(token); + token = parseStatement(token); + listener.endWhileStatement(whileToken, token); + return token; + } + + Token parseDoWhileStatement(Token token) { + Token doToken = token; + listener.beginDoWhileStatement(doToken); + token = expect('do', token); + token = parseStatement(token); + Token whileToken = token; + token = expect('while', token); + token = parseParenthesizedExpression(token); + listener.endDoWhileStatement(doToken, whileToken, token); + return expectSemicolon(token); + } + + Token parseBlock(Token token) { + Token begin = token; + listener.beginBlock(begin); + int statementCount = 0; + token = expect('{', token); + while (notEofOrValue('}', token)) { + token = parseStatement(token); + ++statementCount; + } + listener.endBlock(statementCount, begin, token); + return expect('}', token); + } + + Token parseThrowExpression(Token token, bool allowCascades) { + Token throwToken = token; + listener.beginThrowExpression(throwToken); + token = expect('throw', token); + token = allowCascades + ? parseExpression(token) + : parseExpressionWithoutCascade(token); + listener.endThrowExpression(throwToken, token); + return token; + } + + Token parseRethrowStatement(Token token) { + Token throwToken = token; + listener.beginRethrowStatement(throwToken); + // TODO(kasperl): Disallow throw here. + if (identical(throwToken.stringValue, 'throw')) { + token = expect('throw', token); + } else { + token = expect('rethrow', token); + } + listener.endRethrowStatement(throwToken, token); + return expectSemicolon(token); + } + + Token parseTryStatement(Token token) { + assert(optional('try', token)); + Token tryKeyword = token; + listener.beginTryStatement(tryKeyword); + token = parseBlock(token.next); + int catchCount = 0; + + String value = token.stringValue; + while (identical(value, 'catch') || identical(value, 'on')) { + var onKeyword = null; + if (identical(value, 'on')) { + // on qualified catchPart? + onKeyword = token; + token = parseType(token.next); + value = token.stringValue; + } + Token catchKeyword = null; + if (identical(value, 'catch')) { + catchKeyword = token; + // TODO(ahe): Validate the "parameters". + token = parseFormalParameters(token.next); + } + token = parseBlock(token); + ++catchCount; + listener.handleCatchBlock(onKeyword, catchKeyword); + value = token.stringValue; // while condition + } + + Token finallyKeyword = null; + if (optional('finally', token)) { + finallyKeyword = token; + token = parseBlock(token.next); + listener.handleFinallyBlock(finallyKeyword); + } + listener.endTryStatement(catchCount, tryKeyword, finallyKeyword); + return token; + } + + Token parseSwitchStatement(Token token) { + assert(optional('switch', token)); + Token switchKeyword = token; + listener.beginSwitchStatement(switchKeyword); + token = parseParenthesizedExpression(token.next); + token = parseSwitchBlock(token); + listener.endSwitchStatement(switchKeyword, token); + return token.next; + } + + Token parseSwitchBlock(Token token) { + Token begin = token; + listener.beginSwitchBlock(begin); + token = expect('{', token); + int caseCount = 0; + while (!identical(token.kind, EOF_TOKEN)) { + if (optional('}', token)) { + break; + } + token = parseSwitchCase(token); + ++caseCount; + } + listener.endSwitchBlock(caseCount, begin, token); + expect('}', token); + return token; + } + + /** + * Peek after the following labels (if any). The following token + * is used to determine if the labels belong to a statement or a + * switch case. + */ + Token peekPastLabels(Token token) { + while (token.isIdentifier() && optional(':', token.next)) { + token = token.next.next; + } + return token; + } + + /** + * Parse a group of labels, cases and possibly a default keyword and + * the statements that they select. + */ + Token parseSwitchCase(Token token) { + Token begin = token; + Token defaultKeyword = null; + int expressionCount = 0; + int labelCount = 0; + Token peek = peekPastLabels(token); + while (true) { + // Loop until we find something that can't be part of a switch case. + String value = peek.stringValue; + if (identical(value, 'default')) { + while (!identical(token, peek)) { + token = parseLabel(token); + labelCount++; + } + defaultKeyword = token; + token = expect(':', token.next); + peek = token; + break; + } else if (identical(value, 'case')) { + while (!identical(token, peek)) { + token = parseLabel(token); + labelCount++; + } + Token caseKeyword = token; + token = parseExpression(token.next); + Token colonToken = token; + token = expect(':', token); + listener.handleCaseMatch(caseKeyword, colonToken); + expressionCount++; + peek = peekPastLabels(token); + } else { + if (expressionCount == 0) { + listener.expected("case", token); + } + break; + } + } + // Finally zero or more statements. + int statementCount = 0; + while (!identical(token.kind, EOF_TOKEN)) { + String value = peek.stringValue; + if ((identical(value, 'case')) || + (identical(value, 'default')) || + ((identical(value, '}')) && (identical(token, peek)))) { + // A label just before "}" will be handled as a statement error. + break; + } else { + token = parseStatement(token); + } + statementCount++; + peek = peekPastLabels(token); + } + listener.handleSwitchCase(labelCount, expressionCount, defaultKeyword, + statementCount, begin, token); + return token; + } + + Token parseBreakStatement(Token token) { + assert(optional('break', token)); + Token breakKeyword = token; + token = token.next; + bool hasTarget = false; + if (token.isIdentifier()) { + token = parseIdentifier(token); + hasTarget = true; + } + listener.handleBreakStatement(hasTarget, breakKeyword, token); + return expectSemicolon(token); + } + + Token parseAssertStatement(Token token) { + Token assertKeyword = token; + token = expect('assert', token); + expect('(', token); + token = parseArguments(token); + listener.handleAssertStatement(assertKeyword, token); + return expectSemicolon(token); + } + + Token parseContinueStatement(Token token) { + assert(optional('continue', token)); + Token continueKeyword = token; + token = token.next; + bool hasTarget = false; + if (token.isIdentifier()) { + token = parseIdentifier(token); + hasTarget = true; + } + listener.handleContinueStatement(hasTarget, continueKeyword, token); + return expectSemicolon(token); + } + + Token parseEmptyStatement(Token token) { + listener.handleEmptyStatement(token); + return expectSemicolon(token); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/parser_task.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/parser_task.dart new file mode 100644 index 0000000..e83e6f5 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/parser_task.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2011, 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. + +part of scanner; + +class ParserTask extends CompilerTask { + ParserTask(Compiler compiler) : super(compiler); + String get name => 'Parser'; + + Node parse(Element element) { + return measure(() => element.parseNode(compiler)); + } + + Node parseCompilationUnit(Token token) { + return measure(() { + NodeListener listener = new NodeListener(compiler, null); + Parser parser = new Parser(listener); + parser.parseUnit(token); + Node result = listener.popNode(); + assert(listener.nodes.isEmpty); + return result; + }); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/partial_parser.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/partial_parser.dart new file mode 100644 index 0000000..ec1f2df --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/partial_parser.dart @@ -0,0 +1,141 @@ +// Copyright (c) 2011, 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. + +part of scanner; + +class PartialParser extends Parser { + PartialParser(Listener listener) : super(listener); + + Token parseClassBody(Token token) => skipClassBody(token); + + Token fullParseClassBody(Token token) => super.parseClassBody(token); + + Token parseExpression(Token token) => skipExpression(token); + + Token parseArgumentsOpt(Token token) { + // This method is overridden for two reasons: + // 1. Avoid generating events for arguments. + // 2. Avoid calling skip expression for each argument (which doesn't work). + if (optional('(', token)) { + BeginGroupToken begin = token; + return begin.endGroup.next; + } else { + return token; + } + } + + Token skipExpression(Token token) { + while (true) { + final kind = token.kind; + final value = token.stringValue; + if ((identical(kind, EOF_TOKEN)) || + (identical(value, ';')) || + (identical(value, ',')) || + (identical(value, ']'))) { + break; + } + if (identical(value, '=') || + identical(value, '?') || + identical(value, ':')) { + var nextValue = token.next.stringValue; + if (identical(nextValue, 'const')) { + token = token.next; + nextValue = token.next.stringValue; + } + if (identical(nextValue, '{')) { + // Handle cases like this: + // class Foo { + // var map; + // Foo() : map = {}; + // Foo.x() : map = true ? {} : {}; + // } + BeginGroupToken begin = token.next; + token = (begin.endGroup != null) ? begin.endGroup : token; + token = token.next; + continue; + } + if (identical(nextValue, '<')) { + // Handle cases like this: + // class Foo { + // var map; + // Foo() : map = {}; + // Foo.x() : map = true ? {} : {}; + // } + BeginGroupToken begin = token.next; + token = (begin.endGroup != null) ? begin.endGroup : token; + token = token.next; + if (identical(token.stringValue, '{')) { + begin = token; + token = (begin.endGroup != null) ? begin.endGroup : token; + token = token.next; + } + continue; + } + } + if (!mayParseFunctionExpressions && identical(value, '{')) { + break; + } + if (token is BeginGroupToken) { + BeginGroupToken begin = token; + token = (begin.endGroup != null) ? begin.endGroup : token; + } + token = token.next; + } + return token; + } + + Token skipClassBody(Token token) { + if (!optional('{', token)) { + return listener.expectedClassBodyToSkip(token); + } + BeginGroupToken beginGroupToken = token; + Token endGroup = beginGroupToken.endGroup; + if (endGroup == null) { + return listener.unmatched(beginGroupToken); + } else if (!identical(endGroup.kind, $CLOSE_CURLY_BRACKET)) { + return listener.unmatched(beginGroupToken); + } + return endGroup; + } + + Token parseFunctionBody(Token token, bool isExpression, bool allowAbstract) { + assert(!isExpression); + String value = token.stringValue; + if (identical(value, ';')) { + if (!allowAbstract) { + listener.reportError(token, MessageKind.BODY_EXPECTED); + } + listener.handleNoFunctionBody(token); + } else { + if (identical(value, '=>')) { + token = parseExpression(token.next); + expectSemicolon(token); + } else if (value == '=') { + token = parseRedirectingFactoryBody(token); + expectSemicolon(token); + } else { + token = skipBlock(token); + } + listener.skippedFunctionBody(token); + } + return token; + } + + Token parseFormalParameters(Token token) => skipFormals(token); + + Token skipFormals(Token token) { + listener.beginOptionalFormalParameters(token); + if (!optional('(', token)) { + if (optional(';', token)) { + listener.recoverableError(token, "expected '('"); + return token; + } + return listener.unexpected(token); + } + BeginGroupToken beginGroupToken = token; + Token endToken = beginGroupToken.endGroup; + listener.endFormalParameters(0, token, endToken); + return endToken.next; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/scanner.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/scanner.dart new file mode 100644 index 0000000..8af864a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/scanner.dart @@ -0,0 +1,1096 @@ +// Copyright (c) 2012, 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. + +part of scanner; + +abstract class Scanner { + Token tokenize(); + + factory Scanner(SourceFile file, {bool includeComments: false}) { + if (file is Utf8BytesSourceFile) { + return new Utf8BytesScanner(file, includeComments: includeComments); + } else { + return new StringScanner(file, includeComments: includeComments); + } + } +} + +abstract class AbstractScanner implements Scanner { + // TODO(ahe): Move this class to implementation. + + final bool includeComments; + + /** + * The string offset for the next token that will be created. + * + * Note that in the [Utf8BytesScanner], [stringOffset] and [scanOffset] values + * are different. One string character can be encoded using multiple UTF-8 + * bytes. + */ + int tokenStart = -1; + + /** + * A pointer to the token stream created by this scanner. The first token + * is a special token and not part of the source file. This is an + * implementation detail to avoids special cases in the scanner. This token + * is not exposed to clients of the scanner, which are expected to invoke + * [firstToken] to access the token stream. + */ + final Token tokens = new SymbolToken(EOF_INFO, -1); + + /** + * A pointer to the last scanned token. + */ + Token tail; + + /** + * The source file that is being scanned. This field can be [:null:]. + * If the source file is available, the scanner assigns its [:lineStarts:] and + * [:length:] fields at the end of [tokenize]. + */ + final SourceFile file; + + final List lineStarts = [0]; + + AbstractScanner(this.file, this.includeComments) { + this.tail = this.tokens; + } + + /** + * Advances and returns the next character. + * + * If the next character is non-ASCII, then the returned value depends on the + * scanner implementation. The [Utf8BytesScanner] returns a UTF-8 byte, while + * the [StringScanner] returns a UTF-16 code unit. + * + * The scanner ensures that [advance] is not invoked after it returned [$EOF]. + * This allows implementations to omit bound checks if the data structure ends + * with '0'. + */ + int advance(); + + /** + * Returns the current unicode character. + * + * If the current character is ASCII, then it is returned unchanged. + * + * The [Utf8BytesScanner] decodes the next unicode code point starting at the + * current position. Note that every unicode character is returned as a single + * code point, that is, for '\u{1d11e}' it returns 119070, and the following + * [advance] returns the next character. + * + * The [StringScanner] returns the current character unchanged, which might + * be a surrogate character. In the case of '\u{1d11e}', it returns the first + * code unit 55348, and the following [advance] returns the second code unit + * 56606. + * + * Invoking [currentAsUnicode] multiple times is safe, i.e., + * [:currentAsUnicode(next) == currentAsUnicode(currentAsUnicode(next)):]. + */ + int currentAsUnicode(int next); + + /** + * Returns the character at the next poisition. Like in [advance], the + * [Utf8BytesScanner] returns a UTF-8 byte, while the [StringScanner] returns + * a UTF-16 code unit. + */ + int peek(); + + /** + * Notifies the scanner that unicode characters were detected in either a + * comment or a string literal between [startScanOffset] and the current + * scan offset. + */ + void handleUnicode(int startScanOffset); + + /** + * Returns the current scan offset. + * + * In the [Utf8BytesScanner] this is the offset into the byte list, in the + * [StringScanner] the offset in the source string. + */ + int get scanOffset; + + /** + * Returns the current string offset. + * + * In the [StringScanner] this is identical to the [scanOffset]. In the + * [Utf8BytesScanner] it is computed based on encountered UTF-8 characters. + */ + int get stringOffset; + + /** + * Returns the first token scanned by this [Scanner]. + */ + Token firstToken(); + + /** + * Returns the last token scanned by this [Scanner]. + */ + Token previousToken(); + + /** + * Notifies that a new token starts at current offset. + */ + void beginToken() { + tokenStart = stringOffset; + } + + /** + * Appends a substring from the scan offset [:start:] to the current + * [:scanOffset:] plus the [:extraOffset:]. For example, if the current + * scanOffset is 10, then [:appendSubstringToken(5, -1):] will append the + * substring string [5,9). + * + * Note that [extraOffset] can only be used if the covered character(s) are + * known to be ASCII. + */ + void appendSubstringToken(PrecedenceInfo info, int start, + bool asciiOnly, [int extraOffset]); + + /** Documentation in subclass [ArrayBasedScanner]. */ + void appendStringToken(PrecedenceInfo info, String value); + + /** Documentation in subclass [ArrayBasedScanner]. */ + void appendPrecedenceToken(PrecedenceInfo info); + + /** Documentation in subclass [ArrayBasedScanner]. */ + int select(int choice, PrecedenceInfo yes, PrecedenceInfo no); + + /** Documentation in subclass [ArrayBasedScanner]. */ + void appendKeywordToken(Keyword keyword); + + /** Documentation in subclass [ArrayBasedScanner]. */ + void appendEofToken(); + + /** Documentation in subclass [ArrayBasedScanner]. */ + void appendWhiteSpace(int next); + + /** Documentation in subclass [ArrayBasedScanner]. */ + void lineFeedInMultiline(); + + /** Documentation in subclass [ArrayBasedScanner]. */ + void appendBeginGroup(PrecedenceInfo info); + + /** Documentation in subclass [ArrayBasedScanner]. */ + int appendEndGroup(PrecedenceInfo info, int openKind); + + /** Documentation in subclass [ArrayBasedScanner]. */ + void appendGt(PrecedenceInfo info); + + /** Documentation in subclass [ArrayBasedScanner]. */ + void appendGtGt(PrecedenceInfo info); + + /** Documentation in subclass [ArrayBasedScanner]. */ + void appendComment(start, bool asciiOnly); + + /** Documentation in subclass [ArrayBasedScanner]. */ + void discardOpenLt(); + + /// Return true when at EOF. + bool atEndOfFile(); + + Token tokenize() { + while (!atEndOfFile()) { + int next = advance(); + while (!identical(next, $EOF)) { + next = bigSwitch(next); + } + if (atEndOfFile()) { + appendEofToken(); + } else { + error('Unexpected ${$EOF} byte in input.'); + } + } + + if (file != null) { + file.length = stringOffset; + // One additional line start at the end, see [SourceFile.lineStarts]. + lineStarts.add(stringOffset + 1); + file.lineStarts = lineStarts; + } + + return firstToken(); + } + + int bigSwitch(int next) { + beginToken(); + if (identical(next, $SPACE) || identical(next, $TAB) + || identical(next, $LF) || identical(next, $CR)) { + appendWhiteSpace(next); + next = advance(); + // Sequences of spaces are common, so advance through them fast. + while (identical(next, $SPACE)) { + // We don't invoke [:appendWhiteSpace(next):] here for efficiency, + // assuming that it does not do anything for space characters. + next = advance(); + } + return next; + } + + if ($a <= next && next <= $z) { + if (identical($r, next)) { + return tokenizeRawStringKeywordOrIdentifier(next); + } + return tokenizeKeywordOrIdentifier(next, true); + } + + if (($A <= next && next <= $Z) || + identical(next, $_) || + identical(next, $$)) { + return tokenizeIdentifier(next, scanOffset, true); + } + + if (identical(next, $LT)) { + return tokenizeLessThan(next); + } + + if (identical(next, $GT)) { + return tokenizeGreaterThan(next); + } + + if (identical(next, $EQ)) { + return tokenizeEquals(next); + } + + if (identical(next, $BANG)) { + return tokenizeExclamation(next); + } + + if (identical(next, $PLUS)) { + return tokenizePlus(next); + } + + if (identical(next, $MINUS)) { + return tokenizeMinus(next); + } + + if (identical(next, $STAR)) { + return tokenizeMultiply(next); + } + + if (identical(next, $PERCENT)) { + return tokenizePercent(next); + } + + if (identical(next, $AMPERSAND)) { + return tokenizeAmpersand(next); + } + + if (identical(next, $BAR)) { + return tokenizeBar(next); + } + + if (identical(next, $CARET)) { + return tokenizeCaret(next); + } + + if (identical(next, $OPEN_SQUARE_BRACKET)) { + return tokenizeOpenSquareBracket(next); + } + + if (identical(next, $TILDE)) { + return tokenizeTilde(next); + } + + if (identical(next, $BACKSLASH)) { + appendPrecedenceToken(BACKSLASH_INFO); + return advance(); + } + + if (identical(next, $HASH)) { + return tokenizeTag(next); + } + + if (identical(next, $OPEN_PAREN)) { + appendBeginGroup(OPEN_PAREN_INFO); + return advance(); + } + + if (identical(next, $CLOSE_PAREN)) { + return appendEndGroup(CLOSE_PAREN_INFO, OPEN_PAREN_TOKEN); + } + + if (identical(next, $COMMA)) { + appendPrecedenceToken(COMMA_INFO); + return advance(); + } + + if (identical(next, $COLON)) { + appendPrecedenceToken(COLON_INFO); + return advance(); + } + + if (identical(next, $SEMICOLON)) { + appendPrecedenceToken(SEMICOLON_INFO); + // Type parameters and arguments cannot contain semicolon. + discardOpenLt(); + return advance(); + } + + if (identical(next, $QUESTION)) { + appendPrecedenceToken(QUESTION_INFO); + return advance(); + } + + if (identical(next, $CLOSE_SQUARE_BRACKET)) { + return appendEndGroup(CLOSE_SQUARE_BRACKET_INFO, + OPEN_SQUARE_BRACKET_TOKEN); + } + + if (identical(next, $BACKPING)) { + appendPrecedenceToken(BACKPING_INFO); + return advance(); + } + + if (identical(next, $OPEN_CURLY_BRACKET)) { + appendBeginGroup(OPEN_CURLY_BRACKET_INFO); + return advance(); + } + + if (identical(next, $CLOSE_CURLY_BRACKET)) { + return appendEndGroup(CLOSE_CURLY_BRACKET_INFO, + OPEN_CURLY_BRACKET_TOKEN); + } + + if (identical(next, $SLASH)) { + return tokenizeSlashOrComment(next); + } + + if (identical(next, $AT)) { + return tokenizeAt(next); + } + + if (identical(next, $DQ) || identical(next, $SQ)) { + return tokenizeString(next, scanOffset, false); + } + + if (identical(next, $PERIOD)) { + return tokenizeDotsOrNumber(next); + } + + if (identical(next, $0)) { + return tokenizeHexOrNumber(next); + } + + // TODO(ahe): Would a range check be faster? + if (identical(next, $1) || identical(next, $2) || identical(next, $3) + || identical(next, $4) || identical(next, $5) || identical(next, $6) + || identical(next, $7) || identical(next, $8) || identical(next, $9)) { + return tokenizeNumber(next); + } + + if (identical(next, $EOF)) { + return $EOF; + } + if (next < 0x1f) { + return error("unexpected character $next"); + } + + next = currentAsUnicode(next); + + // The following are non-ASCII characters. + + if (identical(next, $NBSP)) { + appendWhiteSpace(next); + return advance(); + } + + return error("unexpected unicode character $next"); + } + + int tokenizeTag(int next) { + // # or #!.*[\n\r] + if (scanOffset == 0) { + if (identical(peek(), $BANG)) { + int start = scanOffset + 1; + bool asciiOnly = true; + do { + next = advance(); + if (next > 127) asciiOnly = false; + } while (!identical(next, $LF) && + !identical(next, $CR) && + !identical(next, $EOF)); + if (!asciiOnly) handleUnicode(start); + return next; + } + } + appendPrecedenceToken(HASH_INFO); + return advance(); + } + + int tokenizeTilde(int next) { + // ~ ~/ ~/= + next = advance(); + if (identical(next, $SLASH)) { + return select($EQ, TILDE_SLASH_EQ_INFO, TILDE_SLASH_INFO); + } else { + appendPrecedenceToken(TILDE_INFO); + return next; + } + } + + int tokenizeOpenSquareBracket(int next) { + // [ [] []= + next = advance(); + if (identical(next, $CLOSE_SQUARE_BRACKET)) { + Token token = previousToken(); + if (token is KeywordToken && + identical(token.keyword.syntax, 'operator')) { + return select($EQ, INDEX_EQ_INFO, INDEX_INFO); + } + } + appendBeginGroup(OPEN_SQUARE_BRACKET_INFO); + return next; + } + + int tokenizeCaret(int next) { + // ^ ^= + return select($EQ, CARET_EQ_INFO, CARET_INFO); + } + + int tokenizeBar(int next) { + // | || |= + next = advance(); + if (identical(next, $BAR)) { + appendPrecedenceToken(BAR_BAR_INFO); + return advance(); + } else if (identical(next, $EQ)) { + appendPrecedenceToken(BAR_EQ_INFO); + return advance(); + } else { + appendPrecedenceToken(BAR_INFO); + return next; + } + } + + int tokenizeAmpersand(int next) { + // && &= & + next = advance(); + if (identical(next, $AMPERSAND)) { + appendPrecedenceToken(AMPERSAND_AMPERSAND_INFO); + return advance(); + } else if (identical(next, $EQ)) { + appendPrecedenceToken(AMPERSAND_EQ_INFO); + return advance(); + } else { + appendPrecedenceToken(AMPERSAND_INFO); + return next; + } + } + + int tokenizePercent(int next) { + // % %= + return select($EQ, PERCENT_EQ_INFO, PERCENT_INFO); + } + + int tokenizeMultiply(int next) { + // * *= + return select($EQ, STAR_EQ_INFO, STAR_INFO); + } + + int tokenizeMinus(int next) { + // - -- -= + next = advance(); + if (identical(next, $MINUS)) { + appendPrecedenceToken(MINUS_MINUS_INFO); + return advance(); + } else if (identical(next, $EQ)) { + appendPrecedenceToken(MINUS_EQ_INFO); + return advance(); + } else { + appendPrecedenceToken(MINUS_INFO); + return next; + } + } + + int tokenizePlus(int next) { + // + ++ += + next = advance(); + if (identical($PLUS, next)) { + appendPrecedenceToken(PLUS_PLUS_INFO); + return advance(); + } else if (identical($EQ, next)) { + appendPrecedenceToken(PLUS_EQ_INFO); + return advance(); + } else { + appendPrecedenceToken(PLUS_INFO); + return next; + } + } + + int tokenizeExclamation(int next) { + // ! != + // !== is kept for user-friendly error reporting. + + next = advance(); + if (identical(next, $EQ)) { + return select($EQ, BANG_EQ_EQ_INFO, BANG_EQ_INFO); + } + appendPrecedenceToken(BANG_INFO); + return next; + } + + int tokenizeEquals(int next) { + // = == => + // === is kept for user-friendly error reporting. + + // Type parameters and arguments cannot contain any token that + // starts with '='. + discardOpenLt(); + + next = advance(); + if (identical(next, $EQ)) { + return select($EQ, EQ_EQ_EQ_INFO, EQ_EQ_INFO); + } else if (identical(next, $GT)) { + appendPrecedenceToken(FUNCTION_INFO); + return advance(); + } + appendPrecedenceToken(EQ_INFO); + return next; + } + + int tokenizeGreaterThan(int next) { + // > >= >> >>= + next = advance(); + if (identical($EQ, next)) { + appendPrecedenceToken(GT_EQ_INFO); + return advance(); + } else if (identical($GT, next)) { + next = advance(); + if (identical($EQ, next)) { + appendPrecedenceToken(GT_GT_EQ_INFO); + return advance(); + } else { + appendGtGt(GT_GT_INFO); + return next; + } + } else { + appendGt(GT_INFO); + return next; + } + } + + int tokenizeLessThan(int next) { + // < <= << <<= + next = advance(); + if (identical($EQ, next)) { + appendPrecedenceToken(LT_EQ_INFO); + return advance(); + } else if (identical($LT, next)) { + return select($EQ, LT_LT_EQ_INFO, LT_LT_INFO); + } else { + appendBeginGroup(LT_INFO); + return next; + } + } + + int tokenizeNumber(int next) { + int start = scanOffset; + while (true) { + next = advance(); + if ($0 <= next && next <= $9) { + continue; + } else if (identical(next, $e) || identical(next, $E)) { + return tokenizeFractionPart(next, start); + } else { + if (identical(next, $PERIOD)) { + int nextnext = peek(); + if ($0 <= nextnext && nextnext <= $9) { + return tokenizeFractionPart(advance(), start); + } + } + appendSubstringToken(INT_INFO, start, true); + return next; + } + } + return null; + } + + int tokenizeHexOrNumber(int next) { + int x = peek(); + if (identical(x, $x) || identical(x, $X)) { + return tokenizeHex(next); + } + return tokenizeNumber(next); + } + + int tokenizeHex(int next) { + int start = scanOffset; + next = advance(); // Advance past the $x or $X. + bool hasDigits = false; + while (true) { + next = advance(); + if (($0 <= next && next <= $9) + || ($A <= next && next <= $F) + || ($a <= next && next <= $f)) { + hasDigits = true; + } else { + if (!hasDigits) { + return error("hex digit expected"); + } + appendSubstringToken(HEXADECIMAL_INFO, start, true); + return next; + } + } + return null; + } + + int tokenizeDotsOrNumber(int next) { + int start = scanOffset; + next = advance(); + if (($0 <= next && next <= $9)) { + return tokenizeFractionPart(next, start); + } else if (identical($PERIOD, next)) { + return select($PERIOD, PERIOD_PERIOD_PERIOD_INFO, PERIOD_PERIOD_INFO); + } else { + appendPrecedenceToken(PERIOD_INFO); + return next; + } + } + + int tokenizeFractionPart(int next, int start) { + bool done = false; + bool hasDigit = false; + LOOP: while (!done) { + if ($0 <= next && next <= $9) { + hasDigit = true; + } else if (identical($e, next) || identical($E, next)) { + hasDigit = true; + next = tokenizeExponent(advance()); + done = true; + continue LOOP; + } else { + done = true; + continue LOOP; + } + next = advance(); + } + if (!hasDigit) { + // Reduce offset, we already advanced to the token past the period. + appendSubstringToken(INT_INFO, start, true, -1); + + // TODO(ahe): Wrong offset for the period. Cannot call beginToken because + // the scanner already advanced past the period. + if (identical($PERIOD, next)) { + return select($PERIOD, PERIOD_PERIOD_PERIOD_INFO, PERIOD_PERIOD_INFO); + } + appendPrecedenceToken(PERIOD_INFO); + return next; + } + appendSubstringToken(DOUBLE_INFO, start, true); + return next; + } + + int tokenizeExponent(int next) { + if (identical(next, $PLUS) || identical(next, $MINUS)) { + next = advance(); + } + bool hasDigits = false; + while (true) { + if ($0 <= next && next <= $9) { + hasDigits = true; + } else { + if (!hasDigits) { + return error("digit expected"); + } + return next; + } + next = advance(); + } + return null; + } + + int tokenizeSlashOrComment(int next) { + int start = scanOffset; + next = advance(); + if (identical($STAR, next)) { + return tokenizeMultiLineComment(next, start); + } else if (identical($SLASH, next)) { + return tokenizeSingleLineComment(next, start); + } else if (identical($EQ, next)) { + appendPrecedenceToken(SLASH_EQ_INFO); + return advance(); + } else { + appendPrecedenceToken(SLASH_INFO); + return next; + } + } + + int tokenizeSingleLineComment(int next, int start) { + bool asciiOnly = true; + while (true) { + next = advance(); + if (next > 127) asciiOnly = false; + if (identical($LF, next) || + identical($CR, next) || + identical($EOF, next)) { + if (!asciiOnly) handleUnicode(start); + appendComment(start, asciiOnly); + return next; + } + } + return null; + } + + + int tokenizeMultiLineComment(int next, int start) { + bool asciiOnlyComment = true; // Track if the entire comment is ASCII. + bool asciiOnlyLines = true; // Track ASCII since the last handleUnicode. + int unicodeStart = start; + int nesting = 1; + next = advance(); + while (true) { + if (identical($EOF, next)) { + if (!asciiOnlyLines) handleUnicode(unicodeStart); + appendStringToken(BAD_INPUT_INFO, "unterminated multi-line comment"); + break; + } else if (identical($STAR, next)) { + next = advance(); + if (identical($SLASH, next)) { + --nesting; + if (0 == nesting) { + if (!asciiOnlyLines) handleUnicode(unicodeStart); + next = advance(); + appendComment(start, asciiOnlyComment); + break; + } else { + next = advance(); + } + } + } else if (identical($SLASH, next)) { + next = advance(); + if (identical($STAR, next)) { + next = advance(); + ++nesting; + } + } else if (identical(next, $LF)) { + if (!asciiOnlyLines) { + // Synchronize the string offset in the utf8 scanner. + handleUnicode(unicodeStart); + asciiOnlyLines = true; + unicodeStart = scanOffset; + } + lineFeedInMultiline(); + next = advance(); + } else { + if (next > 127) { + asciiOnlyLines = false; + asciiOnlyComment = false; + } + next = advance(); + } + } + return next; + } + + int tokenizeRawStringKeywordOrIdentifier(int next) { + // [next] is $r. + int nextnext = peek(); + if (identical(nextnext, $DQ) || identical(nextnext, $SQ)) { + int start = scanOffset; + next = advance(); + return tokenizeString(next, start, true); + } + return tokenizeKeywordOrIdentifier(next, true); + } + + int tokenizeKeywordOrIdentifier(int next, bool allowDollar) { + KeywordState state = KeywordState.KEYWORD_STATE; + int start = scanOffset; + while (state != null && $a <= next && next <= $z) { + state = state.next(next); + next = advance(); + } + if (state == null || state.keyword == null) { + return tokenizeIdentifier(next, start, allowDollar); + } + if (($A <= next && next <= $Z) || + ($0 <= next && next <= $9) || + identical(next, $_) || + identical(next, $$)) { + return tokenizeIdentifier(next, start, allowDollar); + } else { + appendKeywordToken(state.keyword); + return next; + } + } + + /** + * [allowDollar] can exclude '$', which is not allowed as part of a string + * interpolation identifier. + */ + int tokenizeIdentifier(int next, int start, bool allowDollar) { + while (true) { + if (($a <= next && next <= $z) || + ($A <= next && next <= $Z) || + ($0 <= next && next <= $9) || + identical(next, $_) || + (identical(next, $$) && allowDollar)) { + next = advance(); + } else { + // Identifier ends here. + if (start == scanOffset) { + return error("expected identifier"); + } else { + appendSubstringToken(IDENTIFIER_INFO, start, true); + } + break; + } + } + return next; + } + + int tokenizeAt(int next) { + appendPrecedenceToken(AT_INFO); + return advance(); + } + + int tokenizeString(int next, int start, bool raw) { + int quoteChar = next; + next = advance(); + if (identical(quoteChar, next)) { + next = advance(); + if (identical(quoteChar, next)) { + // Multiline string. + return tokenizeMultiLineString(quoteChar, start, raw); + } else { + // Empty string. + appendSubstringToken(STRING_INFO, start, true); + return next; + } + } + if (raw) { + return tokenizeSingleLineRawString(next, quoteChar, start); + } else { + return tokenizeSingleLineString(next, quoteChar, start); + } + } + + /** + * [next] is the first character after the quote. + * [start] is the scanOffset of the quote. + * + * The token contains a substring of the source file, including the + * string quotes, backslashes for escaping. For interpolated strings, + * the parts before and after are separate tokens. + * + * "a $b c" + * + * gives StringToken("a $), StringToken(b) and StringToken( c"). + */ + int tokenizeSingleLineString(int next, int quoteChar, int start) { + bool asciiOnly = true; + while (!identical(next, quoteChar)) { + if (identical(next, $BACKSLASH)) { + next = advance(); + } else if (identical(next, $$)) { + if (!asciiOnly) handleUnicode(start); + next = tokenizeStringInterpolation(start, asciiOnly); + start = scanOffset; + asciiOnly = true; + continue; + } + if (next <= $CR + && (identical(next, $LF) || + identical(next, $CR) || + identical(next, $EOF))) { + if (!asciiOnly) handleUnicode(start); + return error("unterminated string literal"); + } + if (next > 127) asciiOnly = false; + next = advance(); + } + if (!asciiOnly) handleUnicode(start); + // Advance past the quote character. + next = advance(); + appendSubstringToken(STRING_INFO, start, asciiOnly); + return next; + } + + int tokenizeStringInterpolation(int start, bool asciiOnly) { + appendSubstringToken(STRING_INFO, start, asciiOnly); + beginToken(); // $ starts here. + int next = advance(); + if (identical(next, $OPEN_CURLY_BRACKET)) { + return tokenizeInterpolatedExpression(next); + } else { + return tokenizeInterpolatedIdentifier(next); + } + } + + int tokenizeInterpolatedExpression(int next) { + appendBeginGroup(STRING_INTERPOLATION_INFO); + beginToken(); // The expression starts here. + next = advance(); // Move past the curly bracket. + while (!identical(next, $EOF) && !identical(next, $STX)) { + next = bigSwitch(next); + } + if (identical(next, $EOF)) return next; + next = advance(); // Move past the $STX. + beginToken(); // The string interpolation suffix starts here. + return next; + } + + int tokenizeInterpolatedIdentifier(int next) { + appendPrecedenceToken(STRING_INTERPOLATION_IDENTIFIER_INFO); + beginToken(); // The identifier starts here. + + if ($a <= next && next <= $z) { + next = tokenizeKeywordOrIdentifier(next, false); + } else if (($A <= next && next <= $Z) || identical(next, $_)) { + next = tokenizeIdentifier(next, scanOffset, false); + } else { + error("expected identifier or '{'", shouldAdvance: false); + } + beginToken(); // The string interpolation suffix starts here. + return next; + } + + int tokenizeSingleLineRawString(int next, int quoteChar, int start) { + bool asciiOnly = true; + next = advance(); // Advance past the quote. + while (next != $EOF) { + if (identical(next, quoteChar)) { + if (!asciiOnly) handleUnicode(start); + next = advance(); + appendSubstringToken(STRING_INFO, start, asciiOnly); + return next; + } else if (identical(next, $LF) || identical(next, $CR)) { + if (!asciiOnly) handleUnicode(start); + return error("unterminated string literal"); + } else if (next > 127) { + asciiOnly = false; + } + next = advance(); + } + if (!asciiOnly) handleUnicode(start); + return error("unterminated string literal"); + } + + int tokenizeMultiLineRawString(int quoteChar, int start) { + bool asciiOnlyString = true; + bool asciiOnlyLine = true; + int unicodeStart = start; + int next = advance(); // Advance past the (last) quote (of three). + outer: while (!identical(next, $EOF)) { + while (!identical(next, quoteChar)) { + if (identical(next, $LF)) { + if (!asciiOnlyLine) { + // Synchronize the string offset in the utf8 scanner. + handleUnicode(unicodeStart); + asciiOnlyLine = true; + unicodeStart = scanOffset; + } + lineFeedInMultiline(); + } else if (next > 127) { + asciiOnlyLine = false; + asciiOnlyString = false; + } + next = advance(); + if (identical(next, $EOF)) break outer; + } + next = advance(); + if (identical(next, quoteChar)) { + next = advance(); + if (identical(next, quoteChar)) { + if (!asciiOnlyLine) handleUnicode(unicodeStart); + next = advance(); + appendSubstringToken(STRING_INFO, start, asciiOnlyString); + return next; + } + } + } + if (!asciiOnlyLine) handleUnicode(unicodeStart); + return error("unterminated string literal"); + } + + int tokenizeMultiLineString(int quoteChar, int start, bool raw) { + if (raw) return tokenizeMultiLineRawString(quoteChar, start); + bool asciiOnlyString = true; + bool asciiOnlyLine = true; + int unicodeStart = start; + int next = advance(); // Advance past the (last) quote (of three). + while (!identical(next, $EOF)) { + if (identical(next, $$)) { + if (!asciiOnlyLine) handleUnicode(unicodeStart); + next = tokenizeStringInterpolation(start, asciiOnlyString); + start = scanOffset; + unicodeStart = start; + asciiOnlyString = true; // A new string token is created for the rest. + asciiOnlyLine = true; + continue; + } + if (identical(next, quoteChar)) { + next = advance(); + if (identical(next, quoteChar)) { + next = advance(); + if (identical(next, quoteChar)) { + if (!asciiOnlyLine) handleUnicode(unicodeStart); + next = advance(); + appendSubstringToken(STRING_INFO, start, asciiOnlyString); + return next; + } + } + continue; + } + if (identical(next, $BACKSLASH)) { + next = advance(); + if (identical(next, $EOF)) break; + } + if (identical(next, $LF)) { + if (!asciiOnlyLine) { + // Synchronize the string offset in the utf8 scanner. + handleUnicode(unicodeStart); + asciiOnlyLine = true; + unicodeStart = scanOffset; + } + lineFeedInMultiline(); + } else if (next > 127) { + asciiOnlyString = false; + asciiOnlyLine = false; + } + next = advance(); + } + if (!asciiOnlyLine) handleUnicode(unicodeStart); + return error("unterminated string literal"); + } + + int error(String message, {bool shouldAdvance: true}) { + appendStringToken(BAD_INPUT_INFO, message); + if (atEndOfFile()) return $EOF; + if (shouldAdvance) { + return advance(); // Ensure progress. + } else { + return -1; + } + } + + void unmatchedBeginGroup(BeginGroupToken begin) { + String error = 'unmatched "${begin.stringValue}"'; + Token close = + new StringToken.fromString( + BAD_INPUT_INFO, error, begin.charOffset, canonicalize: true); + + // We want to ensure that unmatched BeginGroupTokens are reported + // as errors. However, the rest of the parser assumes the groups + // are well-balanced and will never look at the endGroup + // token. This is a nice property that allows us to skip quickly + // over correct code. By inserting an additional error token in + // the stream, we can keep ignoring endGroup tokens. + // + // [begin] --next--> [tail] + // [begin] --endG--> [close] --next--> [next] --next--> [tail] + // + // This allows the parser to skip from [begin] via endGroup to [close] and + // ignore the [close] token (assuming it's correct), then the error will be + // reported when parsing the [next] token. + + Token next = new StringToken.fromString( + BAD_INPUT_INFO, error, begin.charOffset, canonicalize: true); + begin.endGroup = close; + close.next = next; + next.next = begin.next; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/scanner_task.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/scanner_task.dart new file mode 100644 index 0000000..955fd87 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/scanner_task.dart @@ -0,0 +1,67 @@ +// Copyright (c) 2012, 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. + +part of scanner; + +class ScannerTask extends CompilerTask { + ScannerTask(Compiler compiler) : super(compiler); + String get name => 'Scanner'; + + void scanLibrary(LibraryElement library) { + CompilationUnitElement compilationUnit = library.entryCompilationUnit; + String canonicalUri = library.canonicalUri.toString(); + String resolvedUri = compilationUnit.script.readableUri.toString(); + if (canonicalUri == resolvedUri) { + compiler.log("Scanning library $canonicalUri"); + } else { + compiler.log("Scanning library $canonicalUri ($resolvedUri)"); + } + scan(compilationUnit); + } + + void scan(CompilationUnitElement compilationUnit) { + measure(() { + scanElements(compilationUnit); + }); + } + + void scanElements(CompilationUnitElement compilationUnit) { + Script script = compilationUnit.script; + Token tokens = new Scanner(script.file, + includeComments: compiler.preserveComments).tokenize(); + if (compiler.preserveComments) { + tokens = compiler.processAndStripComments(tokens); + } + compiler.dietParser.dietParse(compilationUnit, tokens); + } + + /** + * Returns the tokens for the [source]. + * + * The [StringScanner] implementation works on strings that end with a '0' + * value ('\x00'). If [source] does not end with '0', the string is copied + * before scanning. + */ + Token tokenize(String source) { + return measure(() { + return new StringScanner.fromString(source, includeComments: false) + .tokenize(); + }); + } +} + +class DietParserTask extends CompilerTask { + DietParserTask(Compiler compiler) : super(compiler); + final String name = 'Diet Parser'; + + dietParse(CompilationUnitElement compilationUnit, Token tokens) { + measure(() { + Function idGenerator = compiler.getNextFreeClassId; + ElementListener listener = + new ElementListener(compiler, compilationUnit, idGenerator); + PartialParser parser = new PartialParser(listener); + parser.parseUnit(tokens); + }); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/scannerlib.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/scannerlib.dart new file mode 100644 index 0000000..b27bd15 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/scannerlib.dart @@ -0,0 +1,43 @@ +// Copyright (c) 2011, 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. + +library scanner; + +import 'dart:collection' show IterableBase, HashSet; + +import '../elements/elements.dart'; +import '../elements/modelx.dart' + show ElementX, + FunctionElementX, + TypedefElementX, + VariableElementX, + FieldElementX, + VariableList, + ClassElementX, + MetadataAnnotationX, + MixinApplicationElementX; +import '../elements/visitor.dart' + show ElementVisitor; +import '../dart2jslib.dart'; +import '../native_handler.dart' as native; +import '../string_validator.dart'; +import '../tree/tree.dart'; +import '../util/characters.dart'; +import '../util/util.dart'; +import '../source_file.dart' show SourceFile, Utf8BytesSourceFile; +import 'dart:convert' show UTF8, UNICODE_BOM_CHARACTER_RUNE; +import 'dart:typed_data' show Uint8List; + +part 'class_element_parser.dart'; +part 'keyword.dart'; +part 'listener.dart'; +part 'parser.dart'; +part 'parser_task.dart'; +part 'partial_parser.dart'; +part 'scanner.dart'; +part 'scanner_task.dart'; +part 'array_based_scanner.dart'; +part 'utf8_bytes_scanner.dart'; +part 'string_scanner.dart'; +part 'token.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart new file mode 100644 index 0000000..5a30e43 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart @@ -0,0 +1,56 @@ +// Copyright (c) 2011, 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. + +part of scanner; + +/** + * Scanner that reads from a String and creates tokens that points to + * substrings. + */ +class StringScanner extends ArrayBasedScanner { + /** The file content. */ + String string; + + /** The current offset in [string]. */ + int scanOffset = -1; + + StringScanner(SourceFile file, {bool includeComments: false}) + : string = file.slowText(), + super(file, includeComments) { + ensureZeroTermination(); + } + + StringScanner.fromString(this.string, {bool includeComments: false}) + : super(null, includeComments) { + ensureZeroTermination(); + } + + void ensureZeroTermination() { + if (string.isEmpty || string.codeUnitAt(string.length - 1) != 0) { + // TODO(lry): abort instead of copying the array, or warn? + string = string + '\x00'; + } + } + + int advance() => string.codeUnitAt(++scanOffset); + int peek() => string.codeUnitAt(scanOffset + 1); + + int get stringOffset => scanOffset; + + int currentAsUnicode(int next) => next; + + void handleUnicode(int startScanOffset) { } + + Token firstToken() => tokens.next; + Token previousToken() => tail; + + void appendSubstringToken(PrecedenceInfo info, int start, + bool asciiOnly, [int extraOffset = 0]) { + tail.next = new StringToken.fromSubstring(info, string, start, + scanOffset + extraOffset, tokenStart, canonicalize: true); + tail = tail.next; + } + + bool atEndOfFile() => scanOffset >= string.length - 1; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/token.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/token.dart new file mode 100644 index 0000000..8251963 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/token.dart @@ -0,0 +1,666 @@ +// Copyright (c) 2011, 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. + +part of scanner; + +const int EOF_TOKEN = 0; + +const int KEYWORD_TOKEN = $k; +const int IDENTIFIER_TOKEN = $a; +const int BAD_INPUT_TOKEN = $X; +const int DOUBLE_TOKEN = $d; +const int INT_TOKEN = $i; +const int HEXADECIMAL_TOKEN = $x; +const int STRING_TOKEN = $SQ; + +const int AMPERSAND_TOKEN = $AMPERSAND; +const int BACKPING_TOKEN = $BACKPING; +const int BACKSLASH_TOKEN = $BACKSLASH; +const int BANG_TOKEN = $BANG; +const int BAR_TOKEN = $BAR; +const int COLON_TOKEN = $COLON; +const int COMMA_TOKEN = $COMMA; +const int EQ_TOKEN = $EQ; +const int GT_TOKEN = $GT; +const int HASH_TOKEN = $HASH; +const int OPEN_CURLY_BRACKET_TOKEN = $OPEN_CURLY_BRACKET; +const int OPEN_SQUARE_BRACKET_TOKEN = $OPEN_SQUARE_BRACKET; +const int OPEN_PAREN_TOKEN = $OPEN_PAREN; +const int LT_TOKEN = $LT; +const int MINUS_TOKEN = $MINUS; +const int PERIOD_TOKEN = $PERIOD; +const int PLUS_TOKEN = $PLUS; +const int QUESTION_TOKEN = $QUESTION; +const int AT_TOKEN = $AT; +const int CLOSE_CURLY_BRACKET_TOKEN = $CLOSE_CURLY_BRACKET; +const int CLOSE_SQUARE_BRACKET_TOKEN = $CLOSE_SQUARE_BRACKET; +const int CLOSE_PAREN_TOKEN = $CLOSE_PAREN; +const int SEMICOLON_TOKEN = $SEMICOLON; +const int SLASH_TOKEN = $SLASH; +const int TILDE_TOKEN = $TILDE; +const int STAR_TOKEN = $STAR; +const int PERCENT_TOKEN = $PERCENT; +const int CARET_TOKEN = $CARET; + +const int STRING_INTERPOLATION_TOKEN = 128; +const int LT_EQ_TOKEN = STRING_INTERPOLATION_TOKEN + 1; +const int FUNCTION_TOKEN = LT_EQ_TOKEN + 1; +const int SLASH_EQ_TOKEN = FUNCTION_TOKEN + 1; +const int PERIOD_PERIOD_PERIOD_TOKEN = SLASH_EQ_TOKEN + 1; +const int PERIOD_PERIOD_TOKEN = PERIOD_PERIOD_PERIOD_TOKEN + 1; +const int EQ_EQ_EQ_TOKEN = PERIOD_PERIOD_TOKEN + 1; +const int EQ_EQ_TOKEN = EQ_EQ_EQ_TOKEN + 1; +const int LT_LT_EQ_TOKEN = EQ_EQ_TOKEN + 1; +const int LT_LT_TOKEN = LT_LT_EQ_TOKEN + 1; +const int GT_EQ_TOKEN = LT_LT_TOKEN + 1; +const int GT_GT_EQ_TOKEN = GT_EQ_TOKEN + 1; +const int INDEX_EQ_TOKEN = GT_GT_EQ_TOKEN + 1; +const int INDEX_TOKEN = INDEX_EQ_TOKEN + 1; +const int BANG_EQ_EQ_TOKEN = INDEX_TOKEN + 1; +const int BANG_EQ_TOKEN = BANG_EQ_EQ_TOKEN + 1; +const int AMPERSAND_AMPERSAND_TOKEN = BANG_EQ_TOKEN + 1; +const int AMPERSAND_EQ_TOKEN = AMPERSAND_AMPERSAND_TOKEN + 1; +const int BAR_BAR_TOKEN = AMPERSAND_EQ_TOKEN + 1; +const int BAR_EQ_TOKEN = BAR_BAR_TOKEN + 1; +const int STAR_EQ_TOKEN = BAR_EQ_TOKEN + 1; +const int PLUS_PLUS_TOKEN = STAR_EQ_TOKEN + 1; +const int PLUS_EQ_TOKEN = PLUS_PLUS_TOKEN + 1; +const int MINUS_MINUS_TOKEN = PLUS_EQ_TOKEN + 1; +const int MINUS_EQ_TOKEN = MINUS_MINUS_TOKEN + 1; +const int TILDE_SLASH_EQ_TOKEN = MINUS_EQ_TOKEN + 1; +const int TILDE_SLASH_TOKEN = TILDE_SLASH_EQ_TOKEN + 1; +const int PERCENT_EQ_TOKEN = TILDE_SLASH_TOKEN + 1; +const int GT_GT_TOKEN = PERCENT_EQ_TOKEN + 1; +const int CARET_EQ_TOKEN = GT_GT_TOKEN + 1; +const int COMMENT_TOKEN = CARET_EQ_TOKEN + 1; +const int STRING_INTERPOLATION_IDENTIFIER_TOKEN = COMMENT_TOKEN + 1; + +/** + * A token that doubles as a linked list. + */ +abstract class Token implements Spannable { + /** + * The character offset of the start of this token within the source text. + */ + final int charOffset; + + Token(this.charOffset); + + /** + * The next token in the token stream. + */ + Token next; + + /** + * The precedence info for this token. [info] determines the kind and the + * precedence level of this token. + * + * Defined as getter to save a field in the [KeywordToken] subclass. + */ + PrecedenceInfo get info; + + /** + * The string represented by this token, a substring of the source code. + * + * For [StringToken]s the [value] includes the quotes, explicit escapes, etc. + */ + String get value; + + /** + * For symbol and keyword tokens, returns the string value represented by this + * token. For [StringToken]s this method returns [:null:]. + * + * For [SymbolToken]s and [KeywordToken]s, the string value is a compile-time + * constant originating in the [PrecedenceInfo] or in the [Keyword] instance. + * This allows testing for keywords and symbols using [:identical:], e.g., + * [:identical('class', token.value):]. + * + * Note that returning [:null:] for string tokens is important to identify + * symbols and keywords, we cannot use [value] instead. The string literal + * "$a($b" + * produces ..., SymbolToken($), StringToken(a), StringToken((), ... + * + * After parsing the identifier 'a', the parser tests for a function + * declaration using [:identical(next.stringValue, '('):], which (rihgtfully) + * returns false because stringValue returns [:null:]. + */ + String get stringValue; + + /** + * The kind enum of this token as determined by its [info]. + */ + int get kind => info.kind; + + /** + * The precedence level for this token. + */ + int get precedence => info.precedence; + + /** + * True if this token is an identifier. Some keywords allowed as identifiers, + * see implementation in [KeywordToken]. + */ + bool isIdentifier(); + + /** + * Returns a textual representation of this token to be used for debugging + * purposes. The resulting string might contain information about the + * structure of the token, for example 'StringToken(foo)' for the identifier + * token 'foo'. + * + * Use [value] for the text actually parsed by the token. + */ + String toString(); + + /** + * The number of characters parsed by this token. + */ + int get charCount { + if (info == BAD_INPUT_INFO) { + // This is a token that wraps around an error message. Return 1 + // instead of the size of the length of the error message. + return 1; + } else { + return value.length; + } + } + + int get hashCode => computeHashCode(charOffset, info, value); +} + +/** + * A [SymbolToken] represents the symbol in its precendence info. + * Also used for end of file with EOF_INFO. + */ +class SymbolToken extends Token { + + final PrecedenceInfo info; + + SymbolToken(this.info, int charOffset) : super(charOffset); + + String get value => info.value; + + String get stringValue => info.value; + + bool isIdentifier() => false; + + String toString() => "SymbolToken($value)"; +} + +/** + * A [BeginGroupToken] represents a symbol that may be the beginning of + * a pair of brackets, i.e., ( { [ < or ${ + * The [endGroup] token points to the matching closing bracked in case + * it can be identified during scanning. + */ +class BeginGroupToken extends SymbolToken { + Token endGroup; + + BeginGroupToken(PrecedenceInfo info, int charOffset) + : super(info, charOffset); +} + +/** + * A keyword token. + */ +class KeywordToken extends Token { + final Keyword keyword; + + KeywordToken(this.keyword, int charOffset) : super(charOffset); + + PrecedenceInfo get info => keyword.info; + + String get value => keyword.syntax; + + String get stringValue => keyword.syntax; + + bool isIdentifier() => keyword.isPseudo || keyword.isBuiltIn; + + String toString() => "KeywordToken($value)"; +} + +/** + * A String-valued token. Represents identifiers, string literals, + * number literals, comments, and error tokens, using the corresponding + * precedence info. + */ +class StringToken extends Token { + /** + * The length threshold above which substring tokens are computed lazily. + * + * For string tokens that are substrings of the program source, the actual + * substring extraction is performed lazily. This is beneficial because + * not all scanned code is actually used. For unused parts, the substrings + * are never computed and allocated. + */ + static const int LAZY_THRESHOLD = 4; + + var /* String | LazySubtring */ valueOrLazySubstring; + + final PrecedenceInfo info; + + /** + * Creates a non-lazy string token. If [canonicalize] is true, the string + * is canonicalized before the token is created. + */ + StringToken.fromString(this.info, String value, int charOffset, + {bool canonicalize : false}) + : valueOrLazySubstring = canonicalizedString(value, canonicalize), + super(charOffset); + + /** + * Creates a lazy string token. If [canonicalize] is true, the string + * is canonicalized before the token is created. + */ + StringToken.fromSubstring(this.info, String data, int start, int end, + int charOffset, {bool canonicalize : false}) + : super(charOffset) { + int length = end - start; + if (length <= LAZY_THRESHOLD) { + valueOrLazySubstring = canonicalizedString(data.substring(start, end), + canonicalize); + } else { + valueOrLazySubstring = + new LazySubstring(data, start, length, canonicalize); + } + } + + /** + * Creates a lazy string token. If [asciiOnly] is false, the byte array + * is passed through a UTF-8 decoder. + */ + StringToken.fromUtf8Bytes(this.info, List data, int start, int end, + bool asciiOnly, int charOffset) + : super(charOffset) { + int length = end - start; + if (length <= LAZY_THRESHOLD) { + valueOrLazySubstring = decodeUtf8(data, start, end, asciiOnly); + } else { + valueOrLazySubstring = new LazySubstring(data, start, length, asciiOnly); + } + } + + String get value { + if (valueOrLazySubstring is String) { + return valueOrLazySubstring; + } else { + assert(valueOrLazySubstring is LazySubstring); + var data = valueOrLazySubstring.data; + int start = valueOrLazySubstring.start; + int end = start + valueOrLazySubstring.length; + if (data is String) { + valueOrLazySubstring = canonicalizedString( + data.substring(start, end), valueOrLazySubstring.boolValue); + } else { + valueOrLazySubstring = decodeUtf8( + data, start, end, valueOrLazySubstring.boolValue); + } + return valueOrLazySubstring; + } + } + + String get stringValue => null; + + bool isIdentifier() => identical(kind, IDENTIFIER_TOKEN); + + String toString() => "StringToken($value)"; + + static final HashSet canonicalizedSubstrings = + new HashSet(); + + static String canonicalizedString(String s, bool canonicalize) { + if (!canonicalize) return s; + var result = canonicalizedSubstrings.lookup(s); + if (result != null) return result; + canonicalizedSubstrings.add(s); + return s; + } + + static String decodeUtf8(List data, int start, int end, bool asciiOnly) { + var s; + if (asciiOnly) { + // getRange returns an iterator, it does not copy the data. + s = new String.fromCharCodes(data.getRange(start, end)); + } else { + // TODO(lry): this is measurably slow. Also sublist is copied eagerly. + var bytes = data.sublist(start, end); + s = UTF8.decode(bytes); + } + return canonicalizedString(s, true); + } +} + +/** + * This class represents the necessary information to compute a substring + * lazily. The substring can either originate from a string or from + * a [:List:] of UTF-8 bytes. + */ +abstract class LazySubstring { + /** The original data, either a string or a List */ + get data; + + int get start; + int get length; + + /** + * If this substring is based on a String, the [boolValue] indicates wheter + * the resulting substring should be canonicalized. + * + * For substrings based on a byte array, the [boolValue] is true if the + * array only holds ASCII characters. The resulting substring will be + * canonicalized after decoding. + */ + bool get boolValue; + + LazySubstring.internal(); + + factory LazySubstring(data, int start, int length, bool b) { + // See comment on [CompactLazySubstring]. + if (start < 0x100000 && length < 0x200) { + int fields = (start << 9); + fields = fields | length; + fields = fields << 1; + if (b) fields |= 1; + return new CompactLazySubstring(data, fields); + } else { + return new FullLazySubstring(data, start, length, b); + } + } +} + +/** + * This class encodes [start], [length] and [boolValue] in a single + * 30 bit integer. It uses 20 bits for [start], which covers source files + * of 1MB. [length] has 9 bits, which covers 512 characters. + * + * The file html_dart2js.dart is currently around 1MB. + */ +class CompactLazySubstring extends LazySubstring { + final data; + final int fields; + + CompactLazySubstring(this.data, this.fields) : super.internal(); + + int get start => fields >> 10; + int get length => (fields >> 1) & 0x1ff; + bool get boolValue => (fields & 1) == 1; +} + +class FullLazySubstring extends LazySubstring { + final data; + final int start; + final int length; + final bool boolValue; + FullLazySubstring(this.data, this.start, this.length, this.boolValue) + : super.internal(); +} + +bool isUserDefinableOperator(String value) { + return + isBinaryOperator(value) || + isMinusOperator(value) || + isTernaryOperator(value) || + isUnaryOperator(value); +} + +bool isUnaryOperator(String value) => identical(value, '~'); + +bool isBinaryOperator(String value) { + return + (identical(value, '==')) || + (identical(value, '[]')) || + (identical(value, '*')) || + (identical(value, '/')) || + (identical(value, '%')) || + (identical(value, '~/')) || + (identical(value, '+')) || + (identical(value, '<<')) || + (identical(value, '>>')) || + (identical(value, '>=')) || + (identical(value, '>')) || + (identical(value, '<=')) || + (identical(value, '<')) || + (identical(value, '&')) || + (identical(value, '^')) || + (identical(value, '|')); +} + +bool isTernaryOperator(String value) => identical(value, '[]='); + +bool isMinusOperator(String value) => identical(value, '-'); + +class PrecedenceInfo { + final String value; + final int precedence; + final int kind; + + const PrecedenceInfo(this.value, this.precedence, this.kind); + + toString() => 'PrecedenceInfo($value, $precedence, $kind)'; + + int get hashCode => computeHashCode(value, precedence, kind); +} + +// TODO(ahe): The following are not tokens in Dart. +const PrecedenceInfo BACKPING_INFO = + const PrecedenceInfo('`', 0, BACKPING_TOKEN); +const PrecedenceInfo BACKSLASH_INFO = + const PrecedenceInfo('\\', 0, BACKSLASH_TOKEN); +const PrecedenceInfo PERIOD_PERIOD_PERIOD_INFO = + const PrecedenceInfo('...', 0, + PERIOD_PERIOD_PERIOD_TOKEN); + +/** + * The cascade operator has the lowest precedence of any operator + * except assignment. + */ +const int CASCADE_PRECEDENCE = 2; +const PrecedenceInfo PERIOD_PERIOD_INFO = + const PrecedenceInfo('..', CASCADE_PRECEDENCE, + PERIOD_PERIOD_TOKEN); + +const PrecedenceInfo BANG_INFO = + const PrecedenceInfo('!', 0, BANG_TOKEN); +const PrecedenceInfo COLON_INFO = + const PrecedenceInfo(':', 0, COLON_TOKEN); +const PrecedenceInfo INDEX_INFO = + const PrecedenceInfo('[]', 0, INDEX_TOKEN); +const PrecedenceInfo MINUS_MINUS_INFO = + const PrecedenceInfo('--', POSTFIX_PRECEDENCE, + MINUS_MINUS_TOKEN); +const PrecedenceInfo PLUS_PLUS_INFO = + const PrecedenceInfo('++', POSTFIX_PRECEDENCE, + PLUS_PLUS_TOKEN); +const PrecedenceInfo TILDE_INFO = + const PrecedenceInfo('~', 0, TILDE_TOKEN); + +const PrecedenceInfo FUNCTION_INFO = + const PrecedenceInfo('=>', 0, FUNCTION_TOKEN); +const PrecedenceInfo HASH_INFO = + const PrecedenceInfo('#', 0, HASH_TOKEN); +const PrecedenceInfo INDEX_EQ_INFO = + const PrecedenceInfo('[]=', 0, INDEX_EQ_TOKEN); +const PrecedenceInfo SEMICOLON_INFO = + const PrecedenceInfo(';', 0, SEMICOLON_TOKEN); +const PrecedenceInfo COMMA_INFO = + const PrecedenceInfo(',', 0, COMMA_TOKEN); + +const PrecedenceInfo AT_INFO = + const PrecedenceInfo('@', 0, AT_TOKEN); + +// Assignment operators. +const int ASSIGNMENT_PRECEDENCE = 1; +const PrecedenceInfo AMPERSAND_EQ_INFO = + const PrecedenceInfo('&=', + ASSIGNMENT_PRECEDENCE, AMPERSAND_EQ_TOKEN); +const PrecedenceInfo BAR_EQ_INFO = + const PrecedenceInfo('|=', + ASSIGNMENT_PRECEDENCE, BAR_EQ_TOKEN); +const PrecedenceInfo CARET_EQ_INFO = + const PrecedenceInfo('^=', + ASSIGNMENT_PRECEDENCE, CARET_EQ_TOKEN); +const PrecedenceInfo EQ_INFO = + const PrecedenceInfo('=', + ASSIGNMENT_PRECEDENCE, EQ_TOKEN); +const PrecedenceInfo GT_GT_EQ_INFO = + const PrecedenceInfo('>>=', + ASSIGNMENT_PRECEDENCE, GT_GT_EQ_TOKEN); +const PrecedenceInfo LT_LT_EQ_INFO = + const PrecedenceInfo('<<=', + ASSIGNMENT_PRECEDENCE, LT_LT_EQ_TOKEN); +const PrecedenceInfo MINUS_EQ_INFO = + const PrecedenceInfo('-=', + ASSIGNMENT_PRECEDENCE, MINUS_EQ_TOKEN); +const PrecedenceInfo PERCENT_EQ_INFO = + const PrecedenceInfo('%=', + ASSIGNMENT_PRECEDENCE, PERCENT_EQ_TOKEN); +const PrecedenceInfo PLUS_EQ_INFO = + const PrecedenceInfo('+=', + ASSIGNMENT_PRECEDENCE, PLUS_EQ_TOKEN); +const PrecedenceInfo SLASH_EQ_INFO = + const PrecedenceInfo('/=', + ASSIGNMENT_PRECEDENCE, SLASH_EQ_TOKEN); +const PrecedenceInfo STAR_EQ_INFO = + const PrecedenceInfo('*=', + ASSIGNMENT_PRECEDENCE, STAR_EQ_TOKEN); +const PrecedenceInfo TILDE_SLASH_EQ_INFO = + const PrecedenceInfo('~/=', + ASSIGNMENT_PRECEDENCE, TILDE_SLASH_EQ_TOKEN); + +const PrecedenceInfo QUESTION_INFO = + const PrecedenceInfo('?', 3, QUESTION_TOKEN); + +const PrecedenceInfo BAR_BAR_INFO = + const PrecedenceInfo('||', 4, BAR_BAR_TOKEN); + +const PrecedenceInfo AMPERSAND_AMPERSAND_INFO = + const PrecedenceInfo('&&', 5, AMPERSAND_AMPERSAND_TOKEN); + +const PrecedenceInfo BAR_INFO = + const PrecedenceInfo('|', 8, BAR_TOKEN); + +const PrecedenceInfo CARET_INFO = + const PrecedenceInfo('^', 9, CARET_TOKEN); + +const PrecedenceInfo AMPERSAND_INFO = + const PrecedenceInfo('&', 10, AMPERSAND_TOKEN); + +// Equality operators. +const int EQUALITY_PRECEDENCE = 6; +const PrecedenceInfo BANG_EQ_EQ_INFO = + const PrecedenceInfo('!==', + EQUALITY_PRECEDENCE, BANG_EQ_EQ_TOKEN); +const PrecedenceInfo BANG_EQ_INFO = + const PrecedenceInfo('!=', + EQUALITY_PRECEDENCE, BANG_EQ_TOKEN); +const PrecedenceInfo EQ_EQ_EQ_INFO = + const PrecedenceInfo('===', + EQUALITY_PRECEDENCE, EQ_EQ_EQ_TOKEN); +const PrecedenceInfo EQ_EQ_INFO = + const PrecedenceInfo('==', + EQUALITY_PRECEDENCE, EQ_EQ_TOKEN); + +// Relational operators. +const int RELATIONAL_PRECEDENCE = 7; +const PrecedenceInfo GT_EQ_INFO = + const PrecedenceInfo('>=', + RELATIONAL_PRECEDENCE, GT_EQ_TOKEN); +const PrecedenceInfo GT_INFO = + const PrecedenceInfo('>', + RELATIONAL_PRECEDENCE, GT_TOKEN); +const PrecedenceInfo IS_INFO = + const PrecedenceInfo('is', + RELATIONAL_PRECEDENCE, KEYWORD_TOKEN); +const PrecedenceInfo AS_INFO = + const PrecedenceInfo('as', + RELATIONAL_PRECEDENCE, KEYWORD_TOKEN); +const PrecedenceInfo LT_EQ_INFO = + const PrecedenceInfo('<=', + RELATIONAL_PRECEDENCE, LT_EQ_TOKEN); +const PrecedenceInfo LT_INFO = + const PrecedenceInfo('<', + RELATIONAL_PRECEDENCE, LT_TOKEN); + +// Shift operators. +const PrecedenceInfo GT_GT_INFO = + const PrecedenceInfo('>>', 11, GT_GT_TOKEN); +const PrecedenceInfo LT_LT_INFO = + const PrecedenceInfo('<<', 11, LT_LT_TOKEN); + +// Additive operators. +const PrecedenceInfo MINUS_INFO = + const PrecedenceInfo('-', 12, MINUS_TOKEN); +const PrecedenceInfo PLUS_INFO = + const PrecedenceInfo('+', 12, PLUS_TOKEN); + +// Multiplicative operators. +const PrecedenceInfo PERCENT_INFO = + const PrecedenceInfo('%', 13, PERCENT_TOKEN); +const PrecedenceInfo SLASH_INFO = + const PrecedenceInfo('/', 13, SLASH_TOKEN); +const PrecedenceInfo STAR_INFO = + const PrecedenceInfo('*', 13, STAR_TOKEN); +const PrecedenceInfo TILDE_SLASH_INFO = + const PrecedenceInfo('~/', 13, TILDE_SLASH_TOKEN); + +const int POSTFIX_PRECEDENCE = 14; +const PrecedenceInfo PERIOD_INFO = + const PrecedenceInfo('.', POSTFIX_PRECEDENCE, + PERIOD_TOKEN); + +const PrecedenceInfo KEYWORD_INFO = + const PrecedenceInfo('keyword', 0, KEYWORD_TOKEN); + +const PrecedenceInfo EOF_INFO = + const PrecedenceInfo('EOF', 0, EOF_TOKEN); + +const PrecedenceInfo IDENTIFIER_INFO = + const PrecedenceInfo('identifier', 0, IDENTIFIER_TOKEN); + +const PrecedenceInfo BAD_INPUT_INFO = + const PrecedenceInfo('malformed input', 0, + BAD_INPUT_TOKEN); + +const PrecedenceInfo OPEN_PAREN_INFO = + const PrecedenceInfo('(', POSTFIX_PRECEDENCE, + OPEN_PAREN_TOKEN); + +const PrecedenceInfo CLOSE_PAREN_INFO = + const PrecedenceInfo(')', 0, CLOSE_PAREN_TOKEN); + +const PrecedenceInfo OPEN_CURLY_BRACKET_INFO = + const PrecedenceInfo('{', 0, OPEN_CURLY_BRACKET_TOKEN); + +const PrecedenceInfo CLOSE_CURLY_BRACKET_INFO = + const PrecedenceInfo('}', 0, CLOSE_CURLY_BRACKET_TOKEN); + +const PrecedenceInfo INT_INFO = + const PrecedenceInfo('int', 0, INT_TOKEN); + +const PrecedenceInfo STRING_INFO = + const PrecedenceInfo('string', 0, STRING_TOKEN); + +const PrecedenceInfo OPEN_SQUARE_BRACKET_INFO = + const PrecedenceInfo('[', POSTFIX_PRECEDENCE, + OPEN_SQUARE_BRACKET_TOKEN); + +const PrecedenceInfo CLOSE_SQUARE_BRACKET_INFO = + const PrecedenceInfo(']', 0, CLOSE_SQUARE_BRACKET_TOKEN); + +const PrecedenceInfo DOUBLE_INFO = + const PrecedenceInfo('double', 0, DOUBLE_TOKEN); + +const PrecedenceInfo STRING_INTERPOLATION_INFO = + const PrecedenceInfo('\${', 0, + STRING_INTERPOLATION_TOKEN); + +const PrecedenceInfo STRING_INTERPOLATION_IDENTIFIER_INFO = + const PrecedenceInfo('\$', 0, + STRING_INTERPOLATION_IDENTIFIER_TOKEN); + +const PrecedenceInfo HEXADECIMAL_INFO = + const PrecedenceInfo('hexadecimal', 0, HEXADECIMAL_TOKEN); + +const PrecedenceInfo COMMENT_INFO = + const PrecedenceInfo('comment', 0, COMMENT_TOKEN); diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/utf8_bytes_scanner.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/utf8_bytes_scanner.dart new file mode 100644 index 0000000..c2adeb4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/scanner/utf8_bytes_scanner.dart @@ -0,0 +1,213 @@ +// Copyright (c) 2013, 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. + +part of scanner; + +/** + * Scanner that reads from a UTF-8 encoded list of bytes and creates tokens + * that points to substrings. + */ +class Utf8BytesScanner extends ArrayBasedScanner { + /** The file content. */ + List bytes; + + /** + * Points to the offset of the last byte returned by [advance]. + * + * After invoking [currentAsUnicode], the [byteOffset] points to the last + * byte that is part of the (unicode or ASCII) character. That way, [advance] + * can always increase the byte offset by 1. + */ + int byteOffset = -1; + + /** + * The getter [scanOffset] is expected to return the index where the current + * character *starts*. In case of a non-ascii character, after invoking + * [currentAsUnicode], the byte offset points to the *last* byte. + * + * This field keeps track of the number of bytes for the current unicode + * character. For example, if bytes 7,8,9 encode one unicode character, the + * [byteOffset] is 9 (after invoking [currentAsUnicode]). The [scanSlack] + * will be 2, so that [scanOffset] returns 7. + */ + int scanSlack = 0; + + /** + * Holds the [byteOffset] value for which the current [scanSlack] is valid. + */ + int scanSlackOffset = -1; + + /** + * Returns the byte offset of the first byte that belongs to the current + * character. + */ + int get scanOffset { + if (byteOffset == scanSlackOffset) { + return byteOffset - scanSlack; + } else { + return byteOffset; + } + } + + /** + * The difference between the number of bytes and the number of corresponding + * string characters, up to the current [byteOffset]. + */ + int utf8Slack = 0; + + /** + * Creates a new Utf8BytesScanner. The source file is expected to be a + * [Utf8BytesSourceFile] that holds a list of UTF-8 bytes. Otherwise the + * string text of the source file is decoded. + * + * The list of UTF-8 bytes [file.slowUtf8Bytes()] is expected to return an + * array whose last element is '0' to signal the end of the file. If this + * is not the case, the entire array is copied before scanning. + */ + Utf8BytesScanner(SourceFile file, {bool includeComments: false}) + : bytes = file.slowUtf8Bytes(), + super(file, includeComments) { + ensureZeroTermination(); + // Skip a leading BOM. + if (_containsBomAt(0)) byteOffset += 3; + } + + /** + * Creates a new Utf8BytesScanner from a list of UTF-8 bytes. + * + * The last element of the list is expected to be '0' to signal the end of + * the file. If this is not the case, the entire array is copied before + * scanning. + */ + Utf8BytesScanner.fromBytes(this.bytes, {bool includeComments: false}) + : super(null, includeComments) { + ensureZeroTermination(); + } + + void ensureZeroTermination() { + if (bytes.isEmpty || bytes[bytes.length - 1] != 0) { + // TODO(lry): abort instead of copying the array, or warn? + var newBytes = new Uint8List(bytes.length + 1); + for (int i = 0; i < bytes.length; i++) { + newBytes[i] = bytes[i]; + } + newBytes[bytes.length] = 0; + bytes = newBytes; + } + } + + bool _containsBomAt(int offset) { + const BOM_UTF8 = const [0xEF, 0xBB, 0xBF]; + + return offset + 3 < bytes.length && + bytes[offset] == BOM_UTF8[0] && + bytes[offset + 1] == BOM_UTF8[1] && + bytes[offset + 2] == BOM_UTF8[2]; + } + + int advance() => bytes[++byteOffset]; + + int peek() => bytes[byteOffset + 1]; + + /** + * Returns the unicode code point starting at the byte offset [startOffset] + * with the byte [nextByte]. If [advance] is true the current [byteOffset] + * is advanced to the last byte of the code point. + */ + int nextCodePoint(int startOffset, int nextByte, bool advance) { + // The number of 1s in the first byte indicate the number of bytes, at + // least 2. + int numBytes = 2; + int bit = 0x20; + while ((nextByte & bit) != 0) { + numBytes++; + bit >>= 1; + } + int end = startOffset + numBytes; + if (advance) { + byteOffset = end - 1; + } + // TODO(lry): measurably slow, decode creates first a Utf8Decoder and a + // _Utf8Decoder instance. Also the sublist is eagerly allocated. + String codePoint = UTF8.decode(bytes.sublist(startOffset, end)); + if (codePoint.length == 0) { + // The UTF-8 decoder discards leading BOM characters. + // TODO(floitsch): don't just assume that removed characters were the + // BOM. + assert(_containsBomAt(startOffset)); + codePoint = new String.fromCharCode(UNICODE_BOM_CHARACTER_RUNE); + } + if (codePoint.length == 1) { + if (advance) { + utf8Slack += (numBytes - 1); + scanSlack = numBytes - 1; + scanSlackOffset = byteOffset; + } + return codePoint.codeUnitAt(0); + } else if (codePoint.length == 2) { + if (advance) { + utf8Slack += (numBytes - 2); + scanSlack = numBytes - 1; + scanSlackOffset = byteOffset; + stringOffsetSlackOffset = byteOffset; + } + // In case of a surrogate pair, return a single code point. + return codePoint.runes.single; + } else { + throw "Invalid UTF-8 byte sequence: ${bytes.sublist(startOffset, end)}"; + } + } + + int lastUnicodeOffset = -1; + int currentAsUnicode(int next) { + if (next < 128) return next; + // Check if currentAsUnicode was already invoked. + if (byteOffset == lastUnicodeOffset) return next; + int res = nextCodePoint(byteOffset, next, true); + lastUnicodeOffset = byteOffset; + return res; + } + + void handleUnicode(int startScanOffset) { + int end = byteOffset; + // TODO(lry): this measurably slows down the scanner for files with unicode. + String s = UTF8.decode(bytes.sublist(startScanOffset, end)); + utf8Slack += (end - startScanOffset) - s.length; + } + + /** + * This field remembers the byte offset of the last character decoded with + * [nextCodePoint] that used two code units in UTF-16. + * + * [nextCodePoint] returns a single code point for each unicode character, + * even if it needs two code units in UTF-16. + * + * For example, '\u{1d11e}' uses 4 bytes in UTF-8, and two code units in + * UTF-16. The [utf8Slack] is therefore 2. After invoking [nextCodePoint], the + * [byteOffset] points to the last (of 4) bytes. The [stringOffset] should + * return the offset of the first one, which is one position more left than + * the [utf8Slack]. + */ + int stringOffsetSlackOffset = -1; + + int get stringOffset { + if (stringOffsetSlackOffset == byteOffset) { + return byteOffset - utf8Slack - 1; + } else { + return byteOffset - utf8Slack; + } + } + + Token firstToken() => tokens.next; + Token previousToken() => tail; + + void appendSubstringToken(PrecedenceInfo info, int start, bool asciiOnly, + [int extraOffset = 0]) { + tail.next = new StringToken.fromUtf8Bytes( + info, bytes, start, byteOffset + extraOffset, asciiOnly, tokenStart); + tail = tail.next; + } + + bool atEndOfFile() => byteOffset >= bytes.length - 1; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/script.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/script.dart new file mode 100644 index 0000000..9ceafb4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/script.dart @@ -0,0 +1,29 @@ +// Copyright (c) 2011, 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. + +part of dart2js; + +class Script { + final SourceFile file; + + /** + * The readable URI from which this script was loaded. + * + * See [LibraryLoader] for terminology on URIs. + */ + final Uri readableUri; + + + /** + * The resource URI from which this script was loaded. + * + * See [LibraryLoader] for terminology on URIs. + */ + final Uri resourceUri; + + Script(this.readableUri, this.resourceUri, this.file); + + String get text => (file == null) ? null : file.slowText(); + String get name => (file == null) ? null : file.filename; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/source_file.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/source_file.dart new file mode 100644 index 0000000..af1a2be --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/source_file.dart @@ -0,0 +1,198 @@ +// Copyright (c) 2012, 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. + +library source_file; + +import 'dart:math'; +import 'dart:convert' show UTF8; + +/** + * Represents a file of source code. The content can be either a [String] or + * a UTF-8 encoded [List] of bytes. + */ +abstract class SourceFile { + + /** The name of the file. */ + final String filename; + + SourceFile(this.filename); + + /** The text content of the file represented as a String. */ + String slowText(); + + /** The content of the file represented as a UTF-8 encoded [List]. */ + List slowUtf8Bytes(); + + /** + * The length of the string representation of this source file, i.e., + * equivalent to [:slowText().length:], but faster. + */ + int get length; + + /** + * Sets the string length of this source file. For source files based on UTF-8 + * byte arrays, the string length is computed and assigned by the scanner. + */ + set length(int v); + + /** + * A map from line numbers to offsets in the string text representation of + * this source file. + */ + List get lineStarts { + if (lineStartsCache == null) { + // When reporting errors during scanning, the line numbers are not yet + // available and need to be computed using this slow path. + lineStartsCache = lineStartsFromString(slowText()); + } + return lineStartsCache; + } + + /** + * Sets the line numbers map for this source file. This map is computed and + * assigned by the scanner, avoiding a separate traversal of the source file. + * + * The map contains one additional entry at the end of the file, as if the + * source file had one more empty line at the end. This simplifies the binary + * search in [getLine]. + */ + set lineStarts(List v) => lineStartsCache = v; + + List lineStartsCache; + + List lineStartsFromString(String text) { + var starts = [0]; + var index = 0; + while (index < text.length) { + index = text.indexOf('\n', index) + 1; + if (index <= 0) break; + starts.add(index); + } + starts.add(text.length + 1); // One additional line start at the end. + return starts; + } + + /** + * Returns the line number for the offset [position] in the string + * representation of this source file. + */ + int getLine(int position) { + List starts = lineStarts; + if (position < 0 || starts.last <= position) { + throw 'bad position #$position in file $filename with ' + 'length ${length}.'; + } + int first = 0; + int count = starts.length; + while (count > 1) { + int step = count ~/ 2; + int middle = first + step; + int lineStart = starts[middle]; + if (position < lineStart) { + count = step; + } else { + first = middle; + count -= step; + } + } + return first; + } + + /** + * Returns the column number for the offset [position] in the string + * representation of this source file. + */ + int getColumn(int line, int position) { + return position - lineStarts[line]; + } + + String slowSubstring(int start, int end); + + /** + * Create a pretty string representation from a character position + * in the file. + */ + String getLocationMessage(String message, int start, int end, + bool includeText, String color(String x)) { + var line = getLine(start); + var column = getColumn(line, start); + + var buf = new StringBuffer('${filename}:'); + if (start != end || start != 0) { + // Line/column info is relevant. + buf.write('${line + 1}:${column + 1}:'); + } + buf.write('\n$message\n'); + + if (start != end && includeText) { + String textLine; + // +1 for 0-indexing, +1 again to avoid the last line of the file + if ((line + 2) < lineStarts.length) { + textLine = slowSubstring(lineStarts[line], lineStarts[line+1]); + } else { + textLine = '${slowSubstring(lineStarts[line], length)}\n'; + } + + int toColumn = min(column + (end-start), textLine.length); + buf.write(textLine.substring(0, column)); + buf.write(color(textLine.substring(column, toColumn))); + buf.write(textLine.substring(toColumn)); + + int i = 0; + for (; i < column; i++) { + buf.write(' '); + } + + for (; i < toColumn; i++) { + buf.write(color('^')); + } + } + + return buf.toString(); + } +} + +class Utf8BytesSourceFile extends SourceFile { + + /** The UTF-8 encoded content of the source file. */ + final List content; + + Utf8BytesSourceFile(String filename, this.content) : super(filename); + + String slowText() => UTF8.decode(content); + + List slowUtf8Bytes() => content; + + String slowSubstring(int start, int end) { + // TODO(lry): to make this faster, the scanner could record the UTF-8 slack + // for all positions of the source text. We could use [:content.sublist:]. + return slowText().substring(start, end); + } + + int get length { + if (lengthCache == -1) { + // During scanning the length is not yet assigned, so we use a slow path. + length = slowText().length; + } + return lengthCache; + } + set length(int v) => lengthCache = v; + int lengthCache = -1; +} + +class StringSourceFile extends SourceFile { + + final String text; + + StringSourceFile(String filename, this.text) : super(filename); + + int get length => text.length; + set length(int v) { } + + String slowText() => text; + + List slowUtf8Bytes() => UTF8.encode(text); + + String slowSubstring(int start, int end) => text.substring(start, end); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/source_file_provider.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/source_file_provider.dart new file mode 100644 index 0000000..76e7ad3 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/source_file_provider.dart @@ -0,0 +1,173 @@ +// Copyright (c) 2013, 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. + +library source_file_provider; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import '../compiler.dart' as api show Diagnostic; +import 'dart2js.dart' show AbortLeg; +import 'colors.dart' as colors; +import 'source_file.dart'; +import 'filenames.dart'; +import 'util/uri_extras.dart'; +import 'dart:typed_data'; + +List readAll(String filename) { + var file = (new File(filename)).openSync(); + var length = file.lengthSync(); + // +1 to have a 0 terminated list, see [Scanner]. + var buffer = new Uint8List(length + 1); + var bytes = file.readIntoSync(buffer, 0, length); + file.closeSync(); + return buffer; +} + +abstract class SourceFileProvider { + bool isWindows = (Platform.operatingSystem == 'windows'); + Uri cwd = currentDirectory; + Map sourceFiles = {}; + int dartCharactersRead = 0; + + Future readStringFromUri(Uri resourceUri) { + return readUtf8BytesFromUri(resourceUri).then(UTF8.decode); + } + + Future> readUtf8BytesFromUri(Uri resourceUri) { + if (resourceUri.scheme != 'file') { + throw new ArgumentError("Unknown scheme in uri '$resourceUri'"); + } + List source; + try { + source = readAll(resourceUri.toFilePath()); + } on FileSystemException catch (ex) { + return new Future.error( + "Error reading '${relativize(cwd, resourceUri, isWindows)}' " + "(${ex.osError})"); + } + dartCharactersRead += source.length; + sourceFiles[resourceUri.toString()] = new Utf8BytesSourceFile( + relativize(cwd, resourceUri, isWindows), source); + return new Future.value(source); + } + + Future/* | String>*/ call(Uri resourceUri); +} + +class CompilerSourceFileProvider extends SourceFileProvider { + Future> call(Uri resourceUri) => readUtf8BytesFromUri(resourceUri); +} + +class FormattingDiagnosticHandler { + final SourceFileProvider provider; + bool showWarnings = true; + bool showHints = true; + bool verbose = false; + bool isAborting = false; + bool enableColors = false; + bool throwOnError = false; + api.Diagnostic lastKind = null; + + final int FATAL = api.Diagnostic.CRASH.ordinal | api.Diagnostic.ERROR.ordinal; + final int INFO = + api.Diagnostic.INFO.ordinal | api.Diagnostic.VERBOSE_INFO.ordinal; + + FormattingDiagnosticHandler([SourceFileProvider provider]) + : this.provider = + (provider == null) ? new CompilerSourceFileProvider() : provider; + + void info(var message, [api.Diagnostic kind = api.Diagnostic.VERBOSE_INFO]) { + if (!verbose && kind == api.Diagnostic.VERBOSE_INFO) return; + if (enableColors) { + print('${colors.green("Info:")} $message'); + } else { + print('Info: $message'); + } + } + + /// Adds [kind] specific prefix to [message]. + String prefixMessage(String message, api.Diagnostic kind) { + switch (kind) { + case api.Diagnostic.ERROR: + return 'Error: $message'; + case api.Diagnostic.WARNING: + return 'Warning: $message'; + case api.Diagnostic.HINT: + return 'Hint: $message'; + case api.Diagnostic.CRASH: + return 'Internal Error: $message'; + case api.Diagnostic.INFO: + case api.Diagnostic.VERBOSE_INFO: + return 'Info: $message'; + } + throw 'Unexpected diagnostic kind: $kind (${kind.ordinal})'; + } + + void diagnosticHandler(Uri uri, int begin, int end, String message, + api.Diagnostic kind) { + // TODO(ahe): Remove this when source map is handled differently. + if (identical(kind.name, 'source map')) return; + + if (isAborting) return; + isAborting = (kind == api.Diagnostic.CRASH); + + bool fatal = (kind.ordinal & FATAL) != 0; + bool isInfo = (kind.ordinal & INFO) != 0; + if (isInfo && uri == null && kind != api.Diagnostic.INFO) { + info(message, kind); + return; + } + + message = prefixMessage(message, kind); + + // [previousKind]/[lastKind] records the previous non-INFO kind we saw. + // This is used to suppress info about a warning when warnings are + // suppressed, and similar for hints. + var previousKind = lastKind; + if (kind != api.Diagnostic.INFO) { + lastKind = kind; + } + var color; + if (kind == api.Diagnostic.ERROR) { + color = colors.red; + } else if (kind == api.Diagnostic.WARNING) { + if (!showWarnings) return; + color = colors.magenta; + } else if (kind == api.Diagnostic.HINT) { + if (!showHints) return; + color = colors.cyan; + } else if (kind == api.Diagnostic.CRASH) { + color = colors.red; + } else if (kind == api.Diagnostic.INFO) { + if (lastKind == api.Diagnostic.WARNING && !showWarnings) return; + if (lastKind == api.Diagnostic.HINT && !showHints) return; + color = colors.green; + } else { + throw 'Unknown kind: $kind (${kind.ordinal})'; + } + if (!enableColors) { + color = (x) => x; + } + if (uri == null) { + print('${color(message)}'); + } else { + SourceFile file = provider.sourceFiles[uri.toString()]; + if (file != null) { + print(file.getLocationMessage(color(message), begin, end, true, color)); + } else { + throw '$uri: file is null'; + } + } + if (fatal && throwOnError) { + isAborting = true; + throw new AbortLeg(message); + } + } + + void call(Uri uri, int begin, int end, String message, api.Diagnostic kind) { + return diagnosticHandler(uri, begin, end, message, kind); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/source_map_builder.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/source_map_builder.dart new file mode 100644 index 0000000..1f5e908 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/source_map_builder.dart @@ -0,0 +1,226 @@ +// Copyright (c) 2012, 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. + +library source_map_builder; + + +import 'util/util.dart'; +import 'scanner/scannerlib.dart' show Token; +import 'source_file.dart'; +import 'util/uri_extras.dart' show relativize; + +class SourceMapBuilder { + static const int VLQ_BASE_SHIFT = 5; + static const int VLQ_BASE_MASK = (1 << 5) - 1; + static const int VLQ_CONTINUATION_BIT = 1 << 5; + static const int VLQ_CONTINUATION_MASK = 1 << 5; + static const String BASE64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn' + 'opqrstuvwxyz0123456789+/'; + + final Uri uri; + final Uri fileUri; + + List entries; + + Map sourceUrlMap; + List sourceUrlList; + Map sourceNameMap; + List sourceNameList; + + int previousTargetLine; + int previousTargetColumn; + int previousSourceUrlIndex; + int previousSourceLine; + int previousSourceColumn; + int previousSourceNameIndex; + bool firstEntryInLine; + + SourceMapBuilder(this.uri, this.fileUri) { + entries = new List(); + + sourceUrlMap = new Map(); + sourceUrlList = new List(); + sourceNameMap = new Map(); + sourceNameList = new List(); + + previousTargetLine = 0; + previousTargetColumn = 0; + previousSourceUrlIndex = 0; + previousSourceLine = 0; + previousSourceColumn = 0; + previousSourceNameIndex = 0; + firstEntryInLine = true; + } + + void addMapping(int targetOffset, SourceFileLocation sourceLocation) { + entries.add(new SourceMapEntry(sourceLocation, targetOffset)); + } + + void printStringListOn(List strings, StringBuffer buffer) { + bool first = true; + buffer.write('['); + for (String string in strings) { + if (!first) buffer.write(','); + buffer.write('"'); + writeJsonEscapedCharsOn(string, buffer); + buffer.write('"'); + first = false; + } + buffer.write(']'); + } + + String build(SourceFile targetFile) { + StringBuffer mappingsBuffer = new StringBuffer(); + entries.forEach((SourceMapEntry entry) => writeEntry(entry, targetFile, + mappingsBuffer)); + StringBuffer buffer = new StringBuffer(); + buffer.write('{\n'); + buffer.write(' "version": 3,\n'); + if (uri != null && fileUri != null) { + buffer.write(' "file": "${relativize(uri, fileUri, false)}",\n'); + } + buffer.write(' "sourceRoot": "",\n'); + buffer.write(' "sources": '); + if (uri != null) { + sourceUrlList = + sourceUrlList.map((url) => relativize(uri, Uri.parse(url), false)) + .toList(); + } + printStringListOn(sourceUrlList, buffer); + buffer.write(',\n'); + buffer.write(' "names": '); + printStringListOn(sourceNameList, buffer); + buffer.write(',\n'); + buffer.write(' "mappings": "'); + buffer.write(mappingsBuffer); + buffer.write('"\n}\n'); + return buffer.toString(); + } + + void writeEntry(SourceMapEntry entry, SourceFile targetFile, StringBuffer output) { + int targetLine = targetFile.getLine(entry.targetOffset); + int targetColumn = targetFile.getColumn(targetLine, entry.targetOffset); + + if (targetLine > previousTargetLine) { + for (int i = previousTargetLine; i < targetLine; ++i) { + output.write(';'); + } + previousTargetLine = targetLine; + previousTargetColumn = 0; + firstEntryInLine = true; + } + + if (!firstEntryInLine) { + output.write(','); + } + firstEntryInLine = false; + + encodeVLQ(output, targetColumn - previousTargetColumn); + previousTargetColumn = targetColumn; + + if (entry.sourceLocation == null) return; + + String sourceUrl = entry.sourceLocation.getSourceUrl(); + int sourceLine = entry.sourceLocation.getLine(); + int sourceColumn = entry.sourceLocation.getColumn(); + String sourceName = entry.sourceLocation.getSourceName(); + + int sourceUrlIndex = indexOf(sourceUrlList, sourceUrl, sourceUrlMap); + encodeVLQ(output, sourceUrlIndex - previousSourceUrlIndex); + previousSourceUrlIndex = sourceUrlIndex; + + encodeVLQ(output, sourceLine - previousSourceLine); + previousSourceLine = sourceLine; + encodeVLQ(output, sourceColumn - previousSourceColumn); + previousSourceColumn = sourceColumn; + + if (sourceName == null) { + return; + } + + int sourceNameIndex = indexOf(sourceNameList, sourceName, sourceNameMap); + encodeVLQ(output, sourceNameIndex - previousSourceNameIndex); + previousSourceNameIndex = sourceNameIndex; + } + + int indexOf(List list, String value, Map map) { + return map.putIfAbsent(value, () { + int index = list.length; + list.add(value); + return index; + }); + } + + static void encodeVLQ(StringBuffer output, int value) { + int signBit = 0; + if (value < 0) { + signBit = 1; + value = -value; + } + value = (value << 1) | signBit; + do { + int digit = value & VLQ_BASE_MASK; + value >>= VLQ_BASE_SHIFT; + if (value > 0) { + digit |= VLQ_CONTINUATION_BIT; + } + output.write(BASE64_DIGITS[digit]); + } while (value > 0); + } +} + +class SourceMapEntry { + SourceFileLocation sourceLocation; + int targetOffset; + + SourceMapEntry(this.sourceLocation, this.targetOffset); +} + +abstract class SourceFileLocation { + SourceFile sourceFile; + + SourceFileLocation(this.sourceFile) { + assert(isValid()); + } + + int line; + + int get offset; + + String getSourceUrl() => sourceFile.filename; + + int getLine() { + if (line == null) line = sourceFile.getLine(offset); + return line; + } + + int getColumn() => sourceFile.getColumn(getLine(), offset); + + String getSourceName(); + + bool isValid() => offset < sourceFile.length; +} + +class TokenSourceFileLocation extends SourceFileLocation { + final Token token; + + TokenSourceFileLocation(SourceFile sourceFile, this.token) + : super(sourceFile); + + int get offset => token.charOffset; + + String getSourceName() { + if (token.isIdentifier()) return token.value; + return null; + } +} + +class OffsetSourceFileLocation extends SourceFileLocation { + final int offset; + final String sourceName; + OffsetSourceFileLocation(SourceFile sourceFile, this.offset, + [this.sourceName]) : super(sourceFile); + + String getSourceName() => sourceName; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/builder.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/builder.dart new file mode 100644 index 0000000..e2f2be3 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/builder.dart @@ -0,0 +1,6183 @@ +// Copyright (c) 2012, 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. + +part of ssa; + +/** + * A special element for the extra parameter taken by intercepted + * methods. We need to implement [TypedElement.type] because our + * optimizers may look at its declared type. + */ +class InterceptedElement extends ElementX implements TypedElement { + final DartType type; + InterceptedElement(this.type, String name, Element enclosing) + : super(name, ElementKind.PARAMETER, enclosing); + + DartType computeType(Compiler compiler) => type; + + accept(ElementVisitor visitor) => visitor.visitInterceptedElement(this); +} + +class SsaBuilderTask extends CompilerTask { + final CodeEmitterTask emitter; + final JavaScriptBackend backend; + + String get name => 'SSA builder'; + + SsaBuilderTask(JavaScriptBackend backend) + : emitter = backend.emitter, + backend = backend, + super(backend.compiler); + + HGraph build(CodegenWorkItem work) { + return measure(() { + Element element = work.element.implementation; + return compiler.withCurrentElement(element, () { + HInstruction.idCounter = 0; + SsaBuilder builder = + new SsaBuilder(backend, work, emitter.nativeEmitter); + HGraph graph; + ElementKind kind = element.kind; + if (kind == ElementKind.GENERATIVE_CONSTRUCTOR) { + graph = compileConstructor(builder, work); + } else if (kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY || + kind == ElementKind.FUNCTION || + kind == ElementKind.GETTER || + kind == ElementKind.SETTER) { + graph = builder.buildMethod(element); + } else if (kind == ElementKind.FIELD) { + if (element.isInstanceMember()) { + assert(compiler.enableTypeAssertions); + graph = builder.buildCheckedSetter(element); + } else { + graph = builder.buildLazyInitializer(element); + } + } else { + compiler.internalError(element, 'Unexpected element kind $kind.'); + } + assert(graph.isValid()); + if (!identical(kind, ElementKind.FIELD)) { + FunctionElement function = element; + FunctionSignature signature = function.functionSignature; + signature.forEachOptionalParameter((Element parameter) { + // This ensures the default value will be computed. + Constant constant = + compiler.constantHandler.getConstantForVariable(parameter); + backend.registerCompileTimeConstant(constant, work.resolutionTree); + compiler.constantHandler.addCompileTimeConstantForEmission( + constant); + }); + } + if (compiler.tracer.enabled) { + String name; + if (element.isMember()) { + String className = element.getEnclosingClass().name; + String memberName = element.name; + name = "$className.$memberName"; + if (element.isGenerativeConstructorBody()) { + name = "$name (body)"; + } + } else { + name = "${element.name}"; + } + compiler.tracer.traceCompilation( + name, work.compilationContext, compiler); + compiler.tracer.traceGraph('builder', graph); + } + return graph; + }); + }); + } + + HGraph compileConstructor(SsaBuilder builder, CodegenWorkItem work) { + return builder.buildFactory(work.element); + } +} + + +/** + * Keeps track of locals (including parameters and phis) when building. The + * 'this' reference is treated as parameter and hence handled by this class, + * too. + */ +class LocalsHandler { + /** + * The values of locals that can be directly accessed (without redirections + * to boxes or closure-fields). + * + * [directLocals] is iterated, so it is "insertion ordered" to make the + * iteration order a function only of insertions and not a function of + * e.g. Element hash codes. I'd prefer to use a SortedMap but some elements + * don't have source locations for [Elements.compareByPosition]. + */ + Map directLocals; + Map redirectionMapping; + SsaBuilder builder; + ClosureClassMap closureData; + + LocalsHandler(this.builder) + : directLocals = new Map(), + redirectionMapping = new Map(); + + get typesTask => builder.compiler.typesTask; + + /** + * Creates a new [LocalsHandler] based on [other]. We only need to + * copy the [directLocals], since the other fields can be shared + * throughout the AST visit. + */ + LocalsHandler.from(LocalsHandler other) + : directLocals = new Map.from(other.directLocals), + redirectionMapping = other.redirectionMapping, + builder = other.builder, + closureData = other.closureData; + + /** + * Redirects accesses from element [from] to element [to]. The [to] element + * must be a boxed variable or a variable that is stored in a closure-field. + */ + void redirectElement(Element from, Element to) { + assert(redirectionMapping[from] == null); + redirectionMapping[from] = to; + assert(isStoredInClosureField(from) || isBoxed(from)); + } + + HInstruction createBox() { + // TODO(floitsch): Clean up this hack. Should we create a box-object by + // just creating an empty object literal? + JavaScriptBackend backend = builder.backend; + HInstruction box = new HForeign(new js.ObjectInitializer([]), + backend.nonNullType, + []); + builder.add(box); + return box; + } + + /** + * If the scope (function or loop) [node] has captured variables then this + * method creates a box and sets up the redirections. + */ + void enterScope(ast.Node node, Element element) { + // See if any variable in the top-scope of the function is captured. If yes + // we need to create a box-object. + ClosureScope scopeData = closureData.capturingScopes[node]; + if (scopeData == null) return; + HInstruction box; + // The scope has captured variables. + if (element != null && element.isGenerativeConstructorBody()) { + // The box is passed as a parameter to a generative + // constructor body. + JavaScriptBackend backend = builder.backend; + box = builder.addParameter(scopeData.boxElement, backend.nonNullType); + } else { + box = createBox(); + } + // Add the box to the known locals. + directLocals[scopeData.boxElement] = box; + // Make sure that accesses to the boxed locals go into the box. We also + // need to make sure that parameters are copied into the box if necessary. + scopeData.capturedVariableMapping.forEach((Element from, Element to) { + // The [from] can only be a parameter for function-scopes and not + // loop scopes. + if (from.isParameter() && !element.isGenerativeConstructorBody()) { + // Now that the redirection is set up, the update to the local will + // write the parameter value into the box. + // Store the captured parameter in the box. Get the current value + // before we put the redirection in place. + // We don't need to update the local for a generative + // constructor body, because it receives a box that already + // contains the updates as the last parameter. + HInstruction instruction = readLocal(from); + redirectElement(from, to); + updateLocal(from, instruction); + } else { + redirectElement(from, to); + } + }); + } + + /** + * Replaces the current box with a new box and copies over the given list + * of elements from the old box into the new box. + */ + void updateCaptureBox(Element boxElement, List toBeCopiedElements) { + // Create a new box and copy over the values from the old box into the + // new one. + HInstruction oldBox = readLocal(boxElement); + HInstruction newBox = createBox(); + for (Element boxedVariable in toBeCopiedElements) { + // [readLocal] uses the [boxElement] to find its box. By replacing it + // behind its back we can still get to the old values. + updateLocal(boxElement, oldBox); + HInstruction oldValue = readLocal(boxedVariable); + updateLocal(boxElement, newBox); + updateLocal(boxedVariable, oldValue); + } + updateLocal(boxElement, newBox); + } + + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [function] must be an implementation element. + */ + void startFunction(Element element, ast.Node node) { + assert(invariant(element, element.isImplementation)); + Compiler compiler = builder.compiler; + closureData = compiler.closureToClassMapper.computeClosureToClassMapping( + element, node, builder.elements); + + if (element is FunctionElement) { + FunctionElement functionElement = element; + FunctionSignature params = functionElement.functionSignature; + params.orderedForEachParameter((Element parameterElement) { + if (element.isGenerativeConstructorBody()) { + ClosureScope scopeData = closureData.capturingScopes[node]; + if (scopeData != null + && scopeData.capturedVariableMapping.containsKey( + parameterElement)) { + // The parameter will be a field in the box passed as the + // last parameter. So no need to have it. + return; + } + } + HInstruction parameter = builder.addParameter( + parameterElement, + TypeMaskFactory.inferredTypeForElement(parameterElement, compiler)); + builder.parameters[parameterElement] = parameter; + directLocals[parameterElement] = parameter; + }); + } + + enterScope(node, element); + + // If the freeVariableMapping is not empty, then this function was a + // nested closure that captures variables. Redirect the captured + // variables to fields in the closure. + closureData.freeVariableMapping.forEach((Element from, Element to) { + redirectElement(from, to); + }); + JavaScriptBackend backend = compiler.backend; + if (closureData.isClosure()) { + // Inside closure redirect references to itself to [:this:]. + HThis thisInstruction = new HThis(closureData.thisElement, + backend.nonNullType); + builder.graph.thisInstruction = thisInstruction; + builder.graph.entry.addAtEntry(thisInstruction); + updateLocal(closureData.closureElement, thisInstruction); + } else if (element.isInstanceMember()) { + // Once closures have been mapped to classes their instance members might + // not have any thisElement if the closure was created inside a static + // context. + HThis thisInstruction = new HThis( + closureData.thisElement, builder.getTypeOfThis()); + builder.graph.thisInstruction = thisInstruction; + builder.graph.entry.addAtEntry(thisInstruction); + directLocals[closureData.thisElement] = thisInstruction; + } + + // If this method is an intercepted method, add the extra + // parameter to it, that is the actual receiver for intercepted + // classes, or the same as [:this:] for non-intercepted classes. + ClassElement cls = element.getEnclosingClass(); + + // When the class extends a native class, the instance is pre-constructed + // and passed to the generative constructor factory function as a parameter. + // Instead of allocating and initializing the object, the constructor + // 'upgrades' the native subclass object by initializing the Dart fields. + bool isNativeUpgradeFactory = element.isGenerativeConstructor() + && Elements.isNativeOrExtendsNative(cls); + if (backend.isInterceptedMethod(element)) { + bool isInterceptorClass = backend.isInterceptorClass(cls.declaration); + String name = isInterceptorClass ? 'receiver' : '_'; + Element parameter = new InterceptedElement( + cls.thisType, name, element); + HParameterValue value = + new HParameterValue(parameter, builder.getTypeOfThis()); + builder.graph.explicitReceiverParameter = value; + builder.graph.entry.addAfter( + directLocals[closureData.thisElement], value); + if (isInterceptorClass) { + // Only use the extra parameter in intercepted classes. + directLocals[closureData.thisElement] = value; + } + } else if (isNativeUpgradeFactory) { + Element parameter = new InterceptedElement( + cls.thisType, 'receiver', element); + // Unlike `this`, receiver is nullable since direct calls to generative + // constructor call the constructor with `null`. + HParameterValue value = + new HParameterValue(parameter, new TypeMask.exact(cls)); + builder.graph.explicitReceiverParameter = value; + builder.graph.entry.addAtEntry(value); + } + } + + /** + * Returns true if the local can be accessed directly. Boxed variables or + * captured variables that are stored in the closure-field return [:false:]. + */ + bool isAccessedDirectly(Element element) { + assert(element != null); + return redirectionMapping[element] == null + && !closureData.usedVariablesInTry.contains(element); + } + + bool isStoredInClosureField(Element element) { + assert(element != null); + if (isAccessedDirectly(element)) return false; + Element redirectTarget = redirectionMapping[element]; + if (redirectTarget == null) return false; + if (redirectTarget.isMember()) { + assert(redirectTarget is ClosureFieldElement); + return true; + } + return false; + } + + bool isBoxed(Element element) { + if (isAccessedDirectly(element)) return false; + if (isStoredInClosureField(element)) return false; + return redirectionMapping[element] != null; + } + + bool isUsedInTry(Element element) { + return closureData.usedVariablesInTry.contains(element); + } + + /** + * Returns an [HInstruction] for the given element. If the element is + * boxed or stored in a closure then the method generates code to retrieve + * the value. + */ + HInstruction readLocal(Element element) { + if (isAccessedDirectly(element)) { + if (directLocals[element] == null) { + if (element.isTypeVariable()) { + builder.compiler.internalError(builder.compiler.currentElement, + "Runtime type information not available for $element."); + } else { + builder.compiler.internalError(element, + "Cannot find value $element."); + } + } + return directLocals[element]; + } else if (isStoredInClosureField(element)) { + Element redirect = redirectionMapping[element]; + HInstruction receiver = readLocal(closureData.closureElement); + TypeMask type = (element.kind == ElementKind.VARIABLE_LIST) + ? builder.backend.nonNullType + : builder.getTypeOfCapturedVariable(redirect); + assert(element.kind != ElementKind.VARIABLE_LIST + || element is BoxElement); + HInstruction fieldGet = new HFieldGet(redirect, receiver, type); + builder.add(fieldGet); + return fieldGet; + } else if (isBoxed(element)) { + Element redirect = redirectionMapping[element]; + // In the function that declares the captured variable the box is + // accessed as direct local. Inside the nested closure the box is + // accessed through a closure-field. + // Calling [readLocal] makes sure we generate the correct code to get + // the box. + assert(redirect.enclosingElement is BoxElement); + HInstruction box = readLocal(redirect.enclosingElement); + HInstruction lookup = new HFieldGet( + redirect, box, builder.getTypeOfCapturedVariable(redirect)); + builder.add(lookup); + return lookup; + } else { + assert(isUsedInTry(element)); + HLocalValue local = getLocal(element); + HInstruction variable = new HLocalGet( + element, local, builder.backend.dynamicType); + builder.add(variable); + return variable; + } + } + + HInstruction readThis() { + HInstruction res = readLocal(closureData.thisElement); + if (res.instructionType == null) { + res.instructionType = builder.getTypeOfThis(); + } + return res; + } + + HLocalValue getLocal(Element element) { + // If the element is a parameter, we already have a + // HParameterValue for it. We cannot create another one because + // it could then have another name than the real parameter. And + // the other one would not know it is just a copy of the real + // parameter. + if (element.isParameter()) return builder.parameters[element]; + + return builder.activationVariables.putIfAbsent(element, () { + JavaScriptBackend backend = builder.backend; + HLocalValue local = new HLocalValue(element, backend.nonNullType); + builder.graph.entry.addAtExit(local); + return local; + }); + } + + /** + * Sets the [element] to [value]. If the element is boxed or stored in a + * closure then the method generates code to set the value. + */ + void updateLocal(Element element, HInstruction value) { + assert(!isStoredInClosureField(element)); + if (isAccessedDirectly(element)) { + directLocals[element] = value; + } else if (isBoxed(element)) { + Element redirect = redirectionMapping[element]; + // The box itself could be captured, or be local. A local variable that + // is captured will be boxed, but the box itself will be a local. + // Inside the closure the box is stored in a closure-field and cannot + // be accessed directly. + assert(redirect.enclosingElement is BoxElement); + HInstruction box = readLocal(redirect.enclosingElement); + builder.add(new HFieldSet(redirect, box, value)); + } else { + assert(isUsedInTry(element)); + HLocalValue local = getLocal(element); + builder.add(new HLocalSet(element, local, value)); + } + } + + /** + * This function, startLoop, must be called before visiting any children of + * the loop. In particular it needs to be called before executing the + * initializers. + * + * The [LocalsHandler] will make the boxes and updates at the right moment. + * The builder just needs to call [enterLoopBody] and [enterLoopUpdates] + * (for [ast.For] loops) at the correct places. For phi-handling + * [beginLoopHeader] and [endLoop] must also be called. + * + * The correct place for the box depends on the given loop. In most cases + * the box will be created when entering the loop-body: while, do-while, and + * for-in (assuming the call to [:next:] is inside the body) can always be + * constructed this way. + * + * Things are slightly more complicated for [ast.For] loops. If no declared + * loop variable is boxed then the loop-body approach works here too. If a + * loop-variable is boxed we need to introduce a new box for the + * loop-variable before we enter the initializer so that the initializer + * writes the values into the box. In any case we need to create the box + * before the condition since the condition could box the variable. + * Since the first box is created outside the actual loop we have a second + * location where a box is created: just before the updates. This is + * necessary since updates are considered to be part of the next iteration + * (and can again capture variables). + * + * For example the following Dart code prints 1 3 -- 3 4. + * + * var fs = []; + * for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) { + * i++; + * } + * print("--"); + * for (var i = 0; i < 2; i++) fs[i](); + * + * We solve this by emitting the following code (only for [ast.For] loops): + * <== move the first box creation outside the loop. + * ; + * loop-entry: + * if (!) goto loop-exit; + * + * // create a new box and copy the captured loop-variables. + * + * goto loop-entry; + * loop-exit: + */ + void startLoop(ast.Node node) { + ClosureScope scopeData = closureData.capturingScopes[node]; + if (scopeData == null) return; + if (scopeData.hasBoxedLoopVariables()) { + // If there are boxed loop variables then we set up the box and + // redirections already now. This way the initializer can write its + // values into the box. + // For other loops the box will be created when entering the body. + enterScope(node, null); + } + } + + /** + * Create phis at the loop entry for local variables (ready for the values + * from the back edge). Populate the phis with the current values. + */ + void beginLoopHeader(HBasicBlock loopEntry) { + // Create a copy because we modify the map while iterating over it. + Map savedDirectLocals = + new Map.from(directLocals); + + JavaScriptBackend backend = builder.backend; + // Create phis for all elements in the definitions environment. + savedDirectLocals.forEach((Element element, HInstruction instruction) { + if (isAccessedDirectly(element)) { + // We know 'this' cannot be modified. + if (!identical(element, closureData.thisElement)) { + HPhi phi = new HPhi.singleInput( + element, instruction, backend.dynamicType); + loopEntry.addPhi(phi); + directLocals[element] = phi; + } else { + directLocals[element] = instruction; + } + } + }); + } + + void enterLoopBody(ast.Node node) { + ClosureScope scopeData = closureData.capturingScopes[node]; + if (scopeData == null) return; + // If there are no declared boxed loop variables then we did not create the + // box before the initializer and we have to create the box now. + if (!scopeData.hasBoxedLoopVariables()) { + enterScope(node, null); + } + } + + void enterLoopUpdates(ast.Node node) { + // If there are declared boxed loop variables then the updates might have + // access to the box and we must switch to a new box before executing the + // updates. + // In all other cases a new box will be created when entering the body of + // the next iteration. + ClosureScope scopeData = closureData.capturingScopes[node]; + if (scopeData == null) return; + if (scopeData.hasBoxedLoopVariables()) { + updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables); + } + } + + /** + * Goes through the phis created in beginLoopHeader entry and adds the + * input from the back edge (from the current value of directLocals) to them. + */ + void endLoop(HBasicBlock loopEntry) { + // If the loop has an aborting body, we don't update the loop + // phis. + if (loopEntry.predecessors.length == 1) return; + loopEntry.forEachPhi((HPhi phi) { + Element element = phi.sourceElement; + HInstruction postLoopDefinition = directLocals[element]; + phi.addInput(postLoopDefinition); + }); + } + + /** + * Merge [otherLocals] into this locals handler, creating phi-nodes when + * there is a conflict. + * If a phi node is necessary, it will use this handler's instruction as the + * first input, and the otherLocals instruction as the second. + */ + void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) { + // If an element is in one map but not the other we can safely + // ignore it. It means that a variable was declared in the + // block. Since variable declarations are scoped the declared + // variable cannot be alive outside the block. Note: this is only + // true for nodes where we do joins. + Map joinedLocals = + new Map(); + JavaScriptBackend backend = builder.backend; + otherLocals.directLocals.forEach((element, instruction) { + // We know 'this' cannot be modified. + if (identical(element, closureData.thisElement)) { + assert(directLocals[element] == instruction); + joinedLocals[element] = instruction; + } else { + HInstruction mine = directLocals[element]; + if (mine == null) return; + if (identical(instruction, mine)) { + joinedLocals[element] = instruction; + } else { + HInstruction phi = new HPhi.manyInputs( + element, [mine, instruction], backend.dynamicType); + joinBlock.addPhi(phi); + joinedLocals[element] = phi; + } + } + }); + directLocals = joinedLocals; + } + + /** + * When control flow merges, this method can be used to merge several + * localsHandlers into a new one using phis. The new localsHandler is + * returned. Unless it is also in the list, the current localsHandler is not + * used for its values, only for its declared variables. This is a way to + * exclude local values from the result when they are no longer in scope. + */ + LocalsHandler mergeMultiple(List localsHandlers, + HBasicBlock joinBlock) { + assert(localsHandlers.length > 0); + if (localsHandlers.length == 1) return localsHandlers[0]; + Map joinedLocals = + new Map(); + HInstruction thisValue = null; + JavaScriptBackend backend = builder.backend; + directLocals.forEach((Element element, HInstruction instruction) { + if (element != closureData.thisElement) { + HPhi phi = new HPhi.noInputs(element, backend.dynamicType); + joinedLocals[element] = phi; + joinBlock.addPhi(phi); + } else { + // We know that "this" never changes, if it's there. + // Save it for later. While merging, there is no phi for "this", + // so we don't have to special case it in the merge loop. + thisValue = instruction; + } + }); + for (LocalsHandler handler in localsHandlers) { + handler.directLocals.forEach((Element element, HInstruction instruction) { + HPhi phi = joinedLocals[element]; + if (phi != null) { + phi.addInput(instruction); + } + }); + } + if (thisValue != null) { + // If there was a "this" for the scope, add it to the new locals. + joinedLocals[closureData.thisElement] = thisValue; + } + + // Remove locals that are not in all handlers. + directLocals = new Map(); + joinedLocals.forEach((element, instruction) { + if (element != closureData.thisElement + && instruction.inputs.length != localsHandlers.length) { + joinBlock.removePhi(instruction); + } else { + directLocals[element] = instruction; + } + }); + return this; + } +} + + +// Represents a single break/continue instruction. +class JumpHandlerEntry { + final HJump jumpInstruction; + final LocalsHandler locals; + bool isBreak() => jumpInstruction is HBreak; + bool isContinue() => jumpInstruction is HContinue; + JumpHandlerEntry(this.jumpInstruction, this.locals); +} + + +abstract class JumpHandler { + factory JumpHandler(SsaBuilder builder, TargetElement target) { + return new TargetJumpHandler(builder, target); + } + void generateBreak([LabelElement label]); + void generateContinue([LabelElement label]); + void forEachBreak(void action(HBreak instruction, LocalsHandler locals)); + void forEachContinue(void action(HContinue instruction, + LocalsHandler locals)); + bool hasAnyContinue(); + bool hasAnyBreak(); + void close(); + final TargetElement target; + List labels(); +} + +// Insert break handler used to avoid null checks when a target isn't +// used as the target of a break, and therefore doesn't need a break +// handler associated with it. +class NullJumpHandler implements JumpHandler { + final Compiler compiler; + + NullJumpHandler(this.compiler); + + void generateBreak([LabelElement label]) { + compiler.internalError(CURRENT_ELEMENT_SPANNABLE, + 'NullJumpHandler.generateBreak should not be called.'); + } + + void generateContinue([LabelElement label]) { + compiler.internalError(CURRENT_ELEMENT_SPANNABLE, + 'NullJumpHandler.generateContinue should not be called.'); + } + + void forEachBreak(Function ignored) { } + void forEachContinue(Function ignored) { } + void close() { } + bool hasAnyContinue() => false; + bool hasAnyBreak() => false; + + List labels() => const []; + TargetElement get target => null; +} + +// Records breaks until a target block is available. +// Breaks are always forward jumps. +// Continues in loops are implemented as breaks of the body. +// Continues in switches is currently not handled. +class TargetJumpHandler implements JumpHandler { + final SsaBuilder builder; + final TargetElement target; + final List jumps; + + TargetJumpHandler(SsaBuilder builder, this.target) + : this.builder = builder, + jumps = [] { + assert(builder.jumpTargets[target] == null); + builder.jumpTargets[target] = this; + } + + void generateBreak([LabelElement label]) { + HInstruction breakInstruction; + if (label == null) { + breakInstruction = new HBreak(target); + } else { + breakInstruction = new HBreak.toLabel(label); + } + LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); + builder.close(breakInstruction); + jumps.add(new JumpHandlerEntry(breakInstruction, locals)); + } + + void generateContinue([LabelElement label]) { + HInstruction continueInstruction; + if (label == null) { + continueInstruction = new HContinue(target); + } else { + continueInstruction = new HContinue.toLabel(label); + // Switch case continue statements must be handled by the + // [SwitchCaseJumpHandler]. + assert(label.target.statement is! ast.SwitchCase); + } + LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); + builder.close(continueInstruction); + jumps.add(new JumpHandlerEntry(continueInstruction, locals)); + } + + void forEachBreak(Function action) { + for (JumpHandlerEntry entry in jumps) { + if (entry.isBreak()) action(entry.jumpInstruction, entry.locals); + } + } + + void forEachContinue(Function action) { + for (JumpHandlerEntry entry in jumps) { + if (entry.isContinue()) action(entry.jumpInstruction, entry.locals); + } + } + + bool hasAnyContinue() { + for (JumpHandlerEntry entry in jumps) { + if (entry.isContinue()) return true; + } + return false; + } + + bool hasAnyBreak() { + for (JumpHandlerEntry entry in jumps) { + if (entry.isBreak()) return true; + } + return false; + } + + void close() { + // The mapping from TargetElement to JumpHandler is no longer needed. + builder.jumpTargets.remove(target); + } + + List labels() { + List result = null; + for (LabelElement element in target.labels) { + if (result == null) result = []; + result.add(element); + } + return (result == null) ? const [] : result; + } +} + +/// Special [JumpHandler] implementation used to handle continue statements +/// targeting switch cases. +class SwitchCaseJumpHandler extends TargetJumpHandler { + /// Map from switch case targets to indices used to encode the flow of the + /// switch case loop. + final Map targetIndexMap = new Map(); + + SwitchCaseJumpHandler(SsaBuilder builder, + TargetElement target, + ast.SwitchStatement node) + : super(builder, target) { + // The switch case indices must match those computed in + // [SsaFromAstMixin.buildSwitchCaseConstants]. + // Switch indices are 1-based so we can bypass the synthetic loop when no + // cases match simply by branching on the index (which defaults to null). + int switchIndex = 1; + for (ast.SwitchCase switchCase in node.cases) { + for (ast.Node labelOrCase in switchCase.labelsAndCases) { + ast.Node label = labelOrCase.asLabel(); + if (label != null) { + LabelElement labelElement = builder.elements[label]; + if (labelElement != null && labelElement.isContinueTarget) { + TargetElement continueTarget = labelElement.target; + targetIndexMap[continueTarget] = switchIndex; + assert(builder.jumpTargets[continueTarget] == null); + builder.jumpTargets[continueTarget] = this; + } + } + } + switchIndex++; + } + } + + void generateBreak([LabelElement label]) { + if (label == null) { + // Creates a special break instruction for the synthetic loop generated + // for a switch statement with continue statements. See + // [SsaFromAstMixin.buildComplexSwitchStatement] for detail. + + HInstruction breakInstruction = + new HBreak(target, breakSwitchContinueLoop: true); + LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); + builder.close(breakInstruction); + jumps.add(new JumpHandlerEntry(breakInstruction, locals)); + } else { + super.generateBreak(label); + } + } + + bool isContinueToSwitchCase(LabelElement label) { + return label != null && targetIndexMap.containsKey(label.target); + } + + void generateContinue([LabelElement label]) { + if (isContinueToSwitchCase(label)) { + // Creates the special instructions 'label = i; continue l;' used in + // switch statements with continue statements. See + // [SsaFromAstMixin.buildComplexSwitchStatement] for detail. + + assert(label != null); + HInstruction value = builder.graph.addConstantInt( + targetIndexMap[label.target], + builder.compiler); + builder.localsHandler.updateLocal(target, value); + + assert(label.target.labels.contains(label)); + HInstruction continueInstruction = new HContinue(target); + LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); + builder.close(continueInstruction); + jumps.add(new JumpHandlerEntry(continueInstruction, locals)); + } else { + super.generateContinue(label); + } + } + + void close() { + // The mapping from TargetElement to JumpHandler is no longer needed. + for (TargetElement target in targetIndexMap.keys) { + builder.jumpTargets.remove(target); + } + super.close(); + } +} + +/** + * This class builds SSA nodes for functions represented in AST. + */ +class SsaBuilder extends ResolvedVisitor { + final JavaScriptBackend backend; + final ConstantSystem constantSystem; + final CodegenWorkItem work; + final RuntimeTypes rti; + + /* This field is used by the native handler. */ + final NativeEmitter nativeEmitter; + + final HGraph graph = new HGraph(); + + /** + * The current block to add instructions to. Might be null, if we are + * visiting dead code, but see [isReachable]. + */ + HBasicBlock _current; + + HBasicBlock get current => _current; + + void set current(c) { + isReachable = c != null; + _current = c; + } + + /** + * The most recently opened block. Has the same value as [current] while + * the block is open, but unlike [current], it isn't cleared when the + * current block is closed. + */ + HBasicBlock lastOpenedBlock; + + /** + * Indicates whether the current block is dead (because it has a throw or a + * return further up). If this is false, then [current] may be null. If the + * block is dead then it may also be aborted, but for simplicity we only + * abort on statement boundaries, not in the middle of expressions. See + * isAborted. + */ + bool isReachable = true; + + /** + * True if we are visiting the expression of a throw statement. + */ + bool inThrowExpression = false; + + /** + * The loop nesting is consulted when inlining a function invocation in + * [tryInlineMethod]. The inlining heuristics take this information into + * account. + */ + int loopNesting = 0; + + /** + * This stack contains declaration elements of the functions being built + * or inlined by this builder. + */ + final List sourceElementStack = []; + + LocalsHandler localsHandler; + + HInstruction rethrowableException; + + HParameterValue lastAddedParameter; + + Map parameters = {}; + + Map jumpTargets = {}; + + /** + * Variables stored in the current activation. These variables are + * being updated in try/catch blocks, and should be + * accessed indirectly through [HLocalGet] and [HLocalSet]. + */ + Map activationVariables = {}; + + // We build the Ssa graph by simulating a stack machine. + List stack = []; + + SsaBuilder(JavaScriptBackend backend, + CodegenWorkItem work, + this.nativeEmitter) + : this.backend = backend, + this.constantSystem = backend.constantSystem, + this.work = work, + this.rti = backend.rti, + super(work.resolutionTree, backend.compiler) { + localsHandler = new LocalsHandler(this); + sourceElementStack.add(work.element); + } + + Element get sourceElement => sourceElementStack.last; + + HBasicBlock addNewBlock() { + HBasicBlock block = graph.addNewBlock(); + // If adding a new block during building of an expression, it is due to + // conditional expressions or short-circuit logical operators. + return block; + } + + void open(HBasicBlock block) { + block.open(); + current = block; + lastOpenedBlock = block; + } + + HBasicBlock close(HControlFlow end) { + HBasicBlock result = current; + current.close(end); + current = null; + return result; + } + + HBasicBlock closeAndGotoExit(HControlFlow end) { + HBasicBlock result = current; + current.close(end); + current = null; + result.addSuccessor(graph.exit); + return result; + } + + void goto(HBasicBlock from, HBasicBlock to) { + from.close(new HGoto()); + from.addSuccessor(to); + } + + bool isAborted() { + return current == null; + } + + /** + * Creates a new block, transitions to it from any current block, and + * opens the new block. + */ + HBasicBlock openNewBlock() { + HBasicBlock newBlock = addNewBlock(); + if (!isAborted()) goto(current, newBlock); + open(newBlock); + return newBlock; + } + + void add(HInstruction instruction) { + current.add(instruction); + } + + void addWithPosition(HInstruction instruction, ast.Node node) { + add(attachPosition(instruction, node)); + } + + SourceFile currentSourceFile() { + Element element = sourceElement; + // TODO(johnniwinther): remove the 'element.patch' hack. + if (element is FunctionElement) { + FunctionElement functionElement = element; + if (functionElement.patch != null) element = functionElement.patch; + } + Script script = element.getCompilationUnit().script; + return script.file; + } + + void checkValidSourceFileLocation( + SourceFileLocation location, SourceFile sourceFile, int offset) { + if (!location.isValid()) { + throw MessageKind.INVALID_SOURCE_FILE_LOCATION.message( + {'offset': offset, + 'fileName': sourceFile.filename, + 'length': sourceFile.length}); + } + } + + /** + * Returns a complete argument list for a call of [function]. + */ + List completeSendArgumentsList( + FunctionElement function, + Selector selector, + List providedArguments, + ast.Node currentNode) { + assert(invariant(function, function.isImplementation)); + assert(providedArguments != null); + + bool isInstanceMember = function.isInstanceMember(); + // For static calls, [providedArguments] is complete, default arguments + // have been included if necessary, see [addStaticSendArgumentsToList]. + if (!isInstanceMember + || currentNode == null // In erroneous code, currentNode can be null. + || providedArgumentsKnownToBeComplete(currentNode) + || function.isGenerativeConstructorBody() + || selector.isGetter()) { + // For these cases, the provided argument list is known to be complete. + return providedArguments; + } else { + return completeDynamicSendArgumentsList( + selector, function, providedArguments); + } + } + + /** + * Returns a complete argument list for a dynamic call of [function]. The + * initial argument list [providedArguments], created by + * [addDynamicSendArgumentsToList], does not include values for default + * arguments used in the call. The reason is that the target function (which + * defines the defaults) is not known. + * + * However, inlining can only be performed when the target function can be + * resolved statically. The defaults can therefore be included at this point. + * + * The [providedArguments] list contains first all positional arguments, then + * the provided named arguments (the named arguments that are defined in the + * [selector]) in a specific order (see [addDynamicSendArgumentsToList]). + */ + List completeDynamicSendArgumentsList( + Selector selector, + FunctionElement function, + List providedArguments) { + assert(selector.applies(function, compiler)); + FunctionSignature signature = function.functionSignature; + List compiledArguments = new List( + signature.parameterCount + 1); // Plus one for receiver. + + compiledArguments[0] = providedArguments[0]; // Receiver. + int index = 1; + for (; index <= signature.requiredParameterCount; index++) { + compiledArguments[index] = providedArguments[index]; + } + if (!signature.optionalParametersAreNamed) { + signature.forEachOptionalParameter((element) { + if (index < providedArguments.length) { + compiledArguments[index] = providedArguments[index]; + } else { + compiledArguments[index] = + handleConstantForOptionalParameter(element); + } + index++; + }); + } else { + /* Example: + * void foo(a, {b, d, c}) + * foo(0, d = 1, b = 2) + * + * providedArguments = [0, 2, 1] + * selectorArgumentNames = [b, d] + * signature.orderedOptionalParameters = [b, c, d] + * + * For each parameter name in the signature, if the argument name matches + * we use the next provided argument, otherwise we get the default. + */ + List selectorArgumentNames = selector.getOrderedNamedArguments(); + int namedArgumentIndex = 0; + int firstProvidedNamedArgument = index; + signature.orderedOptionalParameters.forEach((element) { + if (namedArgumentIndex < selectorArgumentNames.length && + element.name == selectorArgumentNames[namedArgumentIndex]) { + // The named argument was provided in the function invocation. + compiledArguments[index] = providedArguments[ + firstProvidedNamedArgument + namedArgumentIndex++]; + } else { + compiledArguments[index] = + handleConstantForOptionalParameter(element); + } + index++; + }); + } + return compiledArguments; + } + + /** + * Try to inline [element] within the currect context of the builder. The + * insertion point is the state of the builder. + */ + bool tryInlineMethod(Element element, + Selector selector, + List providedArguments, + ast.Node currentNode) { + backend.registerStaticUse(element, compiler.enqueuer.codegen); + + // Ensure that [element] is an implementation element. + element = element.implementation; + FunctionElement function = element; + bool insideLoop = loopNesting > 0 || graph.calledInLoop; + + // Bail out early if the inlining decision is in the cache and we can't + // inline (no need to check the hard constraints). + bool cachedCanBeInlined = + backend.inlineCache.canInline(function, insideLoop: insideLoop); + if (cachedCanBeInlined == false) return false; + + bool meetsHardConstraints() { + // Don't inline from one output unit to another. If something is deferred + // it is to save space in the loading code. + var getOutputUnit = compiler.deferredLoadTask.outputUnitForElement; + if (getOutputUnit(element) != + getOutputUnit(compiler.currentElement)) { + return false; + } + if (compiler.disableInlining) return false; + + assert(selector != null + || Elements.isStaticOrTopLevel(element) + || element.isGenerativeConstructorBody()); + if (selector != null && !selector.applies(function, compiler)) { + return false; + } + + // Don't inline operator== methods if the parameter can be null. + if (element.name == '==') { + if (element.getEnclosingClass() != compiler.objectClass + && providedArguments[1].canBeNull()) { + return false; + } + } + + // Generative constructors of native classes should not be called directly + // and have an extra argument that causes problems with inlining. + if (element.isGenerativeConstructor() + && Elements.isNativeOrExtendsNative(element.getEnclosingClass())) { + return false; + } + + // A generative constructor body is not seen by global analysis, + // so we should not query for its type. + if (!element.isGenerativeConstructorBody()) { + // Don't inline if the return type was inferred to be non-null empty. + // This means that the function always throws an exception. + TypeMask returnType = + compiler.typesTask.getGuaranteedReturnTypeOfElement(element); + if (returnType != null + && returnType.isEmpty + && !returnType.isNullable) { + isReachable = false; + return false; + } + } + + return true; + } + + bool heuristicSayGoodToGo() { + // Don't inline recursivly + if (inliningStack.any((entry) => entry.function == function)) { + return false; + } + + if (element.isSynthesized) return true; + + if (cachedCanBeInlined == true) return cachedCanBeInlined; + + if (inThrowExpression) return false; + + int numParameters = function.functionSignature.parameterCount; + int maxInliningNodes; + bool useMaxInliningNodes = true; + if (insideLoop) { + maxInliningNodes = InlineWeeder.INLINING_NODES_INSIDE_LOOP + + InlineWeeder.INLINING_NODES_INSIDE_LOOP_ARG_FACTOR * numParameters; + } else { + maxInliningNodes = InlineWeeder.INLINING_NODES_OUTSIDE_LOOP + + InlineWeeder.INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR * numParameters; + } + + // If a method is called only once, and all the methods in the + // inlining stack are called only once as well, we know we will + // save on output size by inlining this method. + TypesInferrer inferrer = compiler.typesTask.typesInferrer; + if (inferrer.isCalledOnce(element) + && (inliningStack.every( + (entry) => inferrer.isCalledOnce(entry.function)))) { + useMaxInliningNodes = false; + } + bool canInline; + ast.FunctionExpression functionNode = function.parseNode(compiler); + canInline = InlineWeeder.canBeInlined( + functionNode, maxInliningNodes, useMaxInliningNodes); + if (canInline) { + backend.inlineCache.markAsInlinable(element, insideLoop: insideLoop); + } else { + backend.inlineCache.markAsNonInlinable(element, insideLoop: insideLoop); + } + return canInline; + } + + void doInlining() { + // Add an explicit null check on the receiver before doing the + // inlining. We use [element] to get the same name in the + // NoSuchMethodError message as if we had called it. + if (element.isInstanceMember() + && !element.isGenerativeConstructorBody() + && (selector.mask == null || selector.mask.isNullable)) { + addWithPosition( + new HFieldGet(null, providedArguments[0], backend.dynamicType, + isAssignable: false), + currentNode); + } + List compiledArguments = completeSendArgumentsList( + function, selector, providedArguments, currentNode); + enterInlinedMethod(function, currentNode, compiledArguments); + inlinedFrom(function, () { + if (!isReachable) { + emitReturn(graph.addConstantNull(compiler), null); + } else { + doInline(function); + } + }); + leaveInlinedMethod(); + } + + if (meetsHardConstraints() && heuristicSayGoodToGo()) { + doInlining(); + return true; + } + + return false; + } + + inlinedFrom(Element element, f()) { + assert(element is FunctionElement || element is VariableElement); + return compiler.withCurrentElement(element, () { + // The [sourceElementStack] contains declaration elements. + sourceElementStack.add(element.declaration); + var result = f(); + sourceElementStack.removeLast(); + return result; + }); + } + + HInstruction handleConstantForOptionalParameter(Element parameter) { + Constant constant = + compiler.constantHandler.getConstantForVariable(parameter); + assert(invariant(parameter, constant != null, + message: 'No constant computed for $parameter')); + return graph.addConstant(constant, compiler); + } + + Element get currentNonClosureClass { + ClassElement cls = sourceElement.getEnclosingClass(); + if (cls != null && cls.isClosure()) { + var closureClass = cls; + return closureClass.methodElement.getEnclosingClass(); + } else { + return cls; + } + } + + /** + * Returns whether this builder is building code for [element]. + */ + bool isBuildingFor(Element element) { + return work.element == element; + } + + /// A stack of [DartType]s the have been seen during inlining of factory + /// constructors. These types are preserved in [HInvokeStatic]s and + /// [HForeignNews] inside the inline code and registered during code + /// generation for these nodes. + // TODO(karlklose): consider removing this and keeping the (substituted) + // types of the type variables in an environment (like the [LocalsHandler]). + final List currentInlinedInstantiations = []; + + final List inliningStack = []; + + Element returnElement; + DartType returnType; + + bool inTryStatement = false; + + Constant getConstantForNode(ast.Node node) { + Constant constant = elements.getConstant(node); + assert(invariant(node, constant != null, + message: 'No constant computed for $node')); + return constant; + } + + HInstruction addConstant(ast.Node node) { + return graph.addConstant(getConstantForNode(node), compiler); + } + + bool isLazilyInitialized(VariableElement element) { + Constant initialValue = + compiler.constantHandler.getConstantForVariable(element); + return initialValue == null; + } + + TypeMask cachedTypeOfThis; + + TypeMask getTypeOfThis() { + TypeMask result = cachedTypeOfThis; + if (result == null) { + Element element = localsHandler.closureData.thisElement; + ClassElement cls = element.enclosingElement.getEnclosingClass(); + if (compiler.world.isUsedAsMixin(cls)) { + // If the enclosing class is used as a mixin, [:this:] can be + // of the class that mixins the enclosing class. These two + // classes do not have a subclass relationship, so, for + // simplicity, we mark the type as an interface type. + result = new TypeMask.nonNullSubtype(cls.declaration); + } else { + result = new TypeMask.nonNullSubclass(cls.declaration); + } + cachedTypeOfThis = result; + } + return result; + } + + Map cachedTypesOfCapturedVariables = + new Map(); + + TypeMask getTypeOfCapturedVariable(Element element) { + assert(element.isField()); + return cachedTypesOfCapturedVariables.putIfAbsent(element, () { + return TypeMaskFactory.inferredTypeForElement(element, compiler); + }); + } + + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [functionElement] must be an implementation element. + */ + HGraph buildMethod(FunctionElement functionElement) { + assert(invariant(functionElement, functionElement.isImplementation)); + graph.calledInLoop = compiler.world.isCalledInLoop(functionElement); + ast.FunctionExpression function = functionElement.parseNode(compiler); + assert(function != null); + assert(!function.modifiers.isExternal()); + assert(elements[function] != null); + openFunction(functionElement, function); + String name = functionElement.name; + // If [functionElement] is `operator==` we explicitely add a null check at + // the beginning of the method. This is to avoid having call sites do the + // null check. + if (name == '==') { + if (!backend.operatorEqHandlesNullArgument(functionElement)) { + handleIf( + function, + () { + HParameterValue parameter = parameters.values.first; + push(new HIdentity( + parameter, graph.addConstantNull(compiler), null, + backend.boolType)); + }, + () { + closeAndGotoExit(new HReturn( + graph.addConstantBool(false, compiler))); + }, + null); + } + } + function.body.accept(this); + return closeFunction(); + } + + HGraph buildCheckedSetter(VariableElement field) { + openFunction(field, field.parseNode(compiler)); + HInstruction thisInstruction = localsHandler.readThis(); + // Use dynamic type because the type computed by the inferrer is + // narrowed to the type annotation. + HInstruction parameter = new HParameterValue(field, backend.dynamicType); + // Add the parameter as the last instruction of the entry block. + // If the method is intercepted, we want the actual receiver + // to be the first parameter. + graph.entry.addBefore(graph.entry.last, parameter); + HInstruction value = + potentiallyCheckType(parameter, field.type); + add(new HFieldSet(field, thisInstruction, value)); + return closeFunction(); + } + + HGraph buildLazyInitializer(VariableElement variable) { + ast.Node node = variable.parseNode(compiler); + openFunction(variable, node); + assert(variable.initializer != null); + visit(variable.initializer); + HInstruction value = pop(); + value = potentiallyCheckType(value, variable.type); + closeAndGotoExit(new HReturn(value)); + return closeFunction(); + } + + /** + * Returns the constructor body associated with the given constructor or + * creates a new constructor body, if none can be found. + * + * Returns [:null:] if the constructor does not have a body. + */ + ConstructorBodyElement getConstructorBody(FunctionElement constructor) { + assert(constructor.isGenerativeConstructor()); + assert(invariant(constructor, constructor.isImplementation)); + if (constructor.isSynthesized) return null; + ast.FunctionExpression node = constructor.parseNode(compiler); + // If we know the body doesn't have any code, we don't generate it. + if (!node.hasBody()) return null; + if (node.hasEmptyBody()) return null; + ClassElement classElement = constructor.getEnclosingClass(); + ConstructorBodyElement bodyElement; + classElement.forEachBackendMember((Element backendMember) { + if (backendMember.isGenerativeConstructorBody()) { + ConstructorBodyElement body = backendMember; + if (body.constructor == constructor) { + // TODO(kasperl): Find a way of stopping the iteration + // through the backend members. + bodyElement = backendMember; + } + } + }); + if (bodyElement == null) { + bodyElement = new ConstructorBodyElementX(constructor); + classElement.addBackendMember(bodyElement); + + if (constructor.isPatch) { + // Create origin body element for patched constructors. + bodyElement.origin = new ConstructorBodyElementX(constructor.origin); + bodyElement.origin.patch = bodyElement; + classElement.origin.addBackendMember(bodyElement.origin); + } + } + assert(bodyElement.isGenerativeConstructorBody()); + return bodyElement; + } + + HParameterValue addParameter(Element element, TypeMask type) { + assert(inliningStack.isEmpty); + HParameterValue result = new HParameterValue(element, type); + if (lastAddedParameter == null) { + graph.entry.addBefore(graph.entry.first, result); + } else { + graph.entry.addAfter(lastAddedParameter, result); + } + lastAddedParameter = result; + return result; + } + + /** + * This method sets up the local state of the builder for inlining [function]. + * The arguments of the function are inserted into the [localsHandler]. + * + * When inlining a function, [:return:] statements are not emitted as + * [HReturn] instructions. Instead, the value of a synthetic element is + * updated in the [localsHandler]. This function creates such an element and + * stores it in the [returnElement] field. + */ + void setupStateForInlining(FunctionElement function, + List compiledArguments) { + localsHandler = new LocalsHandler(this); + localsHandler.closureData = + compiler.closureToClassMapper.computeClosureToClassMapping( + function, function.parseNode(compiler), elements); + // TODO(kasperl): Bad smell. We shouldn't be constructing elements here. + returnElement = new VariableElementX.synthetic("result", + ElementKind.VARIABLE, function); + localsHandler.updateLocal(returnElement, + graph.addConstantNull(compiler)); + + inTryStatement = false; // TODO(lry): why? Document. + + int argumentIndex = 0; + if (function.isInstanceMember()) { + localsHandler.updateLocal(localsHandler.closureData.thisElement, + compiledArguments[argumentIndex++]); + } + + FunctionSignature signature = function.functionSignature; + signature.orderedForEachParameter((Element parameter) { + HInstruction argument = compiledArguments[argumentIndex++]; + localsHandler.updateLocal(parameter, argument); + }); + + ClassElement enclosing = function.getEnclosingClass(); + if ((function.isConstructor() || function.isGenerativeConstructorBody()) + && backend.classNeedsRti(enclosing)) { + enclosing.typeVariables.forEach((TypeVariableType typeVariable) { + HInstruction argument = compiledArguments[argumentIndex++]; + localsHandler.updateLocal(typeVariable.element, argument); + }); + } + assert(argumentIndex == compiledArguments.length); + + elements = compiler.enqueuer.resolution.getCachedElements(function); + assert(elements != null); + returnType = signature.type.returnType; + stack = []; + } + + void restoreState(AstInliningState state) { + localsHandler = state.oldLocalsHandler; + returnElement = state.oldReturnElement; + inTryStatement = state.inTryStatement; + elements = state.oldElements; + returnType = state.oldReturnType; + assert(stack.isEmpty); + stack = state.oldStack; + } + + /** + * Run this builder on the body of the [function] to be inlined. + */ + void visitInlinedFunction(FunctionElement function) { + potentiallyCheckInlinedParameterTypes(function); + if (function.isGenerativeConstructor()) { + buildFactory(function); + } else { + ast.FunctionExpression functionNode = function.parseNode(compiler); + functionNode.body.accept(this); + } + } + + + addInlinedInstantiation(DartType type) { + if (type != null) { + currentInlinedInstantiations.add(type); + } + } + + removeInlinedInstantiation(DartType type) { + if (type != null) { + currentInlinedInstantiations.removeLast(); + } + } + + bool providedArgumentsKnownToBeComplete(ast.Node currentNode) { + /* When inlining the iterator methods generated for a [:for-in:] loop, the + * [currentNode] is the [ForIn] tree. The compiler-generated iterator + * invocations are known to have fully specified argument lists, no default + * arguments are used. See invocations of [pushInvokeDynamic] in + * [visitForIn]. + */ + return currentNode.asForIn() != null; + } + + /** + * In checked mode, generate type tests for the parameters of the inlined + * function. + */ + void potentiallyCheckInlinedParameterTypes(FunctionElement function) { + FunctionSignature signature = function.functionSignature; + signature.orderedForEachParameter((ParameterElement parameter) { + HInstruction argument = localsHandler.readLocal(parameter); + potentiallyCheckType(argument, parameter.type); + }); + } + + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [constructors] must contain only implementation elements. + */ + void inlineSuperOrRedirect(FunctionElement callee, + List compiledArguments, + List constructors, + Map fieldValues, + FunctionElement caller) { + callee = callee.implementation; + compiler.withCurrentElement(callee, () { + constructors.add(callee); + ClassElement enclosingClass = callee.getEnclosingClass(); + if (backend.classNeedsRti(enclosingClass)) { + // If [enclosingClass] needs RTI, we have to give a value to its + // type parameters. + ClassElement currentClass = caller.getEnclosingClass(); + // For a super constructor call, the type is the supertype of + // [currentClass]. For a redirecting constructor, the type is + // the current type. [InterfaceType.asInstanceOf] takes care + // of both. + InterfaceType type = currentClass.thisType.asInstanceOf(enclosingClass); + Link typeVariables = enclosingClass.typeVariables; + type.typeArguments.forEach((DartType argument) { + localsHandler.updateLocal( + typeVariables.head.element, + analyzeTypeArgument(argument)); + typeVariables = typeVariables.tail; + }); + // If the supertype is a raw type, we need to set to null the + // type variables. + assert(typeVariables.isEmpty + || enclosingClass.typeVariables == typeVariables); + while (!typeVariables.isEmpty) { + localsHandler.updateLocal(typeVariables.head.element, + graph.addConstantNull(compiler)); + typeVariables = typeVariables.tail; + } + } + + // For redirecting constructors, the fields have already been + // initialized by the caller. + if (callee.getEnclosingClass() != caller.getEnclosingClass()) { + inlinedFrom(callee, () { + buildFieldInitializers(callee.enclosingElement.implementation, + fieldValues); + }); + } + + int index = 0; + FunctionSignature params = callee.functionSignature; + params.orderedForEachParameter((Element parameter) { + HInstruction argument = compiledArguments[index++]; + // Because we are inlining the initializer, we must update + // what was given as parameter. This will be used in case + // there is a parameter check expression in the initializer. + parameters[parameter] = argument; + localsHandler.updateLocal(parameter, argument); + // Don't forget to update the field, if the parameter is of the + // form [:this.x:]. + if (parameter.kind == ElementKind.FIELD_PARAMETER) { + FieldParameterElement fieldParameterElement = parameter; + fieldValues[fieldParameterElement.fieldElement] = argument; + } + }); + + // Build the initializers in the context of the new constructor. + TreeElements oldElements = elements; + elements = compiler.enqueuer.resolution.getCachedElements(callee); + ClosureClassMap oldClosureData = localsHandler.closureData; + ast.Node node = callee.parseNode(compiler); + ClosureClassMap newClosureData = + compiler.closureToClassMapper.computeClosureToClassMapping( + callee, node, elements); + localsHandler.closureData = newClosureData; + localsHandler.enterScope(node, callee); + buildInitializers(callee, constructors, fieldValues); + localsHandler.closureData = oldClosureData; + elements = oldElements; + }); + } + + /** + * Run through the initializers and inline all field initializers. Recursively + * inlines super initializers. + * + * The constructors of the inlined initializers is added to [constructors] + * with sub constructors having a lower index than super constructors. + * + * Invariant: The [constructor] and elements in [constructors] must all be + * implementation elements. + */ + void buildInitializers(FunctionElement constructor, + List constructors, + Map fieldValues) { + assert(invariant(constructor, constructor.isImplementation)); + if (constructor.isSynthesized) { + List arguments = []; + HInstruction compileArgument(Element element) { + return localsHandler.readLocal(element); + } + + Element target = constructor.targetConstructor.implementation; + Selector.addForwardingElementArgumentsToList( + constructor, + arguments, + target, + compileArgument, + handleConstantForOptionalParameter, + compiler); + inlineSuperOrRedirect( + target, + arguments, + constructors, + fieldValues, + constructor); + return; + } + ast.FunctionExpression functionNode = constructor.parseNode(compiler); + + bool foundSuperOrRedirect = false; + if (functionNode.initializers != null) { + Link initializers = functionNode.initializers.nodes; + for (Link link = initializers; !link.isEmpty; link = link.tail) { + assert(link.head is ast.Send); + if (link.head is !ast.SendSet) { + // A super initializer or constructor redirection. + foundSuperOrRedirect = true; + ast.Send call = link.head; + assert(ast.Initializers.isSuperConstructorCall(call) || + ast.Initializers.isConstructorRedirect(call)); + FunctionElement target = elements[call].implementation; + Selector selector = elements.getSelector(call); + Link arguments = call.arguments; + List compiledArguments = new List(); + inlinedFrom(constructor, () { + addStaticSendArgumentsToList(selector, + arguments, + target, + compiledArguments); + }); + inlineSuperOrRedirect(target, + compiledArguments, + constructors, + fieldValues, + constructor); + } else { + // A field initializer. + ast.SendSet init = link.head; + Link arguments = init.arguments; + assert(!arguments.isEmpty && arguments.tail.isEmpty); + inlinedFrom(constructor, () { + visit(arguments.head); + }); + fieldValues[elements[init]] = pop(); + } + } + } + + if (!foundSuperOrRedirect) { + // No super initializer found. Try to find the default constructor if + // the class is not Object. + ClassElement enclosingClass = constructor.getEnclosingClass(); + ClassElement superClass = enclosingClass.superclass; + if (!enclosingClass.isObject(compiler)) { + assert(superClass != null); + assert(superClass.resolutionState == STATE_DONE); + Selector selector = + new Selector.callDefaultConstructor(enclosingClass.getLibrary()); + // TODO(johnniwinther): Should we find injected constructors as well? + FunctionElement target = superClass.lookupConstructor(selector); + if (target == null) { + compiler.internalError(superClass, + "No default constructor available."); + } + List arguments = []; + selector.addArgumentsToList(const Link(), + arguments, + target.implementation, + null, + handleConstantForOptionalParameter, + compiler); + inlineSuperOrRedirect(target, + arguments, + constructors, + fieldValues, + constructor); + } + } + } + + /** + * Run through the fields of [cls] and add their potential + * initializers. + * + * Invariant: [classElement] must be an implementation element. + */ + void buildFieldInitializers(ClassElement classElement, + Map fieldValues) { + assert(invariant(classElement, classElement.isImplementation)); + classElement.forEachInstanceField( + (ClassElement enclosingClass, VariableElement member) { + compiler.withCurrentElement(member, () { + TreeElements definitions = member.treeElements; + ast.Node node = member.parseNode(compiler); + ast.Expression initializer = member.initializer; + if (initializer == null) { + // Unassigned fields of native classes are not initialized to + // prevent overwriting pre-initialized native properties. + if (!Elements.isNativeOrExtendsNative(classElement)) { + fieldValues[member] = graph.addConstantNull(compiler); + } + } else { + ast.Node right = initializer; + TreeElements savedElements = elements; + elements = definitions; + // In case the field initializer uses closures, run the + // closure to class mapper. + compiler.closureToClassMapper.computeClosureToClassMapping( + member, node, elements); + inlinedFrom(member, () => right.accept(this)); + elements = savedElements; + fieldValues[member] = pop(); + } + }); + }); + } + + /** + * Build the factory function corresponding to the constructor + * [functionElement]: + * - Initialize fields with the values of the field initializers of the + * current constructor and super constructors or constructors redirected + * to, starting from the current constructor. + * - Call the constructor bodies, starting from the constructor(s) in the + * super class(es). + */ + HGraph buildFactory(FunctionElement functionElement) { + functionElement = functionElement.implementation; + ClassElement classElement = + functionElement.getEnclosingClass().implementation; + bool isNativeUpgradeFactory = + Elements.isNativeOrExtendsNative(classElement); + ast.FunctionExpression function = functionElement.parseNode(compiler); + // Note that constructors (like any other static function) do not need + // to deal with optional arguments. It is the callers job to provide all + // arguments as if they were positional. + + if (inliningStack.isEmpty) { + // The initializer list could contain closures. + openFunction(functionElement, function); + } + + Map fieldValues = new Map(); + + // Compile the possible initialization code for local fields and + // super fields. + buildFieldInitializers(classElement, fieldValues); + + // Compile field-parameters such as [:this.x:]. + FunctionSignature params = functionElement.functionSignature; + params.orderedForEachParameter((Element element) { + if (element.kind == ElementKind.FIELD_PARAMETER) { + // If the [element] is a field-parameter then + // initialize the field element with its value. + FieldParameterElement fieldParameterElement = element; + HInstruction parameterValue = localsHandler.readLocal(element); + fieldValues[fieldParameterElement.fieldElement] = parameterValue; + } + }); + + // Analyze the constructor and all referenced constructors and collect + // initializers and constructor bodies. + List constructors = [functionElement]; + buildInitializers(functionElement, constructors, fieldValues); + + // Call the JavaScript constructor with the fields as argument. + List constructorArguments = []; + List fields = []; + + classElement.forEachInstanceField( + (ClassElement enclosingClass, VariableElement member) { + HInstruction value = fieldValues[member]; + if (value == null) { + // Uninitialized native fields are pre-initialized by the native + // implementation. + assert(isNativeUpgradeFactory); + } else { + fields.add(member); + DartType type = member.type; + if (enclosingClass.isMixinApplication) { + // TODO(johnniwinther): Add a member-like abstraction for fields + // that normalizes this. + type = type.substByContext( + enclosingClass.thisType.asInstanceOf( + member.enclosingElement)); + } + constructorArguments.add(potentiallyCheckType(value, type)); + } + }, + includeSuperAndInjectedMembers: true); + + InterfaceType type = classElement.thisType; + TypeMask ssaType = new TypeMask.nonNullExact(classElement.declaration); + List instantiatedTypes; + addInlinedInstantiation(type); + if (!currentInlinedInstantiations.isEmpty) { + instantiatedTypes = new List.from(currentInlinedInstantiations); + } + + HInstruction newObject; + if (!isNativeUpgradeFactory) { + newObject = new HForeignNew(classElement, + ssaType, + constructorArguments, + instantiatedTypes); + add(newObject); + } else { + // Bulk assign to the initialized fields. + newObject = graph.explicitReceiverParameter; + // Null guard ensures an error if we are being called from an explicit + // 'new' of the constructor instead of via an upgrade. It is optimized out + // if there are field initializers. + add(new HFieldGet( + null, newObject, backend.dynamicType, isAssignable: false)); + for (int i = 0; i < fields.length; i++) { + add(new HFieldSet(fields[i], newObject, constructorArguments[i])); + } + } + removeInlinedInstantiation(type); + // Create the runtime type information, if needed. + if (backend.classNeedsRti(classElement)) { + // Read the values of the type arguments and create a list to set on the + // newly create object. We can identify the case where the new list + // would be of the form: + // [getTypeArgumentByIndex(this, 0), .., getTypeArgumentByIndex(this, k)] + // and k is the number of type arguments of this. If this is the case, + // we can simply copy the list from this. + + // These locals are modified by [isIndexedTypeArgumentGet]. + HInstruction source; // The source of the type arguments. + bool allIndexed = true; + int expectedIndex = 0; + ClassElement contextClass; // The class of `this`. + Link typeVariables; // The list of 'remaining type variables' of `this`. + + /// Helper to identify instructions that read a type variable without + /// substitution (that is, directly use the index). These instructions + /// are of the form: + /// HInvokeStatic(getTypeArgumentByIndex, this, index) + /// + /// Return `true` if [instruction] is of that form and the index is the + /// next index in the sequence (held in [expectedIndex]). + bool isIndexedTypeArgumentGet(HInstruction instruction) { + if (instruction is! HInvokeStatic) return false; + HInvokeStatic invoke = instruction; + if (invoke.element != backend.getGetTypeArgumentByIndex()) { + return false; + } + HConstant index = invoke.inputs[1]; + HInstruction newSource = invoke.inputs[0]; + if (newSource is! HThis) { + return false; + } + if (source == null) { + // This is the first match. Extract the context class for the type + // variables and get the list of type variables to keep track of how + // many arguments we need to process. + source = newSource; + contextClass = source.sourceElement.getEnclosingClass(); + typeVariables = contextClass.typeVariables; + } else { + assert(source == newSource); + } + // If there are no more type variables, then there are more type + // arguments for the new object than the source has, and it can't be + // a copy. Otherwise remove one argument. + if (typeVariables.isEmpty) return false; + typeVariables = typeVariables.tail; + // Check that the index is the one we expect. + IntConstant constant = index.constant; + return constant.value == expectedIndex++; + } + + List typeArguments = []; + classElement.typeVariables.forEach((TypeVariableType typeVariable) { + HInstruction argument = localsHandler.readLocal(typeVariable.element); + if (allIndexed && !isIndexedTypeArgumentGet(argument)) { + allIndexed = false; + } + typeArguments.add(argument); + }); + + if (source != null && allIndexed && typeVariables.isEmpty) { + copyRuntimeTypeInfo(source, newObject); + } else { + newObject = + callSetRuntimeTypeInfo(classElement, typeArguments, newObject); + } + } + + // Generate calls to the constructor bodies. + HInstruction interceptor = null; + for (int index = constructors.length - 1; index >= 0; index--) { + FunctionElement constructor = constructors[index]; + assert(invariant(functionElement, constructor.isImplementation)); + ConstructorBodyElement body = getConstructorBody(constructor); + if (body == null) continue; + + List bodyCallInputs = []; + if (isNativeUpgradeFactory) { + if (interceptor == null) { + Constant constant = new InterceptorConstant(classElement.thisType); + interceptor = graph.addConstant(constant, compiler); + } + bodyCallInputs.add(interceptor); + } + bodyCallInputs.add(newObject); + TreeElements elements = + compiler.enqueuer.resolution.getCachedElements(constructor); + ast.Node node = constructor.parseNode(compiler); + ClosureClassMap parameterClosureData = + compiler.closureToClassMapper.getMappingForNestedFunction(node); + + FunctionSignature functionSignature = body.functionSignature; + // Provide the parameters to the generative constructor body. + functionSignature.orderedForEachParameter((parameter) { + // If [parameter] is boxed, it will be a field in the box passed as the + // last parameter. So no need to directly pass it. + if (!localsHandler.isBoxed(parameter)) { + bodyCallInputs.add(localsHandler.readLocal(parameter)); + } + }); + + ClassElement currentClass = constructor.getEnclosingClass(); + if (backend.classNeedsRti(currentClass)) { + // If [currentClass] needs RTI, we add the type variables as + // parameters of the generative constructor body. + currentClass.typeVariables.forEach((DartType argument) { + bodyCallInputs.add(localsHandler.readLocal(argument.element)); + }); + } + + // If there are locals that escape (ie mutated in closures), we + // pass the box to the constructor. + ClosureScope scopeData = parameterClosureData.capturingScopes[node]; + if (scopeData != null) { + bodyCallInputs.add(localsHandler.readLocal(scopeData.boxElement)); + } + + if (!isNativeUpgradeFactory && // TODO(13836): Fix inlining. + tryInlineMethod(body, null, bodyCallInputs, function)) { + pop(); + } else { + HInvokeConstructorBody invoke = new HInvokeConstructorBody( + body.declaration, bodyCallInputs, backend.nonNullType); + invoke.sideEffects = + compiler.world.getSideEffectsOfElement(constructor); + add(invoke); + } + } + if (inliningStack.isEmpty) { + closeAndGotoExit(new HReturn(newObject)); + return closeFunction(); + } else { + localsHandler.updateLocal(returnElement, newObject); + return null; + } + } + + /** + * Documentation wanted -- johnniwinther + * + * Invariant: [functionElement] must be the implementation element. + */ + void openFunction(Element element, ast.Node node) { + assert(invariant(element, element.isImplementation)); + HBasicBlock block = graph.addNewBlock(); + open(graph.entry); + + localsHandler.startFunction(element, node); + close(new HGoto()).addSuccessor(block); + + open(block); + + // Add the type parameters of the class as parameters of this method. This + // must be done before adding the normal parameters, because their types + // may contain references to type variables. + var enclosing = element.enclosingElement; + if ((element.isConstructor() || element.isGenerativeConstructorBody()) + && backend.classNeedsRti(enclosing)) { + enclosing.typeVariables.forEach((TypeVariableType typeVariable) { + HParameterValue param = addParameter( + typeVariable.element, backend.nonNullType); + localsHandler.directLocals[typeVariable.element] = param; + }); + } + + if (element is FunctionElement) { + FunctionElement functionElement = element; + FunctionSignature signature = functionElement.functionSignature; + + // Put the type checks in the first successor of the entry, + // because that is where the type guards will also be inserted. + // This way we ensure that a type guard will dominate the type + // check. + signature.orderedForEachParameter((ParameterElement parameterElement) { + if (element.isGenerativeConstructorBody()) { + ClosureScope scopeData = + localsHandler.closureData.capturingScopes[node]; + if (scopeData != null + && scopeData.capturedVariableMapping.containsKey( + parameterElement)) { + // The parameter will be a field in the box passed as the + // last parameter. So no need to have it. + return; + } + } + HInstruction newParameter = potentiallyCheckType( + localsHandler.directLocals[parameterElement], + parameterElement.type); + localsHandler.directLocals[parameterElement] = newParameter; + }); + + returnType = signature.type.returnType; + } else { + // Otherwise it is a lazy initializer which does not have parameters. + assert(element is VariableElement); + } + } + + HInstruction buildTypeConversion(HInstruction original, + DartType type, + int kind) { + if (type == null) return original; + type = type.unalias(compiler); + if (type.kind == TypeKind.INTERFACE && !type.treatAsRaw) { + TypeMask subtype = new TypeMask.subtype(type.element); + HInstruction representations = buildTypeArgumentRepresentations(type); + add(representations); + return new HTypeConversion.withTypeRepresentation(type, kind, subtype, + original, representations); + } else if (type.kind == TypeKind.TYPE_VARIABLE) { + TypeMask subtype = original.instructionType; + HInstruction typeVariable = addTypeVariableReference(type); + return new HTypeConversion.withTypeRepresentation(type, kind, subtype, + original, typeVariable); + } else if (type.kind == TypeKind.FUNCTION) { + String name = kind == HTypeConversion.CAST_TYPE_CHECK + ? '_asCheck' : '_assertCheck'; + + List arguments = [buildFunctionType(type), original]; + pushInvokeDynamic( + null, + new Selector.call(name, compiler.jsHelperLibrary, 1), + arguments); + + return new HTypeConversion(type, kind, original.instructionType, pop()); + } else { + return original.convertType(compiler, type, kind); + } + } + + HInstruction potentiallyCheckType(HInstruction original, DartType type, + { int kind: HTypeConversion.CHECKED_MODE_CHECK }) { + if (!compiler.enableTypeAssertions) return original; + HInstruction other = buildTypeConversion(original, type, kind); + if (other != original) add(other); + compiler.enqueuer.codegen.registerIsCheck(type, work.resolutionTree); + return other; + } + + void assertIsSubtype(ast.Node node, DartType subtype, DartType supertype, + String message) { + HInstruction subtypeInstruction = analyzeTypeArgument(subtype); + HInstruction supertypeInstruction = analyzeTypeArgument(supertype); + HInstruction messageInstruction = + graph.addConstantString(new ast.DartString.literal(message), compiler); + Element element = backend.getAssertIsSubtype(); + var inputs = [subtypeInstruction, supertypeInstruction, + messageInstruction]; + HInstruction assertIsSubtype = new HInvokeStatic( + element, inputs, subtypeInstruction.instructionType); + compiler.backend.registerTypeVariableBoundsSubtypeCheck(subtype, supertype); + add(assertIsSubtype); + } + + HGraph closeFunction() { + // TODO(kasperl): Make this goto an implicit return. + if (!isAborted()) closeAndGotoExit(new HGoto()); + graph.finalize(); + return graph; + } + + void push(HInstruction instruction) { + add(instruction); + stack.add(instruction); + } + + void pushWithPosition(HInstruction instruction, ast.Node node) { + push(attachPosition(instruction, node)); + } + + HInstruction pop() { + return stack.removeLast(); + } + + void dup() { + stack.add(stack.last); + } + + HInstruction popBoolified() { + HInstruction value = pop(); + if (compiler.enableTypeAssertions) { + return potentiallyCheckType( + value, + compiler.boolClass.rawType, + kind: HTypeConversion.BOOLEAN_CONVERSION_CHECK); + } + HInstruction result = new HBoolify(value, backend.boolType); + add(result); + return result; + } + + HInstruction attachPosition(HInstruction target, ast.Node node) { + if (node != null) { + target.sourcePosition = sourceFileLocationForBeginToken(node); + } + return target; + } + + SourceFileLocation sourceFileLocationForBeginToken(ast.Node node) => + sourceFileLocationForToken(node, node.getBeginToken()); + + SourceFileLocation sourceFileLocationForEndToken(ast.Node node) => + sourceFileLocationForToken(node, node.getEndToken()); + + SourceFileLocation sourceFileLocationForToken(ast.Node node, Token token) { + SourceFile sourceFile = currentSourceFile(); + SourceFileLocation location = + new TokenSourceFileLocation(sourceFile, token); + checkValidSourceFileLocation(location, sourceFile, token.charOffset); + return location; + } + + void visit(ast.Node node) { + if (node != null) node.accept(this); + } + + visitBlock(ast.Block node) { + assert(!isAborted()); + if (!isReachable) return; // This can only happen when inlining. + for (Link link = node.statements.nodes; + !link.isEmpty; + link = link.tail) { + visit(link.head); + if (!isReachable) { + // The block has been aborted by a return or a throw. + if (!stack.isEmpty) { + compiler.internalError(node, 'Non-empty instruction stack.'); + } + return; + } + } + assert(!current.isClosed()); + if (!stack.isEmpty) { + compiler.internalError(node, 'Non-empty instruction stack.'); + } + } + + visitClassNode(ast.ClassNode node) { + compiler.internalError(node, + 'SsaBuilder.visitClassNode should not be called.'); + } + + visitThrowExpression(ast.Expression expression) { + bool old = inThrowExpression; + try { + inThrowExpression = true; + visit(expression); + } finally { + inThrowExpression = old; + } + } + + visitExpressionStatement(ast.ExpressionStatement node) { + if (!isReachable) return; + ast.Throw throwExpression = node.expression.asThrow(); + if (throwExpression != null && inliningStack.isEmpty) { + visitThrowExpression(throwExpression.expression); + handleInTryStatement(); + closeAndGotoExit(new HThrow(pop())); + } else { + visit(node.expression); + pop(); + } + } + + /** + * Creates a new loop-header block. The previous [current] block + * is closed with an [HGoto] and replaced by the newly created block. + * Also notifies the locals handler that we're entering a loop. + */ + JumpHandler beginLoopHeader(ast.Node node) { + assert(!isAborted()); + HBasicBlock previousBlock = close(new HGoto()); + + JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: true); + HBasicBlock loopEntry = graph.addNewLoopHeaderBlock( + jumpHandler.target, + jumpHandler.labels()); + previousBlock.addSuccessor(loopEntry); + open(loopEntry); + + localsHandler.beginLoopHeader(loopEntry); + return jumpHandler; + } + + /** + * Ends the loop: + * - creates a new block and adds it as successor to the [branchBlock] and + * any blocks that end in break. + * - opens the new block (setting as [current]). + * - notifies the locals handler that we're exiting a loop. + * [savedLocals] are the locals from the end of the loop condition. + * [branchBlock] is the exit (branching) block of the condition. For the + * while and for loops this is at the top of the loop. For do-while it is + * the end of the body. It is null for degenerate do-while loops that have + * no back edge because they abort (throw/return/break in the body and have + * no continues). + */ + void endLoop(HBasicBlock loopEntry, + HBasicBlock branchBlock, + JumpHandler jumpHandler, + LocalsHandler savedLocals) { + HBasicBlock loopExitBlock = addNewBlock(); + List breakHandlers = []; + // Collect data for the successors and the phis at each break. + jumpHandler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) { + breakInstruction.block.addSuccessor(loopExitBlock); + breakHandlers.add(locals); + }); + // The exit block is a successor of the loop condition if it is reached. + // We don't add the successor in the case of a while/for loop that aborts + // because the caller of endLoop will be wiring up a special empty else + // block instead. + if (branchBlock != null) { + branchBlock.addSuccessor(loopExitBlock); + } + // Update the phis at the loop entry with the current values of locals. + localsHandler.endLoop(loopEntry); + + // Start generating code for the exit block. + open(loopExitBlock); + + // Create a new localsHandler for the loopExitBlock with the correct phis. + if (!breakHandlers.isEmpty) { + if (branchBlock != null) { + // Add the values of the locals at the end of the condition block to + // the phis. These are the values that flow to the exit if the + // condition fails. + breakHandlers.add(savedLocals); + } + localsHandler = savedLocals.mergeMultiple(breakHandlers, loopExitBlock); + } else { + localsHandler = savedLocals; + } + } + + HSubGraphBlockInformation wrapStatementGraph(SubGraph statements) { + if (statements == null) return null; + return new HSubGraphBlockInformation(statements); + } + + HSubExpressionBlockInformation wrapExpressionGraph(SubExpression expression) { + if (expression == null) return null; + return new HSubExpressionBlockInformation(expression); + } + + // For while loops, initializer and update are null. + // The condition function must return a boolean result. + // None of the functions must leave anything on the stack. + void handleLoop(ast.Node loop, + void initialize(), + HInstruction condition(), + void update(), + void body()) { + // Generate: + // + // loop-entry: + // if (!) goto loop-exit; + // + // + // goto loop-entry; + // loop-exit: + + localsHandler.startLoop(loop); + + // The initializer. + SubExpression initializerGraph = null; + HBasicBlock startBlock; + if (initialize != null) { + HBasicBlock initializerBlock = openNewBlock(); + startBlock = initializerBlock; + initialize(); + assert(!isAborted()); + initializerGraph = + new SubExpression(initializerBlock, current); + } + + loopNesting++; + JumpHandler jumpHandler = beginLoopHeader(loop); + HLoopInformation loopInfo = current.loopInformation; + HBasicBlock conditionBlock = current; + if (startBlock == null) startBlock = conditionBlock; + + HInstruction conditionInstruction = condition(); + HBasicBlock conditionExitBlock = + close(new HLoopBranch(conditionInstruction)); + SubExpression conditionExpression = + new SubExpression(conditionBlock, conditionExitBlock); + + // Save the values of the local variables at the end of the condition + // block. These are the values that will flow to the loop exit if the + // condition fails. + LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); + + // The body. + HBasicBlock beginBodyBlock = addNewBlock(); + conditionExitBlock.addSuccessor(beginBodyBlock); + open(beginBodyBlock); + + localsHandler.enterLoopBody(loop); + body(); + + SubGraph bodyGraph = new SubGraph(beginBodyBlock, lastOpenedBlock); + HBasicBlock bodyBlock = current; + if (current != null) close(new HGoto()); + + SubExpression updateGraph; + + bool loopIsDegenerate = !jumpHandler.hasAnyContinue() && bodyBlock == null; + if (!loopIsDegenerate) { + // Update. + // We create an update block, even when we are in a while loop. There the + // update block is the jump-target for continue statements. We could avoid + // the creation if there is no continue, but for now we always create it. + HBasicBlock updateBlock = addNewBlock(); + + List continueHandlers = []; + jumpHandler.forEachContinue((HContinue instruction, + LocalsHandler locals) { + instruction.block.addSuccessor(updateBlock); + continueHandlers.add(locals); + }); + + + if (bodyBlock != null) { + continueHandlers.add(localsHandler); + bodyBlock.addSuccessor(updateBlock); + } + + open(updateBlock); + localsHandler = + continueHandlers[0].mergeMultiple(continueHandlers, updateBlock); + + HLabeledBlockInformation labelInfo; + List labels = jumpHandler.labels(); + TargetElement target = elements[loop]; + if (!labels.isEmpty) { + beginBodyBlock.setBlockFlow( + new HLabeledBlockInformation( + new HSubGraphBlockInformation(bodyGraph), + jumpHandler.labels(), + isContinue: true), + updateBlock); + } else if (target != null && target.isContinueTarget) { + beginBodyBlock.setBlockFlow( + new HLabeledBlockInformation.implicit( + new HSubGraphBlockInformation(bodyGraph), + target, + isContinue: true), + updateBlock); + } + + localsHandler.enterLoopUpdates(loop); + + update(); + + HBasicBlock updateEndBlock = close(new HGoto()); + // The back-edge completing the cycle. + updateEndBlock.addSuccessor(conditionBlock); + updateGraph = new SubExpression(updateBlock, updateEndBlock); + + endLoop(conditionBlock, conditionExitBlock, jumpHandler, savedLocals); + + conditionBlock.postProcessLoopHeader(); + HLoopBlockInformation info = + new HLoopBlockInformation( + HLoopBlockInformation.loopType(loop), + wrapExpressionGraph(initializerGraph), + wrapExpressionGraph(conditionExpression), + wrapStatementGraph(bodyGraph), + wrapExpressionGraph(updateGraph), + conditionBlock.loopInformation.target, + conditionBlock.loopInformation.labels, + sourceFileLocationForBeginToken(loop), + sourceFileLocationForEndToken(loop)); + + startBlock.setBlockFlow(info, current); + loopInfo.loopBlockInformation = info; + } else { + // The body of the for/while loop always aborts, so there is no back edge. + // We turn the code into: + // if (condition) { + // body; + // } else { + // // We always create an empty else block to avoid critical edges. + // } + // + // If there is any break in the body, we attach a synthetic + // label to the if. + HBasicBlock elseBlock = addNewBlock(); + open(elseBlock); + close(new HGoto()); + // Pass the elseBlock as the branchBlock, because that's the block we go + // to just before leaving the 'loop'. + endLoop(conditionBlock, elseBlock, jumpHandler, savedLocals); + + SubGraph elseGraph = new SubGraph(elseBlock, elseBlock); + // Remove the loop information attached to the header. + conditionBlock.loopInformation = null; + + // Remove the [HLoopBranch] instruction and replace it with + // [HIf]. + HInstruction condition = conditionExitBlock.last.inputs[0]; + conditionExitBlock.addAtExit(new HIf(condition)); + conditionExitBlock.addSuccessor(elseBlock); + conditionExitBlock.remove(conditionExitBlock.last); + HIfBlockInformation info = + new HIfBlockInformation( + wrapExpressionGraph(conditionExpression), + wrapStatementGraph(bodyGraph), + wrapStatementGraph(elseGraph)); + + conditionExitBlock.setBlockFlow(info, current); + HIf ifBlock = conditionExitBlock.last; + ifBlock.blockInformation = conditionExitBlock.blockFlow; + + // If the body has any break, attach a synthesized label to the + // if block. + if (jumpHandler.hasAnyBreak()) { + TargetElement target = elements[loop]; + LabelElement label = target.addLabel(null, 'loop'); + label.setBreakTarget(); + SubGraph labelGraph = new SubGraph(conditionBlock, current); + HLabeledBlockInformation labelInfo = new HLabeledBlockInformation( + new HSubGraphBlockInformation(labelGraph), + [label]); + + conditionBlock.setBlockFlow(labelInfo, current); + + jumpHandler.forEachBreak((HBreak breakInstruction, _) { + HBasicBlock block = breakInstruction.block; + block.addAtExit(new HBreak.toLabel(label)); + block.remove(breakInstruction); + }); + } + } + jumpHandler.close(); + loopNesting--; + } + + visitFor(ast.For node) { + assert(isReachable); + assert(node.body != null); + void buildInitializer() { + if (node.initializer == null) return; + ast.Node initializer = node.initializer; + if (initializer != null) { + visit(initializer); + if (initializer.asExpression() != null) { + pop(); + } + } + } + HInstruction buildCondition() { + if (node.condition == null) { + return graph.addConstantBool(true, compiler); + } + visit(node.condition); + return popBoolified(); + } + void buildUpdate() { + for (ast.Expression expression in node.update) { + visit(expression); + assert(!isAborted()); + // The result of the update instruction isn't used, and can just + // be dropped. + HInstruction updateInstruction = pop(); + } + } + void buildBody() { + visit(node.body); + } + handleLoop(node, buildInitializer, buildCondition, buildUpdate, buildBody); + } + + visitWhile(ast.While node) { + assert(isReachable); + HInstruction buildCondition() { + visit(node.condition); + return popBoolified(); + } + handleLoop(node, + () {}, + buildCondition, + () {}, + () { visit(node.body); }); + } + + visitDoWhile(ast.DoWhile node) { + assert(isReachable); + LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); + localsHandler.startLoop(node); + loopNesting++; + JumpHandler jumpHandler = beginLoopHeader(node); + HLoopInformation loopInfo = current.loopInformation; + HBasicBlock loopEntryBlock = current; + HBasicBlock bodyEntryBlock = current; + TargetElement target = elements[node]; + bool hasContinues = target != null && target.isContinueTarget; + if (hasContinues) { + // Add extra block to hang labels on. + // It doesn't currently work if they are on the same block as the + // HLoopInfo. The handling of HLabeledBlockInformation will visit a + // SubGraph that starts at the same block again, so the HLoopInfo is + // either handled twice, or it's handled after the labeled block info, + // both of which generate the wrong code. + // Using a separate block is just a simple workaround. + bodyEntryBlock = openNewBlock(); + } + localsHandler.enterLoopBody(node); + visit(node.body); + + // If there are no continues we could avoid the creation of the condition + // block. This could also lead to a block having multiple entries and exits. + HBasicBlock bodyExitBlock; + bool isAbortingBody = false; + if (current != null) { + bodyExitBlock = close(new HGoto()); + } else { + isAbortingBody = true; + bodyExitBlock = lastOpenedBlock; + } + + SubExpression conditionExpression; + bool loopIsDegenerate = isAbortingBody && !hasContinues; + if (!loopIsDegenerate) { + HBasicBlock conditionBlock = addNewBlock(); + + List continueHandlers = []; + jumpHandler.forEachContinue((HContinue instruction, + LocalsHandler locals) { + instruction.block.addSuccessor(conditionBlock); + continueHandlers.add(locals); + }); + + if (!isAbortingBody) { + bodyExitBlock.addSuccessor(conditionBlock); + } + + if (!continueHandlers.isEmpty) { + if (!isAbortingBody) continueHandlers.add(localsHandler); + localsHandler = + savedLocals.mergeMultiple(continueHandlers, conditionBlock); + SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock); + List labels = jumpHandler.labels(); + HSubGraphBlockInformation bodyInfo = + new HSubGraphBlockInformation(bodyGraph); + HLabeledBlockInformation info; + if (!labels.isEmpty) { + info = new HLabeledBlockInformation(bodyInfo, labels, + isContinue: true); + } else { + info = new HLabeledBlockInformation.implicit(bodyInfo, target, + isContinue: true); + } + bodyEntryBlock.setBlockFlow(info, conditionBlock); + } + open(conditionBlock); + + visit(node.condition); + assert(!isAborted()); + HInstruction conditionInstruction = popBoolified(); + HBasicBlock conditionEndBlock = close( + new HLoopBranch(conditionInstruction, HLoopBranch.DO_WHILE_LOOP)); + + HBasicBlock avoidCriticalEdge = addNewBlock(); + conditionEndBlock.addSuccessor(avoidCriticalEdge); + open(avoidCriticalEdge); + close(new HGoto()); + avoidCriticalEdge.addSuccessor(loopEntryBlock); // The back-edge. + + conditionExpression = + new SubExpression(conditionBlock, conditionEndBlock); + + endLoop(loopEntryBlock, conditionEndBlock, jumpHandler, localsHandler); + + loopEntryBlock.postProcessLoopHeader(); + SubGraph bodyGraph = new SubGraph(loopEntryBlock, bodyExitBlock); + HLoopBlockInformation loopBlockInfo = + new HLoopBlockInformation( + HLoopBlockInformation.DO_WHILE_LOOP, + null, + wrapExpressionGraph(conditionExpression), + wrapStatementGraph(bodyGraph), + null, + loopEntryBlock.loopInformation.target, + loopEntryBlock.loopInformation.labels, + sourceFileLocationForBeginToken(node), + sourceFileLocationForEndToken(node)); + loopEntryBlock.setBlockFlow(loopBlockInfo, current); + loopInfo.loopBlockInformation = loopBlockInfo; + } else { + // Since the loop has no back edge, we remove the loop information on the + // header. + loopEntryBlock.loopInformation = null; + + if (jumpHandler.hasAnyBreak()) { + // Null branchBlock because the body of the do-while loop always aborts, + // so we never get to the condition. + endLoop(loopEntryBlock, null, jumpHandler, localsHandler); + + // Since the body of the loop has a break, we attach a synthesized label + // to the body. + SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock); + TargetElement target = elements[node]; + LabelElement label = target.addLabel(null, 'loop'); + label.setBreakTarget(); + HLabeledBlockInformation info = new HLabeledBlockInformation( + new HSubGraphBlockInformation(bodyGraph), [label]); + loopEntryBlock.setBlockFlow(info, current); + jumpHandler.forEachBreak((HBreak breakInstruction, _) { + HBasicBlock block = breakInstruction.block; + block.addAtExit(new HBreak.toLabel(label)); + block.remove(breakInstruction); + }); + } + } + jumpHandler.close(); + loopNesting--; + } + + visitFunctionExpression(ast.FunctionExpression node) { + ClosureClassMap nestedClosureData = + compiler.closureToClassMapper.getMappingForNestedFunction(node); + assert(nestedClosureData != null); + assert(nestedClosureData.closureClassElement != null); + ClassElement closureClassElement = + nestedClosureData.closureClassElement; + FunctionElement callElement = nestedClosureData.callElement; + // TODO(ahe): This should be registered in codegen, not here. + compiler.enqueuer.codegen.addToWorkList(callElement); + // TODO(ahe): This should be registered in codegen, not here. + compiler.enqueuer.codegen.registerInstantiatedClass( + closureClassElement, work.resolutionTree); + + List capturedVariables = []; + closureClassElement.forEachMember((_, Element member) { + // The backendMembers also contains the call method(s). We are only + // interested in the fields. + if (member.isField()) { + Element capturedLocal = nestedClosureData.capturedFieldMapping[member]; + assert(capturedLocal != null); + capturedVariables.add(localsHandler.readLocal(capturedLocal)); + } + }); + + TypeMask type = new TypeMask.nonNullExact(compiler.functionClass); + push(new HForeignNew(closureClassElement, type, capturedVariables)); + + Element methodElement = nestedClosureData.closureElement; + if (compiler.backend.methodNeedsRti(methodElement)) { + compiler.backend.registerGenericClosure( + methodElement, compiler.enqueuer.codegen, work.resolutionTree); + } + } + + visitFunctionDeclaration(ast.FunctionDeclaration node) { + assert(isReachable); + visit(node.function); + localsHandler.updateLocal(elements[node], pop()); + } + + visitIdentifier(ast.Identifier node) { + if (node.isThis()) { + stack.add(localsHandler.readThis()); + } else { + compiler.internalError(node, + "SsaFromAstMixin.visitIdentifier on non-this."); + } + } + + visitIf(ast.If node) { + assert(isReachable); + handleIf(node, + () => visit(node.condition), + () => visit(node.thenPart), + node.elsePart != null ? () => visit(node.elsePart) : null); + } + + void handleIf(ast.Node diagnosticNode, + void visitCondition(), void visitThen(), void visitElse()) { + SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, diagnosticNode); + branchBuilder.handleIf(visitCondition, visitThen, visitElse); + } + + void visitLogicalAndOr(ast.Send node, ast.Operator op) { + SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, node); + branchBuilder.handleLogicalAndOrWithLeftNode( + node.receiver, + () { visit(node.argumentsNode); }, + isAnd: ("&&" == op.source)); + } + + void visitLogicalNot(ast.Send node) { + assert(node.argumentsNode is ast.Prefix); + visit(node.receiver); + HNot not = new HNot(popBoolified(), backend.boolType); + pushWithPosition(not, node); + } + + void visitUnary(ast.Send node, ast.Operator op) { + assert(node.argumentsNode is ast.Prefix); + visit(node.receiver); + assert(!identical(op.token.kind, PLUS_TOKEN)); + HInstruction operand = pop(); + + // See if we can constant-fold right away. This avoids rewrites later on. + if (operand is HConstant) { + UnaryOperation operation = constantSystem.lookupUnary(op.source); + HConstant constant = operand; + Constant folded = operation.fold(constant.constant); + if (folded != null) { + stack.add(graph.addConstant(folded, compiler)); + return; + } + } + + pushInvokeDynamic(node, elements.getSelector(node), [operand]); + } + + void visitBinary(HInstruction left, + ast.Operator op, + HInstruction right, + Selector selector, + ast.Send send) { + switch (op.source) { + case "===": + pushWithPosition( + new HIdentity(left, right, null, backend.boolType), op); + return; + case "!==": + HIdentity eq = new HIdentity(left, right, null, backend.boolType); + add(eq); + pushWithPosition(new HNot(eq, backend.boolType), op); + return; + } + + pushInvokeDynamic(send, selector, [left, right], location: op); + if (op.source == '!=') { + pushWithPosition(new HNot(popBoolified(), backend.boolType), op); + } + } + + HInstruction generateInstanceSendReceiver(ast.Send send) { + assert(Elements.isInstanceSend(send, elements)); + if (send.receiver == null) { + return localsHandler.readThis(); + } + visit(send.receiver); + return pop(); + } + + String getTargetName(ErroneousElement error, [String prefix]) { + String result = error.name; + if (prefix != null) { + result = '$prefix $result'; + } + return result; + } + + /** + * Returns a set of interceptor classes that contain the given + * [selector]. + */ + void generateInstanceGetterWithCompiledReceiver(ast.Send send, + Selector selector, + HInstruction receiver) { + assert(Elements.isInstanceSend(send, elements)); + assert(selector.isGetter()); + pushInvokeDynamic(send, selector, [receiver]); + } + + void generateGetter(ast.Send send, Element element) { + if (element != null && element.isForeign(compiler)) { + visitForeignGetter(send); + } else if (Elements.isStaticOrTopLevelField(element)) { + Constant value; + if (element.isField() && !element.isAssignable()) { + // A static final or const. Get its constant value and inline it if + // the value can be compiled eagerly. + value = compiler.constantHandler.getConstantForVariable(element); + } + if (value != null) { + HConstant instruction = graph.addConstant(value, compiler); + stack.add(instruction); + // The inferrer may have found a better type than the constant + // handler in the case of lists, because the constant handler + // does not look at elements in the list. + TypeMask type = + TypeMaskFactory.inferredTypeForElement(element, compiler); + if (!type.containsAll(compiler) && !instruction.isConstantNull()) { + // TODO(13429): The inferrer should know that an element + // cannot be null. + instruction.instructionType = type.nonNullable(); + } + } else if (element.isField() && isLazilyInitialized(element)) { + HInstruction instruction = new HLazyStatic( + element, + TypeMaskFactory.inferredTypeForElement(element, compiler)); + push(instruction); + } else { + if (element.isGetter()) { + pushInvokeStatic(send, element, []); + } else { + // TODO(5346): Try to avoid the need for calling [declaration] before + // creating an [HStatic]. + HInstruction instruction = new HStatic( + element.declaration, + TypeMaskFactory.inferredTypeForElement(element, compiler)); + push(instruction); + } + } + } else if (Elements.isInstanceSend(send, elements)) { + HInstruction receiver = generateInstanceSendReceiver(send); + generateInstanceGetterWithCompiledReceiver( + send, elements.getSelector(send), receiver); + } else if (Elements.isStaticOrTopLevelFunction(element)) { + // TODO(5346): Try to avoid the need for calling [declaration] before + // creating an [HStatic]. + push(new HStatic(element.declaration, backend.nonNullType)); + // TODO(ahe): This should be registered in codegen. + compiler.enqueuer.codegen.registerGetOfStaticFunction( + element.declaration); + } else if (Elements.isErroneousElement(element)) { + // An erroneous element indicates an unresolved static getter. + generateThrowNoSuchMethod(send, + getTargetName(element, 'get'), + argumentNodes: const Link()); + } else { + stack.add(localsHandler.readLocal(element)); + } + } + + void generateInstanceSetterWithCompiledReceiver(ast.Send send, + HInstruction receiver, + HInstruction value, + {Selector selector, + ast.Node location}) { + assert(send == null || Elements.isInstanceSend(send, elements)); + if (selector == null) { + assert(send != null); + selector = elements.getSelector(send); + } + if (location == null) { + assert(send != null); + location = send; + } + assert(selector.isSetter()); + pushInvokeDynamic(location, selector, [receiver, value]); + pop(); + stack.add(value); + } + + void generateNonInstanceSetter(ast.SendSet send, + Element element, + HInstruction value, + {ast.Node location}) { + assert(send == null || !Elements.isInstanceSend(send, elements)); + if (location == null) { + assert(send != null); + location = send; + } + if (Elements.isStaticOrTopLevelField(element)) { + if (element.isSetter()) { + pushInvokeStatic(location, element, [value]); + pop(); + } else { + VariableElement field = element; + value = + potentiallyCheckType(value, field.type); + addWithPosition(new HStaticStore(element, value), location); + } + stack.add(value); + } else if (Elements.isErroneousElement(element)) { + List arguments = + send == null ? const [] : [value]; + // An erroneous element indicates an unresolved static setter. + generateThrowNoSuchMethod(location, getTargetName(element, 'set'), + argumentValues: arguments); + } else { + stack.add(value); + // If the value does not already have a name, give it here. + if (value.sourceElement == null) { + value.sourceElement = element; + } + HInstruction checked = + potentiallyCheckType(value, element.computeType(compiler)); + if (!identical(checked, value)) { + pop(); + stack.add(checked); + } + localsHandler.updateLocal(element, checked); + } + } + + HInstruction invokeInterceptor(HInstruction receiver) { + HInterceptor interceptor = new HInterceptor(receiver, backend.nonNullType); + add(interceptor); + return interceptor; + } + + HForeign createForeign(js.Expression code, + TypeMask type, + List inputs) { + return new HForeign(code, type, inputs); + } + + HLiteralList buildLiteralList(List inputs) { + return new HLiteralList(inputs, backend.extendableArrayType); + } + + // TODO(karlklose): change construction of the representations to be GVN'able + // (dartbug.com/7182). + HInstruction buildTypeArgumentRepresentations(DartType type) { + // Compute the representation of the type arguments, including access + // to the runtime type information for type variables as instructions. + if (type.kind == TypeKind.TYPE_VARIABLE) { + return buildLiteralList([addTypeVariableReference(type)]); + } else { + assert(type.element.isClass()); + InterfaceType interface = type; + List inputs = []; + bool first = true; + List templates = []; + for (DartType argument in interface.typeArguments) { + templates.add(rti.getTypeRepresentationWithHashes(argument, (variable) { + HInstruction runtimeType = addTypeVariableReference(variable); + inputs.add(runtimeType); + })); + } + String template = '[${templates.join(', ')}]'; + js.Expression code = js.js.parseForeignJS(template); + HInstruction representation = + createForeign(code, backend.readableArrayType, inputs); + return representation; + } + } + + visitOperatorSend(ast.Send node) { + ast.Operator op = node.selector; + if ("[]" == op.source) { + visitDynamicSend(node); + } else if ("&&" == op.source || + "||" == op.source) { + visitLogicalAndOr(node, op); + } else if ("!" == op.source) { + visitLogicalNot(node); + } else if (node.argumentsNode is ast.Prefix) { + visitUnary(node, op); + } else if ("is" == op.source) { + visitIsSend(node); + } else if ("as" == op.source) { + visit(node.receiver); + HInstruction expression = pop(); + DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); + if (type.kind == TypeKind.MALFORMED_TYPE) { + ErroneousElement element = type.element; + generateTypeError(node, element.message); + } else { + HInstruction converted = buildTypeConversion( + expression, type, HTypeConversion.CAST_TYPE_CHECK); + if (converted != expression) add(converted); + stack.add(converted); + } + } else { + visit(node.receiver); + visit(node.argumentsNode); + var right = pop(); + var left = pop(); + visitBinary(left, op, right, elements.getSelector(node), node); + } + } + + void visitIsSend(ast.Send node) { + visit(node.receiver); + HInstruction expression = pop(); + bool isNot = node.isIsNotCheck; + DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); + type = type.unalias(compiler); + HInstruction instruction = buildIsNode(node, type, expression); + if (isNot) { + add(instruction); + instruction = new HNot(instruction, backend.boolType); + } + push(instruction); + } + + HInstruction buildIsNode(ast.Node node, DartType type, HInstruction expression) { + type = type.unalias(compiler); + if (type.kind == TypeKind.FUNCTION) { + List arguments = [buildFunctionType(type), expression]; + pushInvokeDynamic( + node, new Selector.call('_isTest', compiler.jsHelperLibrary, 1), + arguments); + return new HIs.compound(type, expression, pop(), backend.boolType); + } else if (type.kind == TypeKind.TYPE_VARIABLE) { + HInstruction runtimeType = addTypeVariableReference(type); + Element helper = backend.getCheckSubtypeOfRuntimeType(); + List inputs = [expression, runtimeType]; + pushInvokeStatic(null, helper, inputs, backend.boolType); + HInstruction call = pop(); + return new HIs.variable(type, expression, call, backend.boolType); + } else if (RuntimeTypes.hasTypeArguments(type)) { + ClassElement element = type.element; + Element helper = backend.getCheckSubtype(); + HInstruction representations = + buildTypeArgumentRepresentations(type); + add(representations); + String operator = + backend.namer.operatorIs(backend.getImplementationClass(element)); + HInstruction isFieldName = addConstantString(operator); + HInstruction asFieldName = compiler.world.hasAnySubtype(element) + ? addConstantString(backend.namer.substitutionName(element)) + : graph.addConstantNull(compiler); + List inputs = [expression, + isFieldName, + representations, + asFieldName]; + pushInvokeStatic(node, helper, inputs, backend.boolType); + HInstruction call = pop(); + return new HIs.compound(type, expression, call, backend.boolType); + } else if (type.kind == TypeKind.MALFORMED_TYPE) { + ErroneousElement element = type.element; + generateTypeError(node, element.message); + HInstruction call = pop(); + return new HIs.compound(type, expression, call, backend.boolType); + } else { + if (backend.hasDirectCheckFor(type)) { + return new HIs.direct(type, expression, backend.boolType); + } + // TODO(johnniwinther): Avoid interceptor if unneeded. + return new HIs.raw( + type, expression, invokeInterceptor(expression), backend.boolType); + } + } + + HInstruction buildFunctionType(FunctionType type) { + type.accept(new TypeBuilder(), this); + return pop(); + } + + void addDynamicSendArgumentsToList(ast.Send node, List list) { + Selector selector = elements.getSelector(node); + if (selector.namedArgumentCount == 0) { + addGenericSendArgumentsToList(node.arguments, list); + } else { + // Visit positional arguments and add them to the list. + Link arguments = node.arguments; + int positionalArgumentCount = selector.positionalArgumentCount; + for (int i = 0; + i < positionalArgumentCount; + arguments = arguments.tail, i++) { + visit(arguments.head); + list.add(pop()); + } + + // Visit named arguments and add them into a temporary map. + Map instructions = + new Map(); + List namedArguments = selector.namedArguments; + int nameIndex = 0; + for (; !arguments.isEmpty; arguments = arguments.tail) { + visit(arguments.head); + instructions[namedArguments[nameIndex++]] = pop(); + } + + // Iterate through the named arguments to add them to the list + // of instructions, in an order that can be shared with + // selectors with the same named arguments. + List orderedNames = selector.getOrderedNamedArguments(); + for (String name in orderedNames) { + list.add(instructions[name]); + } + } + } + + /** + * Returns true if the arguments were compatible with the function signature. + * + * Invariant: [element] must be an implementation element. + */ + bool addStaticSendArgumentsToList(Selector selector, + Link arguments, + FunctionElement element, + List list) { + assert(invariant(element, element.isImplementation)); + + HInstruction compileArgument(ast.Node argument) { + visit(argument); + return pop(); + } + + return selector.addArgumentsToList(arguments, + list, + element, + compileArgument, + handleConstantForOptionalParameter, + compiler); + } + + void addGenericSendArgumentsToList(Link link, List list) { + for (; !link.isEmpty; link = link.tail) { + visit(link.head); + list.add(pop()); + } + } + + visitDynamicSend(ast.Send node) { + Selector selector = elements.getSelector(node); + + List inputs = []; + HInstruction receiver = generateInstanceSendReceiver(node); + inputs.add(receiver); + addDynamicSendArgumentsToList(node, inputs); + + pushInvokeDynamic(node, selector, inputs); + if (selector.isSetter() || selector.isIndexSet()) { + pop(); + stack.add(inputs.last); + } + } + + visitClosureSend(ast.Send node) { + Selector selector = elements.getSelector(node); + assert(node.receiver == null); + Element element = elements[node]; + HInstruction closureTarget; + if (element == null) { + visit(node.selector); + closureTarget = pop(); + } else { + assert(Elements.isLocal(element)); + closureTarget = localsHandler.readLocal(element); + } + var inputs = []; + inputs.add(closureTarget); + addDynamicSendArgumentsToList(node, inputs); + Selector closureSelector = new Selector.callClosureFrom(selector); + pushWithPosition( + new HInvokeClosure(closureSelector, inputs, backend.dynamicType), + node); + } + + void handleForeignJs(ast.Send node) { + Link link = node.arguments; + // If the invoke is on foreign code, don't visit the first + // argument, which is the type, and the second argument, + // which is the foreign code. + if (link.isEmpty || link.tail.isEmpty) { + compiler.internalError(node.argumentsNode, + 'At least two arguments expected.'); + } + native.NativeBehavior nativeBehavior = + compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node); + + List inputs = []; + addGenericSendArgumentsToList(link.tail.tail, inputs); + + TypeMask ssaType = + TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler); + push(new HForeign(nativeBehavior.codeAst, ssaType, inputs, + effects: nativeBehavior.sideEffects, + nativeBehavior: nativeBehavior)); + return; + } + + void handleForeignJsCurrentIsolateContext(ast.Send node) { + if (!node.arguments.isEmpty) { + compiler.internalError(node, + 'Too many arguments to JS_CURRENT_ISOLATE_CONTEXT.'); + } + + if (!compiler.hasIsolateSupport()) { + // If the isolate library is not used, we just generate code + // to fetch the current isolate. + String name = backend.namer.currentIsolate; + push(new HForeign(new js.LiteralString(name), + backend.dynamicType, + [])); + } else { + // Call a helper method from the isolate library. The isolate + // library uses its own isolate structure, that encapsulates + // Leg's isolate. + Element element = compiler.isolateHelperLibrary.find('_currentIsolate'); + if (element == null) { + compiler.internalError(node, + 'Isolate library and compiler mismatch.'); + } + pushInvokeStatic(null, element, [], backend.dynamicType); + } + } + + void handleForeingJsGetFlag(ast.Send node) { + List arguments = node.arguments.toList(); + ast.Node argument; + switch (arguments.length) { + case 0: + compiler.reportError( + node, MessageKind.GENERIC, + {'text': 'Error: Expected one argument to JS_GET_FLAG.'}); + return; + case 1: + argument = arguments[0]; + break; + default: + for (int i = 1; i < arguments.length; i++) { + compiler.reportError( + arguments[i], MessageKind.GENERIC, + {'text': 'Error: Extra argument to JS_GET_FLAG.'}); + } + return; + } + ast.LiteralString string = argument.asLiteralString(); + if (string == null) { + compiler.reportError( + argument, MessageKind.GENERIC, + {'text': 'Error: Expected a literal string.'}); + } + String name = string.dartString.slowToString(); + bool value = false; + if (name == 'MUST_RETAIN_METADATA') { + value = backend.mustRetainMetadata; + } else { + compiler.reportError( + node, MessageKind.GENERIC, + {'text': 'Error: Unknown internal flag "$name".'}); + } + stack.add(graph.addConstantBool(value, compiler)); + } + + void handleForeignJsGetName(ast.Send node) { + List arguments = node.arguments.toList(); + ast.Node argument; + switch (arguments.length) { + case 0: + compiler.reportError( + node, MessageKind.GENERIC, + {'text': 'Error: Expected one argument to JS_GET_NAME.'}); + return; + case 1: + argument = arguments[0]; + break; + default: + for (int i = 1; i < arguments.length; i++) { + compiler.reportError( + arguments[i], MessageKind.GENERIC, + {'text': 'Error: Extra argument to JS_GET_NAME.'}); + } + return; + } + ast.LiteralString string = argument.asLiteralString(); + if (string == null) { + compiler.reportError( + argument, MessageKind.GENERIC, + {'text': 'Error: Expected a literal string.'}); + } + stack.add( + addConstantString( + backend.namer.getNameForJsGetName( + argument, string.dartString.slowToString()))); + } + + void handleJsInterceptorConstant(ast.Send node) { + // Single argument must be a TypeConstant which is converted into a + // InterceptorConstant. + if (!node.arguments.isEmpty && node.arguments.tail.isEmpty) { + ast.Node argument = node.arguments.head; + visit(argument); + HInstruction argumentInstruction = pop(); + if (argumentInstruction is HConstant) { + Constant argumentConstant = argumentInstruction.constant; + if (argumentConstant is TypeConstant) { + Constant constant = + new InterceptorConstant(argumentConstant.representedType); + HInstruction instruction = graph.addConstant(constant, compiler); + stack.add(instruction); + return; + } + } + } + compiler.reportError(node, + MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT); + stack.add(graph.addConstantNull(compiler)); + } + + void handleForeignJsCallInIsolate(ast.Send node) { + Link link = node.arguments; + if (!compiler.hasIsolateSupport()) { + // If the isolate library is not used, we just invoke the + // closure. + visit(link.tail.head); + Selector selector = new Selector.callClosure(0); + push(new HInvokeClosure(selector, + [pop()], + backend.dynamicType)); + } else { + // Call a helper method from the isolate library. + Element element = compiler.isolateHelperLibrary.find('_callInIsolate'); + if (element == null) { + compiler.internalError(node, + 'Isolate library and compiler mismatch.'); + } + List inputs = []; + addGenericSendArgumentsToList(link, inputs); + pushInvokeStatic(node, element, inputs, backend.dynamicType); + } + } + + FunctionSignature handleForeignRawFunctionRef(ast.Send node, String name) { + if (node.arguments.isEmpty || !node.arguments.tail.isEmpty) { + compiler.internalError(node.argumentsNode, + '"$name" requires exactly one argument.'); + } + ast.Node closure = node.arguments.head; + Element element = elements[closure]; + if (!Elements.isStaticOrTopLevelFunction(element)) { + compiler.internalError(closure, + '"$name" requires a static or top-level method.'); + } + FunctionElement function = element; + // TODO(johnniwinther): Try to eliminate the need to distinguish declaration + // and implementation signatures. Currently it is need because the + // signatures have different elements for parameters. + FunctionElement implementation = function.implementation; + FunctionSignature params = implementation.functionSignature; + if (params.optionalParameterCount != 0) { + compiler.internalError(closure, + '"$name" does not handle closure with optional parameters.'); + } + + compiler.enqueuer.codegen.registerStaticUse(element); + push(new HForeign(backend.namer.elementAccess(element), + backend.dynamicType, + [])); + return params; + } + + void handleForeignDartClosureToJs(ast.Send node, String name) { + // TODO(ahe): This implements DART_CLOSURE_TO_JS and should probably take + // care to wrap the closure in another closure that saves the current + // isolate. + handleForeignRawFunctionRef(node, name); + } + + void handleForeignSetCurrentIsolate(ast.Send node) { + if (node.arguments.isEmpty || !node.arguments.tail.isEmpty) { + compiler.internalError(node.argumentsNode, + 'Exactly one argument required.'); + } + visit(node.arguments.head); + String isolateName = backend.namer.currentIsolate; + SideEffects sideEffects = new SideEffects.empty(); + sideEffects.setAllSideEffects(); + push(new HForeign(js.js("$isolateName = #"), + backend.dynamicType, + [pop()], + effects: sideEffects)); + } + + void handleForeignCreateIsolate(ast.Send node) { + if (!node.arguments.isEmpty) { + compiler.internalError(node.argumentsNode, 'Too many arguments.'); + } + String constructorName = backend.namer.isolateName; + push(new HForeign(js.js("new $constructorName()"), + backend.dynamicType, + [])); + } + + void handleForeignDartObjectJsConstructorFunction(ast.Send node) { + if (!node.arguments.isEmpty) { + compiler.internalError(node.argumentsNode, 'Too many arguments.'); + } + String jsClassReference = backend.namer.isolateAccess(compiler.objectClass); + push(new HForeign(new js.LiteralString(jsClassReference), + backend.dynamicType, + [])); + } + + void handleForeignJsCurrentIsolate(ast.Send node) { + if (!node.arguments.isEmpty) { + compiler.internalError(node.argumentsNode, 'Too many arguments.'); + } + push(new HForeign(new js.LiteralString(backend.namer.currentIsolate), + backend.dynamicType, + [])); + } + + visitForeignSend(ast.Send node) { + Selector selector = elements.getSelector(node); + String name = selector.name; + if (name == 'JS') { + handleForeignJs(node); + } else if (name == 'JS_CURRENT_ISOLATE_CONTEXT') { + handleForeignJsCurrentIsolateContext(node); + } else if (name == 'JS_CALL_IN_ISOLATE') { + handleForeignJsCallInIsolate(node); + } else if (name == 'DART_CLOSURE_TO_JS') { + handleForeignDartClosureToJs(node, 'DART_CLOSURE_TO_JS'); + } else if (name == 'RAW_DART_FUNCTION_REF') { + handleForeignRawFunctionRef(node, 'RAW_DART_FUNCTION_REF'); + } else if (name == 'JS_SET_CURRENT_ISOLATE') { + handleForeignSetCurrentIsolate(node); + } else if (name == 'JS_CREATE_ISOLATE') { + handleForeignCreateIsolate(node); + } else if (name == 'JS_OPERATOR_IS_PREFIX') { + stack.add(addConstantString(backend.namer.operatorIsPrefix())); + } else if (name == 'JS_OBJECT_CLASS_NAME') { + String name = backend.namer.getRuntimeTypeName(compiler.objectClass); + stack.add(addConstantString(name)); + } else if (name == 'JS_NULL_CLASS_NAME') { + String name = backend.namer.getRuntimeTypeName(compiler.nullClass); + stack.add(addConstantString(name)); + } else if (name == 'JS_FUNCTION_CLASS_NAME') { + String name = backend.namer.getRuntimeTypeName(compiler.functionClass); + stack.add(addConstantString(name)); + } else if (name == 'JS_OPERATOR_AS_PREFIX') { + stack.add(addConstantString(backend.namer.operatorAsPrefix())); + } else if (name == 'JS_SIGNATURE_NAME') { + stack.add(addConstantString(backend.namer.operatorSignature())); + } else if (name == 'JS_FUNCTION_TYPE_TAG') { + stack.add(addConstantString(backend.namer.functionTypeTag())); + } else if (name == 'JS_FUNCTION_TYPE_VOID_RETURN_TAG') { + stack.add(addConstantString(backend.namer.functionTypeVoidReturnTag())); + } else if (name == 'JS_FUNCTION_TYPE_RETURN_TYPE_TAG') { + stack.add(addConstantString(backend.namer.functionTypeReturnTypeTag())); + } else if (name == + 'JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG') { + stack.add(addConstantString( + backend.namer.functionTypeRequiredParametersTag())); + } else if (name == + 'JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG') { + stack.add(addConstantString( + backend.namer.functionTypeOptionalParametersTag())); + } else if (name == + 'JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG') { + stack.add(addConstantString( + backend.namer.functionTypeNamedParametersTag())); + } else if (name == 'JS_DART_OBJECT_CONSTRUCTOR') { + handleForeignDartObjectJsConstructorFunction(node); + } else if (name == 'JS_IS_INDEXABLE_FIELD_NAME') { + Element element = compiler.findHelper( + 'JavaScriptIndexingBehavior'); + stack.add(addConstantString(backend.namer.operatorIs(element))); + } else if (name == 'JS_CURRENT_ISOLATE') { + handleForeignJsCurrentIsolate(node); + } else if (name == 'JS_GET_NAME') { + handleForeignJsGetName(node); + } else if (name == 'JS_GET_FLAG') { + handleForeingJsGetFlag(node); + } else if (name == 'JS_EFFECT') { + stack.add(graph.addConstantNull(compiler)); + } else if (name == 'JS_INTERCEPTOR_CONSTANT') { + handleJsInterceptorConstant(node); + } else { + throw "Unknown foreign: ${selector}"; + } + } + + visitForeignGetter(ast.Send node) { + Element element = elements[node]; + // Until now we only handle these as getters. + invariant(node, element.isDeferredLoaderGetter()); + FunctionElement deferredLoader = element; + Element loadFunction = compiler.loadLibraryFunction; + PrefixElement prefixElement = deferredLoader.enclosingElement; + String loadId = compiler.deferredLoadTask + .importDeferName[prefixElement.deferredImport]; + var inputs = [graph.addConstantString( + new ast.DartString.literal(loadId), compiler)]; + push(new HInvokeStatic(loadFunction, inputs, backend.nonNullType, + targetCanThrow: false)); + } + + generateSuperNoSuchMethodSend(ast.Send node, + Selector selector, + List arguments) { + String name = selector.name; + + ClassElement cls = currentNonClosureClass; + Element element = cls.lookupSuperMember(Compiler.NO_SUCH_METHOD); + if (compiler.enabledInvokeOn + && element.enclosingElement.declaration != compiler.objectClass) { + // Register the call as dynamic if [noSuchMethod] on the super + // class is _not_ the default implementation from [Object], in + // case the [noSuchMethod] implementation calls + // [JSInvocationMirror._invokeOn]. + compiler.enqueuer.codegen.registerSelectorUse(selector.asUntyped); + } + String publicName = name; + if (selector.isSetter()) publicName += '='; + + Constant nameConstant = constantSystem.createString( + new ast.DartString.literal(publicName)); + + String internalName = backend.namer.invocationName(selector); + Constant internalNameConstant = + constantSystem.createString(new ast.DartString.literal(internalName)); + + Element createInvocationMirror = backend.getCreateInvocationMirror(); + var argumentsInstruction = buildLiteralList(arguments); + add(argumentsInstruction); + + var argumentNames = new List(); + for (String argumentName in selector.namedArguments) { + Constant argumentNameConstant = + constantSystem.createString(new ast.DartString.literal(argumentName)); + argumentNames.add(graph.addConstant(argumentNameConstant, compiler)); + } + var argumentNamesInstruction = buildLiteralList(argumentNames); + add(argumentNamesInstruction); + + Constant kindConstant = + constantSystem.createInt(selector.invocationMirrorKind); + + pushInvokeStatic(null, + createInvocationMirror, + [graph.addConstant(nameConstant, compiler), + graph.addConstant(internalNameConstant, compiler), + graph.addConstant(kindConstant, compiler), + argumentsInstruction, + argumentNamesInstruction], + backend.dynamicType); + + var inputs = [pop()]; + push(buildInvokeSuper(compiler.noSuchMethodSelector, element, inputs)); + } + + visitSuperSend(ast.Send node) { + Selector selector = elements.getSelector(node); + Element element = elements[node]; + if (Elements.isUnresolved(element)) { + List arguments = []; + if (!node.isPropertyAccess) { + addGenericSendArgumentsToList(node.arguments, arguments); + } + return generateSuperNoSuchMethodSend(node, selector, arguments); + } + List inputs = []; + if (node.isPropertyAccess) { + push(buildInvokeSuper(selector, element, inputs)); + } else if (element.isFunction() || element.isGenerativeConstructor()) { + if (selector.applies(element, compiler)) { + // TODO(5347): Try to avoid the need for calling [implementation] before + // calling [addStaticSendArgumentsToList]. + FunctionElement function = element.implementation; + bool succeeded = addStaticSendArgumentsToList(selector, node.arguments, + function, inputs); + assert(succeeded); + push(buildInvokeSuper(selector, element, inputs)); + } else if (element.isGenerativeConstructor()) { + generateWrongArgumentCountError(node, element, node.arguments); + } else { + addGenericSendArgumentsToList(node.arguments, inputs); + generateSuperNoSuchMethodSend(node, selector, inputs); + } + } else { + HInstruction target = buildInvokeSuper(selector, element, inputs); + add(target); + inputs = [target]; + addDynamicSendArgumentsToList(node, inputs); + Selector closureSelector = new Selector.callClosureFrom(selector); + push(new HInvokeClosure(closureSelector, inputs, backend.dynamicType)); + } + } + + bool needsSubstitutionForTypeVariableAccess(ClassElement cls) { + if (compiler.world.isUsedAsMixin(cls)) return true; + + Set subclasses = compiler.world.subclassesOf(cls); + return subclasses != null && subclasses.any((ClassElement subclass) { + return !rti.isTrivialSubstitution(subclass, cls); + }); + } + + /** + * Generate code to extract the type arguments from the object, substitute + * them as an instance of the type we are testing against (if necessary), and + * extract the type argument by the index of the variable in the list of type + * variables for that class. + */ + HInstruction readTypeVariable(ClassElement cls, + TypeVariableElement variable) { + assert(sourceElement.isInstanceMember()); + + HInstruction target = localsHandler.readThis(); + HConstant index = graph.addConstantInt( + RuntimeTypes.getTypeVariableIndex(variable), + compiler); + + if (needsSubstitutionForTypeVariableAccess(cls)) { + // TODO(ahe): Creating a string here is unfortunate. It is slow (due to + // string concatenation in the implementation), and may prevent + // segmentation of '$'. + String substitutionNameString = backend.namer.getNameForRti(cls); + HInstruction substitutionName = graph.addConstantString( + new ast.LiteralDartString(substitutionNameString), compiler); + pushInvokeStatic(null, + backend.getGetRuntimeTypeArgument(), + [target, substitutionName, index], + backend.dynamicType); + } else { + pushInvokeStatic(null, backend.getGetTypeArgumentByIndex(), + [target, index], + backend.dynamicType); + } + return pop(); + } + + // TODO(karlklose): this is needed to avoid a bug where the resolved type is + // not stored on a type annotation in the closure translator. Remove when + // fixed. + bool hasDirectLocal(Element element) { + return !localsHandler.isAccessedDirectly(element) || + localsHandler.directLocals[element] != null; + } + + /** + * Helper to create an instruction that gets the value of a type variable. + */ + HInstruction addTypeVariableReference(TypeVariableType type) { + Element member = sourceElement; + bool isClosure = member.enclosingElement.isClosure(); + if (isClosure) { + ClosureClassElement closureClass = member.enclosingElement; + member = closureClass.methodElement; + member = member.getOutermostEnclosingMemberOrTopLevel(); + } + bool isInConstructorContext = member.isConstructor() || + member.isGenerativeConstructorBody(); + if (isClosure) { + if (member.isFactoryConstructor() || + (isInConstructorContext && hasDirectLocal(type.element))) { + // The type variable is used from a closure in a factory constructor. + // The value of the type argument is stored as a local on the closure + // itself. + return localsHandler.readLocal(type.element); + } else if (member.isFunction() || + member.isGetter() || + member.isSetter() || + isInConstructorContext) { + // The type variable is stored on the "enclosing object" and needs to be + // accessed using the this-reference in the closure. + return readTypeVariable(member.getEnclosingClass(), type.element); + } else { + assert(member.isField()); + // The type variable is stored in a parameter of the method. + return localsHandler.readLocal(type.element); + } + } else if (isInConstructorContext || + // When [member] is a field, we can be either + // generating a checked setter or inlining its + // initializer in a constructor. An initializer is + // never built standalone, so [isBuildingFor] will + // always return true when seeing one. + (member.isField() && !isBuildingFor(member))) { + // The type variable is stored in a parameter of the method. + return localsHandler.readLocal(type.element); + } else if (member.isInstanceMember()) { + // The type variable is stored on the object. + return readTypeVariable(member.getEnclosingClass(), + type.element); + } else { + // TODO(ngeoffray): Match the VM behavior and throw an + // exception at runtime. + compiler.internalError(type.element, + 'Unimplemented unresolved type variable.'); + return null; + } + } + + HInstruction analyzeTypeArgument(DartType argument) { + if (argument.treatAsDynamic) { + // Represent [dynamic] as [null]. + return graph.addConstantNull(compiler); + } + + if (argument.kind == TypeKind.TYPE_VARIABLE) { + return addTypeVariableReference(argument); + } + + List inputs = []; + + String template = rti.getTypeRepresentationWithHashes(argument, (variable) { + inputs.add(addTypeVariableReference(variable)); + }); + + js.Expression code = js.js.parseForeignJS(template); + HInstruction result = createForeign(code, backend.stringType, inputs); + add(result); + return result; + } + + HInstruction handleListConstructor(InterfaceType type, + ast.Node currentNode, + HInstruction newObject) { + if (!backend.classNeedsRti(type.element) || type.treatAsRaw) { + return newObject; + } + List inputs = []; + type.typeArguments.forEach((DartType argument) { + inputs.add(analyzeTypeArgument(argument)); + }); + // TODO(15489): Register at codegen. + compiler.enqueuer.codegen.registerInstantiatedType(type, elements); + return callSetRuntimeTypeInfo(type.element, inputs, newObject); + } + + void copyRuntimeTypeInfo(HInstruction source, HInstruction target) { + Element copyHelper = backend.getCopyTypeArguments(); + pushInvokeStatic(null, copyHelper, [source, target]); + pop(); + } + + HInstruction callSetRuntimeTypeInfo(ClassElement element, + List rtiInputs, + HInstruction newObject) { + if (!backend.classNeedsRti(element) || element.typeVariables.isEmpty) { + return newObject; + } + + HInstruction typeInfo = buildLiteralList(rtiInputs); + add(typeInfo); + + // Set the runtime type information on the object. + Element typeInfoSetterElement = backend.getSetRuntimeTypeInfo(); + pushInvokeStatic( + null, + typeInfoSetterElement, + [newObject, typeInfo], + backend.dynamicType); + + // The new object will now be referenced through the + // `setRuntimeTypeInfo` call. We therefore set the type of that + // instruction to be of the object's type. + assert(stack.last is HInvokeStatic || stack.last == newObject); + stack.last.instructionType = newObject.instructionType; + return pop(); + } + + handleNewSend(ast.NewExpression node) { + ast.Send send = node.send; + bool isFixedList = false; + bool isFixedListConstructorCall = + Elements.isFixedListConstructorCall(elements[send], send, compiler); + bool isGrowableListConstructorCall = + Elements.isGrowableListConstructorCall(elements[send], send, compiler); + + TypeMask computeType(element) { + Element originalElement = elements[send]; + if (isFixedListConstructorCall + || Elements.isFilledListConstructorCall( + originalElement, send, compiler)) { + isFixedList = true; + TypeMask inferred = + TypeMaskFactory.inferredForNode(sourceElement, send, compiler); + return inferred.containsAll(compiler) + ? backend.fixedArrayType + : inferred; + } else if (isGrowableListConstructorCall) { + TypeMask inferred = + TypeMaskFactory.inferredForNode(sourceElement, send, compiler); + return inferred.containsAll(compiler) + ? backend.extendableArrayType + : inferred; + } else if (Elements.isConstructorOfTypedArraySubclass( + originalElement, compiler)) { + isFixedList = true; + TypeMask inferred = + TypeMaskFactory.inferredForNode(sourceElement, send, compiler); + ClassElement cls = element.getEnclosingClass(); + assert(cls.thisType.element.isNative()); + return inferred.containsAll(compiler) + ? new TypeMask.nonNullExact(cls.thisType.element) + : inferred; + } else if (element.isGenerativeConstructor()) { + ClassElement cls = element.getEnclosingClass(); + return new TypeMask.nonNullExact(cls.thisType.element); + } else { + return TypeMaskFactory.inferredReturnTypeForElement( + originalElement, compiler); + } + } + + Element constructor = elements[send]; + Selector selector = elements.getSelector(send); + FunctionElement functionElement = constructor; + constructor = functionElement.redirectionTarget; + + final bool isSymbolConstructor = + functionElement == compiler.symbolConstructor; + final bool isJSArrayTypedConstructor = + functionElement == backend.jsArrayTypedConstructor; + + if (isSymbolConstructor) { + constructor = compiler.symbolValidatedConstructor; + assert(invariant(send, constructor != null, + message: 'Constructor Symbol.validated is missing')); + selector = compiler.symbolValidatedConstructorSelector; + assert(invariant(send, selector != null, + message: 'Constructor Symbol.validated is missing')); + } + + bool isRedirected = functionElement.isRedirectingFactory; + InterfaceType type = elements.getType(node); + InterfaceType expectedType = functionElement.computeTargetType(type); + + if (checkTypeVariableBounds(node, type)) return; + + var inputs = []; + if (constructor.isGenerativeConstructor() && + Elements.isNativeOrExtendsNative(constructor.getEnclosingClass())) { + // Native class generative constructors take a pre-constructed object. + inputs.add(graph.addConstantNull(compiler)); + } + // TODO(5347): Try to avoid the need for calling [implementation] before + // calling [addStaticSendArgumentsToList]. + bool succeeded = addStaticSendArgumentsToList(selector, send.arguments, + constructor.implementation, + inputs); + if (!succeeded) { + generateWrongArgumentCountError(send, constructor, send.arguments); + return; + } + + if (constructor.isFactoryConstructor() && + !expectedType.typeArguments.isEmpty) { + compiler.enqueuer.codegen.registerFactoryWithTypeArguments(elements); + } + + TypeMask elementType = computeType(constructor); + if (isFixedListConstructorCall) { + if (!inputs[0].isNumber(compiler)) { + HTypeConversion conversion = new HTypeConversion( + null, HTypeConversion.ARGUMENT_TYPE_CHECK, backend.numType, + inputs[0], null); + add(conversion); + inputs[0] = conversion; + } + js.Expression code = js.js.parseForeignJS('Array(#)'); + var behavior = new native.NativeBehavior(); + behavior.typesReturned.add(expectedType); + // The allocation can throw only if the given length is a double + // or negative. + bool canThrow = true; + if (inputs[0].isInteger(compiler) && inputs[0] is HConstant) { + var constant = inputs[0]; + if (constant.constant.value >= 0) canThrow = false; + } + HForeign foreign = new HForeign( + code, elementType, inputs, nativeBehavior: behavior, + canThrow: canThrow); + push(foreign); + TypesInferrer inferrer = compiler.typesTask.typesInferrer; + if (inferrer.isFixedArrayCheckedForGrowable(send)) { + js.Expression code = js.js.parseForeignJS(r'#.fixed$length = init'); + // We set the instruction as [canThrow] to avoid it being dead code. + // We need a finer grained side effect. + add(new HForeign( + code, backend.nullType, [stack.last], canThrow: true)); + } + } else if (isGrowableListConstructorCall) { + push(buildLiteralList([])); + stack.last.instructionType = elementType; + } else { + ClassElement cls = constructor.getEnclosingClass(); + if (cls.isAbstract && constructor.isGenerativeConstructor()) { + generateAbstractClassInstantiationError(send, cls.name); + return; + } + if (backend.classNeedsRti(cls)) { + Link typeVariable = cls.typeVariables; + expectedType.typeArguments.forEach((DartType argument) { + inputs.add(analyzeTypeArgument(argument)); + typeVariable = typeVariable.tail; + }); + assert(typeVariable.isEmpty); + } + + addInlinedInstantiation(expectedType); + pushInvokeStatic(node, constructor, inputs, elementType); + removeInlinedInstantiation(expectedType); + } + HInstruction newInstance = stack.last; + if (isFixedList) { + // Overwrite the element type, in case the allocation site has + // been inlined. + newInstance.instructionType = elementType; + JavaScriptItemCompilationContext context = work.compilationContext; + context.allocatedFixedLists.add(newInstance); + } + + // The List constructor forwards to a Dart static method that does + // not know about the type argument. Therefore we special case + // this constructor to have the setRuntimeTypeInfo called where + // the 'new' is done. + if (backend.classNeedsRti(compiler.listClass) && + (isFixedListConstructorCall || isGrowableListConstructorCall || + isJSArrayTypedConstructor)) { + newInstance = handleListConstructor(type, send, pop()); + stack.add(newInstance); + } + + // Finally, if we called a redirecting factory constructor, check the type. + if (isRedirected) { + HInstruction checked = potentiallyCheckType(newInstance, type); + if (checked != newInstance) { + pop(); + stack.add(checked); + } + } + } + + /// In checked mode checks the [type] of [node] to be well-bounded. The method + /// returns [:true:] if an error can be statically determined. + bool checkTypeVariableBounds(ast.NewExpression node, InterfaceType type) { + if (!compiler.enableTypeAssertions) return false; + + Map> seenChecksMap = + new Map>(); + bool definitelyFails = false; + + addTypeVariableBoundCheck(GenericType instance, + DartType typeArgument, + TypeVariableType typeVariable, + DartType bound) { + if (definitelyFails) return; + + int subtypeRelation = compiler.types.computeSubtypeRelation(typeArgument, bound); + if (subtypeRelation == Types.IS_SUBTYPE) return; + + String message = + "Can't create an instance of malbounded type '$type': " + "'${typeArgument}' is not a subtype of bound '${bound}' for " + "type variable '${typeVariable}' of type " + "${type == instance + ? "'${type.element.thisType}'" + : "'${instance.element.thisType}' on the supertype " + "'${instance}' of '${type}'" + }."; + if (subtypeRelation == Types.NOT_SUBTYPE) { + generateTypeError(node, message); + definitelyFails = true; + return; + } else if (subtypeRelation == Types.MAYBE_SUBTYPE) { + Set seenChecks = + seenChecksMap.putIfAbsent(typeArgument, () => new Set()); + if (!seenChecks.contains(bound)) { + seenChecks.add(bound); + assertIsSubtype(node, typeArgument, bound, message); + } + } + } + + compiler.types.checkTypeVariableBounds(type, addTypeVariableBoundCheck); + if (definitelyFails) { + return true; + } + for (InterfaceType supertype in type.element.allSupertypes) { + DartType instance = type.asInstanceOf(supertype.element); + compiler.types.checkTypeVariableBounds(instance, + addTypeVariableBoundCheck); + if (definitelyFails) { + return true; + } + } + return false; + } + + visitAssert(node) { + if (!compiler.enableUserAssertions) { + stack.add(graph.addConstantNull(compiler)); + return; + } + visitStaticSend(node); + } + + visitStaticSend(ast.Send node) { + Selector selector = elements.getSelector(node); + Element element = elements[node]; + if (element.isForeign(compiler) && element.isFunction()) { + visitForeignSend(node); + return; + } + if (element.isErroneous()) { + // An erroneous element indicates that the funciton could not be resolved + // (a warning has been issued). + generateThrowNoSuchMethod(node, + getTargetName(element), + argumentNodes: node.arguments); + return; + } + invariant(element, !element.isGenerativeConstructor()); + if (element.isFunction()) { + var inputs = []; + // TODO(5347): Try to avoid the need for calling [implementation] before + // calling [addStaticSendArgumentsToList]. + bool succeeded = addStaticSendArgumentsToList(selector, node.arguments, + element.implementation, + inputs); + if (!succeeded) { + generateWrongArgumentCountError(node, element, node.arguments); + return; + } + + if (element == compiler.identicalFunction) { + pushWithPosition( + new HIdentity(inputs[0], inputs[1], null, backend.boolType), node); + return; + } + + pushInvokeStatic(node, element, inputs); + } else { + generateGetter(node, element); + List inputs = [pop()]; + addDynamicSendArgumentsToList(node, inputs); + Selector closureSelector = new Selector.callClosureFrom(selector); + pushWithPosition( + new HInvokeClosure(closureSelector, inputs, backend.dynamicType), + node); + } + } + + HConstant addConstantString(String string) { + ast.DartString dartString = new ast.DartString.literal(string); + Constant constant = constantSystem.createString(dartString); + return graph.addConstant(constant, compiler); + } + + visitTypeReferenceSend(ast.Send node) { + Element element = elements[node]; + if (element.isClass() || element.isTypedef()) { + // TODO(karlklose): add type representation + if (node.isCall) { + // The node itself is not a constant but we register the selector (the + // identifier that refers to the class/typedef) as a constant. + stack.add(addConstant(node.selector)); + } else { + stack.add(addConstant(node)); + } + } else if (element.isTypeVariable()) { + TypeVariableElement typeVariable = element; + HInstruction value = addTypeVariableReference(typeVariable.type); + pushInvokeStatic(node, + backend.getRuntimeTypeToString(), + [value], + backend.stringType); + pushInvokeStatic(node, + backend.getCreateRuntimeType(), + [pop()]); + } else { + internalError('unexpected element kind $element', node: node); + } + if (node.isCall) { + // This send is of the form 'e(...)', where e is resolved to a type + // reference. We create a regular closure call on the result of the type + // reference instead of creating a NoSuchMethodError to avoid pulling it + // in if it is not used (e.g., in a try/catch). + HInstruction target = pop(); + Selector selector = elements.getSelector(node); + List inputs = [target]; + addDynamicSendArgumentsToList(node, inputs); + Selector closureSelector = new Selector.callClosureFrom(selector); + push(new HInvokeClosure(closureSelector, inputs, backend.dynamicType)); + } + } + + visitGetterSend(ast.Send node) { + generateGetter(node, elements[node]); + } + + // TODO(antonm): migrate rest of SsaFromAstMixin to internalError. + internalError(String reason, {ast.Node node}) { + compiler.internalError(node, reason); + } + + void generateError(ast.Node node, String message, Element helper) { + HInstruction errorMessage = addConstantString(message); + pushInvokeStatic(node, helper, [errorMessage]); + } + + void generateRuntimeError(ast.Node node, String message) { + generateError(node, message, backend.getThrowRuntimeError()); + } + + void generateTypeError(ast.Node node, String message) { + generateError(node, message, backend.getThrowTypeError()); + } + + void generateAbstractClassInstantiationError(ast.Node node, String message) { + generateError(node, + message, + backend.getThrowAbstractClassInstantiationError()); + } + + void generateThrowNoSuchMethod(ast.Node diagnosticNode, + String methodName, + {Link argumentNodes, + List argumentValues, + List existingArguments}) { + Element helper = backend.getThrowNoSuchMethod(); + Constant receiverConstant = + constantSystem.createString(new ast.DartString.empty()); + HInstruction receiver = graph.addConstant(receiverConstant, compiler); + ast.DartString dartString = new ast.DartString.literal(methodName); + Constant nameConstant = constantSystem.createString(dartString); + HInstruction name = graph.addConstant(nameConstant, compiler); + if (argumentValues == null) { + argumentValues = []; + argumentNodes.forEach((argumentNode) { + visit(argumentNode); + HInstruction value = pop(); + argumentValues.add(value); + }); + } + HInstruction arguments = buildLiteralList(argumentValues); + add(arguments); + HInstruction existingNamesList; + if (existingArguments != null) { + List existingNames = []; + for (String name in existingArguments) { + HInstruction nameConstant = + graph.addConstantString(new ast.DartString.literal(name), compiler); + existingNames.add(nameConstant); + } + existingNamesList = buildLiteralList(existingNames); + add(existingNamesList); + } else { + existingNamesList = graph.addConstantNull(compiler); + } + pushInvokeStatic(diagnosticNode, + helper, + [receiver, name, arguments, existingNamesList]); + } + + /** + * Generate code to throw a [NoSuchMethodError] exception for calling a + * method with a wrong number of arguments or mismatching named optional + * arguments. + */ + void generateWrongArgumentCountError(ast.Node diagnosticNode, + FunctionElement function, + Link argumentNodes) { + List existingArguments = []; + FunctionSignature signature = function.functionSignature; + signature.forEachParameter((Element parameter) { + existingArguments.add(parameter.name); + }); + generateThrowNoSuchMethod(diagnosticNode, + function.name, + argumentNodes: argumentNodes, + existingArguments: existingArguments); + } + + visitNewExpression(ast.NewExpression node) { + Element element = elements[node.send]; + final bool isSymbolConstructor = element == compiler.symbolConstructor; + if (!Elements.isErroneousElement(element)) { + FunctionElement function = element; + element = function.redirectionTarget; + } + if (Elements.isErroneousElement(element)) { + ErroneousElement error = element; + if (error.messageKind == MessageKind.CANNOT_FIND_CONSTRUCTOR) { + generateThrowNoSuchMethod(node.send, + getTargetName(error, 'constructor'), + argumentNodes: node.send.arguments); + } else { + Message message = error.messageKind.message(error.messageArguments); + generateRuntimeError(node.send, message.toString()); + } + } else if (node.isConst()) { + stack.add(addConstant(node)); + if (isSymbolConstructor) { + ConstructedConstant symbol = elements.getConstant(node); + StringConstant stringConstant = symbol.fields.single; + String nameString = stringConstant.toDartString().slowToString(); + compiler.enqueuer.codegen.registerConstSymbol(nameString, elements); + } + } else { + handleNewSend(node); + } + } + + void pushInvokeDynamic(ast.Node node, + Selector selector, + List arguments, + {ast.Node location}) { + if (location == null) location = node; + + // We prefer to not inline certain operations on indexables, + // because the constant folder will handle them better and turn + // them into simpler instructions that allow further + // optimizations. + bool isOptimizableOperationOnIndexable(Selector selector, Element element) { + bool isLength = selector.isGetter() + && selector.name == "length"; + if (isLength || selector.isIndex()) { + TypeMask type = new TypeMask.nonNullExact( + element.getEnclosingClass().declaration); + return type.satisfies(backend.jsIndexableClass, compiler); + } else if (selector.isIndexSet()) { + TypeMask type = new TypeMask.nonNullExact( + element.getEnclosingClass().declaration); + return type.satisfies(backend.jsMutableIndexableClass, compiler); + } else { + return false; + } + } + + bool isOptimizableOperation(Selector selector, Element element) { + ClassElement cls = element.getEnclosingClass(); + if (isOptimizableOperationOnIndexable(selector, element)) return true; + if (!backend.interceptedClasses.contains(cls)) return false; + if (selector.isOperator()) return true; + if (selector.isSetter()) return true; + if (selector.isIndex()) return true; + if (selector.isIndexSet()) return true; + if (element == backend.jsArrayAdd + || element == backend.jsArrayRemoveLast + || element == backend.jsStringSplit) { + return true; + } + return false; + } + + Element element = compiler.world.locateSingleElement(selector); + if (element != null + && !element.isField() + && !(element.isGetter() && selector.isCall()) + && !(element.isFunction() && selector.isGetter()) + && !isOptimizableOperation(selector, element)) { + if (tryInlineMethod(element, selector, arguments, node)) { + return; + } + } + + HInstruction receiver = arguments[0]; + List inputs = []; + bool isIntercepted = backend.isInterceptedSelector(selector); + if (isIntercepted) { + inputs.add(invokeInterceptor(receiver)); + } + inputs.addAll(arguments); + TypeMask type = TypeMaskFactory.inferredTypeForSelector(selector, compiler); + if (selector.isGetter()) { + pushWithPosition( + new HInvokeDynamicGetter(selector, null, inputs, type), + location); + } else if (selector.isSetter()) { + pushWithPosition( + new HInvokeDynamicSetter(selector, null, inputs, type), + location); + } else { + pushWithPosition( + new HInvokeDynamicMethod(selector, inputs, type, isIntercepted), + location); + } + } + + void pushInvokeStatic(ast.Node location, + Element element, + List arguments, + [TypeMask type]) { + if (tryInlineMethod(element, null, arguments, location)) { + return; + } + + if (type == null) { + type = TypeMaskFactory.inferredReturnTypeForElement(element, compiler); + } + bool targetCanThrow = !compiler.world.getCannotThrow(element); + // TODO(5346): Try to avoid the need for calling [declaration] before + // creating an [HInvokeStatic]. + HInvokeStatic instruction = new HInvokeStatic( + element.declaration, arguments, type, targetCanThrow: targetCanThrow); + if (!currentInlinedInstantiations.isEmpty) { + instruction.instantiatedTypes = new List.from( + currentInlinedInstantiations); + } + instruction.sideEffects = compiler.world.getSideEffectsOfElement(element); + if (location == null) { + push(instruction); + } else { + pushWithPosition(instruction, location); + } + } + + HInstruction buildInvokeSuper(Selector selector, + Element element, + List arguments) { + HInstruction receiver = localsHandler.readThis(); + // TODO(5346): Try to avoid the need for calling [declaration] before + // creating an [HStatic]. + List inputs = []; + if (backend.isInterceptedSelector(selector) && + // Fields don't need an interceptor; consider generating HFieldGet/Set + // instead. + element.kind != ElementKind.FIELD) { + inputs.add(invokeInterceptor(receiver)); + } + inputs.add(receiver); + inputs.addAll(arguments); + TypeMask type; + if (!element.isGetter() && selector.isGetter()) { + type = TypeMaskFactory.inferredTypeForElement(element, compiler); + } else { + type = TypeMaskFactory.inferredReturnTypeForElement(element, compiler); + } + HInstruction instruction = new HInvokeSuper( + element, + currentNonClosureClass, + selector, + inputs, + type, + isSetter: selector.isSetter() || selector.isIndexSet()); + instruction.sideEffects = compiler.world.getSideEffectsOfSelector(selector); + return instruction; + } + + void handleComplexOperatorSend(ast.SendSet node, + HInstruction receiver, + Link arguments) { + HInstruction rhs; + if (node.isPrefix || node.isPostfix) { + rhs = graph.addConstantInt(1, compiler); + } else { + visit(arguments.head); + assert(arguments.tail.isEmpty); + rhs = pop(); + } + visitBinary(receiver, node.assignmentOperator, rhs, + elements.getOperatorSelectorInComplexSendSet(node), node); + } + + visitSendSet(ast.SendSet node) { + Element element = elements[node]; + if (!Elements.isUnresolved(element) && element.impliesType()) { + ast.Identifier selector = node.selector; + generateThrowNoSuchMethod(node, selector.source, + argumentNodes: node.arguments); + return; + } + ast.Operator op = node.assignmentOperator; + if (node.isSuperCall) { + HInstruction result; + List setterInputs = []; + if (identical(node.assignmentOperator.source, '=')) { + addDynamicSendArgumentsToList(node, setterInputs); + result = setterInputs.last; + } else { + Element getter = elements[node.selector]; + List getterInputs = []; + Link arguments = node.arguments; + if (node.isIndex) { + // If node is of the from [:super.foo[0] += 2:], the send has + // two arguments: the index and the left hand side. We get + // the index and add it as input of the getter and the + // setter. + visit(arguments.head); + arguments = arguments.tail; + HInstruction index = pop(); + getterInputs.add(index); + setterInputs.add(index); + } + HInstruction getterInstruction; + Selector getterSelector = + elements.getGetterSelectorInComplexSendSet(node); + if (Elements.isUnresolved(getter)) { + generateSuperNoSuchMethodSend( + node, + getterSelector, + getterInputs); + getterInstruction = pop(); + } else { + getterInstruction = buildInvokeSuper( + getterSelector, getter, getterInputs); + add(getterInstruction); + } + handleComplexOperatorSend(node, getterInstruction, arguments); + setterInputs.add(pop()); + + if (node.isPostfix) { + result = getterInstruction; + } else { + result = setterInputs.last; + } + } + Selector setterSelector = elements.getSelector(node); + if (Elements.isUnresolved(element) + || !setterSelector.applies(element, compiler)) { + generateSuperNoSuchMethodSend( + node, setterSelector, setterInputs); + pop(); + } else { + add(buildInvokeSuper(setterSelector, element, setterInputs)); + } + stack.add(result); + } else if (node.isIndex) { + if ("=" == op.source) { + visitDynamicSend(node); + } else { + visit(node.receiver); + HInstruction receiver = pop(); + Link arguments = node.arguments; + HInstruction index; + if (node.isIndex) { + visit(arguments.head); + arguments = arguments.tail; + index = pop(); + } + + pushInvokeDynamic( + node, + elements.getGetterSelectorInComplexSendSet(node), + [receiver, index]); + HInstruction getterInstruction = pop(); + + handleComplexOperatorSend(node, getterInstruction, arguments); + HInstruction value = pop(); + + pushInvokeDynamic( + node, elements.getSelector(node), [receiver, index, value]); + pop(); + + if (node.isPostfix) { + stack.add(getterInstruction); + } else { + stack.add(value); + } + } + } else if ("=" == op.source) { + Link link = node.arguments; + assert(!link.isEmpty && link.tail.isEmpty); + if (Elements.isInstanceSend(node, elements)) { + HInstruction receiver = generateInstanceSendReceiver(node); + visit(link.head); + generateInstanceSetterWithCompiledReceiver(node, receiver, pop()); + } else { + visit(link.head); + generateNonInstanceSetter(node, element, pop()); + } + } else if (identical(op.source, "is")) { + compiler.internalError(op, "is-operator as SendSet."); + } else { + assert("++" == op.source || "--" == op.source || + node.assignmentOperator.source.endsWith("=")); + + // [receiver] is only used if the node is an instance send. + HInstruction receiver = null; + Element getter = elements[node.selector]; + + if (!Elements.isUnresolved(getter) && getter.impliesType()) { + ast.Identifier selector = node.selector; + generateThrowNoSuchMethod(node, selector.source, + argumentNodes: node.arguments); + return; + } else if (Elements.isInstanceSend(node, elements)) { + receiver = generateInstanceSendReceiver(node); + generateInstanceGetterWithCompiledReceiver( + node, elements.getGetterSelectorInComplexSendSet(node), receiver); + } else { + generateGetter(node, getter); + } + HInstruction getterInstruction = pop(); + handleComplexOperatorSend(node, getterInstruction, node.arguments); + HInstruction value = pop(); + assert(value != null); + if (Elements.isInstanceSend(node, elements)) { + assert(receiver != null); + generateInstanceSetterWithCompiledReceiver(node, receiver, value); + } else { + assert(receiver == null); + generateNonInstanceSetter(node, element, value); + } + if (node.isPostfix) { + pop(); + stack.add(getterInstruction); + } + } + } + + void visitLiteralInt(ast.LiteralInt node) { + stack.add(graph.addConstantInt(node.value, compiler)); + } + + void visitLiteralDouble(ast.LiteralDouble node) { + stack.add(graph.addConstantDouble(node.value, compiler)); + } + + void visitLiteralBool(ast.LiteralBool node) { + stack.add(graph.addConstantBool(node.value, compiler)); + } + + void visitLiteralString(ast.LiteralString node) { + stack.add(graph.addConstantString(node.dartString, compiler)); + } + + void visitLiteralSymbol(ast.LiteralSymbol node) { + stack.add(addConstant(node)); + compiler.enqueuer.codegen.registerConstSymbol( + node.slowNameString, elements); + } + + void visitStringJuxtaposition(ast.StringJuxtaposition node) { + if (!node.isInterpolation) { + // This is a simple string with no interpolations. + stack.add(graph.addConstantString(node.dartString, compiler)); + return; + } + StringBuilderVisitor stringBuilder = new StringBuilderVisitor(this, node); + stringBuilder.visit(node); + stack.add(stringBuilder.result); + } + + void visitLiteralNull(ast.LiteralNull node) { + stack.add(graph.addConstantNull(compiler)); + } + + visitNodeList(ast.NodeList node) { + for (Link link = node.nodes; !link.isEmpty; link = link.tail) { + if (isAborted()) { + compiler.reportWarning(link.head, + MessageKind.GENERIC, {'text': 'dead code'}); + } else { + visit(link.head); + } + } + } + + void visitParenthesizedExpression(ast.ParenthesizedExpression node) { + visit(node.expression); + } + + visitOperator(ast.Operator node) { + // Operators are intercepted in their surrounding Send nodes. + compiler.internalError(node, + 'SsaBuilder.visitOperator should not be called.'); + } + + visitCascade(ast.Cascade node) { + visit(node.expression); + // Remove the result and reveal the duplicated receiver on the stack. + pop(); + } + + visitCascadeReceiver(ast.CascadeReceiver node) { + visit(node.expression); + dup(); + } + + void handleInTryStatement() { + if (!inTryStatement) return; + HBasicBlock block = close(new HExitTry()); + HBasicBlock newBlock = graph.addNewBlock(); + block.addSuccessor(newBlock); + open(newBlock); + } + + visitRethrow(ast.Rethrow node) { + HInstruction exception = rethrowableException; + if (exception == null) { + exception = graph.addConstantNull(compiler); + compiler.internalError(node, + 'rethrowableException should not be null.'); + } + handleInTryStatement(); + closeAndGotoExit(new HThrow(exception, isRethrow: true)); + } + + visitReturn(ast.Return node) { + if (identical(node.getBeginToken().stringValue, 'native')) { + native.handleSsaNative(this, node.expression); + return; + } + HInstruction value; + if (node.isRedirectingFactoryBody) { + FunctionElement element = elements[node.expression].implementation; + FunctionElement function = sourceElement; + List inputs = []; + FunctionSignature calleeSignature = element.functionSignature; + FunctionSignature callerSignature = function.functionSignature; + callerSignature.forEachRequiredParameter((Element element) { + inputs.add(localsHandler.readLocal(element)); + }); + List calleeOptionals = + calleeSignature.orderedOptionalParameters; + List callerOptionals = + callerSignature.orderedOptionalParameters; + int i = 0; + for (; i < callerOptionals.length; i++) { + inputs.add(localsHandler.readLocal(callerOptionals[i])); + } + for (; i < calleeOptionals.length; i++) { + inputs.add(handleConstantForOptionalParameter(calleeOptionals[i])); + } + + if (backend.classNeedsRti(element.getEnclosingClass())) { + ClassElement cls = function.getEnclosingClass(); + Link typeVariable = cls.typeVariables; + InterfaceType type = elements.getType(node.expression); + type.typeArguments.forEach((DartType argument) { + inputs.add(analyzeTypeArgument(argument)); + typeVariable = typeVariable.tail; + }); + assert(typeVariable.isEmpty); + } + pushInvokeStatic(node, element, inputs); + value = pop(); + } else if (node.expression == null) { + value = graph.addConstantNull(compiler); + } else { + visit(node.expression); + value = pop(); + value = potentiallyCheckType(value, returnType); + } + + handleInTryStatement(); + emitReturn(value, node); + } + + visitThrow(ast.Throw node) { + visitThrowExpression(node.expression); + if (isReachable) { + handleInTryStatement(); + push(new HThrowExpression(pop())); + isReachable = false; + } + } + + visitTypeAnnotation(ast.TypeAnnotation node) { + compiler.internalError(node, + 'Visiting type annotation in SSA builder.'); + } + + visitVariableDefinitions(ast.VariableDefinitions node) { + assert(isReachable); + for (Link link = node.definitions.nodes; + !link.isEmpty; + link = link.tail) { + ast.Node definition = link.head; + if (definition is ast.Identifier) { + HInstruction initialValue = graph.addConstantNull(compiler); + localsHandler.updateLocal(elements[definition], initialValue); + } else { + assert(definition is ast.SendSet); + visitSendSet(definition); + pop(); // Discard value. + } + } + } + + HInstruction setRtiIfNeeded(HInstruction object, ast.Node node) { + InterfaceType type = elements.getType(node); + if (!backend.classNeedsRti(type.element) || type.treatAsRaw) { + return object; + } + List arguments = []; + for (DartType argument in type.typeArguments) { + arguments.add(analyzeTypeArgument(argument)); + } + // TODO(15489): Register at codegen. + compiler.enqueuer.codegen.registerInstantiatedType(type, elements); + return callSetRuntimeTypeInfo(type.element, arguments, object); + } + + visitLiteralList(ast.LiteralList node) { + HInstruction instruction; + + if (node.isConst()) { + instruction = addConstant(node); + } else { + List inputs = []; + for (Link link = node.elements.nodes; + !link.isEmpty; + link = link.tail) { + visit(link.head); + inputs.add(pop()); + } + instruction = buildLiteralList(inputs); + add(instruction); + instruction = setRtiIfNeeded(instruction, node); + } + + TypeMask type = + TypeMaskFactory.inferredForNode(sourceElement, node, compiler); + if (!type.containsAll(compiler)) instruction.instructionType = type; + stack.add(instruction); + } + + visitConditional(ast.Conditional node) { + SsaBranchBuilder brancher = new SsaBranchBuilder(this, node); + brancher.handleConditional(() => visit(node.condition), + () => visit(node.thenExpression), + () => visit(node.elseExpression)); + } + + visitStringInterpolation(ast.StringInterpolation node) { + StringBuilderVisitor stringBuilder = new StringBuilderVisitor(this, node); + stringBuilder.visit(node); + stack.add(stringBuilder.result); + } + + visitStringInterpolationPart(ast.StringInterpolationPart node) { + // The parts are iterated in visitStringInterpolation. + compiler.internalError(node, + 'SsaBuilder.visitStringInterpolation should not be called.'); + } + + visitEmptyStatement(ast.EmptyStatement node) { + // Do nothing, empty statement. + } + + visitModifiers(ast.Modifiers node) { + compiler.unimplemented(node, 'SsaFromAstMixin.visitModifiers.'); + } + + visitBreakStatement(ast.BreakStatement node) { + assert(!isAborted()); + handleInTryStatement(); + TargetElement target = elements[node]; + assert(target != null); + JumpHandler handler = jumpTargets[target]; + assert(handler != null); + if (node.target == null) { + handler.generateBreak(); + } else { + LabelElement label = elements[node.target]; + handler.generateBreak(label); + } + } + + visitContinueStatement(ast.ContinueStatement node) { + handleInTryStatement(); + TargetElement target = elements[node]; + assert(target != null); + JumpHandler handler = jumpTargets[target]; + assert(handler != null); + if (node.target == null) { + handler.generateContinue(); + } else { + LabelElement label = elements[node.target]; + assert(label != null); + handler.generateContinue(label); + } + } + + /** + * Creates a [JumpHandler] for a statement. The node must be a jump + * target. If there are no breaks or continues targeting the statement, + * a special "null handler" is returned. + * + * [isLoopJump] is [:true:] when the jump handler is for a loop. This is used + * to distinguish the synthetized loop created for a switch statement with + * continue statements from simple switch statements. + */ + JumpHandler createJumpHandler(ast.Statement node, {bool isLoopJump}) { + TargetElement element = elements[node]; + if (element == null || !identical(element.statement, node)) { + // No breaks or continues to this node. + return new NullJumpHandler(compiler); + } + if (isLoopJump && node is ast.SwitchStatement) { + // Create a special jump handler for loops created for switch statements + // with continue statements. + return new SwitchCaseJumpHandler(this, element, node); + } + return new JumpHandler(this, element); + } + + visitForIn(ast.ForIn node) { + // Generate a structure equivalent to: + // Iterator $iter = .iterator; + // while ($iter.moveNext()) { + // E = $iter.current; + // + // } + + // The iterator is shared between initializer, condition and body. + HInstruction iterator; + void buildInitializer() { + Selector selector = elements.getIteratorSelector(node); + visit(node.expression); + HInstruction receiver = pop(); + pushInvokeDynamic(node, selector, [receiver]); + iterator = pop(); + } + HInstruction buildCondition() { + Selector selector = elements.getMoveNextSelector(node); + pushInvokeDynamic(node, selector, [iterator]); + return popBoolified(); + } + void buildBody() { + Selector call = elements.getCurrentSelector(node); + pushInvokeDynamic(node, call, [iterator]); + + ast.Node identifier = node.declaredIdentifier; + Element variable = elements[identifier]; + Selector selector = elements.getSelector(identifier); + + HInstruction value = pop(); + if (identifier.asSend() != null + && Elements.isInstanceSend(identifier, elements)) { + HInstruction receiver = generateInstanceSendReceiver(identifier); + assert(receiver != null); + generateInstanceSetterWithCompiledReceiver( + null, + receiver, + value, + selector: selector, + location: identifier); + } else { + generateNonInstanceSetter(null, variable, value, location: identifier); + } + pop(); // Pop the value pushed by the setter call. + + visit(node.body); + } + handleLoop(node, buildInitializer, buildCondition, () {}, buildBody); + } + + visitLabel(ast.Label node) { + compiler.internalError(node, 'SsaFromAstMixin.visitLabel.'); + } + + visitLabeledStatement(ast.LabeledStatement node) { + ast.Statement body = node.statement; + if (body is ast.Loop + || body is ast.SwitchStatement + || Elements.isUnusedLabel(node, elements)) { + // Loops and switches handle their own labels. + visit(body); + return; + } + TargetElement targetElement = elements[body]; + LocalsHandler beforeLocals = new LocalsHandler.from(localsHandler); + assert(targetElement.isBreakTarget); + JumpHandler handler = new JumpHandler(this, targetElement); + // Introduce a new basic block. + HBasicBlock entryBlock = openNewBlock(); + visit(body); + SubGraph bodyGraph = new SubGraph(entryBlock, lastOpenedBlock); + + HBasicBlock joinBlock = graph.addNewBlock(); + List breakHandlers = []; + handler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) { + breakInstruction.block.addSuccessor(joinBlock); + breakHandlers.add(locals); + }); + bool hasBreak = breakHandlers.length > 0; + if (!isAborted()) { + goto(current, joinBlock); + breakHandlers.add(localsHandler); + } + open(joinBlock); + localsHandler = beforeLocals.mergeMultiple(breakHandlers, joinBlock); + + if (hasBreak) { + // There was at least one reachable break, so the label is needed. + entryBlock.setBlockFlow( + new HLabeledBlockInformation(new HSubGraphBlockInformation(bodyGraph), + handler.labels()), + joinBlock); + } + handler.close(); + } + + visitLiteralMap(ast.LiteralMap node) { + if (node.isConst()) { + stack.add(addConstant(node)); + return; + } + List inputs = []; + for (Link link = node.entries.nodes; + !link.isEmpty; + link = link.tail) { + visit(link.head); + inputs.add(pop()); + inputs.add(pop()); + } + HLiteralList keyValuePairs = buildLiteralList(inputs); + add(keyValuePairs); + TypeMask mapType = new TypeMask.nonNullSubtype(backend.mapLiteralClass); + pushInvokeStatic(node, backend.getMapMaker(), [keyValuePairs], mapType); + stack.add(setRtiIfNeeded(pop(), node)); + } + + visitLiteralMapEntry(ast.LiteralMapEntry node) { + visit(node.value); + visit(node.key); + } + + visitNamedArgument(ast.NamedArgument node) { + visit(node.expression); + } + + Map buildSwitchCaseConstants(ast.SwitchStatement node) { + Map constants = new Map(); + for (ast.SwitchCase switchCase in node.cases) { + for (ast.Node labelOrCase in switchCase.labelsAndCases) { + if (labelOrCase is ast.CaseMatch) { + ast.CaseMatch match = labelOrCase; + Constant constant = getConstantForNode(match.expression); + constants[labelOrCase] = constant; + } + } + } + return constants; + } + + visitSwitchStatement(ast.SwitchStatement node) { + Map constants = buildSwitchCaseConstants(node); + + // The switch case indices must match those computed in + // [SwitchCaseJumpHandler]. + bool hasContinue = false; + Map caseIndex = new Map(); + int switchIndex = 1; + bool hasDefault = false; + for (ast.SwitchCase switchCase in node.cases) { + for (ast.Node labelOrCase in switchCase.labelsAndCases) { + ast.Node label = labelOrCase.asLabel(); + if (label != null) { + LabelElement labelElement = elements[label]; + if (labelElement != null && labelElement.isContinueTarget) { + hasContinue = true; + } + } + } + if (switchCase.isDefaultCase) { + hasDefault = true; + } + caseIndex[switchCase] = switchIndex; + switchIndex++; + } + if (!hasContinue) { + // If the switch statement has no switch cases targeted by continue + // statements we encode the switch statement directly. + buildSimpleSwitchStatement(node, constants); + } else { + buildComplexSwitchStatement(node, constants, caseIndex, hasDefault); + } + } + + /** + * Builds a simple switch statement which does not handle uses of continue + * statements to labeled switch cases. + */ + void buildSimpleSwitchStatement(ast.SwitchStatement node, + Map constants) { + JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false); + HInstruction buildExpression() { + visit(node.expression); + return pop(); + } + Iterable getConstants(ast.SwitchCase switchCase) { + List constantList = []; + for (ast.Node labelOrCase in switchCase.labelsAndCases) { + if (labelOrCase is ast.CaseMatch) { + constantList.add(constants[labelOrCase]); + } + } + return constantList; + } + bool isDefaultCase(ast.SwitchCase switchCase) { + return switchCase.isDefaultCase; + } + void buildSwitchCase(ast.SwitchCase node) { + visit(node.statements); + } + handleSwitch(node, + jumpHandler, + buildExpression, + node.cases, + getConstants, + isDefaultCase, + buildSwitchCase); + jumpHandler.close(); + } + + /** + * Builds a switch statement that can handle arbitrary uses of continue + * statements to labeled switch cases. + */ + void buildComplexSwitchStatement(ast.SwitchStatement node, + Map constants, + Map caseIndex, + bool hasDefault) { + // If the switch statement has switch cases targeted by continue + // statements we create the following encoding: + // + // switch (e) { + // l_1: case e0: s_1; break; + // l_2: case e1: s_2; continue l_i; + // ... + // l_n: default: s_n; continue l_j; + // } + // + // is encoded as + // + // var target; + // switch (e) { + // case e1: target = 1; break; + // case e2: target = 2; break; + // ... + // default: target = n; break; + // } + // l: while (true) { + // switch (target) { + // case 1: s_1; break l; + // case 2: s_2; target = i; continue l; + // ... + // case n: s_n; target = j; continue l; + // } + // } + + TargetElement switchTarget = elements[node]; + HInstruction initialValue = graph.addConstantNull(compiler); + localsHandler.updateLocal(switchTarget, initialValue); + + JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false); + var switchCases = node.cases; + if (!hasDefault) { + // Use [:null:] as the marker for a synthetic default clause. + // The synthetic default is added because otherwise, there would be no + // good place to give a default value to the local. + switchCases = node.cases.nodes.toList()..add(null); + } + HInstruction buildExpression() { + visit(node.expression); + return pop(); + } + Iterable getConstants(ast.SwitchCase switchCase) { + List constantList = []; + if (switchCase != null) { + for (ast.Node labelOrCase in switchCase.labelsAndCases) { + if (labelOrCase is ast.CaseMatch) { + constantList.add(constants[labelOrCase]); + } + } + } + return constantList; + } + bool isDefaultCase(ast.SwitchCase switchCase) { + return switchCase == null || switchCase.isDefaultCase; + } + void buildSwitchCase(ast.SwitchCase switchCase) { + if (switchCase != null) { + // Generate 'target = i; break;' for switch case i. + int index = caseIndex[switchCase]; + HInstruction value = graph.addConstantInt(index, compiler); + localsHandler.updateLocal(switchTarget, value); + } else { + // Generate synthetic default case 'target = null; break;'. + HInstruction value = graph.addConstantNull(compiler); + localsHandler.updateLocal(switchTarget, value); + } + jumpTargets[switchTarget].generateBreak(); + } + handleSwitch(node, + jumpHandler, + buildExpression, + switchCases, + getConstants, + isDefaultCase, + buildSwitchCase); + jumpHandler.close(); + + HInstruction buildCondition() => + graph.addConstantBool(true, compiler); + + void buildSwitch() { + HInstruction buildExpression() { + return localsHandler.readLocal(switchTarget); + } + Iterable getConstants(ast.SwitchCase switchCase) { + return [constantSystem.createInt(caseIndex[switchCase])]; + } + void buildSwitchCase(ast.SwitchCase switchCase) { + visit(switchCase.statements); + if (!isAborted()) { + // Ensure that we break the loop if the case falls through. (This + // is only possible for the last case.) + jumpTargets[switchTarget].generateBreak(); + } + } + // Pass a [NullJumpHandler] because the target for the contained break + // is not the generated switch statement but instead the loop generated + // in the call to [handleLoop] below. + handleSwitch(node, + new NullJumpHandler(compiler), + buildExpression, node.cases, getConstants, + (_) => false, // No case is default. + buildSwitchCase); + } + + void buildLoop() { + handleLoop(node, + () {}, + buildCondition, + () {}, + buildSwitch); + } + + if (hasDefault) { + buildLoop(); + } else { + // If the switch statement has no default case, surround the loop with + // a test of the target. + void buildCondition() { + js.Expression code = js.js.parseForeignJS('#'); + push(createForeign(code, + backend.boolType, + [localsHandler.readLocal(switchTarget)])); + } + handleIf(node, buildCondition, buildLoop, () => {}); + } + } + + /** + * Creates a switch statement. + * + * [jumpHandler] is the [JumpHandler] for the created switch statement. + * [buildExpression] creates the switch expression. + * [switchCases] must be either an [Iterable] of [ast.SwitchCase] nodes or + * a [Link] or a [ast.NodeList] of [ast.SwitchCase] nodes. + * [getConstants] returns the set of constants for a switch case. + * [isDefaultCase] returns [:true:] if the provided switch case should be + * considered default for the created switch statement. + * [buildSwitchCase] creates the statements for the switch case. + */ + void handleSwitch(ast.Node errorNode, + JumpHandler jumpHandler, + HInstruction buildExpression(), + var switchCases, + Iterable getConstants(ast.SwitchCase switchCase), + bool isDefaultCase(ast.SwitchCase switchCase), + void buildSwitchCase(ast.SwitchCase switchCase)) { + Map constants = new Map(); + + HBasicBlock expressionStart = openNewBlock(); + HInstruction expression = buildExpression(); + if (switchCases.isEmpty) { + return; + } + + HSwitch switchInstruction = new HSwitch([expression]); + HBasicBlock expressionEnd = close(switchInstruction); + LocalsHandler savedLocals = localsHandler; + + List statements = []; + bool hasDefault = false; + Element getFallThroughErrorElement = backend.getFallThroughError(); + HasNextIterator caseIterator = + new HasNextIterator(switchCases.iterator); + while (caseIterator.hasNext) { + ast.SwitchCase switchCase = caseIterator.next(); + HBasicBlock block = graph.addNewBlock(); + for (Constant constant in getConstants(switchCase)) { + HConstant hConstant = graph.addConstant(constant, compiler); + switchInstruction.inputs.add(hConstant); + hConstant.usedBy.add(switchInstruction); + expressionEnd.addSuccessor(block); + } + + if (isDefaultCase(switchCase)) { + // An HSwitch has n inputs and n+1 successors, the last being the + // default case. + expressionEnd.addSuccessor(block); + hasDefault = true; + } + open(block); + localsHandler = new LocalsHandler.from(savedLocals); + buildSwitchCase(switchCase); + if (!isAborted()) { + if (caseIterator.hasNext) { + pushInvokeStatic(switchCase, getFallThroughErrorElement, []); + HInstruction error = pop(); + closeAndGotoExit(new HThrow(error)); + } else if (!isDefaultCase(switchCase)) { + // If there is no default, we will add one later to avoid + // the critical edge. So we generate a break statement to make + // sure the last case does not fall through to the default case. + jumpHandler.generateBreak(); + } + } + statements.add( + new HSubGraphBlockInformation(new SubGraph(block, lastOpenedBlock))); + } + + // Add a join-block if necessary. + // We create [joinBlock] early, and then go through the cases that might + // want to jump to it. In each case, if we add [joinBlock] as a successor + // of another block, we also add an element to [caseHandlers] that is used + // to create the phis in [joinBlock]. + // If we never jump to the join block, [caseHandlers] will stay empty, and + // the join block is never added to the graph. + HBasicBlock joinBlock = new HBasicBlock(); + List caseHandlers = []; + jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) { + instruction.block.addSuccessor(joinBlock); + caseHandlers.add(locals); + }); + jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) { + assert(invariant(errorNode, false, + message: 'Continue cannot target a switch.')); + }); + if (!isAborted()) { + current.close(new HGoto()); + lastOpenedBlock.addSuccessor(joinBlock); + caseHandlers.add(localsHandler); + } + if (!hasDefault) { + // Always create a default case, to avoid a critical edge in the + // graph. + HBasicBlock defaultCase = addNewBlock(); + expressionEnd.addSuccessor(defaultCase); + open(defaultCase); + close(new HGoto()); + defaultCase.addSuccessor(joinBlock); + caseHandlers.add(savedLocals); + statements.add(new HSubGraphBlockInformation(new SubGraph( + defaultCase, defaultCase))); + } + assert(caseHandlers.length == joinBlock.predecessors.length); + if (caseHandlers.length != 0) { + graph.addBlock(joinBlock); + open(joinBlock); + if (caseHandlers.length == 1) { + localsHandler = caseHandlers[0]; + } else { + localsHandler = savedLocals.mergeMultiple(caseHandlers, joinBlock); + } + } else { + // The joinblock is not used. + joinBlock = null; + } + + HSubExpressionBlockInformation expressionInfo = + new HSubExpressionBlockInformation(new SubExpression(expressionStart, + expressionEnd)); + expressionStart.setBlockFlow( + new HSwitchBlockInformation(expressionInfo, + statements, + jumpHandler.target, + jumpHandler.labels()), + joinBlock); + + jumpHandler.close(); + } + + visitSwitchCase(ast.SwitchCase node) { + compiler.internalError(node, 'SsaFromAstMixin.visitSwitchCase.'); + } + + visitCaseMatch(ast.CaseMatch node) { + compiler.internalError(node, 'SsaFromAstMixin.visitCaseMatch.'); + } + + visitTryStatement(ast.TryStatement node) { + // Save the current locals. The catch block and the finally block + // must not reuse the existing locals handler. None of the variables + // that have been defined in the body-block will be used, but for + // loops we will add (unnecessary) phis that will reference the body + // variables. This makes it look as if the variables were used + // in a non-dominated block. + LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); + HBasicBlock enterBlock = openNewBlock(); + HTry tryInstruction = new HTry(); + close(tryInstruction); + bool oldInTryStatement = inTryStatement; + inTryStatement = true; + + HBasicBlock startTryBlock; + HBasicBlock endTryBlock; + HBasicBlock startCatchBlock; + HBasicBlock endCatchBlock; + HBasicBlock startFinallyBlock; + HBasicBlock endFinallyBlock; + + startTryBlock = graph.addNewBlock(); + open(startTryBlock); + visit(node.tryBlock); + // We use a [HExitTry] instead of a [HGoto] for the try block + // because it will have multiple successors: the join block, and + // the catch or finally block. + if (!isAborted()) endTryBlock = close(new HExitTry()); + SubGraph bodyGraph = new SubGraph(startTryBlock, lastOpenedBlock); + SubGraph catchGraph = null; + HLocalValue exception = null; + + if (!node.catchBlocks.isEmpty) { + localsHandler = new LocalsHandler.from(savedLocals); + startCatchBlock = graph.addNewBlock(); + open(startCatchBlock); + // TODO(kasperl): Bad smell. We shouldn't be constructing elements here. + // Note that the name of this element is irrelevant. + Element element = new VariableElementX.synthetic('exception', + ElementKind.PARAMETER, sourceElement); + exception = new HLocalValue(element, backend.nonNullType); + add(exception); + HInstruction oldRethrowableException = rethrowableException; + rethrowableException = exception; + + pushInvokeStatic(node, backend.getExceptionUnwrapper(), [exception]); + HInvokeStatic unwrappedException = pop(); + tryInstruction.exception = exception; + Link link = node.catchBlocks.nodes; + + void pushCondition(ast.CatchBlock catchBlock) { + if (catchBlock.onKeyword != null) { + DartType type = elements.getType(catchBlock.type); + if (type == null) { + compiler.internalError(catchBlock.type, 'On with no type.'); + } + HInstruction condition = + buildIsNode(catchBlock.type, type, unwrappedException); + push(condition); + } else { + ast.VariableDefinitions declaration = catchBlock.formals.nodes.head; + HInstruction condition = null; + if (declaration.type == null) { + condition = graph.addConstantBool(true, compiler); + stack.add(condition); + } else { + // TODO(aprelev@gmail.com): Once old catch syntax is removed + // "if" condition above and this "else" branch should be deleted as + // type of declared variable won't matter for the catch + // condition. + DartType type = elements.getType(declaration.type); + if (type == null) { + compiler.internalError(catchBlock, 'Catch with unresolved type.'); + } + condition = buildIsNode(declaration.type, type, unwrappedException); + push(condition); + } + } + } + + void visitThen() { + ast.CatchBlock catchBlock = link.head; + link = link.tail; + if (catchBlock.exception != null) { + localsHandler.updateLocal(elements[catchBlock.exception], + unwrappedException); + } + ast.Node trace = catchBlock.trace; + if (trace != null) { + pushInvokeStatic(trace, backend.getTraceFromException(), [exception]); + HInstruction traceInstruction = pop(); + localsHandler.updateLocal(elements[trace], traceInstruction); + } + visit(catchBlock); + } + + void visitElse() { + if (link.isEmpty) { + closeAndGotoExit(new HThrow(exception, isRethrow: true)); + } else { + ast.CatchBlock newBlock = link.head; + handleIf(node, + () { pushCondition(newBlock); }, + visitThen, visitElse); + } + } + + ast.CatchBlock firstBlock = link.head; + handleIf(node, () { pushCondition(firstBlock); }, visitThen, visitElse); + if (!isAborted()) endCatchBlock = close(new HGoto()); + + rethrowableException = oldRethrowableException; + tryInstruction.catchBlock = startCatchBlock; + catchGraph = new SubGraph(startCatchBlock, lastOpenedBlock); + } + + SubGraph finallyGraph = null; + if (node.finallyBlock != null) { + localsHandler = new LocalsHandler.from(savedLocals); + startFinallyBlock = graph.addNewBlock(); + open(startFinallyBlock); + visit(node.finallyBlock); + if (!isAborted()) endFinallyBlock = close(new HGoto()); + tryInstruction.finallyBlock = startFinallyBlock; + finallyGraph = new SubGraph(startFinallyBlock, lastOpenedBlock); + } + + HBasicBlock exitBlock = graph.addNewBlock(); + + addOptionalSuccessor(b1, b2) { if (b2 != null) b1.addSuccessor(b2); } + addExitTrySuccessor(successor) { + if (successor == null) return; + // Iterate over all blocks created inside this try/catch, and + // attach successor information to blocks that end with + // [HExitTry]. + for (int i = startTryBlock.id; i < successor.id; i++) { + HBasicBlock block = graph.blocks[i]; + var last = block.last; + if (last is HExitTry) { + block.addSuccessor(successor); + } + } + } + + // Setup all successors. The entry block that contains the [HTry] + // has 1) the body, 2) the catch, 3) the finally, and 4) the exit + // blocks as successors. + enterBlock.addSuccessor(startTryBlock); + addOptionalSuccessor(enterBlock, startCatchBlock); + addOptionalSuccessor(enterBlock, startFinallyBlock); + enterBlock.addSuccessor(exitBlock); + + // The body has either the catch or the finally block as successor. + if (endTryBlock != null) { + assert(startCatchBlock != null || startFinallyBlock != null); + endTryBlock.addSuccessor( + startCatchBlock != null ? startCatchBlock : startFinallyBlock); + endTryBlock.addSuccessor(exitBlock); + } + + // The catch block has either the finally or the exit block as + // successor. + if (endCatchBlock != null) { + endCatchBlock.addSuccessor( + startFinallyBlock != null ? startFinallyBlock : exitBlock); + } + + // The finally block has the exit block as successor. + if (endFinallyBlock != null) { + endFinallyBlock.addSuccessor(exitBlock); + } + + // If a block inside try/catch aborts (eg with a return statement), + // we explicitely mark this block a predecessor of the catch + // block and the finally block. + addExitTrySuccessor(startCatchBlock); + addExitTrySuccessor(startFinallyBlock); + + // Use the locals handler not altered by the catch and finally + // blocks. + localsHandler = savedLocals; + open(exitBlock); + enterBlock.setBlockFlow( + new HTryBlockInformation( + wrapStatementGraph(bodyGraph), + exception, + wrapStatementGraph(catchGraph), + wrapStatementGraph(finallyGraph)), + exitBlock); + inTryStatement = oldInTryStatement; + } + + visitCatchBlock(ast.CatchBlock node) { + visit(node.block); + } + + visitTypedef(ast.Typedef node) { + compiler.unimplemented(node, 'SsaFromAstMixin.visitTypedef.'); + } + + visitTypeVariable(ast.TypeVariable node) { + compiler.internalError(node, 'SsaFromAstMixin.visitTypeVariable.'); + } + + /** + * This method is invoked before inlining the body of [function] into this + * [SsaBuilder]. + */ + void enterInlinedMethod(FunctionElement function, + ast.Node _, + List compiledArguments) { + AstInliningState state = new AstInliningState( + function, returnElement, returnType, elements, stack, localsHandler, + inTryStatement); + inliningStack.add(state); + + // Setting up the state of the (AST) builder is performed even when the + // inlined function is in IR, because the irInliner uses the [returnElement] + // of the AST builder. + setupStateForInlining(function, compiledArguments); + } + + void leaveInlinedMethod() { + HInstruction result = localsHandler.readLocal(returnElement); + AstInliningState state = inliningStack.removeLast(); + restoreState(state); + stack.add(result); + } + + void doInline(FunctionElement function) { + visitInlinedFunction(function); + } + + void emitReturn(HInstruction value, ast.Node node) { + if (inliningStack.isEmpty) { + closeAndGotoExit(attachPosition(new HReturn(value), node)); + } else { + localsHandler.updateLocal(returnElement, value); + } + } +} + +/** + * Visitor that handles generation of string literals (LiteralString, + * StringInterpolation), and otherwise delegates to the given visitor for + * non-literal subexpressions. + * TODO(lrn): Consider whether to handle compile time constant int/boolean + * expressions as well. + */ +class StringBuilderVisitor extends ast.Visitor { + final SsaBuilder builder; + final ast.Node diagnosticNode; + + /** + * The string value generated so far. + */ + HInstruction result = null; + + StringBuilderVisitor(this.builder, this.diagnosticNode); + + void visit(ast.Node node) { + node.accept(this); + } + + visitNode(ast.Node node) { + builder.compiler.internalError(node, 'Unexpected node.'); + } + + void visitExpression(ast.Node node) { + node.accept(builder); + HInstruction expression = builder.pop(); + if (!expression.isConstantString()) { + expression = new HStringify(expression, node, builder.backend.stringType); + builder.add(expression); + } + result = (result == null) ? expression : concat(result, expression); + } + + void visitStringInterpolation(ast.StringInterpolation node) { + node.visitChildren(this); + } + + void visitStringInterpolationPart(ast.StringInterpolationPart node) { + visit(node.expression); + visit(node.string); + } + + void visitStringJuxtaposition(ast.StringJuxtaposition node) { + node.visitChildren(this); + } + + void visitNodeList(ast.NodeList node) { + node.visitChildren(this); + } + + HInstruction concat(HInstruction left, HInstruction right) { + HInstruction instruction = new HStringConcat( + left, right, diagnosticNode, builder.backend.stringType); + builder.add(instruction); + return instruction; + } +} + +/** + * This class visits the method that is a candidate for inlining and + * finds whether it is too difficult to inline. + */ +class InlineWeeder extends ast.Visitor { + // Invariant: *INSIDE_LOOP* > *OUTSIDE_LOOP* + static const INLINING_NODES_OUTSIDE_LOOP = 18; + static const INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR = 3; + static const INLINING_NODES_INSIDE_LOOP = 42; + static const INLINING_NODES_INSIDE_LOOP_ARG_FACTOR = 4; + + bool seenReturn = false; + bool tooDifficult = false; + int nodeCount = 0; + final int maxInliningNodes; + final bool useMaxInliningNodes; + + InlineWeeder(this.maxInliningNodes, this.useMaxInliningNodes); + + static bool canBeInlined(ast.FunctionExpression functionExpression, + int maxInliningNodes, + bool useMaxInliningNodes) { + InlineWeeder weeder = + new InlineWeeder(maxInliningNodes, useMaxInliningNodes); + weeder.visit(functionExpression.initializers); + weeder.visit(functionExpression.body); + return !weeder.tooDifficult; + } + + bool registerNode() { + if (!useMaxInliningNodes) return true; + if (nodeCount++ > maxInliningNodes) { + tooDifficult = true; + return false; + } else { + return true; + } + } + + void visit(ast.Node node) { + if (node != null) node.accept(this); + } + + void visitNode(ast.Node node) { + if (!registerNode()) return; + if (seenReturn) { + tooDifficult = true; + } else { + node.visitChildren(this); + } + } + + void visitFunctionExpression(ast.Node node) { + if (!registerNode()) return; + tooDifficult = true; + } + + void visitFunctionDeclaration(ast.Node node) { + if (!registerNode()) return; + tooDifficult = true; + } + + void visitSend(ast.Send node) { + if (!registerNode()) return; + node.visitChildren(this); + } + + visitLoop(ast.Node node) { + // It's actually not difficult to inline a method with a loop, but + // our measurements show that it's currently better to not inline a + // method that contains a loop. + tooDifficult = true; + } + + void visitRethrow(ast.Rethrow node) { + if (!registerNode()) return; + tooDifficult = true; + } + + void visitReturn(ast.Return node) { + if (!registerNode()) return; + if (seenReturn + || identical(node.getBeginToken().stringValue, 'native') + || node.isRedirectingFactoryBody) { + tooDifficult = true; + return; + } + node.visitChildren(this); + seenReturn = true; + } + + void visitTryStatement(ast.Node node) { + if (!registerNode()) return; + tooDifficult = true; + } + + void visitThrow(ast.Throw node) { + if (!registerNode()) return; + // For now, we don't want to handle throw after a return even if + // it is in an "if". + if (seenReturn) tooDifficult = true; + } +} + +abstract class InliningState { + /** + * Invariant: [function] must be an implementation element. + */ + final FunctionElement function; + + InliningState(this.function) { + assert(function.isImplementation); + } +} + +class AstInliningState extends InliningState { + final Element oldReturnElement; + final DartType oldReturnType; + final TreeElements oldElements; + final List oldStack; + final LocalsHandler oldLocalsHandler; + final bool inTryStatement; + + AstInliningState(FunctionElement function, + this.oldReturnElement, + this.oldReturnType, + this.oldElements, + this.oldStack, + this.oldLocalsHandler, + this.inTryStatement): super(function); +} + +class SsaBranch { + final SsaBranchBuilder branchBuilder; + final HBasicBlock block; + LocalsHandler startLocals; + LocalsHandler exitLocals; + SubGraph graph; + + SsaBranch(this.branchBuilder) : block = new HBasicBlock(); +} + +class SsaBranchBuilder { + final SsaBuilder builder; + final ast.Node diagnosticNode; + + SsaBranchBuilder(this.builder, [this.diagnosticNode]); + + Compiler get compiler => builder.compiler; + + void checkNotAborted() { + if (builder.isAborted()) { + compiler.unimplemented(diagnosticNode, "aborted control flow"); + } + } + + void buildCondition(void visitCondition(), + SsaBranch conditionBranch, + SsaBranch thenBranch, + SsaBranch elseBranch) { + startBranch(conditionBranch); + visitCondition(); + checkNotAborted(); + assert(identical(builder.current, builder.lastOpenedBlock)); + HInstruction conditionValue = builder.popBoolified(); + HIf branch = new HIf(conditionValue); + HBasicBlock conditionExitBlock = builder.current; + builder.close(branch); + conditionBranch.exitLocals = builder.localsHandler; + conditionExitBlock.addSuccessor(thenBranch.block); + conditionExitBlock.addSuccessor(elseBranch.block); + bool conditionBranchLocalsCanBeReused = + mergeLocals(conditionBranch, thenBranch, mayReuseFromLocals: true); + mergeLocals(conditionBranch, elseBranch, + mayReuseFromLocals: conditionBranchLocalsCanBeReused); + + conditionBranch.graph = + new SubExpression(conditionBranch.block, conditionExitBlock); + } + + /** + * Returns true if the locals of the [fromBranch] may be reused. A [:true:] + * return value implies that [mayReuseFromLocals] was set to [:true:]. + */ + bool mergeLocals(SsaBranch fromBranch, SsaBranch toBranch, + {bool mayReuseFromLocals}) { + LocalsHandler fromLocals = fromBranch.exitLocals; + if (toBranch.startLocals == null) { + if (mayReuseFromLocals) { + toBranch.startLocals = fromLocals; + return false; + } else { + toBranch.startLocals = new LocalsHandler.from(fromLocals); + return true; + } + } else { + toBranch.startLocals.mergeWith(fromLocals, toBranch.block); + return true; + } + } + + void startBranch(SsaBranch branch) { + builder.graph.addBlock(branch.block); + builder.localsHandler = branch.startLocals; + builder.open(branch.block); + } + + HInstruction buildBranch(SsaBranch branch, + void visitBranch(), + SsaBranch joinBranch, + bool isExpression) { + startBranch(branch); + visitBranch(); + branch.graph = new SubGraph(branch.block, builder.lastOpenedBlock); + branch.exitLocals = builder.localsHandler; + if (!builder.isAborted()) { + builder.goto(builder.current, joinBranch.block); + mergeLocals(branch, joinBranch, mayReuseFromLocals: true); + } + if (isExpression) { + checkNotAborted(); + return builder.pop(); + } + return null; + } + + handleIf(void visitCondition(), void visitThen(), void visitElse()) { + if (visitElse == null) { + // Make sure to have an else part to avoid a critical edge. A + // critical edge is an edge that connects a block with multiple + // successors to a block with multiple predecessors. We avoid + // such edges because they prevent inserting copies during code + // generation of phi instructions. + visitElse = () {}; + } + + _handleDiamondBranch(visitCondition, visitThen, visitElse, false); + } + + handleConditional(void visitCondition(), void visitThen(), void visitElse()) { + assert(visitElse != null); + _handleDiamondBranch(visitCondition, visitThen, visitElse, true); + } + + void handleLogicalAndOr(void left(), void right(), {bool isAnd}) { + // x && y is transformed into: + // t0 = boolify(x); + // if (t0) { + // t1 = boolify(y); + // } + // result = phi(t1, false); + // + // x || y is transformed into: + // t0 = boolify(x); + // if (not(t0)) { + // t1 = boolify(y); + // } + // result = phi(t1, true); + HInstruction boolifiedLeft; + HInstruction boolifiedRight; + + void visitCondition() { + left(); + boolifiedLeft = builder.popBoolified(); + builder.stack.add(boolifiedLeft); + if (!isAnd) { + builder.push(new HNot(builder.pop(), builder.backend.boolType)); + } + } + + void visitThen() { + right(); + boolifiedRight = builder.popBoolified(); + } + + handleIf(visitCondition, visitThen, null); + HConstant notIsAnd = + builder.graph.addConstantBool(!isAnd, builder.compiler); + JavaScriptBackend backend = builder.backend; + HPhi result = new HPhi.manyInputs(null, + [boolifiedRight, notIsAnd], + backend.dynamicType); + builder.current.addPhi(result); + builder.stack.add(result); + } + + void handleLogicalAndOrWithLeftNode(ast.Node left, + void visitRight(), + {bool isAnd}) { + // This method is similar to [handleLogicalAndOr] but optimizes the case + // where left is a logical "and" or logical "or". + // + // For example (x && y) && z is transformed into x && (y && z): + // t0 = boolify(x); + // if (t0) { + // t1 = boolify(y); + // if (t1) { + // t2 = boolify(z); + // } + // t3 = phi(t2, false); + // } + // result = phi(t3, false); + + ast.Send send = left.asSend(); + if (send != null && + (isAnd ? send.isLogicalAnd : send.isLogicalOr)) { + ast.Node newLeft = send.receiver; + Link link = send.argumentsNode.nodes; + assert(link.tail.isEmpty); + ast.Node middle = link.head; + handleLogicalAndOrWithLeftNode( + newLeft, + () => handleLogicalAndOrWithLeftNode(middle, visitRight, + isAnd: isAnd), + isAnd: isAnd); + } else { + handleLogicalAndOr(() => builder.visit(left), visitRight, isAnd: isAnd); + } + } + + void _handleDiamondBranch(void visitCondition(), + void visitThen(), + void visitElse(), + bool isExpression) { + SsaBranch conditionBranch = new SsaBranch(this); + SsaBranch thenBranch = new SsaBranch(this); + SsaBranch elseBranch = new SsaBranch(this); + SsaBranch joinBranch = new SsaBranch(this); + + conditionBranch.startLocals = builder.localsHandler; + builder.goto(builder.current, conditionBranch.block); + + buildCondition(visitCondition, conditionBranch, thenBranch, elseBranch); + HInstruction thenValue = + buildBranch(thenBranch, visitThen, joinBranch, isExpression); + HInstruction elseValue = + buildBranch(elseBranch, visitElse, joinBranch, isExpression); + + if (isExpression) { + assert(thenValue != null && elseValue != null); + JavaScriptBackend backend = builder.backend; + HPhi phi = new HPhi.manyInputs( + null, [thenValue, elseValue], backend.dynamicType); + joinBranch.block.addPhi(phi); + builder.stack.add(phi); + } + + HBasicBlock thenBlock = thenBranch.block; + HBasicBlock elseBlock = elseBranch.block; + HBasicBlock joinBlock; + // If at least one branch did not abort, open the joinBranch. + if (!joinBranch.block.predecessors.isEmpty) { + startBranch(joinBranch); + joinBlock = joinBranch.block; + } + + HIfBlockInformation info = + new HIfBlockInformation( + new HSubExpressionBlockInformation(conditionBranch.graph), + new HSubGraphBlockInformation(thenBranch.graph), + new HSubGraphBlockInformation(elseBranch.graph)); + + HBasicBlock conditionStartBlock = conditionBranch.block; + conditionStartBlock.setBlockFlow(info, joinBlock); + SubGraph conditionGraph = conditionBranch.graph; + HIf branch = conditionGraph.end.last; + assert(branch is HIf); + branch.blockInformation = conditionStartBlock.blockFlow; + } +} + +class TypeBuilder implements DartTypeVisitor { + void visitType(DartType type, _) { + throw 'Internal error $type'; + } + + void visitVoidType(VoidType type, SsaBuilder builder) { + ClassElement cls = builder.compiler.findHelper('VoidRuntimeType'); + builder.push(new HVoidType(type, new TypeMask.exact(cls))); + } + + void visitTypeVariableType(TypeVariableType type, + SsaBuilder builder) { + ClassElement cls = builder.compiler.findHelper('RuntimeType'); + TypeMask instructionType = new TypeMask.subclass(cls); + if (!builder.sourceElement.enclosingElement.isClosure() && + builder.sourceElement.isInstanceMember()) { + HInstruction receiver = builder.localsHandler.readThis(); + builder.push(new HReadTypeVariable(type, receiver, instructionType)); + } else { + builder.push( + new HReadTypeVariable.noReceiver( + type, builder.addTypeVariableReference(type), instructionType)); + } + } + + void visitFunctionType(FunctionType type, SsaBuilder builder) { + type.returnType.accept(this, builder); + HInstruction returnType = builder.pop(); + List inputs = [returnType]; + + for (DartType parameter in type.parameterTypes) { + parameter.accept(this, builder); + inputs.add(builder.pop()); + } + + for (DartType parameter in type.optionalParameterTypes) { + parameter.accept(this, builder); + inputs.add(builder.pop()); + } + + Link namedParameterTypes = type.namedParameterTypes; + for (String name in type.namedParameters) { + ast.DartString dartString = new ast.DartString.literal(name); + inputs.add( + builder.graph.addConstantString(dartString, builder.compiler)); + namedParameterTypes.head.accept(this, builder); + inputs.add(builder.pop()); + namedParameterTypes = namedParameterTypes.tail; + } + + ClassElement cls = builder.compiler.findHelper('RuntimeFunctionType'); + builder.push(new HFunctionType(inputs, type, new TypeMask.exact(cls))); + } + + void visitMalformedType(MalformedType type, SsaBuilder builder) { + visitDynamicType(builder.compiler.types.dynamicType, builder); + } + + void visitStatementType(StatementType type, SsaBuilder builder) { + throw 'not implemented visitStatementType($type)'; + } + + void visitGenericType(GenericType type, SsaBuilder builder) { + throw 'not implemented visitGenericType($type)'; + } + + void visitInterfaceType(InterfaceType type, SsaBuilder builder) { + List inputs = []; + for (DartType typeArgument in type.typeArguments) { + typeArgument.accept(this, builder); + inputs.add(builder.pop()); + } + ClassElement cls; + if (type.typeArguments.isEmpty) { + cls = builder.compiler.findHelper('RuntimeTypePlain'); + } else { + cls = builder.compiler.findHelper('RuntimeTypeGeneric'); + } + builder.push(new HInterfaceType(inputs, type, new TypeMask.exact(cls))); + } + + void visitTypedefType(TypedefType type, SsaBuilder builder) { + DartType unaliased = type.unalias(builder.compiler); + if (unaliased is TypedefType) throw 'unable to unalias $type'; + unaliased.accept(this, builder); + } + + void visitDynamicType(DynamicType type, SsaBuilder builder) { + ClassElement cls = builder.compiler.findHelper('DynamicRuntimeType'); + builder.push(new HDynamicType(type, new TypeMask.exact(cls))); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/codegen.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/codegen.dart new file mode 100644 index 0000000..b18b054 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/codegen.dart @@ -0,0 +1,2681 @@ +// Copyright (c) 2012, 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. + +part of ssa; + +class SsaCodeGeneratorTask extends CompilerTask { + + final JavaScriptBackend backend; + + SsaCodeGeneratorTask(JavaScriptBackend backend) + : this.backend = backend, + super(backend.compiler); + String get name => 'SSA code generator'; + NativeEmitter get nativeEmitter => backend.emitter.nativeEmitter; + + + js.Node attachPosition(js.Node node, Element element) { + // TODO(sra): Attaching positions might be cleaner if the source position + // was on a wrapping node. + SourceFile sourceFile = sourceFileOfElement(element); + ast.Node expression = element.implementation.parseNode(backend.compiler); + Token beginToken; + Token endToken; + if (expression == null) { + // Synthesized node. Use the enclosing element for the location. + beginToken = endToken = element.position(); + } else { + beginToken = expression.getBeginToken(); + endToken = expression.getEndToken(); + } + // TODO(podivilov): find the right sourceFile here and remove offset + // checks below. + if (beginToken.charOffset < sourceFile.length) { + node.sourcePosition = + new TokenSourceFileLocation(sourceFile, beginToken); + } + if (endToken.charOffset < sourceFile.length) { + node.endSourcePosition = + new TokenSourceFileLocation(sourceFile, endToken); + } + return node; + } + + SourceFile sourceFileOfElement(Element element) { + // TODO(johnniwinther): remove the 'element.patch' hack. + FunctionElement functionElement = element.asFunctionElement(); + if (functionElement != null && functionElement.patch != null) { + element = functionElement.patch; + } + return element.getCompilationUnit().script.file; + } + + js.Fun buildJavaScriptFunction(FunctionElement element, + List parameters, + js.Block body) { + return attachPosition(new js.Fun(parameters, body), element); + } + + js.Expression generateCode(CodegenWorkItem work, HGraph graph) { + if (work.element.isField()) { + return generateLazyInitializer(work, graph); + } else { + return generateMethod(work, graph); + } + } + + js.Expression generateLazyInitializer(work, graph) { + return measure(() { + compiler.tracer.traceGraph("codegen", graph); + SsaCodeGenerator codegen = new SsaCodeGenerator(backend, work); + codegen.visitGraph(graph); + return new js.Fun(codegen.parameters, + attachPosition(codegen.body, work.element)); + }); + } + + js.Expression generateMethod(CodegenWorkItem work, HGraph graph) { + return measure(() { + SsaCodeGenerator codegen = new SsaCodeGenerator(backend, work); + codegen.visitGraph(graph); + compiler.tracer.traceGraph("codegen", graph); + FunctionElement element = work.element; + return buildJavaScriptFunction(element, codegen.parameters, codegen.body); + }); + } +} + +typedef void ElementAction(Element element); + +class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { + /** + * Returned by [expressionType] to tell how code can be generated for + * a subgraph. + * - [TYPE_STATEMENT] means that the graph must be generated as a statement, + * which is always possible. + * - [TYPE_EXPRESSION] means that the graph can be generated as an expression, + * or possibly several comma-separated expressions. + * - [TYPE_DECLARATION] means that the graph can be generated as an + * expression, and that it only generates expressions of the form + * variable = expression + * which are also valid as parts of a "var" declaration. + */ + static const int TYPE_STATEMENT = 0; + static const int TYPE_EXPRESSION = 1; + static const int TYPE_DECLARATION = 2; + + /** + * Whether we are currently generating expressions instead of statements. + * This includes declarations, which are generated as expressions. + */ + bool isGeneratingExpression = false; + + final JavaScriptBackend backend; + final CodegenWorkItem work; + + final Set generateAtUseSite; + final Set controlFlowOperators; + final Map breakAction; + final Map continueAction; + final List parameters; + + js.Block currentContainer; + js.Block get body => currentContainer; + List expressionStack; + List oldContainerStack; + + /** + * Contains the names of the instructions, as well as the parallel + * copies to perform on block transitioning. + */ + VariableNames variableNames; + bool shouldGroupVarDeclarations = false; + + /** + * While generating expressions, we can't insert variable declarations. + * Instead we declare them at the start of the function. When minifying + * we do this most of the time, because it reduces the size unless there + * is only one variable. + */ + final Set collectedVariableDeclarations; + + /** + * Set of variables and parameters that have already been declared. + */ + final Set declaredLocals; + + int indent = 0; + HGraph currentGraph; + + // Records a block-information that is being handled specially. + // Used to break bad recursion. + HBlockInformation currentBlockInformation; + // The subgraph is used to delimit traversal for some constructions, e.g., + // if branches. + SubGraph subGraph; + + SsaCodeGenerator(this.backend, CodegenWorkItem work) + : this.work = work, + declaredLocals = new Set(), + collectedVariableDeclarations = new Set(), + currentContainer = new js.Block.empty(), + parameters = [], + expressionStack = [], + oldContainerStack = [], + generateAtUseSite = new Set(), + controlFlowOperators = new Set(), + breakAction = new Map(), + continueAction = new Map(); + + Compiler get compiler => backend.compiler; + NativeEmitter get nativeEmitter => backend.emitter.nativeEmitter; + CodegenEnqueuer get world => backend.compiler.enqueuer.codegen; + + bool isGenerateAtUseSite(HInstruction instruction) { + return generateAtUseSite.contains(instruction); + } + + bool hasNonBitOpUser(HInstruction instruction, Set phiSet) { + for (HInstruction user in instruction.usedBy) { + if (user is HPhi) { + if (!phiSet.contains(user)) { + phiSet.add(user); + if (hasNonBitOpUser(user, phiSet)) return true; + } + } else if (user is! HBitNot && user is! HBinaryBitOp) { + return true; + } + } + return false; + } + + bool requiresUintConversion(instruction) { + if (instruction.isUInt31(compiler)) return false; + // If the result of a bit-operation is only used by other bit + // operations, we do not have to convert to an unsigned integer. + return hasNonBitOpUser(instruction, new Set()); + } + + /** + * If the [instruction] is not `null` it will be used to attach the position + * to the [statement]. + */ + void pushStatement(js.Statement statement, [HInstruction instruction]) { + assert(expressionStack.isEmpty); + if (instruction != null) { + attachLocation(statement, instruction); + } + currentContainer.statements.add(statement); + } + + void insertStatementAtStart(js.Statement statement) { + currentContainer.statements.insert(0, statement); + } + + /** + * If the [instruction] is not `null` it will be used to attach the position + * to the [expression]. + */ + pushExpressionAsStatement(js.Expression expression, + [HInstruction instruction]) { + pushStatement(new js.ExpressionStatement(expression), instruction); + } + + /** + * If the [instruction] is not `null` it will be used to attach the position + * to the [expression]. + */ + push(js.Expression expression, [HInstruction instruction]) { + if (instruction != null) { + attachLocation(expression, instruction); + } + expressionStack.add(expression); + } + + js.Expression pop() { + return expressionStack.removeLast(); + } + + attachLocationToLast(HInstruction instruction) { + attachLocation(expressionStack.last, instruction); + } + + js.Node attachLocation(js.Node jsNode, HInstruction instruction) { + jsNode.sourcePosition = instruction.sourcePosition; + return jsNode; + } + + js.Node attachLocationRange(js.Node jsNode, + SourceFileLocation sourcePosition, + SourceFileLocation endSourcePosition) { + jsNode.sourcePosition = sourcePosition; + jsNode.endSourcePosition = endSourcePosition; + return jsNode; + } + + void preGenerateMethod(HGraph graph) { + new SsaInstructionSelection(compiler).visitGraph(graph); + new SsaTypeKnownRemover().visitGraph(graph); + new SsaInstructionMerger(generateAtUseSite, compiler).visitGraph(graph); + new SsaConditionMerger( + generateAtUseSite, controlFlowOperators).visitGraph(graph); + SsaLiveIntervalBuilder intervalBuilder = new SsaLiveIntervalBuilder( + compiler, generateAtUseSite, controlFlowOperators); + intervalBuilder.visitGraph(graph); + SsaVariableAllocator allocator = new SsaVariableAllocator( + compiler, + intervalBuilder.liveInstructions, + intervalBuilder.liveIntervals, + generateAtUseSite); + allocator.visitGraph(graph); + variableNames = allocator.names; + shouldGroupVarDeclarations = allocator.names.numberOfVariables > 1; + } + + void handleDelayedVariableDeclarations() { + // If we have only one variable declaration and the first statement is an + // assignment to that variable then we can merge the two. We count the + // number of variables in the variable allocator to try to avoid this issue, + // but it sometimes happens that the variable allocator introduces a + // temporary variable that it later eliminates. + if (!collectedVariableDeclarations.isEmpty) { + if (collectedVariableDeclarations.length == 1 && + currentContainer.statements.length >= 1 && + currentContainer.statements[0] is js.ExpressionStatement) { + String name = collectedVariableDeclarations.first; + js.ExpressionStatement statement = currentContainer.statements[0]; + if (statement.expression is js.Assignment) { + js.Assignment assignment = statement.expression; + if (!assignment.isCompound && + assignment.leftHandSide is js.VariableReference) { + js.VariableReference variableReference = assignment.leftHandSide; + if (variableReference.name == name) { + js.VariableDeclaration decl = new js.VariableDeclaration(name); + js.VariableInitialization initialization = + new js.VariableInitialization(decl, assignment.value); + currentContainer.statements[0] = new js.ExpressionStatement( + new js.VariableDeclarationList([initialization])); + return; + } + } + } + } + // If we can't merge the declaration with the first assignment then we + // just do it with a new var z,y,x; statement. + List declarations = + []; + collectedVariableDeclarations.forEach((String name) { + declarations.add(new js.VariableInitialization( + new js.VariableDeclaration(name), null)); + }); + var declarationList = new js.VariableDeclarationList(declarations); + insertStatementAtStart(new js.ExpressionStatement(declarationList)); + } + } + + visitGraph(HGraph graph) { + preGenerateMethod(graph); + currentGraph = graph; + indent++; // We are already inside a function. + subGraph = new SubGraph(graph.entry, graph.exit); + visitBasicBlock(graph.entry); + handleDelayedVariableDeclarations(); + } + + void visitSubGraph(SubGraph newSubGraph) { + SubGraph oldSubGraph = subGraph; + subGraph = newSubGraph; + visitBasicBlock(subGraph.start); + subGraph = oldSubGraph; + } + + /** + * Check whether a sub-graph can be generated as an expression, or even + * as a declaration, or if it has to fall back to being generated as + * a statement. + * Expressions are anything that doesn't generate control flow constructs. + * Declarations must only generate assignments on the form "id = expression", + * and not, e.g., expressions where the value isn't assigned, or where it's + * assigned to something that's not a simple variable. + */ + int expressionType(HExpressionInformation info) { + // The only HExpressionInformation used as part of a HBlockInformation is + // current HSubExpressionBlockInformation, so it's the only one reaching + // here. If we start using the other HExpressionInformation types too, + // this code should be generalized. + assert(info is HSubExpressionBlockInformation); + HSubExpressionBlockInformation expressionInfo = info; + SubGraph limits = expressionInfo.subExpression; + + // Start assuming that we can generate declarations. If we find a + // counter-example, we degrade our assumption to either expression or + // statement, and in the latter case, we can return immediately since + // it can't get any worse. E.g., a function call where the return value + // isn't used can't be in a declaration. + int result = TYPE_DECLARATION; + HBasicBlock basicBlock = limits.start; + do { + HInstruction current = basicBlock.first; + while (current != basicBlock.last) { + // E.g, bounds check. + if (current.isControlFlow()) { + return TYPE_STATEMENT; + } + // HFieldSet generates code on the form x.y = ..., which isn't + // valid in a declaration, but it also always have no uses, so + // it's caught by that test too. + assert(current is! HFieldSet || current.usedBy.isEmpty); + if (current.usedBy.isEmpty) { + result = TYPE_EXPRESSION; + } + current = current.next; + } + if (current is HGoto) { + basicBlock = basicBlock.successors[0]; + } else if (current is HConditionalBranch) { + if (generateAtUseSite.contains(current)) { + // Short-circuit control flow operator trickery. + // Check the second half, which will continue into the join. + // (The first half is [inputs[0]], the second half is [successors[0]], + // and [successors[1]] is the join-block). + basicBlock = basicBlock.successors[0]; + } else { + // We allow an expression to end on an HIf (a condition expression). + return identical(basicBlock, limits.end) ? result : TYPE_STATEMENT; + } + } else { + // Expression-incompatible control flow. + return TYPE_STATEMENT; + } + } while (limits.contains(basicBlock)); + return result; + } + + bool isJSExpression(HExpressionInformation info) { + return !identical(expressionType(info), TYPE_STATEMENT); + } + + bool isJSCondition(HExpressionInformation info) { + HSubExpressionBlockInformation graph = info; + SubExpression limits = graph.subExpression; + return !identical(expressionType(info), TYPE_STATEMENT) && + (limits.end.last is HConditionalBranch); + } + + /** + * Generate statements from block information. + * If the block information contains expressions, generate only + * assignments, and if it ends in a conditional branch, don't generate + * the condition. + */ + void generateStatements(HBlockInformation block) { + if (block is HStatementInformation) { + block.accept(this); + } else { + HSubExpressionBlockInformation expression = block; + visitSubGraph(expression.subExpression); + } + } + + js.Block generateStatementsInNewBlock(HBlockInformation block) { + js.Block result = new js.Block.empty(); + js.Block oldContainer = currentContainer; + currentContainer = result; + generateStatements(block); + currentContainer = oldContainer; + return result; + } + + /** + * If the [block] only contains one statement returns that statement. If the + * that statement itself is a block, recursively calls this method. + * + * If the block is empty, returns a new instance of [js.NOP]. + */ + js.Statement unwrapStatement(js.Block block) { + int len = block.statements.length; + if (len == 0) return new js.EmptyStatement(); + if (len == 1) { + js.Statement result = block.statements[0]; + if (result is ast.Block) return unwrapStatement(result); + return result; + } + return block; + } + + /** + * Generate expressions from block information. + */ + js.Expression generateExpression(HExpressionInformation expression) { + // Currently we only handle sub-expression graphs. + assert(expression is HSubExpressionBlockInformation); + + bool oldIsGeneratingExpression = isGeneratingExpression; + isGeneratingExpression = true; + List oldExpressionStack = expressionStack; + List sequenceElements = []; + expressionStack = sequenceElements; + HSubExpressionBlockInformation expressionSubGraph = expression; + visitSubGraph(expressionSubGraph.subExpression); + expressionStack = oldExpressionStack; + isGeneratingExpression = oldIsGeneratingExpression; + if (sequenceElements.isEmpty) { + // Happens when the initializer, condition or update of a loop is empty. + return null; + } else if (sequenceElements.length == 1) { + return sequenceElements[0]; + } else { + return new js.Sequence(sequenceElements); + } + } + + /** + * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET]. + */ + List visitArguments(List inputs, + {int start: HInvoke.ARGUMENTS_OFFSET}) { + assert(inputs.length >= start); + List result = []; + for (int i = start; i < inputs.length; i++) { + use(inputs[i]); + result.add(pop()); + } + return result; + } + + bool isVariableDeclared(String variableName) { + return declaredLocals.contains(variableName) || + collectedVariableDeclarations.contains(variableName); + } + + js.Expression generateExpressionAssignment(String variableName, + js.Expression value) { + if (value is js.Binary) { + js.Binary binary = value; + String op = binary.op; + if (op == '+' || op == '-' || op == '/' || op == '*' || op == '%' || + op == '^' || op == '&' || op == '|') { + if (binary.left is js.VariableUse && + (binary.left as js.VariableUse).name == variableName) { + // We know now, that we can shorten x = x + y into x += y. + // Also check for the shortcut where y equals 1: x++ and x--. + if ((op == '+' || op == '-') && + binary.right is js.LiteralNumber && + (binary.right as js.LiteralNumber).value == "1") { + return new js.Prefix(op == '+' ? '++' : '--', binary.left); + } + return new js.Assignment.compound(binary.left, op, binary.right); + } + } + } + return new js.Assignment(new js.VariableUse(variableName), value); + } + + void assignVariable(String variableName, js.Expression value) { + if (isGeneratingExpression) { + // If we are in an expression then we can't declare the variable here. + // We have no choice, but to use it and then declare it separately. + if (!isVariableDeclared(variableName)) { + collectedVariableDeclarations.add(variableName); + } + push(generateExpressionAssignment(variableName, value)); + // Otherwise if we are trying to declare inline and we are in a statement + // then we declare (unless it was already declared). + } else if (!shouldGroupVarDeclarations && + !declaredLocals.contains(variableName)) { + // It may be necessary to remove it from the ones to be declared later. + collectedVariableDeclarations.remove(variableName); + declaredLocals.add(variableName); + js.VariableDeclaration decl = new js.VariableDeclaration(variableName); + js.VariableInitialization initialization = + new js.VariableInitialization(decl, value); + + pushExpressionAsStatement(new js.VariableDeclarationList( + [initialization])); + } else { + // Otherwise we are just going to use it. If we have not already declared + // it then we make sure we will declare it later. + if (!declaredLocals.contains(variableName)) { + collectedVariableDeclarations.add(variableName); + } + pushExpressionAsStatement( + generateExpressionAssignment(variableName, value)); + } + } + + void define(HInstruction instruction) { + // For simple type checks like i = intTypeCheck(i), we don't have to + // emit an assignment, because the intTypeCheck just returns its + // argument. + bool needsAssignment = true; + if (instruction is HTypeConversion) { + HTypeConversion typeConversion = instruction; + String inputName = variableNames.getName(typeConversion.checkedInput); + if (variableNames.getName(instruction) == inputName) { + needsAssignment = false; + } + } + if (instruction is HLocalValue) { + needsAssignment = false; + } + + if (needsAssignment && + !instruction.isControlFlow() && variableNames.hasName(instruction)) { + visitExpression(instruction); + assignVariable(variableNames.getName(instruction), pop()); + return; + } + + if (isGeneratingExpression) { + visitExpression(instruction); + } else { + visitStatement(instruction); + } + } + + void use(HInstruction argument) { + if (isGenerateAtUseSite(argument)) { + visitExpression(argument); + } else if (argument is HCheck && !variableNames.hasName(argument)) { + HCheck check = argument; + // This can only happen if the checked node does not have a name. + assert(!variableNames.hasName(check.checkedInput)); + use(check.checkedInput); + } else { + assert(variableNames.hasName(argument)); + push(new js.VariableUse(variableNames.getName(argument))); + } + } + + visit(HInstruction node) { + node.accept(this); + } + + visitExpression(HInstruction node) { + bool oldIsGeneratingExpression = isGeneratingExpression; + isGeneratingExpression = true; + visit(node); + isGeneratingExpression = oldIsGeneratingExpression; + } + + visitStatement(HInstruction node) { + assert(!isGeneratingExpression); + visit(node); + if (!expressionStack.isEmpty) { + assert(expressionStack.length == 1); + pushExpressionAsStatement(pop()); + } + } + + void continueAsBreak(LabelElement target) { + pushStatement(new js.Break(backend.namer.continueLabelName(target))); + } + + void implicitContinueAsBreak(TargetElement target) { + pushStatement(new js.Break( + backend.namer.implicitContinueLabelName(target))); + } + + void implicitBreakWithLabel(TargetElement target) { + pushStatement(new js.Break(backend.namer.implicitBreakLabelName(target))); + } + + js.Statement wrapIntoLabels(js.Statement result, List labels) { + for (LabelElement label in labels) { + if (label.isTarget) { + String breakLabelString = backend.namer.breakLabelName(label); + result = new js.LabeledStatement(breakLabelString, result); + } + } + return result; + } + + + // The regular [visitIf] method implements the needed logic. + bool visitIfInfo(HIfBlockInformation info) => false; + + bool visitSwitchInfo(HSwitchBlockInformation info) { + bool isExpression = isJSExpression(info.expression); + if (!isExpression) { + generateStatements(info.expression); + } + + if (isExpression) { + push(generateExpression(info.expression)); + } else { + use(info.expression.conditionExpression); + } + js.Expression key = pop(); + List cases = []; + HSwitch switchInstruction = info.expression.end.last; + List inputs = switchInstruction.inputs; + List successors = switchInstruction.block.successors; + + js.Block oldContainer = currentContainer; + for (int inputIndex = 1, statementIndex = 0; + inputIndex < inputs.length; + statementIndex++) { + HBasicBlock successor = successors[inputIndex - 1]; + // If liveness analysis has figured out that this case is dead, + // omit the code for it. + if (successor.isLive) { + do { + visit(inputs[inputIndex]); + currentContainer = new js.Block.empty(); + cases.add(new js.Case(pop(), currentContainer)); + inputIndex++; + } while ((successors[inputIndex - 1] == successor) + && (inputIndex < inputs.length)); + + generateStatements(info.statements[statementIndex]); + } else { + // Skip all the case statements that belong to this + // block. + while ((successors[inputIndex - 1] == successor) + && (inputIndex < inputs.length)) { + ++inputIndex; + } + } + } + + // If the default case is dead, we omit it. Likewise, if it is an + // empty block, we omit it, too. + if (info.statements.last.start.isLive) { + currentContainer = new js.Block.empty(); + generateStatements(info.statements.last); + if (currentContainer.statements.isNotEmpty) { + cases.add(new js.Default(currentContainer)); + } + } + + currentContainer = oldContainer; + + js.Statement result = new js.Switch(key, cases); + pushStatement(wrapIntoLabels(result, info.labels)); + return true; + } + + bool visitSequenceInfo(HStatementSequenceInformation info) { + return false; + } + + bool visitSubGraphInfo(HSubGraphBlockInformation info) { + visitSubGraph(info.subGraph); + return true; + } + + bool visitSubExpressionInfo(HSubExpressionBlockInformation info) { + return false; + } + + bool visitAndOrInfo(HAndOrBlockInformation info) { + return false; + } + + bool visitTryInfo(HTryBlockInformation info) { + js.Block body = generateStatementsInNewBlock(info.body); + js.Catch catchPart = null; + js.Block finallyPart = null; + if (info.catchBlock != null) { + void register(ClassElement classElement) { + if (classElement != null) { + world.registerInstantiatedClass(classElement, work.resolutionTree); + } + } + register(backend.jsPlainJavaScriptObjectClass); + register(backend.jsUnknownJavaScriptObjectClass); + + HLocalValue exception = info.catchVariable; + String name = variableNames.getName(exception); + js.VariableDeclaration decl = new js.VariableDeclaration(name); + js.Block catchBlock = generateStatementsInNewBlock(info.catchBlock); + catchPart = new js.Catch(decl, catchBlock); + } + if (info.finallyBlock != null) { + finallyPart = generateStatementsInNewBlock(info.finallyBlock); + } + pushStatement(new js.Try(body, catchPart, finallyPart)); + return true; + } + + void visitBodyIgnoreLabels(HLoopBlockInformation info) { + if (info.body.start.isLabeledBlock()) { + HBlockInformation oldInfo = currentBlockInformation; + currentBlockInformation = info.body.start.blockFlow.body; + generateStatements(info.body); + currentBlockInformation = oldInfo; + } else { + generateStatements(info.body); + } + } + + bool visitLoopInfo(HLoopBlockInformation info) { + HExpressionInformation condition = info.condition; + bool isConditionExpression = isJSCondition(condition); + + js.Loop loop; + + switch (info.kind) { + // Treate all three "test-first" loops the same way. + case HLoopBlockInformation.FOR_LOOP: + case HLoopBlockInformation.WHILE_LOOP: + case HLoopBlockInformation.FOR_IN_LOOP: + case HLoopBlockInformation.SWITCH_CONTINUE_LOOP: + HBlockInformation initialization = info.initializer; + int initializationType = TYPE_STATEMENT; + if (initialization != null) { + initializationType = expressionType(initialization); + if (initializationType == TYPE_STATEMENT) { + generateStatements(initialization); + initialization = null; + } + } + if (isConditionExpression && + info.updates != null && isJSExpression(info.updates)) { + // If we have an updates graph, and it's expressible as an + // expression, generate a for-loop. + js.Expression jsInitialization = null; + if (initialization != null) { + int delayedVariablesCount = collectedVariableDeclarations.length; + jsInitialization = generateExpression(initialization); + if (!shouldGroupVarDeclarations && + delayedVariablesCount < collectedVariableDeclarations.length) { + // We just added a new delayed variable-declaration. See if we + // can put in a 'var' in front of the initialization to make it + // go away. + List expressions; + if (jsInitialization is js.Sequence) { + js.Sequence sequence = jsInitialization; + expressions = sequence.expressions; + } else { + expressions = [jsInitialization]; + } + bool canTransformToVariableDeclaration = true; + for (js.Expression expression in expressions) { + bool expressionIsVariableAssignment = false; + if (expression is js.Assignment) { + js.Assignment assignment = expression; + if (assignment.leftHandSide is js.VariableUse && + assignment.compoundTarget == null) { + expressionIsVariableAssignment = true; + } + } + if (!expressionIsVariableAssignment) { + canTransformToVariableDeclaration = false; + break; + } + } + if (canTransformToVariableDeclaration) { + List inits = + []; + for (js.Assignment assignment in expressions) { + String id = (assignment.leftHandSide as js.VariableUse).name; + js.Node declaration = new js.VariableDeclaration(id); + inits.add(new js.VariableInitialization(declaration, + assignment.value)); + collectedVariableDeclarations.remove(id); + declaredLocals.add(id); + } + jsInitialization = new js.VariableDeclarationList(inits); + } + } + } + js.Expression jsCondition = generateExpression(condition); + js.Expression jsUpdates = generateExpression(info.updates); + // The body might be labeled. Ignore this when recursing on the + // subgraph. + // TODO(lrn): Remove this extra labeling when handling all loops + // using subgraphs. + js.Block oldContainer = currentContainer; + js.Statement body = new js.Block.empty(); + currentContainer = body; + visitBodyIgnoreLabels(info); + currentContainer = oldContainer; + body = unwrapStatement(body); + loop = new js.For(jsInitialization, jsCondition, jsUpdates, body); + } else { + // We have either no update graph, or it's too complex to + // put in an expression. + if (initialization != null) { + generateStatements(initialization); + } + js.Expression jsCondition; + js.Block oldContainer = currentContainer; + js.Statement body = new js.Block.empty(); + if (isConditionExpression) { + jsCondition = generateExpression(condition); + currentContainer = body; + } else { + jsCondition = newLiteralBool(true); + currentContainer = body; + generateStatements(condition); + use(condition.conditionExpression); + js.Expression ifTest = new js.Prefix("!", pop()); + js.Break jsBreak = new js.Break(null); + pushStatement(new js.If.noElse(ifTest, jsBreak)); + } + if (info.updates != null) { + wrapLoopBodyForContinue(info); + generateStatements(info.updates); + } else { + visitBodyIgnoreLabels(info); + } + currentContainer = oldContainer; + body = unwrapStatement(body); + loop = new js.While(jsCondition, body); + } + break; + case HLoopBlockInformation.DO_WHILE_LOOP: + if (info.initializer != null) { + generateStatements(info.initializer); + } + js.Block oldContainer = currentContainer; + js.Block body = new js.Block.empty(); + // If there are phi copies in the block that jumps to the + // loop entry, we must emit the condition like this: + // do { + // body; + // if (condition) { + // phi updates; + // continue; + // } else { + // break; + // } + // } while (true); + HBasicBlock avoidEdge = info.end.successors[0]; + js.Block updateBody = new js.Block.empty(); + currentContainer = updateBody; + assignPhisOfSuccessors(avoidEdge); + bool hasPhiUpdates = !updateBody.statements.isEmpty; + currentContainer = body; + visitBodyIgnoreLabels(info); + if (info.updates != null) { + generateStatements(info.updates); + } + if (isConditionExpression) { + push(generateExpression(condition)); + } else { + generateStatements(condition); + use(condition.conditionExpression); + } + js.Expression jsCondition = pop(); + if (jsCondition == null) { + // If the condition is dead code, we turn the do-while into + // a simpler while because we will never reach the condition + // at the end of the loop anyway. + loop = new js.While(newLiteralBool(true), unwrapStatement(body)); + } else { + if (hasPhiUpdates) { + updateBody.statements.add(new js.Continue(null)); + body.statements.add( + new js.If(jsCondition, updateBody, new js.Break(null))); + jsCondition = newLiteralBool(true); + } + loop = new js.Do(unwrapStatement(body), jsCondition); + } + currentContainer = oldContainer; + break; + default: + compiler.internalError(condition.conditionExpression, + 'Unexpected loop kind: ${info.kind}.'); + } + attachLocationRange(loop, info.sourcePosition, info.endSourcePosition); + js.Statement result = loop; + if (info.kind == HLoopBlockInformation.SWITCH_CONTINUE_LOOP) { + String continueLabelString = + backend.namer.implicitContinueLabelName(info.target); + result = new js.LabeledStatement(continueLabelString, result); + } + pushStatement(wrapIntoLabels(result, info.labels)); + return true; + } + + bool visitLabeledBlockInfo(HLabeledBlockInformation labeledBlockInfo) { + Link continueOverrides = const Link(); + + js.Block oldContainer = currentContainer; + js.Block body = new js.Block.empty(); + js.Statement result = body; + + currentContainer = body; + + // If [labeledBlockInfo.isContinue], the block is an artificial + // block around the body of a loop with an update block, so that + // continues of the loop can be written as breaks of the body + // block. + if (labeledBlockInfo.isContinue) { + for (LabelElement label in labeledBlockInfo.labels) { + if (label.isContinueTarget) { + String labelName = backend.namer.continueLabelName(label); + result = new js.LabeledStatement(labelName, result); + continueAction[label] = continueAsBreak; + continueOverrides = continueOverrides.prepend(label); + } + } + // For handling unlabeled continues from the body of a loop. + // TODO(lrn): Consider recording whether the target is in fact + // a target of an unlabeled continue, and not generate this if it isn't. + TargetElement target = labeledBlockInfo.target; + String labelName = backend.namer.implicitContinueLabelName(target); + result = new js.LabeledStatement(labelName, result); + continueAction[target] = implicitContinueAsBreak; + continueOverrides = continueOverrides.prepend(target); + } else { + for (LabelElement label in labeledBlockInfo.labels) { + if (label.isBreakTarget) { + String labelName = backend.namer.breakLabelName(label); + result = new js.LabeledStatement(labelName, result); + } + } + } + TargetElement target = labeledBlockInfo.target; + if (target.isSwitch) { + // This is an extra block around a switch that is generated + // as a nested if/else chain. We add an extra break target + // so that case code can break. + String labelName = backend.namer.implicitBreakLabelName(target); + result = new js.LabeledStatement(labelName, result); + breakAction[target] = implicitBreakWithLabel; + } + + currentContainer = body; + generateStatements(labeledBlockInfo.body); + + if (labeledBlockInfo.isContinue) { + while (!continueOverrides.isEmpty) { + continueAction.remove(continueOverrides.head); + continueOverrides = continueOverrides.tail; + } + } else { + breakAction.remove(labeledBlockInfo.target); + } + + currentContainer = oldContainer; + pushStatement(result); + return true; + } + + // Wraps a loop body in a block to make continues have a target to break + // to (if necessary). + void wrapLoopBodyForContinue(HLoopBlockInformation info) { + TargetElement target = info.target; + if (target != null && target.isContinueTarget) { + js.Block oldContainer = currentContainer; + js.Block body = new js.Block.empty(); + currentContainer = body; + js.Statement result = body; + for (LabelElement label in info.labels) { + if (label.isContinueTarget) { + String labelName = backend.namer.continueLabelName(label); + result = new js.LabeledStatement(labelName, result); + continueAction[label] = continueAsBreak; + } + } + String labelName = backend.namer.implicitContinueLabelName(target); + result = new js.LabeledStatement(labelName, result); + continueAction[info.target] = implicitContinueAsBreak; + visitBodyIgnoreLabels(info); + continueAction.remove(info.target); + for (LabelElement label in info.labels) { + if (label.isContinueTarget) { + continueAction.remove(label); + } + } + currentContainer = oldContainer; + pushStatement(result); + } else { + // Loop body contains no continues, so we don't need a break target. + generateStatements(info.body); + } + } + + bool handleBlockFlow(HBlockFlow block) { + HBlockInformation info = block.body; + // If we reach here again while handling the attached information, + // e.g., because we call visitSubGraph on a subgraph starting on + // the same block, don't handle it again. + // When the structure graph is complete, we will be able to have + // different structures starting on the same basic block (e.g., an + // "if" and its condition). + if (identical(info, currentBlockInformation)) return false; + + HBlockInformation oldBlockInformation = currentBlockInformation; + currentBlockInformation = info; + bool success = info.accept(this); + currentBlockInformation = oldBlockInformation; + if (success) { + HBasicBlock continuation = block.continuation; + if (continuation != null) { + visitBasicBlock(continuation); + } + } + return success; + } + + void visitBasicBlock(HBasicBlock node) { + if (!node.isLive) return; + + // Abort traversal if we are leaving the currently active sub-graph. + if (!subGraph.contains(node)) return; + + // If this node has block-structure based information attached, + // try using that to traverse from here. + if (node.blockFlow != null && handleBlockFlow(node.blockFlow)) { + return; + } + iterateBasicBlock(node); + } + + void emitAssignment(String destination, String source) { + assignVariable(destination, new js.VariableUse(source)); + } + + /** + * Sequentialize a list of conceptually parallel copies. Parallel + * copies may contain cycles, that this method breaks. + */ + void sequentializeCopies(Iterable copies, + String tempName, + void doAssignment(String target, String source)) { + // Map to keep track of the current location (ie the variable that + // holds the initial value) of a variable. + Map currentLocation = new Map(); + + // Map to keep track of the initial value of a variable. + Map initialValue = new Map(); + + // List of variables to assign a value. + List worklist = []; + + // List of variables that we can assign a value to (ie are not + // being used anymore). + List ready = []; + + // Prune [copies] by removing self-copies. + List prunedCopies = []; + for (Copy copy in copies) { + if (copy.source != copy.destination) { + prunedCopies.add(copy); + } + } + copies = prunedCopies; + + + // For each copy, set the current location of the source to + // itself, and the initial value of the destination to the source. + // Add the destination to the list of copies to make. + for (Copy copy in copies) { + currentLocation[copy.source] = copy.source; + initialValue[copy.destination] = copy.source; + worklist.add(copy.destination); + } + + // For each copy, if the destination does not have a current + // location, then we can safely assign to it. + for (Copy copy in copies) { + if (currentLocation[copy.destination] == null) { + ready.add(copy.destination); + } + } + + while (!worklist.isEmpty) { + while (!ready.isEmpty) { + String destination = ready.removeLast(); + String source = initialValue[destination]; + // Since [source] might have been updated, use the current + // location of [source] + String copy = currentLocation[source]; + doAssignment(destination, copy); + // Now [destination] is the current location of [source]. + currentLocation[source] = destination; + // If [source] hasn't been updated and needs to have a value, + // add it to the list of variables that can be updated. Copies + // of [source] will now use [destination]. + if (source == copy && initialValue[source] != null) { + ready.add(source); + } + } + + // Check if we have a cycle. + String current = worklist.removeLast(); + // If [current] is used as a source, and the assignment has been + // done, we are done with this variable. Otherwise there is a + // cycle that we break by using a temporary name. + if (currentLocation[current] != null + && current != currentLocation[initialValue[current]]) { + doAssignment(tempName, current); + currentLocation[current] = tempName; + // [current] can now be safely updated. Copies of [current] + // will now use [tempName]. + ready.add(current); + } + } + } + + void assignPhisOfSuccessors(HBasicBlock node) { + CopyHandler handler = variableNames.getCopyHandler(node); + if (handler == null) return; + + // Map the instructions to strings. + Iterable copies = handler.copies.map((Copy copy) { + return new Copy(variableNames.getName(copy.source), + variableNames.getName(copy.destination)); + }); + + sequentializeCopies(copies, variableNames.getSwapTemp(), emitAssignment); + + for (Copy copy in handler.assignments) { + String name = variableNames.getName(copy.destination); + use(copy.source); + assignVariable(name, pop()); + } + } + + void iterateBasicBlock(HBasicBlock node) { + HInstruction instruction = node.first; + while (!identical(instruction, node.last)) { + if (!isGenerateAtUseSite(instruction)) { + define(instruction); + } + instruction = instruction.next; + } + assignPhisOfSuccessors(node); + visit(instruction); + } + + visitInvokeBinary(HInvokeBinary node, String op) { + use(node.left); + js.Expression jsLeft = pop(); + use(node.right); + push(new js.Binary(op, jsLeft, pop()), node); + } + + visitRelational(HRelational node, String op) => visitInvokeBinary(node, op); + + // We want the outcome of bit-operations to be positive. We use the unsigned + // shift operator to achieve this. + visitBitInvokeBinary(HBinaryBitOp node, String op) { + visitInvokeBinary(node, op); + if (op != '>>>' && requiresUintConversion(node)) { + push(new js.Binary(">>>", pop(), new js.LiteralNumber("0")), node); + } + } + + visitInvokeUnary(HInvokeUnary node, String op) { + use(node.operand); + push(new js.Prefix(op, pop()), node); + } + + // We want the outcome of bit-operations to be positive. We use the unsigned + // shift operator to achieve this. + visitBitInvokeUnary(HInvokeUnary node, String op) { + visitInvokeUnary(node, op); + if (requiresUintConversion(node)) { + push(new js.Binary(">>>", pop(), new js.LiteralNumber("0")), node); + } + } + + void emitIdentityComparison(HIdentity instruction, bool inverse) { + String op = instruction.singleComparisonOp; + HInstruction left = instruction.left; + HInstruction right = instruction.right; + if (op != null) { + use(left); + js.Expression jsLeft = pop(); + use(right); + push(new js.Binary(mapRelationalOperator(op, inverse), jsLeft, pop())); + } else { + assert(NullConstant.JsNull == 'null'); + use(left); + js.Binary leftEqualsNull = + new js.Binary("==", pop(), new js.LiteralNull()); + use(right); + js.Binary rightEqualsNull = + new js.Binary(mapRelationalOperator("==", inverse), + pop(), new js.LiteralNull()); + use(right); + use(left); + js.Binary tripleEq = new js.Binary(mapRelationalOperator("===", inverse), + pop(), pop()); + + push(new js.Conditional(leftEqualsNull, rightEqualsNull, tripleEq)); + } + } + + visitIdentity(HIdentity node) { + emitIdentityComparison(node, false); + } + + visitAdd(HAdd node) => visitInvokeBinary(node, '+'); + visitDivide(HDivide node) => visitInvokeBinary(node, '/'); + visitMultiply(HMultiply node) => visitInvokeBinary(node, '*'); + visitSubtract(HSubtract node) => visitInvokeBinary(node, '-'); + visitBitAnd(HBitAnd node) => visitBitInvokeBinary(node, '&'); + visitBitNot(HBitNot node) => visitBitInvokeUnary(node, '~'); + visitBitOr(HBitOr node) => visitBitInvokeBinary(node, '|'); + visitBitXor(HBitXor node) => visitBitInvokeBinary(node, '^'); + visitShiftLeft(HShiftLeft node) => visitBitInvokeBinary(node, '<<'); + visitShiftRight(HShiftRight node) => visitBitInvokeBinary(node, '>>>'); + + visitTruncatingDivide(HTruncatingDivide node) { + assert(node.left.isUInt31(compiler)); + assert(node.right.isPositiveInteger(compiler)); + use(node.left); + js.Expression jsLeft = pop(); + use(node.right); + push(new js.Binary('/', jsLeft, pop()), node); + push(new js.Binary('|', pop(), new js.LiteralNumber("0")), node); + } + + visitNegate(HNegate node) => visitInvokeUnary(node, '-'); + + visitLess(HLess node) => visitRelational(node, '<'); + visitLessEqual(HLessEqual node) => visitRelational(node, '<='); + visitGreater(HGreater node) => visitRelational(node, '>'); + visitGreaterEqual(HGreaterEqual node) => visitRelational(node, '>='); + + visitBoolify(HBoolify node) { + assert(node.inputs.length == 1); + use(node.inputs[0]); + push(new js.Binary('===', pop(), newLiteralBool(true)), node); + } + + visitExit(HExit node) { + // Don't do anything. + } + + visitGoto(HGoto node) { + HBasicBlock block = node.block; + assert(block.successors.length == 1); + List dominated = block.dominatedBlocks; + // With the exception of the entry-node which dominates its successor + // and the exit node, no block finishing with a 'goto' can have more than + // one dominated block (since it has only one successor). + // If the successor is dominated by another block, then the other block + // is responsible for visiting the successor. + if (dominated.isEmpty) return; + if (dominated.length > 2) { + compiler.internalError(node, 'dominated.length = ${dominated.length}'); + } + if (dominated.length == 2 && block != currentGraph.entry) { + compiler.internalError(node, 'node.block != currentGraph.entry'); + } + assert(dominated[0] == block.successors[0]); + visitBasicBlock(dominated[0]); + } + + visitLoopBranch(HLoopBranch node) { + assert(node.block == subGraph.end); + // We are generating code for a loop condition. + // If we are generating the subgraph as an expression, the + // condition will be generated as the expression. + // Otherwise, we don't generate the expression, and leave that + // to the code that called [visitSubGraph]. + if (isGeneratingExpression) { + use(node.inputs[0]); + } + } + + /** + * Checks if [map] contains an [ElementAction] for [element], and + * if so calls that action and returns true. + * Otherwise returns false. + */ + bool tryCallAction(Map map, Element element) { + ElementAction action = map[element]; + if (action == null) return false; + action(element); + return true; + } + + visitBreak(HBreak node) { + assert(node.block.successors.length == 1); + if (node.label != null) { + LabelElement label = node.label; + if (!tryCallAction(breakAction, label)) { + pushStatement(new js.Break(backend.namer.breakLabelName(label)), node); + } + } else { + TargetElement target = node.target; + if (!tryCallAction(breakAction, target)) { + if (node.breakSwitchContinueLoop) { + pushStatement(new js.Break( + backend.namer.implicitContinueLabelName(target)), node); + } else { + pushStatement(new js.Break(null), node); + } + } + } + } + + visitContinue(HContinue node) { + assert(node.block.successors.length == 1); + if (node.label != null) { + LabelElement label = node.label; + if (!tryCallAction(continueAction, label)) { + // TODO(floitsch): should this really be the breakLabelName? + pushStatement(new js.Continue(backend.namer.breakLabelName(label)), + node); + } + } else { + TargetElement target = node.target; + if (!tryCallAction(continueAction, target)) { + if (target.statement is ast.SwitchStatement) { + pushStatement(new js.Continue( + backend.namer.implicitContinueLabelName(target)), node); + } else { + pushStatement(new js.Continue(null), node); + } + } + } + } + + visitExitTry(HExitTry node) { + // An [HExitTry] is used to represent the control flow graph of a + // try/catch block, ie the try body is always a predecessor + // of the catch and finally. Here, we continue visiting the try + // body by visiting the block that contains the user-level control + // flow instruction. + visitBasicBlock(node.bodyTrySuccessor); + } + + visitTry(HTry node) { + // We should never get here. Try/catch/finally is always handled using block + // information in [visitTryInfo]. + compiler.internalError(node, 'visitTry should not be called.'); + } + + bool tryControlFlowOperation(HIf node) { + if (!controlFlowOperators.contains(node)) return false; + HPhi phi = node.joinBlock.phis.first; + bool atUseSite = isGenerateAtUseSite(phi); + // Don't generate a conditional operator in this situation: + // i = condition ? bar() : i; + // But generate this instead: + // if (condition) i = bar(); + // Usually, the variable name is longer than 'if' and it takes up + // more space to duplicate the name. + if (!atUseSite + && variableNames.getName(phi) == variableNames.getName(phi.inputs[1])) { + return false; + } + if (!atUseSite) define(phi); + visitBasicBlock(node.joinBlock); + return true; + } + + void generateIf(HIf node, HIfBlockInformation info) { + use(node.inputs[0]); + js.Expression test = pop(); + + HStatementInformation thenGraph = info.thenGraph; + HStatementInformation elseGraph = info.elseGraph; + js.Statement thenPart = + unwrapStatement(generateStatementsInNewBlock(thenGraph)); + js.Statement elsePart = + unwrapStatement(generateStatementsInNewBlock(elseGraph)); + + pushStatement(new js.If(test, thenPart, elsePart), node); + } + + visitIf(HIf node) { + if (tryControlFlowOperation(node)) return; + + HInstruction condition = node.inputs[0]; + HIfBlockInformation info = node.blockInformation.body; + + if (condition.isConstant()) { + HConstant constant = condition; + if (constant.constant.isTrue) { + generateStatements(info.thenGraph); + } else { + generateStatements(info.elseGraph); + } + } else { + generateIf(node, info); + } + + HBasicBlock joinBlock = node.joinBlock; + if (joinBlock != null && !identical(joinBlock.dominator, node.block)) { + // The join block is dominated by a block in one of the branches. + // The subgraph traversal never reached it, so we visit it here + // instead. + visitBasicBlock(joinBlock); + } + + // Visit all the dominated blocks that are not part of the then or else + // branches, and is not the join block. + // Depending on how the then/else branches terminate + // (e.g., return/throw/break) there can be any number of these. + List dominated = node.block.dominatedBlocks; + for (int i = 2; i < dominated.length; i++) { + visitBasicBlock(dominated[i]); + } + } + + js.Call jsPropertyCall(js.Expression receiver, + String fieldName, + List arguments) { + return new js.Call(new js.PropertyAccess.field(receiver, fieldName), + arguments); + } + + void visitInterceptor(HInterceptor node) { + backend.registerSpecializedGetInterceptor(node.interceptedClasses); + String name = backend.namer.getInterceptorName( + backend.getInterceptorMethod, node.interceptedClasses); + var isolate = new js.VariableUse( + backend.namer.globalObjectFor(compiler.interceptorsLibrary)); + use(node.receiver); + List arguments = [pop()]; + push(jsPropertyCall(isolate, name, arguments), node); + backend.registerUseInterceptor(world); + } + + visitInvokeDynamicMethod(HInvokeDynamicMethod node) { + use(node.receiver); + js.Expression object = pop(); + String name = node.selector.name; + String methodName; + List arguments = visitArguments(node.inputs); + Element target = node.element; + + if (target != null && !node.isInterceptedCall) { + if (target == backend.jsArrayAdd) { + methodName = 'push'; + } else if (target == backend.jsArrayRemoveLast) { + methodName = 'pop'; + } else if (target == backend.jsStringSplit) { + methodName = 'split'; + // Split returns a List, so we make sure the backend knows the + // list class is instantiated. + world.registerInstantiatedClass( + compiler.listClass, work.resolutionTree); + } else if (target == backend.jsStringOperatorAdd) { + push(new js.Binary('+', object, arguments[0]), node); + return; + } else if (target.isNative() && target.isFunction() + && !node.isInterceptedCall) { + // A direct (i.e. non-interceptor) native call is the result of + // optimization. The optimization ensures any type checks or + // conversions have been satisified. + methodName = target.fixedBackendName(); + } + } + + if (methodName == null) { + methodName = backend.namer.invocationName(node.selector); + registerMethodInvoke(node); + } + push(jsPropertyCall(object, methodName, arguments), node); + } + + void visitInvokeConstructorBody(HInvokeConstructorBody node) { + use(node.inputs[0]); + js.Expression object = pop(); + String methodName = backend.namer.getNameOfInstanceMember(node.element); + List arguments = visitArguments(node.inputs); + push(jsPropertyCall(object, methodName, arguments), node); + world.registerStaticUse(node.element); + } + + void visitOneShotInterceptor(HOneShotInterceptor node) { + List arguments = visitArguments(node.inputs); + var isolate = new js.VariableUse( + backend.namer.globalObjectFor(compiler.interceptorsLibrary)); + Selector selector = getOptimizedSelectorFor(node, node.selector); + String methodName = backend.registerOneShotInterceptor(selector); + push(jsPropertyCall(isolate, methodName, arguments), node); + if (selector.isGetter()) { + registerGetter(node); + } else if (selector.isSetter()) { + registerSetter(node); + } else { + registerMethodInvoke(node); + } + backend.registerUseInterceptor(world); + } + + Selector getOptimizedSelectorFor(HInvokeDynamic node, Selector selector) { + if (node.element != null) { + // Create an artificial type mask to make sure only + // [node.element] will be enqueued. We're not using the receiver + // type because our optimizations might end up in a state where the + // invoke dynamic knows more than the receiver. + ClassElement enclosing = node.element.getEnclosingClass(); + TypeMask receiverType = new TypeMask.nonNullExact(enclosing.declaration); + return new TypedSelector(receiverType, selector); + } + // If [JSInvocationMirror._invokeOn] is enabled, and this call + // might hit a `noSuchMethod`, we register an untyped selector. + return selector.extendIfReachesAll(compiler); + } + + void registerMethodInvoke(HInvokeDynamic node) { + Selector selector = getOptimizedSelectorFor(node, node.selector); + + // If we don't know what we're calling or if we are calling a getter, + // we need to register that fact that we may be calling a closure + // with the same arguments. + Element target = node.element; + if (target == null || target.isGetter()) { + // TODO(kasperl): If we have a typed selector for the call, we + // may know something about the types of closures that need + // the specific closure call method. + Selector call = new Selector.callClosureFrom(selector); + world.registerDynamicInvocation(call); + } + world.registerDynamicInvocation(selector); + } + + void registerSetter(HInvokeDynamic node) { + Selector selector = getOptimizedSelectorFor(node, node.selector); + world.registerDynamicSetter(selector); + } + + void registerGetter(HInvokeDynamic node) { + Selector selector = getOptimizedSelectorFor(node, node.selector); + world.registerDynamicGetter(selector); + } + + visitInvokeDynamicSetter(HInvokeDynamicSetter node) { + use(node.receiver); + String name = backend.namer.invocationName(node.selector); + push(jsPropertyCall(pop(), name, visitArguments(node.inputs)), node); + registerSetter(node); + } + + visitInvokeDynamicGetter(HInvokeDynamicGetter node) { + use(node.receiver); + String name = backend.namer.invocationName(node.selector); + push(jsPropertyCall(pop(), name, visitArguments(node.inputs)), node); + registerGetter(node); + } + + visitInvokeClosure(HInvokeClosure node) { + Selector call = new Selector.callClosureFrom(node.selector); + use(node.receiver); + push(jsPropertyCall(pop(), + backend.namer.invocationName(call), + visitArguments(node.inputs)), + node); + world.registerDynamicInvocation(call); + } + + visitInvokeStatic(HInvokeStatic node) { + Element element = node.element; + ClassElement cls = element.getEnclosingClass(); + List instantiatedTypes = node.instantiatedTypes; + + world.registerStaticUse(element); + + if (instantiatedTypes != null && !instantiatedTypes.isEmpty) { + instantiatedTypes.forEach((type) { + world.registerInstantiatedType(type, work.resolutionTree); + }); + } + + push(new js.VariableUse(backend.namer.isolateAccess(node.element))); + push(new js.Call(pop(), visitArguments(node.inputs, start: 0)), node); + } + + visitInvokeSuper(HInvokeSuper node) { + Element superMethod = node.element; + world.registerStaticUse(superMethod); + ClassElement superClass = superMethod.getEnclosingClass(); + if (superMethod.kind == ElementKind.FIELD) { + String fieldName = backend.namer.instanceFieldPropertyName(superMethod); + use(node.inputs[0]); + js.PropertyAccess access = + new js.PropertyAccess.field(pop(), fieldName); + if (node.isSetter) { + use(node.value); + push(new js.Assignment(access, pop()), node); + } else { + push(access, node); + } + } else { + Selector selector = node.selector; + String methodName; + if (selector.isGetter()) { + // If the selector we need to register a typed getter to the + // [world]. The emitter needs to know if it needs to emit a + // bound closure for a method. + TypeMask receiverType = new TypeMask.nonNullExact(superClass); + selector = new TypedSelector(receiverType, selector); + // TODO(floitsch): we know the target. We shouldn't register a + // dynamic getter. + world.registerDynamicGetter(selector); + world.registerGetterForSuperMethod(node.element); + methodName = backend.namer.invocationName(selector); + } else { + methodName = backend.namer.getNameOfInstanceMember(superMethod); + } + js.PropertyAccess method = + backend.namer.elementAccess(superClass)['prototype'][methodName]; + push(jsPropertyCall( + method, "call", visitArguments(node.inputs, start: 0)), node); + } + } + + visitFieldGet(HFieldGet node) { + use(node.receiver); + Element element = node.element; + if (node.isNullCheck) { + // We access a JavaScript member we know all objects besides + // null and undefined have: V8 does not like accessing a member + // that does not exist. + push(new js.PropertyAccess.field(pop(), 'toString'), node); + } else if (element == backend.jsIndexableLength) { + // We're accessing a native JavaScript property called 'length' + // on a JS String or a JS array. Therefore, the name of that + // property should not be mangled. + push(new js.PropertyAccess.field(pop(), 'length'), node); + } else { + String name = backend.namer.instanceFieldPropertyName(element); + push(new js.PropertyAccess.field(pop(), name), node); + world.registerFieldGetter(element); + } + } + + visitFieldSet(HFieldSet node) { + Element element = node.element; + world.registerFieldSetter(element); + String name = backend.namer.instanceFieldPropertyName(element); + use(node.receiver); + js.Expression receiver = pop(); + use(node.value); + push(new js.Assignment(new js.PropertyAccess.field(receiver, name), pop()), + node); + } + + visitLocalGet(HLocalGet node) { + use(node.receiver); + } + + visitLocalSet(HLocalSet node) { + use(node.value); + assignVariable(variableNames.getName(node.receiver), pop()); + } + + void registerForeignTypes(HForeign node) { + native.NativeBehavior nativeBehavior = node.nativeBehavior; + if (nativeBehavior == null) return; + nativeBehavior.typesReturned.forEach((type) { + if (type is DartType) { + world.registerInstantiatedType(type, work.resolutionTree); + } + }); + } + + visitForeign(HForeign node) { + List inputs = node.inputs; + if (node.isJsStatement()) { + if (!inputs.isEmpty) { + compiler.internalError(node, "Foreign statement with inputs."); + } + pushStatement(node.codeAst, node); + } else { + if (!inputs.isEmpty) { + List interpolatedExpressions = []; + for (int i = 0; i < inputs.length; i++) { + use(inputs[i]); + interpolatedExpressions.add(pop()); + } + var visitor = new js.UninterpolateJSExpression(interpolatedExpressions); + push(visitor.visit(node.codeAst), node); + } else { + push(node.codeAst, node); + } + } + + // TODO(sra): Tell world.nativeEnqueuer about the types created here. + registerForeignTypes(node); + } + + visitForeignNew(HForeignNew node) { + String jsClassReference = backend.namer.isolateAccess(node.element); + List arguments = visitArguments(node.inputs, start: 0); + // TODO(floitsch): jsClassReference is an Access. We shouldn't treat it + // as if it was a string. + push(new js.New(new js.VariableUse(jsClassReference), arguments), node); + registerForeignTypes(node); + if (node.instantiatedTypes == null) { + return; + } + node.instantiatedTypes.forEach((type) { + world.registerInstantiatedType(type, work.resolutionTree); + }); + } + + js.Expression newLiteralBool(bool value) { + if (compiler.enableMinification) { + // Use !0 for true, !1 for false. + return new js.Prefix("!", new js.LiteralNumber(value ? "0" : "1")); + } else { + return new js.LiteralBool(value); + } + } + + void generateConstant(Constant constant) { + if (constant.isFunction) { + FunctionConstant function = constant; + world.registerStaticUse(function.element); + } + if (constant.isType) { + // If the type is a web component, we need to ensure the constructors are + // available to 'upgrade' the native object. + TypeConstant type = constant; + Element element = type.representedType.element; + if (element != null && element.isClass()) { + backend.customElementsAnalysis.registerTypeConstant(element, world); + } + } + push(backend.emitter.constantReference(constant)); + } + + visitConstant(HConstant node) { + assert(isGenerateAtUseSite(node)); + generateConstant(node.constant); + + backend.registerCompileTimeConstant(node.constant, work.resolutionTree); + compiler.constantHandler.addCompileTimeConstantForEmission(node.constant); + } + + visitNot(HNot node) { + assert(node.inputs.length == 1); + generateNot(node.inputs[0]); + attachLocationToLast(node); + } + + static String mapRelationalOperator(String op, bool inverse) { + Map inverseOperator = const { + "==" : "!=", + "!=" : "==", + "===": "!==", + "!==": "===", + "<" : ">=", + "<=" : ">", + ">" : "<=", + ">=" : "<" + }; + return inverse ? inverseOperator[op] : op; + } + + void generateNot(HInstruction input) { + bool canGenerateOptimizedComparison(HInstruction instruction) { + if (instruction is !HRelational) return false; + + HRelational relational = instruction; + BinaryOperation operation = relational.operation(backend.constantSystem); + + HInstruction left = relational.left; + HInstruction right = relational.right; + if (left.isStringOrNull(compiler) && right.isStringOrNull(compiler)) { + return true; + } + + // This optimization doesn't work for NaN, so we only do it if the + // type is known to be an integer. + return left.isInteger(compiler) && right.isInteger(compiler); + } + + bool handledBySpecialCase = false; + if (isGenerateAtUseSite(input)) { + handledBySpecialCase = true; + if (input is HIs) { + emitIs(input, '!=='); + } else if (input is HIsViaInterceptor) { + emitIsViaInterceptor(input, true); + } else if (input is HNot) { + use(input.inputs[0]); + } else if (input is HIdentity) { + emitIdentityComparison(input, true); + } else if (input is HBoolify) { + use(input.inputs[0]); + push(new js.Binary("!==", pop(), newLiteralBool(true)), input); + } else if (canGenerateOptimizedComparison(input)) { + HRelational relational = input; + BinaryOperation operation = + relational.operation(backend.constantSystem); + String op = mapRelationalOperator(operation.name, true); + visitRelational(input, op); + } else { + handledBySpecialCase = false; + } + } + if (!handledBySpecialCase) { + use(input); + push(new js.Prefix("!", pop())); + } + } + + visitParameterValue(HParameterValue node) { + assert(!isGenerateAtUseSite(node)); + String name = variableNames.getName(node); + parameters.add(new js.Parameter(name)); + declaredLocals.add(name); + } + + visitLocalValue(HLocalValue node) { + assert(!isGenerateAtUseSite(node)); + String name = variableNames.getName(node); + collectedVariableDeclarations.add(name); + } + + visitPhi(HPhi node) { + // This method is only called for phis that are generated at use + // site. A phi can be generated at use site only if it is the + // result of a control flow operation. + HBasicBlock ifBlock = node.block.dominator; + assert(controlFlowOperators.contains(ifBlock.last)); + HInstruction input = ifBlock.last.inputs[0]; + if (input.isConstantFalse()) { + use(node.inputs[1]); + } else if (input.isConstantTrue()) { + use(node.inputs[0]); + } else if (node.inputs[1].isConstantBoolean()) { + String operation = node.inputs[1].isConstantFalse() ? '&&' : '||'; + if (operation == '||') { + generateNot(input); + } else { + use(input); + } + js.Expression left = pop(); + use(node.inputs[0]); + push(new js.Binary(operation, left, pop())); + } else { + use(input); + js.Expression test = pop(); + use(node.inputs[0]); + js.Expression then = pop(); + use(node.inputs[1]); + push(new js.Conditional(test, then, pop())); + } + } + + visitReturn(HReturn node) { + assert(node.inputs.length == 1); + HInstruction input = node.inputs[0]; + if (input.isConstantNull()) { + pushStatement(new js.Return(null), node); + } else { + use(node.inputs[0]); + pushStatement(new js.Return(pop()), node); + } + } + + visitThis(HThis node) { + push(new js.This()); + } + + visitThrow(HThrow node) { + if (node.isRethrow) { + use(node.inputs[0]); + pushStatement(new js.Throw(pop()), node); + } else { + generateThrowWithHelper('wrapException', node.inputs[0]); + } + } + + visitRangeConversion(HRangeConversion node) { + // Range conversion instructions are removed by the value range + // analyzer. + assert(false); + } + + visitBoundsCheck(HBoundsCheck node) { + // TODO(ngeoffray): Separate the two checks of the bounds check, so, + // e.g., the zero checks can be shared if possible. + + // If the checks always succeeds, we would have removed the bounds check + // completely. + assert(node.staticChecks != HBoundsCheck.ALWAYS_TRUE); + if (node.staticChecks != HBoundsCheck.ALWAYS_FALSE) { + js.Expression under; + js.Expression over; + if (node.staticChecks != HBoundsCheck.ALWAYS_ABOVE_ZERO) { + use(node.index); + if (node.index.isInteger(compiler)) { + under = js.js("# < 0", pop()); + } else { + js.Expression jsIndex = pop(); + under = js.js("# >>> 0 !== #", [jsIndex, jsIndex]); + } + } else if (!node.index.isInteger(compiler)) { + checkInt(node.index, '!=='); + under = pop(); + } + if (node.staticChecks != HBoundsCheck.ALWAYS_BELOW_LENGTH) { + var index = node.index; + use(index); + js.Expression jsIndex = pop(); + use(node.length); + over = new js.Binary(">=", jsIndex, pop()); + } + assert(over != null || under != null); + js.Expression underOver = under == null + ? over + : over == null + ? under + : new js.Binary("||", under, over); + js.Statement thenBody = new js.Block.empty(); + js.Block oldContainer = currentContainer; + currentContainer = thenBody; + generateThrowWithHelper('ioore', [node.array, node.index]); + currentContainer = oldContainer; + thenBody = unwrapStatement(thenBody); + pushStatement(new js.If.noElse(underOver, thenBody), node); + } else { + generateThrowWithHelper('ioore', [node.array, node.index]); + } + } + + void generateThrowWithHelper(String helperName, argument) { + Element helper = compiler.findHelper(helperName); + world.registerStaticUse(helper); + js.VariableUse jsHelper = + new js.VariableUse(backend.namer.isolateAccess(helper)); + List arguments = []; + var location; + if (argument is List) { + location = argument[0]; + argument.forEach((instruction) { + use(instruction); + arguments.add(pop()); + }); + } else { + location = argument; + use(argument); + arguments.add(pop()); + } + js.Call value = new js.Call(jsHelper, arguments); + attachLocation(value, location); + // BUG(4906): Using throw/return here adds to the size of the generated code + // but it has the advantage of explicitly telling the JS engine that + // this code path will terminate abruptly. Needs more work. + if (helperName == 'wrapException') { + pushStatement(new js.Throw(value)); + } else { + pushStatement(new js.Return(value)); + } + } + + visitThrowExpression(HThrowExpression node) { + HInstruction argument = node.inputs[0]; + use(argument); + + Element helper = compiler.findHelper("throwExpression"); + world.registerStaticUse(helper); + + js.VariableUse jsHelper = + new js.VariableUse(backend.namer.isolateAccess(helper)); + js.Call value = new js.Call(jsHelper, [pop()]); + attachLocation(value, argument); + push(value, node); + } + + void visitSwitch(HSwitch node) { + // Switches are handled using [visitSwitchInfo]. + } + + void visitStatic(HStatic node) { + Element element = node.element; + if (element.isFunction()) { + push(new js.VariableUse( + backend.namer.isolateStaticClosureAccess(node.element))); + } else { + push(new js.VariableUse(backend.namer.isolateAccess(node.element))); + } + world.registerStaticUse(element); + } + + void visitLazyStatic(HLazyStatic node) { + Element element = node.element; + world.registerStaticUse(element); + String lazyGetter = backend.namer.isolateLazyInitializerAccess(element); + js.VariableUse target = new js.VariableUse(lazyGetter); + js.Call call = new js.Call(target, []); + push(call, node); + } + + void visitStaticStore(HStaticStore node) { + world.registerStaticUse(node.element); + js.Node variable = backend.namer.elementAccess(node.element); + use(node.inputs[0]); + push(new js.Assignment(variable, pop()), node); + } + + void visitStringConcat(HStringConcat node) { + use(node.left); + js.Expression jsLeft = pop(); + use(node.right); + push(new js.Binary('+', jsLeft, pop()), node); + } + + void visitStringify(HStringify node) { + HInstruction input = node.inputs.first; + if (input.isString(compiler)) { + use(input); + } else if (input.isInteger(compiler) || input.isBoolean(compiler)) { + // JavaScript's + operator with a string for the left operand will convert + // the right operand to a string, and the conversion result is correct. + use(input); + if (node.usedBy.length == 1 + && node.usedBy[0] is HStringConcat + && node.usedBy[0].inputs[1] == node) { + // The context is already + value. + } else { + // Force an empty string for the first operand. + push(new js.Binary('+', js.string(""), pop()), node); + } + } else { + Element convertToString = backend.getStringInterpolationHelper(); + world.registerStaticUse(convertToString); + js.VariableUse variableUse = + new js.VariableUse(backend.namer.isolateAccess(convertToString)); + use(input); + push(new js.Call(variableUse, [pop()]), node); + } + } + + void visitLiteralList(HLiteralList node) { + world.registerInstantiatedClass( + compiler.listClass, work.resolutionTree); + generateArrayLiteral(node); + } + + void generateArrayLiteral(HLiteralList node) { + int len = node.inputs.length; + List elements = []; + for (int i = 0; i < len; i++) { + use(node.inputs[i]); + elements.add(new js.ArrayElement(i, pop())); + } + push(new js.ArrayInitializer(len, elements), node); + } + + void visitIndex(HIndex node) { + use(node.receiver); + js.Expression receiver = pop(); + use(node.index); + push(new js.PropertyAccess(receiver, pop()), node); + } + + void visitIndexAssign(HIndexAssign node) { + use(node.receiver); + js.Expression receiver = pop(); + use(node.index); + js.Expression index = pop(); + use(node.value); + push(new js.Assignment(new js.PropertyAccess(receiver, index), pop()), + node); + } + + void checkInt(HInstruction input, String cmp) { + use(input); + js.Expression left = pop(); + use(input); + js.Expression or0 = new js.Binary("|", pop(), new js.LiteralNumber("0")); + push(new js.Binary(cmp, left, or0)); + } + + void checkBigInt(HInstruction input, String cmp) { + use(input); + js.Expression left = pop(); + use(input); + js.Expression right = pop(); + // TODO(4984): Deal with infinity and -0.0. + push(js.js('Math.floor(#) $cmp #', [left, right])); + } + + void checkTypeOf(HInstruction input, String cmp, String typeName) { + use(input); + js.Expression typeOf = new js.Prefix("typeof", pop()); + push(new js.Binary(cmp, typeOf, js.string(typeName))); + } + + void checkNum(HInstruction input, String cmp) + => checkTypeOf(input, cmp, 'number'); + + void checkDouble(HInstruction input, String cmp) => checkNum(input, cmp); + + void checkString(HInstruction input, String cmp) + => checkTypeOf(input, cmp, 'string'); + + void checkBool(HInstruction input, String cmp) + => checkTypeOf(input, cmp, 'boolean'); + + void checkObject(HInstruction input, String cmp) { + assert(NullConstant.JsNull == 'null'); + if (cmp == "===") { + checkTypeOf(input, '===', 'object'); + js.Expression left = pop(); + use(input); + js.Expression notNull = new js.Binary("!==", pop(), new js.LiteralNull()); + push(new js.Binary("&&", left, notNull)); + } else { + assert(cmp == "!=="); + checkTypeOf(input, '!==', 'object'); + js.Expression left = pop(); + use(input); + js.Expression eqNull = new js.Binary("===", pop(), new js.LiteralNull()); + push(new js.Binary("||", left, eqNull)); + } + } + + void checkArray(HInstruction input, String cmp) { + use(input); + js.PropertyAccess constructor = + new js.PropertyAccess.field(pop(), 'constructor'); + push(new js.Binary(cmp, constructor, new js.VariableUse('Array'))); + } + + void checkFieldExists(HInstruction input, String fieldName) { + use(input); + js.PropertyAccess field = new js.PropertyAccess.field(pop(), fieldName); + // Double negate to boolify the result. + push(new js.Prefix('!', new js.Prefix('!', field))); + } + + void checkFieldDoesNotExist(HInstruction input, String fieldName) { + use(input); + js.PropertyAccess field = new js.PropertyAccess.field(pop(), fieldName); + push(new js.Prefix('!', field)); + } + + void checkImmutableArray(HInstruction input) { + checkFieldExists(input, 'immutable\$list'); + } + + void checkMutableArray(HInstruction input) { + checkFieldDoesNotExist(input, 'immutable\$list'); + } + + void checkExtendableArray(HInstruction input) { + checkFieldDoesNotExist(input, 'fixed\$length'); + } + + void checkFixedArray(HInstruction input) { + checkFieldExists(input, 'fixed\$length'); + } + + void checkNull(HInstruction input) { + use(input); + push(new js.Binary('==', pop(), new js.LiteralNull())); + } + + void checkNonNull(HInstruction input) { + use(input); + push(new js.Binary('!=', pop(), new js.LiteralNull())); + } + + bool checkIndexingBehavior(HInstruction input, {bool negative: false}) { + if (!compiler.enqueuer.resolution.seenClasses.contains( + backend.jsIndexingBehaviorInterface)) { + return false; + } + + use(input); + js.Expression object1 = pop(); + use(input); + js.Expression object2 = pop(); + push(backend.generateIsJsIndexableCall(object1, object2)); + if (negative) push(new js.Prefix('!', pop())); + return true; + } + + void checkType(HInstruction input, HInstruction interceptor, + DartType type, {bool negative: false}) { + Element element = type.element; + if (element == backend.jsArrayClass) { + checkArray(input, negative ? '!==': '==='); + return; + } else if (element == backend.jsMutableArrayClass) { + if (negative) { + checkImmutableArray(input); + } else { + checkMutableArray(input); + } + return; + } else if (element == backend.jsExtendableArrayClass) { + if (negative) { + checkFixedArray(input); + } else { + checkExtendableArray(input); + } + return; + } else if (element == backend.jsFixedArrayClass) { + if (negative) { + checkExtendableArray(input); + } else { + checkFixedArray(input); + } + return; + } + if (interceptor != null) { + checkTypeViaProperty(interceptor, type, negative); + } else { + checkTypeViaProperty(input, type, negative); + } + } + + void checkTypeViaProperty(HInstruction input, DartType type, bool negative) { + world.registerIsCheck(type, work.resolutionTree); + + use(input); + + js.PropertyAccess field = + new js.PropertyAccess.field(pop(), backend.namer.operatorIsType(type)); + // We always negate at least once so that the result is boolified. + push(new js.Prefix('!', field)); + // If the result is not negated, put another '!' in front. + if (!negative) push(new js.Prefix('!', pop())); + } + + void handleNumberOrStringSupertypeCheck(HInstruction input, + HInstruction interceptor, + DartType type, + { bool negative: false }) { + assert(!identical(type.element, compiler.listClass) + && !Elements.isListSupertype(type.element, compiler) + && !Elements.isStringOnlySupertype(type.element, compiler)); + String relation = negative ? '!==' : '==='; + checkNum(input, relation); + js.Expression numberTest = pop(); + checkString(input, relation); + js.Expression stringTest = pop(); + checkObject(input, relation); + js.Expression objectTest = pop(); + checkType(input, interceptor, type, negative: negative); + String combiner = negative ? '&&' : '||'; + String combiner2 = negative ? '||' : '&&'; + push(new js.Binary(combiner, + new js.Binary(combiner, numberTest, stringTest), + new js.Binary(combiner2, objectTest, pop()))); + } + + void handleStringSupertypeCheck(HInstruction input, + HInstruction interceptor, + DartType type, + { bool negative: false }) { + assert(!identical(type.element, compiler.listClass) + && !Elements.isListSupertype(type.element, compiler) + && !Elements.isNumberOrStringSupertype(type.element, compiler)); + String relation = negative ? '!==' : '==='; + checkString(input, relation); + js.Expression stringTest = pop(); + checkObject(input, relation); + js.Expression objectTest = pop(); + checkType(input, interceptor, type, negative: negative); + String combiner = negative ? '||' : '&&'; + push(new js.Binary(negative ? '&&' : '||', + stringTest, + new js.Binary(combiner, objectTest, pop()))); + } + + void handleListOrSupertypeCheck(HInstruction input, + HInstruction interceptor, + DartType type, + { bool negative: false }) { + assert(!identical(type.element, compiler.stringClass) + && !Elements.isStringOnlySupertype(type.element, compiler) + && !Elements.isNumberOrStringSupertype(type.element, compiler)); + String relation = negative ? '!==' : '==='; + checkObject(input, relation); + js.Expression objectTest = pop(); + checkArray(input, relation); + js.Expression arrayTest = pop(); + checkType(input, interceptor, type, negative: negative); + String combiner = negative ? '&&' : '||'; + push(new js.Binary(negative ? '||' : '&&', + objectTest, + new js.Binary(combiner, arrayTest, pop()))); + } + + void visitIs(HIs node) { + emitIs(node, "==="); + } + + void visitIsViaInterceptor(HIsViaInterceptor node) { + emitIsViaInterceptor(node, false); + } + + void emitIs(HIs node, String relation) { + DartType type = node.typeExpression; + world.registerIsCheck(type, work.resolutionTree); + HInstruction input = node.expression; + + // If this is changed to single == there are several places below that must + // be changed to match. + assert(relation == '===' || relation == '!=='); + bool negative = relation == '!=='; + + if (node.isVariableCheck || node.isCompoundCheck) { + use(node.checkCall); + if (negative) push(new js.Prefix('!', pop())); + } else { + assert(node.isRawCheck); + HInstruction interceptor = node.interceptor; + LibraryElement coreLibrary = compiler.coreLibrary; + ClassElement objectClass = compiler.objectClass; + Element element = type.element; + if (element == compiler.nullClass) { + if (negative) { + checkNonNull(input); + } else { + checkNull(input); + } + } else if (identical(element, objectClass) || type.treatAsDynamic) { + // The constant folder also does this optimization, but we make + // it safe by assuming it may have not run. + push(newLiteralBool(!negative), node); + } else if (element == compiler.stringClass) { + checkString(input, relation); + attachLocationToLast(node); + } else if (element == compiler.doubleClass) { + checkDouble(input, relation); + attachLocationToLast(node); + } else if (element == compiler.numClass) { + checkNum(input, relation); + attachLocationToLast(node); + } else if (element == compiler.boolClass) { + checkBool(input, relation); + attachLocationToLast(node); + } else if (element == compiler.intClass) { + // The is check in the code tells us that it might not be an + // int. So we do a typeof first to avoid possible + // deoptimizations on the JS engine due to the Math.floor check. + checkNum(input, relation); + js.Expression numTest = pop(); + checkBigInt(input, relation); + push(new js.Binary(negative ? '||' : '&&', numTest, pop()), node); + } else if (Elements.isNumberOrStringSupertype(element, compiler)) { + handleNumberOrStringSupertypeCheck( + input, interceptor, type, negative: negative); + attachLocationToLast(node); + } else if (Elements.isStringOnlySupertype(element, compiler)) { + handleStringSupertypeCheck( + input, interceptor, type, negative: negative); + attachLocationToLast(node); + } else if (identical(element, compiler.listClass) + || Elements.isListSupertype(element, compiler)) { + handleListOrSupertypeCheck( + input, interceptor, type, negative: negative); + attachLocationToLast(node); + } else if (type.kind == TypeKind.FUNCTION) { + checkType(input, interceptor, type, negative: negative); + attachLocationToLast(node); + } else if ((input.canBePrimitive(compiler) + && !input.canBePrimitiveArray(compiler)) + || input.canBeNull()) { + checkObject(input, relation); + js.Expression objectTest = pop(); + checkType(input, interceptor, type, negative: negative); + push(new js.Binary(negative ? '||' : '&&', objectTest, pop()), node); + } else { + checkType(input, interceptor, type, negative: negative); + attachLocationToLast(node); + } + } + } + + void emitIsViaInterceptor(HIsViaInterceptor node, bool negative) { + checkTypeViaProperty(node.interceptor, node.typeExpression, negative); + attachLocationToLast(node); + } + + js.Expression generateTest(HInstruction input, TypeMask checkedType) { + TypeMask receiver = input.instructionType; + // Figure out if it is beneficial to turn this into a null check. + // V8 generally prefers 'typeof' checks, but for integers and + // indexable primitives we cannot compile this test into a single + // typeof check so the null check is cheaper. + bool turnIntoNumCheck = input.isIntegerOrNull(compiler) + && checkedType.containsOnlyInt(compiler); + bool turnIntoNullCheck = !turnIntoNumCheck + && (checkedType.nullable() == receiver) + && (checkedType.containsOnlyInt(compiler) + || checkedType.satisfies(backend.jsIndexableClass, compiler)); + js.Expression test; + if (turnIntoNullCheck) { + use(input); + test = new js.Binary("==", pop(), new js.LiteralNull()); + } else if (checkedType.containsOnlyInt(compiler) && !turnIntoNumCheck) { + // input is !int + checkInt(input, '!=='); + test = pop(); + } else if (checkedType.containsOnlyNum(compiler) || turnIntoNumCheck) { + // input is !num + checkNum(input, '!=='); + test = pop(); + } else if (checkedType.containsOnlyBool(compiler)) { + // input is !bool + checkBool(input, '!=='); + test = pop(); + } else if (checkedType.containsOnlyString(compiler)) { + // input is !string + checkString(input, '!=='); + test = pop(); + } else if (checkedType.satisfies(backend.jsExtendableArrayClass, + compiler)) { + // input is !Object || input is !Array || input.isFixed + checkObject(input, '!=='); + js.Expression objectTest = pop(); + checkArray(input, '!=='); + js.Expression arrayTest = pop(); + checkFixedArray(input); + test = new js.Binary('||', objectTest, arrayTest); + test = new js.Binary('||', test, pop()); + } else if (checkedType.satisfies(backend.jsMutableArrayClass, compiler)) { + // input is !Object + // || ((input is !Array || input.isImmutable) + // && input is !JsIndexingBehavior) + checkObject(input, '!=='); + js.Expression objectTest = pop(); + checkArray(input, '!=='); + js.Expression arrayTest = pop(); + checkImmutableArray(input); + js.Binary notArrayOrImmutable = new js.Binary('||', arrayTest, pop()); + + js.Binary notIndexing = checkIndexingBehavior(input, negative: true) + ? new js.Binary('&&', notArrayOrImmutable, pop()) + : notArrayOrImmutable; + test = new js.Binary('||', objectTest, notIndexing); + } else if (checkedType.satisfies(backend.jsArrayClass, compiler)) { + // input is !Object + // || (input is !Array && input is !JsIndexingBehavior) + checkObject(input, '!=='); + js.Expression objectTest = pop(); + checkArray(input, '!=='); + js.Expression arrayTest = pop(); + + js.Expression notIndexing = checkIndexingBehavior(input, negative: true) + ? new js.Binary('&&', arrayTest, pop()) + : arrayTest; + test = new js.Binary('||', objectTest, notIndexing); + } else if (checkedType.satisfies(backend.jsIndexableClass, compiler)) { + // input is !String + // && (input is !Object + // || (input is !Array && input is !JsIndexingBehavior)) + checkString(input, '!=='); + js.Expression stringTest = pop(); + checkObject(input, '!=='); + js.Expression objectTest = pop(); + checkArray(input, '!=='); + js.Expression arrayTest = pop(); + + js.Binary notIndexingTest = checkIndexingBehavior(input, negative: true) + ? new js.Binary('&&', arrayTest, pop()) + : arrayTest; + js.Binary notObjectOrIndexingTest = + new js.Binary('||', objectTest, notIndexingTest); + test = new js.Binary('&&', stringTest, notObjectOrIndexingTest); + } else { + compiler.internalError(input, 'Unexpected check.'); + } + return test; + } + + void visitTypeConversion(HTypeConversion node) { + if (node.isArgumentTypeCheck || node.isReceiverTypeCheck) { + // An int check if the input is not int or null, is not + // sufficient for doing a argument or receiver check. + assert(!node.checkedType.containsOnlyInt(compiler) || + node.checkedInput.isIntegerOrNull(compiler)); + js.Expression test = generateTest(node.checkedInput, node.checkedType); + js.Block oldContainer = currentContainer; + js.Statement body = new js.Block.empty(); + currentContainer = body; + if (node.isArgumentTypeCheck) { + generateThrowWithHelper('iae', node.checkedInput); + } else if (node.isReceiverTypeCheck) { + use(node.checkedInput); + String methodName = + backend.namer.invocationName(node.receiverTypeCheckSelector); + js.Expression call = jsPropertyCall(pop(), methodName, []); + pushStatement(new js.Return(call)); + } + currentContainer = oldContainer; + body = unwrapStatement(body); + pushStatement(new js.If.noElse(test, body), node); + return; + } + + assert(node.isCheckedModeCheck || node.isCastTypeCheck); + DartType type = node.typeExpression; + assert(type.kind != TypeKind.TYPEDEF); + if (type.kind == TypeKind.FUNCTION) { + // TODO(5022): We currently generate $isFunction checks for + // function types. + world.registerIsCheck( + compiler.functionClass.rawType, work.resolutionTree); + } + world.registerIsCheck(type, work.resolutionTree); + + CheckedModeHelper helper; + if (node.isBooleanConversionCheck) { + helper = + const CheckedModeHelper('boolConversionCheck'); + } else { + helper = + backend.getCheckedModeHelper(type, typeCast: node.isCastTypeCheck); + } + + if (helper == null) { + assert(type.kind == TypeKind.FUNCTION); + use(node.inputs[0]); + } else { + push(helper.generateCall(this, node)); + } + } + + void visitTypeKnown(HTypeKnown node) { + // [HTypeKnown] instructions are removed before generating code. + assert(false); + } + + void visitFunctionType(HFunctionType node) { + FunctionType type = node.dartType; + int inputCount = 0; + use(node.inputs[inputCount++]); + js.Expression returnType = pop(); + + List parameterTypes = []; + for (var _ in type.parameterTypes) { + use(node.inputs[inputCount++]); + parameterTypes.add(pop()); + } + + List optionalParameterTypes = []; + for (var _ in type.optionalParameterTypes) { + use(node.inputs[inputCount++]); + optionalParameterTypes.add(pop()); + } + + List namedParameters = []; + for (var _ in type.namedParameters) { + use(node.inputs[inputCount++]); + js.Expression name = pop(); + use(node.inputs[inputCount++]); + namedParameters.add(new js.Property(name, pop())); + } + + if (namedParameters.isEmpty) { + var arguments = [returnType]; + if (!parameterTypes.isEmpty || !optionalParameterTypes.isEmpty) { + arguments.add(new js.ArrayInitializer.from(parameterTypes)); + } + if (!optionalParameterTypes.isEmpty) { + arguments.add(new js.ArrayInitializer.from(optionalParameterTypes)); + } + push(accessHelper('buildFunctionType')(arguments)); + } else { + var arguments = [ + returnType, + new js.ArrayInitializer.from(parameterTypes), + new js.ObjectInitializer(namedParameters)]; + push(accessHelper('buildNamedFunctionType')(arguments)); + } + } + + void visitReadTypeVariable(HReadTypeVariable node) { + TypeVariableElement element = node.dartType.element; + Element helperElement = compiler.findHelper('convertRtiToRuntimeType'); + world.registerStaticUse(helperElement); + + use(node.inputs[0]); + if (node.hasReceiver) { + if (backend.isInterceptorClass(element.getEnclosingClass())) { + int index = RuntimeTypes.getTypeVariableIndex(element); + js.Expression receiver = pop(); + js.Expression helper = backend.namer.elementAccess(helperElement); + push(helper(js.js(r'#.$builtinTypeInfo && #.$builtinTypeInfo[#]', + [receiver, receiver, js.js.toExpression(index)]))); + } else { + backend.emitter.registerReadTypeVariable(element); + push( + js.js('#.${backend.namer.readTypeVariableName(element)}()', pop())); + } + } else { + push( + backend.namer.elementAccess( + compiler.findHelper('convertRtiToRuntimeType'))(pop())); + } + } + + void visitInterfaceType(HInterfaceType node) { + List typeArguments = []; + for (HInstruction type in node.inputs) { + use(type); + typeArguments.add(pop()); + } + + ClassElement cls = node.dartType.element; + var arguments = [ + backend.namer.elementAccess(backend.getImplementationClass(cls))]; + if (!typeArguments.isEmpty) { + arguments.add(new js.ArrayInitializer.from(typeArguments)); + } + push(accessHelper('buildInterfaceType')(arguments)); + } + + void visitVoidType(HVoidType node) { + push(accessHelper('getVoidRuntimeType')()); + } + + void visitDynamicType(HDynamicType node) { + push(accessHelper('getDynamicRuntimeType')()); + } + + js.PropertyAccess accessHelper(String name) { + Element helper = compiler.findHelper(name); + if (helper == null) { + // For mocked-up tests. + return js.js('(void 0).$name'); + } + world.registerStaticUse(helper); + return backend.namer.elementAccess(helper); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart new file mode 100644 index 0000000..f4c8950 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart @@ -0,0 +1,595 @@ +// Copyright (c) 2012, 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. + +part of ssa; + +/** + * Replaces some instructions with specialized versions to make codegen easier. + * Caches codegen information on nodes. + */ +class SsaInstructionSelection extends HBaseVisitor { + final Compiler compiler; + HGraph graph; + + SsaInstructionSelection(this.compiler); + + JavaScriptBackend get backend => compiler.backend; + + void visitGraph(HGraph graph) { + this.graph = graph; + visitDominatorTree(graph); + } + + visitBasicBlock(HBasicBlock block) { + HInstruction instruction = block.first; + while (instruction != null) { + HInstruction next = instruction.next; + HInstruction replacement = instruction.accept(this); + if (replacement != instruction) { + block.rewrite(instruction, replacement); + + // If the replacement instruction does not know its source element, use + // the source element of the instruction. + if (replacement.sourceElement == null) { + replacement.sourceElement = instruction.sourceElement; + } + if (replacement.sourcePosition == null) { + replacement.sourcePosition = instruction.sourcePosition; + } + if (!replacement.isInBasicBlock()) { + // The constant folding can return an instruction that is already + // part of the graph (like an input), so we only add the replacement + // if necessary. + block.addAfter(instruction, replacement); + // Visit the replacement as the next instruction in case it can also + // be constant folded away. + next = replacement; + } + block.remove(instruction); + } + instruction = next; + } + } + + HInstruction visitInstruction(HInstruction node) { + return node; + } + + HInstruction visitIs(HIs node) { + if (node.kind == HIs.RAW_CHECK) { + HInstruction interceptor = node.interceptor; + if (interceptor != null) { + return new HIsViaInterceptor(node.typeExpression, interceptor, + backend.boolType); + } + } + return node; + } + + HInstruction visitIdentity(HIdentity node) { + node.singleComparisonOp = simpleOp(node.left, node.right); + return node; + } + + String simpleOp(HInstruction left, HInstruction right) { + // Returns the single identity comparison (== or ===) or null if a more + // complex expression is required. + TypeMask leftType = left.instructionType; + TypeMask rightType = right.instructionType; + if (leftType.isNullable && rightType.isNullable) { + if (left.isConstantNull() || + right.isConstantNull() || + (left.isPrimitive(compiler) && + leftType == rightType)) { + return '=='; + } + return null; + } + return '==='; + } + + HInstruction visitInvokeDynamic(HInvokeDynamic node) { + if (node.isInterceptedCall) { + // Calls of the form + // + // a.foo$1(a, x) + // + // where the interceptor calling convention is used come from recognizing + // that 'a' is a 'self-interceptor'. If the selector matches only methods + // that ignore the explicit receiver parameter, replace occurences of the + // receiver argument with a dummy receiver '0': + // + // a.foo$1(a, x) ---> a.foo$1(0, x) + // + // This often reduces the number of references to 'a' to one, allowing 'a' + // to be generated at use to avoid a temporary, e.g. + // + // t1 = b.get$thing(); + // t1.foo$1(t1, x) + // ---> + // b.get$thing().foo$1(0, x) + // + Selector selector = node.selector; + if (backend.isInterceptedSelector(selector) && + !backend.isInterceptedMixinSelector(selector)) { + HInstruction interceptor = node.inputs[0]; + HInstruction receiverArgument = node.inputs[1]; + + // TODO(15720): The test here should be + // + // interceptor.nonCheck() == receiverArgument.nonCheck() + // + if (interceptor == receiverArgument) { + // TODO(15933): Make automatically generated property extraction + // closures work with the dummy receiver optimization. + if (!selector.isGetter()) { + Constant constant = new DummyConstant( + receiverArgument.instructionType); + HConstant dummy = graph.addConstant(constant, compiler); + receiverArgument.usedBy.remove(node); + node.inputs[1] = dummy; + dummy.usedBy.add(node); + } + } + } + } + + return node; + } +} + +/** + * Remove [HTypeKnown] instructions from the graph, to make codegen + * analysis easier. + */ +class SsaTypeKnownRemover extends HBaseVisitor { + + void visitGraph(HGraph graph) { + visitDominatorTree(graph); + } + + void visitBasicBlock(HBasicBlock block) { + HInstruction instruction = block.first; + while (instruction != null) { + HInstruction next = instruction.next; + instruction.accept(this); + instruction = next; + } + } + + void visitTypeKnown(HTypeKnown instruction) { + instruction.block.rewrite(instruction, instruction.checkedInput); + instruction.block.remove(instruction); + } +} + +/** + * Instead of emitting each SSA instruction with a temporary variable + * mark instructions that can be emitted at their use-site. + * For example, in: + * t0 = 4; + * t1 = 3; + * t2 = add(t0, t1); + * t0 and t1 would be marked and the resulting code would then be: + * t2 = add(4, 3); + */ +class SsaInstructionMerger extends HBaseVisitor { + final Compiler compiler; + /** + * List of [HInstruction] that the instruction merger expects in + * order when visiting the inputs of an instruction. + */ + List expectedInputs; + /** + * Set of pure [HInstruction] that the instruction merger expects to + * find. The order of pure instructions do not matter, as they will + * not be affected by side effects. + */ + Set pureInputs; + Set generateAtUseSite; + + void markAsGenerateAtUseSite(HInstruction instruction) { + assert(!instruction.isJsStatement()); + generateAtUseSite.add(instruction); + } + + SsaInstructionMerger(this.generateAtUseSite, this.compiler); + + void visitGraph(HGraph graph) { + visitDominatorTree(graph); + } + + void analyzeInputs(HInstruction user, int start) { + List inputs = user.inputs; + for (int i = start; i < inputs.length; i++) { + HInstruction input = inputs[i]; + if (!generateAtUseSite.contains(input) + && !input.isCodeMotionInvariant() + && input.usedBy.length == 1 + && input is !HPhi + && input is !HLocalValue + && !input.isJsStatement()) { + if (input.isPure()) { + // Only consider a pure input if it is in the same loop. + // Otherwise, we might move GVN'ed instruction back into the + // loop. + if (user.hasSameLoopHeaderAs(input)) { + // Move it closer to [user], so that instructions in + // between do not prevent making it generate at use site. + input.moveBefore(user); + pureInputs.add(input); + // Previous computations done on [input] are now invalid + // because we moved [input] to another place. So all + // non code motion invariant instructions need + // to be removed from the [generateAtUseSite] set. + input.inputs.forEach((instruction) { + if (!instruction.isCodeMotionInvariant()) { + generateAtUseSite.remove(instruction); + } + }); + // Visit the pure input now so that the expected inputs + // are after the expected inputs of [user]. + input.accept(this); + } + } else { + expectedInputs.add(input); + } + } + } + } + + void visitInstruction(HInstruction instruction) { + // A code motion invariant instruction is dealt before visiting it. + assert(!instruction.isCodeMotionInvariant()); + analyzeInputs(instruction, 0); + } + + // The codegen might use the input multiple times, so it must not be + // set generate at use site. + void visitIs(HIs instruction) {} + + // A bounds check method must not have its first input generated at use site, + // because it's using it twice. + void visitBoundsCheck(HBoundsCheck instruction) { + analyzeInputs(instruction, 1); + } + + // An identity operation must only have its inputs generated at use site if + // does not require an expression with multiple uses (because of null / + // undefined). + void visitIdentity(HIdentity instruction) { + if (instruction.singleComparisonOp != null) { + super.visitIdentity(instruction); + } + // Do nothing. + } + + void visitTypeConversion(HTypeConversion instruction) { + if (!instruction.isArgumentTypeCheck + && !instruction.isReceiverTypeCheck) { + assert(instruction.isCheckedModeCheck || instruction.isCastTypeCheck); + // Checked mode checks and cast checks compile to code that + // only use their input once, so we can safely visit them + // and try to merge the input. + visitInstruction(instruction); + } + } + + void visitTypeKnown(HTypeKnown instruction) { + // [HTypeKnown] instructions are removed before code generation. + assert(false); + } + + void tryGenerateAtUseSite(HInstruction instruction) { + if (instruction.isControlFlow()) return; + markAsGenerateAtUseSite(instruction); + } + + bool isBlockSinglePredecessor(HBasicBlock block) { + return block.successors.length == 1 + && block.successors[0].predecessors.length == 1; + } + + void visitBasicBlock(HBasicBlock block) { + // Compensate from not merging blocks: if the block is the + // single predecessor of its single successor, let the successor + // visit it. + if (isBlockSinglePredecessor(block)) return; + + tryMergingExpressions(block); + } + + void tryMergingExpressions(HBasicBlock block) { + // Visit each instruction of the basic block in last-to-first order. + // Keep a list of expected inputs of the current "expression" being + // merged. If instructions occur in the expected order, they are + // included in the expression. + + // The expectedInputs list holds non-trivial instructions that may + // be generated at their use site, if they occur in the correct order. + if (expectedInputs == null) expectedInputs = new List(); + if (pureInputs == null) pureInputs = new Set(); + + // Pop instructions from expectedInputs until instruction is found. + // Return true if it is found, or false if not. + bool findInInputsAndPopNonMatching(HInstruction instruction) { + assert(!instruction.isPure()); + while (!expectedInputs.isEmpty) { + HInstruction nextInput = expectedInputs.removeLast(); + assert(!generateAtUseSite.contains(nextInput)); + assert(nextInput.usedBy.length == 1); + if (identical(nextInput, instruction)) { + return true; + } + } + return false; + } + + block.last.accept(this); + for (HInstruction instruction = block.last.previous; + instruction != null; + instruction = instruction.previous) { + if (generateAtUseSite.contains(instruction)) { + continue; + } + if (instruction.isCodeMotionInvariant()) { + markAsGenerateAtUseSite(instruction); + continue; + } + if (instruction.isPure()) { + if (pureInputs.contains(instruction)) { + tryGenerateAtUseSite(instruction); + } else { + // If the input is not in the [pureInputs] set, it has not + // been visited or should not be generated at use-site. The most + // likely reason for the latter, is that the instruction is used + // in more than one location. + // We must either clear the expectedInputs, or move the pure + // instruction's inputs in front of the existing ones. + // Example: + // t1 = foo(); // side-effect. + // t2 = bar(); // side-effect. + // t3 = pure(t2); // used more than once. + // f(t1, t3); // expected inputs of 'f': t1. + // use(t3); + // + // If we don't clear the expected inputs we end up in a situation + // where pure pushes "t2" on top of "t1" leading to: + // t3 = pure(bar()); + // f(foo(), t3); + // use(t3); + // + // If we clear the expected-inputs list we have the correct + // output: + // t1 = foo(); + // t3 = pure(bar()); + // f(t1, t3); + // use(t3); + // + // Clearing is, however, not optimal. + // Example: + // t1 = foo(); // t1 is now used by `pure`. + // t2 = bar(); // t2 is now used by `f`. + // t3 = pure(t1); + // f(t2, t3); + // use(t3); + // + // If we clear the expected-inputs we can't generate-at-use any of + // the instructions. + // + // The optimal solution is to move the inputs of 'pure' in + // front of the expectedInputs list. This makes sense, since we + // push expected-inputs from left-to right, and the `pure` function + // invocation is "more left" (i.e. before) the first argument of `f`. + // With that approach we end up with: + // t3 = pure(foo(); + // f(bar(), t3); + // use(t3); + // + int oldLength = expectedInputs.length; + instruction.accept(this); + if (oldLength != 0 && oldLength != expectedInputs.length) { + // Move the pure instruction's inputs to the front. + List newInputs = expectedInputs.sublist(oldLength); + int newCount = newInputs.length; + expectedInputs.setRange( + newCount, newCount + oldLength, expectedInputs); + expectedInputs.setRange(0, newCount, newInputs); + } + } + } else { + if (findInInputsAndPopNonMatching(instruction)) { + // The current instruction is the next non-trivial + // expected input. + tryGenerateAtUseSite(instruction); + } else { + assert(expectedInputs.isEmpty); + } + instruction.accept(this); + } + } + + if (block.predecessors.length == 1 + && isBlockSinglePredecessor(block.predecessors[0])) { + assert(block.phis.isEmpty); + tryMergingExpressions(block.predecessors[0]); + } else { + expectedInputs = null; + pureInputs = null; + } + } +} + +/** + * Detect control flow arising from short-circuit logical and + * conditional operators, and prepare the program to be generated + * using these operators instead of nested ifs and boolean variables. + */ +class SsaConditionMerger extends HGraphVisitor { + Set generateAtUseSite; + Set controlFlowOperators; + + void markAsGenerateAtUseSite(HInstruction instruction) { + assert(!instruction.isJsStatement()); + generateAtUseSite.add(instruction); + } + + SsaConditionMerger(this.generateAtUseSite, this.controlFlowOperators); + + void visitGraph(HGraph graph) { + visitPostDominatorTree(graph); + } + + /** + * Check if a block has at least one statement other than + * [instruction]. + */ + bool hasAnyStatement(HBasicBlock block, HInstruction instruction) { + // If [instruction] is not in [block], then if the block is not + // empty, we know there will be a statement to emit. + if (!identical(instruction.block, block)) return !identical(block.last, block.first); + + // If [instruction] is not the last instruction of the block + // before the control flow instruction, or the last instruction, + // then we will have to emit a statement for that last instruction. + if (instruction != block.last + && !identical(instruction, block.last.previous)) return true; + + // If one of the instructions in the block until [instruction] is + // not generated at use site, then we will have to emit a + // statement for it. + // TODO(ngeoffray): we could generate a comma separated + // list of expressions. + for (HInstruction temp = block.first; + !identical(temp, instruction); + temp = temp.next) { + if (!generateAtUseSite.contains(temp)) return true; + } + + return false; + } + + bool isSafeToGenerateAtUseSite(HInstruction user, HInstruction input) { + // A [HForeign] instruction uses operators and if we generate + // [input] at use site, the precedence might be wrong. + if (user is HForeign) return false; + // A [HCheck] instruction with control flow uses its input + // multiple times, so we avoid generating it at use site. + if (user is HCheck && user.isControlFlow()) return false; + // A [HIs] instruction uses its input multiple times, so we + // avoid generating it at use site. + if (user is HIs) return false; + return true; + } + + void visitBasicBlock(HBasicBlock block) { + if (block.last is !HIf) return; + HIf startIf = block.last; + HBasicBlock end = startIf.joinBlock; + + // We check that the structure is the following: + // If + // / \ + // / \ + // 1 expr goto + // goto / + // \ / + // \ / + // phi(expr, true|false) + // + // and the same for nested nodes: + // + // If + // / \ + // / \ + // 1 expr1 \ + // If \ + // / \ \ + // / \ goto + // 1 expr2 | + // goto goto | + // \ / | + // \ / | + // phi1(expr2, true|false) + // \ | + // \ | + // phi(phi1, true|false) + + if (end == null) return; + if (end.phis.isEmpty) return; + if (!identical(end.phis.first, end.phis.last)) return; + HBasicBlock elseBlock = startIf.elseBlock; + + if (!identical(end.predecessors[1], elseBlock)) return; + HPhi phi = end.phis.first; + HInstruction thenInput = phi.inputs[0]; + HInstruction elseInput = phi.inputs[1]; + if (thenInput.isJsStatement() || elseInput.isJsStatement()) return; + + if (hasAnyStatement(elseBlock, elseInput)) return; + assert(elseBlock.successors.length == 1); + assert(end.predecessors.length == 2); + + HBasicBlock thenBlock = startIf.thenBlock; + // Skip trivial goto blocks. + while (thenBlock.successors[0] != end && thenBlock.first is HGoto) { + thenBlock = thenBlock.successors[0]; + } + + // If the [thenBlock] is already a control flow operation, and does not + // have any statement and its join block is [end], we can emit a + // sequence of control flow operation. + if (controlFlowOperators.contains(thenBlock.last)) { + HIf otherIf = thenBlock.last; + if (!identical(otherIf.joinBlock, end)) { + // This could be a join block that just feeds into our join block. + HBasicBlock otherJoin = otherIf.joinBlock; + if (otherJoin.first != otherJoin.last) return; + if (otherJoin.successors.length != 1) return; + if (otherJoin.successors[0] != end) return; + if (otherJoin.phis.isEmpty) return; + if (!identical(otherJoin.phis.first, otherJoin.phis.last)) return; + HPhi otherPhi = otherJoin.phis.first; + if (thenInput != otherPhi) return; + if (elseInput != otherPhi.inputs[1]) return; + } + if (hasAnyStatement(thenBlock, otherIf)) return; + } else { + if (!identical(end.predecessors[0], thenBlock)) return; + if (hasAnyStatement(thenBlock, thenInput)) return; + assert(thenBlock.successors.length == 1); + } + + // From now on, we have recognized a control flow operation built from + // the builder. Mark the if instruction as such. + controlFlowOperators.add(startIf); + + // Find the next non-HGoto instruction following the phi. + HInstruction nextInstruction = phi.block.first; + while (nextInstruction is HGoto) { + nextInstruction = nextInstruction.block.successors[0].first; + } + + // If the operation is only used by the first instruction + // of its block and is safe to be generated at use site, mark it + // so. + if (phi.usedBy.length == 1 + && phi.usedBy[0] == nextInstruction + && isSafeToGenerateAtUseSite(phi.usedBy[0], phi)) { + markAsGenerateAtUseSite(phi); + } + + if (identical(elseInput.block, elseBlock)) { + assert(elseInput.usedBy.length == 1); + markAsGenerateAtUseSite(elseInput); + } + + // If [thenInput] is defined in the first predecessor, then it is only used + // by [phi] and can be generated at use site. + if (identical(thenInput.block, end.predecessors[0])) { + assert(thenInput.usedBy.length == 1); + markAsGenerateAtUseSite(thenInput); + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart new file mode 100644 index 0000000..ecfd632 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart @@ -0,0 +1,295 @@ +// Copyright (c) 2013, 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. + +part of ssa; + +/** + * This phase simplifies interceptors in multiple ways: + * + * 1) If the interceptor is for an object whose type is known, it + * tries to use a constant interceptor instead. + * + * 2) It specializes interceptors based on the selectors it is being + * called with. + * + * 3) If we know the object is not intercepted, we just use it + * instead. + * + * 4) It replaces all interceptors that are used only once with + * one-shot interceptors. It saves code size and makes the receiver of + * an intercepted call a candidate for being generated at use site. + * + */ +class SsaSimplifyInterceptors extends HBaseVisitor + implements OptimizationPhase { + final String name = "SsaSimplifyInterceptors"; + final ConstantSystem constantSystem; + final Compiler compiler; + final CodegenWorkItem work; + HGraph graph; + + SsaSimplifyInterceptors(this.compiler, this.constantSystem, this.work); + + void visitGraph(HGraph graph) { + this.graph = graph; + visitDominatorTree(graph); + } + + void visitBasicBlock(HBasicBlock node) { + currentBlock = node; + + HInstruction instruction = node.first; + while (instruction != null) { + bool shouldRemove = instruction.accept(this); + HInstruction next = instruction.next; + if (shouldRemove) { + instruction.block.remove(instruction); + } + instruction = next; + } + } + + bool visitInstruction(HInstruction instruction) => false; + + bool visitInvoke(HInvoke invoke) { + if (!invoke.isInterceptedCall) return false; + var interceptor = invoke.inputs[0]; + if (interceptor is! HInterceptor) return false; + HInstruction constant = tryComputeConstantInterceptor( + invoke.inputs[1], interceptor.interceptedClasses); + if (constant != null) { + invoke.changeUse(interceptor, constant); + } + return false; + } + + bool canUseSelfForInterceptor(HInstruction instruction, + Set interceptedClasses) { + JavaScriptBackend backend = compiler.backend; + if (instruction.canBePrimitive(compiler)) { + // Primitives always need interceptors. + return false; + } + if (instruction.canBeNull() + && interceptedClasses.contains(backend.jsNullClass)) { + // Need the JSNull interceptor. + return false; + } + + // [interceptedClasses] is sparse - it is just the classes that define some + // intercepted method. Their subclasses (that inherit the method) are + // implicit, so we have to extend them. + TypeMask receiverType = instruction.instructionType; + return interceptedClasses + .where((cls) => cls != compiler.objectClass) + .map((cls) => backend.classesMixedIntoInterceptedClasses.contains(cls) + ? new TypeMask.subtype(cls) + : new TypeMask.subclass(cls)) + .every((mask) => receiverType.intersection(mask, compiler).isEmpty); + } + + HInstruction tryComputeConstantInterceptor( + HInstruction input, + Set interceptedClasses) { + if (input == graph.explicitReceiverParameter) { + // If `explicitReceiverParameter` is set it means the current method is an + // interceptor method, and `this` is the interceptor. The caller just did + // `getInterceptor(foo).currentMethod(foo)` to enter the current method. + return graph.thisInstruction; + } + + ClassElement constantInterceptor; + JavaScriptBackend backend = compiler.backend; + if (input.canBeNull()) { + if (input.isNull()) { + constantInterceptor = backend.jsNullClass; + } + } else if (input.isInteger(compiler)) { + constantInterceptor = backend.jsIntClass; + } else if (input.isDouble(compiler)) { + constantInterceptor = backend.jsDoubleClass; + } else if (input.isBoolean(compiler)) { + constantInterceptor = backend.jsBoolClass; + } else if (input.isString(compiler)) { + constantInterceptor = backend.jsStringClass; + } else if (input.isArray(compiler)) { + constantInterceptor = backend.jsArrayClass; + } else if (input.isNumber(compiler) + && !interceptedClasses.contains(backend.jsIntClass) + && !interceptedClasses.contains(backend.jsDoubleClass)) { + // If the method being intercepted is not defined in [int] or [double] we + // can safely use the number interceptor. This is because none of the + // [int] or [double] methods are called from a method defined on [num]. + constantInterceptor = backend.jsNumberClass; + } else { + // Try to find constant interceptor for a native class. If the receiver + // is constrained to a leaf native class, we can use the class's + // interceptor directly. + + // TODO(sra): Key DOM classes like Node, Element and Event are not leaf + // classes. When the receiver type is not a leaf class, we might still be + // able to use the receiver class as a constant interceptor. It is + // usually the case that methods defined on a non-leaf class don't test + // for a subclass or call methods defined on a subclass. Provided the + // code is completely insensitive to the specific instance subclasses, we + // can use the non-leaf class directly. + ClassElement element = input.instructionType.singleClass(compiler); + if (element != null && element.isNative()) { + constantInterceptor = element; + } + } + + if (constantInterceptor == null) return null; + + // If we just happen to be in an instance method of the constant + // interceptor, `this` is a shorter alias. + if (constantInterceptor == work.element.getEnclosingClass() + && graph.thisInstruction != null) { + return graph.thisInstruction; + } + + Constant constant = new InterceptorConstant(constantInterceptor.thisType); + return graph.addConstant(constant, compiler); + } + + HInstruction findDominator(Iterable instructions) { + HInstruction result; + L1: for (HInstruction candidate in instructions) { + for (HInstruction current in instructions) { + if (current != candidate && !candidate.dominates(current)) continue L1; + } + result = candidate; + break; + } + return result; + } + + bool visitInterceptor(HInterceptor node) { + if (node.isConstant()) return false; + + // If the interceptor is used by multiple instructions, specialize + // it with a set of classes it intercepts. + Set interceptedClasses; + JavaScriptBackend backend = compiler.backend; + HInstruction dominator = findDominator(node.usedBy); + // If there is a call that dominates all other uses, we can use just the + // selector of that instruction. + if (dominator is HInvokeDynamic && + dominator.isCallOnInterceptor(compiler) && + node == dominator.receiver) { + interceptedClasses = + backend.getInterceptedClassesOn(dominator.selector.name); + + // If we found that we need number, we must still go through all + // uses to check if they require int, or double. + if (interceptedClasses.contains(backend.jsNumberClass) + && !(interceptedClasses.contains(backend.jsDoubleClass) + || interceptedClasses.contains(backend.jsIntClass))) { + for (HInstruction user in node.usedBy) { + if (user is! HInvoke) continue; + Set intercepted = + backend.getInterceptedClassesOn(user.selector.name); + if (intercepted.contains(backend.jsIntClass)) { + interceptedClasses.add(backend.jsIntClass); + } + if (intercepted.contains(backend.jsDoubleClass)) { + interceptedClasses.add(backend.jsDoubleClass); + } + } + } + } else { + interceptedClasses = new Set(); + for (HInstruction user in node.usedBy) { + if (user is HInvokeDynamic && + user.isCallOnInterceptor(compiler) && + node == user.receiver) { + interceptedClasses.addAll( + backend.getInterceptedClassesOn(user.selector.name)); + } else { + // Use a most general interceptor for other instructions, example, + // is-checks and escaping interceptors. + interceptedClasses.addAll(backend.interceptedClasses); + break; + } + } + } + + HInstruction receiver = node.receiver; + if (canUseSelfForInterceptor(receiver, interceptedClasses)) { + return rewriteToUseSelfAsInterceptor(node, receiver); + } + + // Try computing a constant interceptor. + HInstruction constantInterceptor = + tryComputeConstantInterceptor(receiver, interceptedClasses); + if (constantInterceptor != null) { + node.block.rewrite(node, constantInterceptor); + return false; + } + + node.interceptedClasses = interceptedClasses; + + // Try creating a one-shot interceptor. + if (node.usedBy.length != 1) return false; + if (node.usedBy[0] is !HInvokeDynamic) return false; + + HInvokeDynamic user = node.usedBy[0]; + + // If [node] was loop hoisted, we keep the interceptor. + if (!user.hasSameLoopHeaderAs(node)) return false; + + // Replace the user with a [HOneShotInterceptor]. + HConstant nullConstant = graph.addConstantNull(compiler); + List inputs = new List.from(user.inputs); + inputs[0] = nullConstant; + HOneShotInterceptor interceptor = new HOneShotInterceptor( + user.selector, inputs, user.instructionType, node.interceptedClasses); + interceptor.sourcePosition = user.sourcePosition; + interceptor.sourceElement = user.sourceElement; + + HBasicBlock block = user.block; + block.addAfter(user, interceptor); + block.rewrite(user, interceptor); + block.remove(user); + return true; + } + + bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { + node.block.rewrite(node, receiver); + return false; + } + + bool visitOneShotInterceptor(HOneShotInterceptor node) { + HInstruction constant = tryComputeConstantInterceptor( + node.inputs[1], node.interceptedClasses); + + if (constant == null) return false; + + Selector selector = node.selector; + HInstruction instruction; + if (selector.isGetter()) { + instruction = new HInvokeDynamicGetter( + selector, + node.element, + [constant, node.inputs[1]], + node.instructionType); + } else if (selector.isSetter()) { + instruction = new HInvokeDynamicSetter( + selector, + node.element, + [constant, node.inputs[1], node.inputs[2]], + node.instructionType); + } else { + List inputs = new List.from(node.inputs); + inputs[0] = constant; + instruction = new HInvokeDynamicMethod( + selector, inputs, node.instructionType, true); + } + + HBasicBlock block = node.block; + block.addAfter(node, instruction); + block.rewrite(node, instruction); + return true; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart new file mode 100644 index 0000000..727a7d4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart @@ -0,0 +1,733 @@ +// Copyright (c) 2013, 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. + +part of ssa; + +/** + * [InvokeDynamicSpecializer] and its subclasses are helpers to + * optimize intercepted dynamic calls. It knows what input types + * would be beneficial for performance, and how to change a invoke + * dynamic to a builtin instruction (e.g. HIndex, HBitNot). + */ +class InvokeDynamicSpecializer { + const InvokeDynamicSpecializer(); + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + Selector selector = instruction.selector; + TypeMask type = TypeMaskFactory.inferredTypeForSelector(selector, compiler); + instruction.sideEffects = compiler.world.getSideEffectsOfSelector(selector); + if (!instruction.sideEffects.hasSideEffects()) { + instruction.setUseGvn(); + } else { + instruction.clearUseGvn(); + } + return type; + } + + HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, + Compiler compiler) { + return null; + } + + Operation operation(ConstantSystem constantSystem) => null; + + static InvokeDynamicSpecializer lookupSpecializer(Selector selector) { + if (selector.kind == SelectorKind.INDEX) { + return selector.name == '[]' + ? const IndexSpecializer() + : const IndexAssignSpecializer(); + } else if (selector.kind == SelectorKind.OPERATOR) { + if (selector.name == 'unary-') { + return const UnaryNegateSpecializer(); + } else if (selector.name == '~') { + return const BitNotSpecializer(); + } else if (selector.name == '+') { + return const AddSpecializer(); + } else if (selector.name == '-') { + return const SubtractSpecializer(); + } else if (selector.name == '*') { + return const MultiplySpecializer(); + } else if (selector.name == '/') { + return const DivideSpecializer(); + } else if (selector.name == '~/') { + return const TruncatingDivideSpecializer(); + } else if (selector.name == '%') { + return const ModuloSpecializer(); + } else if (selector.name == '>>') { + return const ShiftRightSpecializer(); + } else if (selector.name == '<<') { + return const ShiftLeftSpecializer(); + } else if (selector.name == '&') { + return const BitAndSpecializer(); + } else if (selector.name == '|') { + return const BitOrSpecializer(); + } else if (selector.name == '^') { + return const BitXorSpecializer(); + } else if (selector.name == '==') { + return const EqualsSpecializer(); + } else if (selector.name == '<') { + return const LessSpecializer(); + } else if (selector.name == '<=') { + return const LessEqualSpecializer(); + } else if (selector.name == '>') { + return const GreaterSpecializer(); + } else if (selector.name == '>=') { + return const GreaterEqualSpecializer(); + } + } + return const InvokeDynamicSpecializer(); + } +} + +class IndexAssignSpecializer extends InvokeDynamicSpecializer { + const IndexAssignSpecializer(); + + HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, + Compiler compiler) { + if (instruction.inputs[1].isMutableIndexable(compiler)) { + if (!instruction.inputs[2].isInteger(compiler) + && compiler.enableTypeAssertions) { + // We want the right checked mode error. + return null; + } + return new HIndexAssign(instruction.inputs[1], + instruction.inputs[2], + instruction.inputs[3], + instruction.selector); + } + return null; + } +} + +class IndexSpecializer extends InvokeDynamicSpecializer { + const IndexSpecializer(); + + HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, + Compiler compiler) { + if (!instruction.inputs[1].isIndexablePrimitive(compiler)) return null; + if (!instruction.inputs[2].isInteger(compiler) + && compiler.enableTypeAssertions) { + // We want the right checked mode error. + return null; + } + TypeMask receiverType = + instruction.getDartReceiver(compiler).instructionType; + Selector refined = new TypedSelector(receiverType, instruction.selector); + TypeMask type = TypeMaskFactory.inferredTypeForSelector(refined, compiler); + return new HIndex( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, type); + } +} + +class BitNotSpecializer extends InvokeDynamicSpecializer { + const BitNotSpecializer(); + + UnaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.bitNot; + } + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + // All bitwise operations on primitive types either produce an + // integer or throw an error. + JavaScriptBackend backend = compiler.backend; + if (instruction.inputs[1].isPrimitiveOrNull(compiler)) { + return backend.uint32Type; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + HInstruction input = instruction.inputs[1]; + if (input.isNumber(compiler)) { + return new HBitNot(input, instruction.selector, + computeTypeFromInputTypes(instruction, compiler)); + } + return null; + } +} + +class UnaryNegateSpecializer extends InvokeDynamicSpecializer { + const UnaryNegateSpecializer(); + + UnaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.negate; + } + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + TypeMask operandType = instruction.inputs[1].instructionType; + if (instruction.inputs[1].isNumberOrNull(compiler)) return operandType; + return super.computeTypeFromInputTypes(instruction, compiler); + } + + HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, + Compiler compiler) { + HInstruction input = instruction.inputs[1]; + if (input.isNumber(compiler)) { + return new HNegate(input, instruction.selector, input.instructionType); + } + return null; + } +} + +abstract class BinaryArithmeticSpecializer extends InvokeDynamicSpecializer { + const BinaryArithmeticSpecializer(); + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + JavaScriptBackend backend = compiler.backend; + if (left.isIntegerOrNull(compiler) && right.isIntegerOrNull(compiler)) { + return backend.intType; + } + if (left.isNumberOrNull(compiler)) { + if (left.isDoubleOrNull(compiler) || right.isDoubleOrNull(compiler)) { + return backend.doubleType; + } + return backend.numType; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + bool isBuiltin(HInvokeDynamic instruction, Compiler compiler) { + return instruction.inputs[1].isNumber(compiler) + && instruction.inputs[2].isNumber(compiler); + } + + HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, + Compiler compiler) { + if (isBuiltin(instruction, compiler)) { + HInstruction builtin = newBuiltinVariant(instruction, compiler); + if (builtin != null) return builtin; + // Even if there is no builtin equivalent instruction, we know + // the instruction does not have any side effect, and that it + // can be GVN'ed. + clearAllSideEffects(instruction); + } + return null; + } + + void clearAllSideEffects(HInstruction instruction) { + instruction.sideEffects.clearAllSideEffects(); + instruction.sideEffects.clearAllDependencies(); + instruction.setUseGvn(); + } + + bool inputsArePositiveIntegers(HInstruction instruction, Compiler compiler) { + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + JavaScriptBackend backend = compiler.backend; + return left.isPositiveIntegerOrNull(compiler) + && right.isPositiveIntegerOrNull(compiler); + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, Compiler compiler); + + Selector renameToOptimizedSelector(String name, + Selector selector, + Compiler compiler) { + if (selector.name == name) return selector; + return new TypedSelector( + selector.mask, + new Selector(SelectorKind.CALL, + name, + compiler.interceptorsLibrary, + selector.argumentCount)); + } +} + +class AddSpecializer extends BinaryArithmeticSpecializer { + const AddSpecializer(); + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + if (inputsArePositiveIntegers(instruction, compiler)) { + JavaScriptBackend backend = compiler.backend; + return backend.positiveIntType; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.add; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + return new HAdd( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, computeTypeFromInputTypes(instruction, compiler)); + } +} + +class DivideSpecializer extends BinaryArithmeticSpecializer { + const DivideSpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.divide; + } + + TypeMask computeTypeFromInputTypes(HInstruction instruction, + Compiler compiler) { + HInstruction left = instruction.inputs[1]; + JavaScriptBackend backend = compiler.backend; + if (left.isNumberOrNull(compiler)) { + return backend.doubleType; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return new HDivide( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, backend.doubleType); + } +} + +class ModuloSpecializer extends BinaryArithmeticSpecializer { + const ModuloSpecializer(); + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + if (inputsArePositiveIntegers(instruction, compiler)) { + JavaScriptBackend backend = compiler.backend; + return backend.positiveIntType; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.modulo; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + // Modulo cannot be mapped to the native operator (different semantics). + return null; + } +} + +class MultiplySpecializer extends BinaryArithmeticSpecializer { + const MultiplySpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.multiply; + } + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + if (inputsArePositiveIntegers(instruction, compiler)) { + JavaScriptBackend backend = compiler.backend; + return backend.positiveIntType; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + return new HMultiply( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, computeTypeFromInputTypes(instruction, compiler)); + } +} + +class SubtractSpecializer extends BinaryArithmeticSpecializer { + const SubtractSpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.subtract; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + return new HSubtract( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, computeTypeFromInputTypes(instruction, compiler)); + } +} + +class TruncatingDivideSpecializer extends BinaryArithmeticSpecializer { + const TruncatingDivideSpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.truncatingDivide; + } + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + if (inputsArePositiveIntegers(instruction, compiler)) { + JavaScriptBackend backend = compiler.backend; + return backend.positiveIntType; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + bool isNotZero(HInstruction instruction, Compiler compiler) { + if (!instruction.isConstantInteger()) return false; + HConstant rightConstant = instruction; + IntConstant intConstant = rightConstant.constant; + int count = intConstant.value; + return count != 0; + } + + HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, + Compiler compiler) { + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + if (isBuiltin(instruction, compiler)) { + if (right.isPositiveInteger(compiler) && isNotZero(right, compiler)) { + if (left.isUInt31(compiler)) { + return newBuiltinVariant(instruction, compiler); + } + // We can call _tdivFast because the rhs is a 32bit integer + // and not 0, nor -1. + instruction.selector = renameToOptimizedSelector( + '_tdivFast', instruction.selector, compiler); + } + clearAllSideEffects(instruction); + } + return null; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + return new HTruncatingDivide( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, computeTypeFromInputTypes(instruction, compiler)); + } +} + +abstract class BinaryBitOpSpecializer extends BinaryArithmeticSpecializer { + const BinaryBitOpSpecializer(); + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + // All bitwise operations on primitive types either produce an + // integer or throw an error. + HInstruction left = instruction.inputs[1]; + JavaScriptBackend backend = compiler.backend; + if (left.isPrimitiveOrNull(compiler)) { + return backend.uint32Type; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + bool argumentLessThan32(HInstruction instruction) { + if (!instruction.isConstantInteger()) return false; + HConstant rightConstant = instruction; + IntConstant intConstant = rightConstant.constant; + int count = intConstant.value; + return count >= 0 && count <= 31; + } + + bool isPositive(HInstruction instruction, Compiler compiler) { + // TODO: We should use the value range analysis. Currently, ranges + // are discarded just after the analysis. + return instruction.isPositiveInteger(compiler); + } +} + +class ShiftLeftSpecializer extends BinaryBitOpSpecializer { + const ShiftLeftSpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.shiftLeft; + } + + HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, + Compiler compiler) { + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + if (left.isNumber(compiler)) { + if (argumentLessThan32(right)) { + return newBuiltinVariant(instruction, compiler); + } + // Even if there is no builtin equivalent instruction, we know + // the instruction does not have any side effect, and that it + // can be GVN'ed. + clearAllSideEffects(instruction); + Selector selector = instruction.selector; + if (isPositive(right, compiler)) { + instruction.selector = renameToOptimizedSelector( + '_shlPositive', instruction.selector, compiler); + } + } + return null; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return new HShiftLeft( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, computeTypeFromInputTypes(instruction, compiler)); + } +} + +class ShiftRightSpecializer extends BinaryBitOpSpecializer { + const ShiftRightSpecializer(); + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + JavaScriptBackend backend = compiler.backend; + if (left.isUInt32(compiler)) return left.instructionType; + return super.computeTypeFromInputTypes(instruction, compiler); + } + + HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, + Compiler compiler) { + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + if (left.isNumber(compiler)) { + if (argumentLessThan32(right) && isPositive(left, compiler)) { + return newBuiltinVariant(instruction, compiler); + } + // Even if there is no builtin equivalent instruction, we know + // the instruction does not have any side effect, and that it + // can be GVN'ed. + clearAllSideEffects(instruction); + if (isPositive(right, compiler) && isPositive(left, compiler)) { + instruction.selector = renameToOptimizedSelector( + '_shrBothPositive', instruction.selector, compiler); + } else if (isPositive(left, compiler) && right.isNumber(compiler)) { + instruction.selector = renameToOptimizedSelector( + '_shrReceiverPositive', instruction.selector, compiler); + } else if (isPositive(right, compiler)) { + instruction.selector = renameToOptimizedSelector( + '_shrOtherPositive', instruction.selector, compiler); + } + } + return null; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return new HShiftRight( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, computeTypeFromInputTypes(instruction, compiler)); + } + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.shiftRight; + } +} + +class BitOrSpecializer extends BinaryBitOpSpecializer { + const BitOrSpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.bitOr; + } + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + JavaScriptBackend backend = compiler.backend; + if (left.isUInt31(compiler) && right.isUInt31(compiler)) { + return backend.uint31Type; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return new HBitOr( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, computeTypeFromInputTypes(instruction, compiler)); + } +} + +class BitAndSpecializer extends BinaryBitOpSpecializer { + const BitAndSpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.bitAnd; + } + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + JavaScriptBackend backend = compiler.backend; + if (left.isUInt31(compiler) || right.isUInt31(compiler)) { + return backend.uint31Type; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return new HBitAnd( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, computeTypeFromInputTypes(instruction, compiler)); + } +} + +class BitXorSpecializer extends BinaryBitOpSpecializer { + const BitXorSpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.bitXor; + } + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + JavaScriptBackend backend = compiler.backend; + if (left.isUInt31(compiler) && right.isUInt31(compiler)) { + return backend.uint31Type; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return new HBitXor( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, computeTypeFromInputTypes(instruction, compiler)); + } +} + +abstract class RelationalSpecializer extends InvokeDynamicSpecializer { + const RelationalSpecializer(); + + TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + if (instruction.inputs[1].isPrimitiveOrNull(compiler)) { + return backend.boolType; + } + return super.computeTypeFromInputTypes(instruction, compiler); + } + + HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, + Compiler compiler) { + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + if (left.isNumber(compiler) && right.isNumber(compiler)) { + return newBuiltinVariant(instruction, compiler); + } + return null; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, Compiler compiler); +} + +class EqualsSpecializer extends RelationalSpecializer { + const EqualsSpecializer(); + + HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, + Compiler compiler) { + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + TypeMask instructionType = left.instructionType; + if (right.isConstantNull() || left.isPrimitiveOrNull(compiler)) { + return newBuiltinVariant(instruction, compiler); + } + Selector selector = + new TypedSelector(instructionType, instruction.selector); + World world = compiler.world; + JavaScriptBackend backend = compiler.backend; + Iterable matches = world.allFunctions.filter(selector); + // This test relies the on `Object.==` and `Interceptor.==` always being + // implemented because if the selector matches by subtype, it still will be + // a regular object or an interceptor. + if (matches.every(backend.isDefaultEqualityImplementation)) { + return newBuiltinVariant(instruction, compiler); + } + return null; + } + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.equal; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return new HIdentity( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, backend.boolType); + } +} + +class LessSpecializer extends RelationalSpecializer { + const LessSpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.less; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return new HLess( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, backend.boolType); + } +} + +class GreaterSpecializer extends RelationalSpecializer { + const GreaterSpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.greater; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return new HGreater( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, backend.boolType); + } +} + +class GreaterEqualSpecializer extends RelationalSpecializer { + const GreaterEqualSpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.greaterEqual; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return new HGreaterEqual( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, backend.boolType); + } +} + +class LessEqualSpecializer extends RelationalSpecializer { + const LessEqualSpecializer(); + + BinaryOperation operation(ConstantSystem constantSystem) { + return constantSystem.lessEqual; + } + + HInstruction newBuiltinVariant(HInvokeDynamic instruction, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return new HLessEqual( + instruction.inputs[1], instruction.inputs[2], + instruction.selector, backend.boolType); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/nodes.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/nodes.dart new file mode 100644 index 0000000..fdedd2a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/nodes.dart @@ -0,0 +1,2975 @@ +// Copyright (c) 2012, 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. + +part of ssa; + +abstract class HVisitor { + R visitAdd(HAdd node); + R visitBitAnd(HBitAnd node); + R visitBitNot(HBitNot node); + R visitBitOr(HBitOr node); + R visitBitXor(HBitXor node); + R visitBoolify(HBoolify node); + R visitBoundsCheck(HBoundsCheck node); + R visitBreak(HBreak node); + R visitConstant(HConstant node); + R visitContinue(HContinue node); + R visitDivide(HDivide node); + R visitExit(HExit node); + R visitExitTry(HExitTry node); + R visitFieldGet(HFieldGet node); + R visitFieldSet(HFieldSet node); + R visitForeign(HForeign node); + R visitForeignNew(HForeignNew node); + R visitGoto(HGoto node); + R visitGreater(HGreater node); + R visitGreaterEqual(HGreaterEqual node); + R visitIdentity(HIdentity node); + R visitIf(HIf node); + R visitIndex(HIndex node); + R visitIndexAssign(HIndexAssign node); + R visitInterceptor(HInterceptor node); + R visitInvokeClosure(HInvokeClosure node); + R visitInvokeDynamicGetter(HInvokeDynamicGetter node); + R visitInvokeDynamicMethod(HInvokeDynamicMethod node); + R visitInvokeDynamicSetter(HInvokeDynamicSetter node); + R visitInvokeStatic(HInvokeStatic node); + R visitInvokeSuper(HInvokeSuper node); + R visitInvokeConstructorBody(HInvokeConstructorBody node); + R visitIs(HIs node); + R visitIsViaInterceptor(HIsViaInterceptor node); + R visitLazyStatic(HLazyStatic node); + R visitLess(HLess node); + R visitLessEqual(HLessEqual node); + R visitLiteralList(HLiteralList node); + R visitLocalGet(HLocalGet node); + R visitLocalSet(HLocalSet node); + R visitLocalValue(HLocalValue node); + R visitLoopBranch(HLoopBranch node); + R visitMultiply(HMultiply node); + R visitNegate(HNegate node); + R visitNot(HNot node); + R visitOneShotInterceptor(HOneShotInterceptor node); + R visitParameterValue(HParameterValue node); + R visitPhi(HPhi node); + R visitRangeConversion(HRangeConversion node); + R visitReturn(HReturn node); + R visitShiftLeft(HShiftLeft node); + R visitShiftRight(HShiftRight node); + R visitStatic(HStatic node); + R visitStaticStore(HStaticStore node); + R visitStringConcat(HStringConcat node); + R visitStringify(HStringify node); + R visitSubtract(HSubtract node); + R visitSwitch(HSwitch node); + R visitThis(HThis node); + R visitThrow(HThrow node); + R visitThrowExpression(HThrowExpression node); + R visitTruncatingDivide(HTruncatingDivide node); + R visitTry(HTry node); + R visitTypeConversion(HTypeConversion node); + R visitTypeKnown(HTypeKnown node); + R visitReadTypeVariable(HReadTypeVariable node); + R visitFunctionType(HFunctionType node); + R visitVoidType(HVoidType node); + R visitInterfaceType(HInterfaceType node); + R visitDynamicType(HDynamicType node); +} + +abstract class HGraphVisitor { + visitDominatorTree(HGraph graph) { + void visitBasicBlockAndSuccessors(HBasicBlock block) { + visitBasicBlock(block); + List dominated = block.dominatedBlocks; + for (int i = 0; i < dominated.length; i++) { + visitBasicBlockAndSuccessors(dominated[i]); + } + } + + visitBasicBlockAndSuccessors(graph.entry); + } + + visitPostDominatorTree(HGraph graph) { + void visitBasicBlockAndSuccessors(HBasicBlock block) { + List dominated = block.dominatedBlocks; + for (int i = dominated.length - 1; i >= 0; i--) { + visitBasicBlockAndSuccessors(dominated[i]); + } + visitBasicBlock(block); + } + + visitBasicBlockAndSuccessors(graph.entry); + } + + visitBasicBlock(HBasicBlock block); +} + +abstract class HInstructionVisitor extends HGraphVisitor { + HBasicBlock currentBlock; + + visitInstruction(HInstruction node); + + visitBasicBlock(HBasicBlock node) { + void visitInstructionList(HInstructionList list) { + HInstruction instruction = list.first; + while (instruction != null) { + visitInstruction(instruction); + instruction = instruction.next; + assert(instruction != list.first); + } + } + + currentBlock = node; + visitInstructionList(node); + } +} + +class HGraph { + HBasicBlock entry; + HBasicBlock exit; + HThis thisInstruction; + /// Receiver parameter, set for methods using interceptor calling convention. + HParameterValue explicitReceiverParameter; + bool isRecursiveMethod = false; + bool calledInLoop = false; + final List blocks; + + // We canonicalize all constants used within a graph so we do not + // have to worry about them for global value numbering. + Map constants; + + HGraph() + : blocks = new List(), + constants = new Map() { + entry = addNewBlock(); + // The exit block will be added later, so it has an id that is + // after all others in the system. + exit = new HBasicBlock(); + } + + void addBlock(HBasicBlock block) { + int id = blocks.length; + block.id = id; + blocks.add(block); + assert(identical(blocks[id], block)); + } + + HBasicBlock addNewBlock() { + HBasicBlock result = new HBasicBlock(); + addBlock(result); + return result; + } + + HBasicBlock addNewLoopHeaderBlock(TargetElement target, + List labels) { + HBasicBlock result = addNewBlock(); + result.loopInformation = + new HLoopInformation(result, target, labels); + return result; + } + + HConstant addConstant(Constant constant, Compiler compiler) { + HConstant result = constants[constant]; + if (result == null) { + TypeMask type = constant.computeMask(compiler); + result = new HConstant.internal(constant, type); + entry.addAtExit(result); + constants[constant] = result; + } else if (result.block == null) { + // The constant was not used anymore. + entry.addAtExit(result); + } + return result; + } + + HConstant addConstantInt(int i, Compiler compiler) { + return addConstant(compiler.backend.constantSystem.createInt(i), compiler); + } + + HConstant addConstantDouble(double d, Compiler compiler) { + return addConstant( + compiler.backend.constantSystem.createDouble(d), compiler); + } + + HConstant addConstantString(ast.DartString str, + Compiler compiler) { + return addConstant( + compiler.backend.constantSystem.createString(str), + compiler); + } + + HConstant addConstantBool(bool value, Compiler compiler) { + return addConstant( + compiler.backend.constantSystem.createBool(value), compiler); + } + + HConstant addConstantNull(Compiler compiler) { + return addConstant(compiler.backend.constantSystem.createNull(), compiler); + } + + void finalize() { + addBlock(exit); + exit.open(); + exit.close(new HExit()); + assignDominators(); + } + + void assignDominators() { + // Run through the blocks in order of increasing ids so we are + // guaranteed that we have computed dominators for all blocks + // higher up in the dominator tree. + for (int i = 0, length = blocks.length; i < length; i++) { + HBasicBlock block = blocks[i]; + List predecessors = block.predecessors; + if (block.isLoopHeader()) { + block.assignCommonDominator(predecessors[0]); + } else { + for (int j = predecessors.length - 1; j >= 0; j--) { + block.assignCommonDominator(predecessors[j]); + } + } + } + } + + bool isValid() { + HValidator validator = new HValidator(); + validator.visitGraph(this); + return validator.isValid; + } +} + +class HBaseVisitor extends HGraphVisitor implements HVisitor { + HBasicBlock currentBlock; + + visitBasicBlock(HBasicBlock node) { + currentBlock = node; + + HInstruction instruction = node.first; + while (instruction != null) { + instruction.accept(this); + instruction = instruction.next; + } + } + + visitInstruction(HInstruction instruction) {} + + visitBinaryArithmetic(HBinaryArithmetic node) => visitInvokeBinary(node); + visitBinaryBitOp(HBinaryBitOp node) => visitInvokeBinary(node); + visitInvoke(HInvoke node) => visitInstruction(node); + visitInvokeBinary(HInvokeBinary node) => visitInstruction(node); + visitInvokeDynamic(HInvokeDynamic node) => visitInvoke(node); + visitInvokeDynamicField(HInvokeDynamicField node) => visitInvokeDynamic(node); + visitInvokeUnary(HInvokeUnary node) => visitInstruction(node); + visitConditionalBranch(HConditionalBranch node) => visitControlFlow(node); + visitControlFlow(HControlFlow node) => visitInstruction(node); + visitFieldAccess(HFieldAccess node) => visitInstruction(node); + visitRelational(HRelational node) => visitInvokeBinary(node); + + visitAdd(HAdd node) => visitBinaryArithmetic(node); + visitBitAnd(HBitAnd node) => visitBinaryBitOp(node); + visitBitNot(HBitNot node) => visitInvokeUnary(node); + visitBitOr(HBitOr node) => visitBinaryBitOp(node); + visitBitXor(HBitXor node) => visitBinaryBitOp(node); + visitBoolify(HBoolify node) => visitInstruction(node); + visitBoundsCheck(HBoundsCheck node) => visitCheck(node); + visitBreak(HBreak node) => visitJump(node); + visitContinue(HContinue node) => visitJump(node); + visitCheck(HCheck node) => visitInstruction(node); + visitConstant(HConstant node) => visitInstruction(node); + visitDivide(HDivide node) => visitBinaryArithmetic(node); + visitExit(HExit node) => visitControlFlow(node); + visitExitTry(HExitTry node) => visitControlFlow(node); + visitFieldGet(HFieldGet node) => visitFieldAccess(node); + visitFieldSet(HFieldSet node) => visitFieldAccess(node); + visitForeign(HForeign node) => visitInstruction(node); + visitForeignNew(HForeignNew node) => visitForeign(node); + visitGoto(HGoto node) => visitControlFlow(node); + visitGreater(HGreater node) => visitRelational(node); + visitGreaterEqual(HGreaterEqual node) => visitRelational(node); + visitIdentity(HIdentity node) => visitRelational(node); + visitIf(HIf node) => visitConditionalBranch(node); + visitIndex(HIndex node) => visitInstruction(node); + visitIndexAssign(HIndexAssign node) => visitInstruction(node); + visitInterceptor(HInterceptor node) => visitInstruction(node); + visitInvokeClosure(HInvokeClosure node) + => visitInvokeDynamic(node); + visitInvokeConstructorBody(HInvokeConstructorBody node) + => visitInvokeStatic(node); + visitInvokeDynamicMethod(HInvokeDynamicMethod node) + => visitInvokeDynamic(node); + visitInvokeDynamicGetter(HInvokeDynamicGetter node) + => visitInvokeDynamicField(node); + visitInvokeDynamicSetter(HInvokeDynamicSetter node) + => visitInvokeDynamicField(node); + visitInvokeStatic(HInvokeStatic node) => visitInvoke(node); + visitInvokeSuper(HInvokeSuper node) => visitInvokeStatic(node); + visitJump(HJump node) => visitControlFlow(node); + visitLazyStatic(HLazyStatic node) => visitInstruction(node); + visitLess(HLess node) => visitRelational(node); + visitLessEqual(HLessEqual node) => visitRelational(node); + visitLiteralList(HLiteralList node) => visitInstruction(node); + visitLocalGet(HLocalGet node) => visitFieldAccess(node); + visitLocalSet(HLocalSet node) => visitFieldAccess(node); + visitLocalValue(HLocalValue node) => visitInstruction(node); + visitLoopBranch(HLoopBranch node) => visitConditionalBranch(node); + visitNegate(HNegate node) => visitInvokeUnary(node); + visitNot(HNot node) => visitInstruction(node); + visitOneShotInterceptor(HOneShotInterceptor node) + => visitInvokeDynamic(node); + visitPhi(HPhi node) => visitInstruction(node); + visitMultiply(HMultiply node) => visitBinaryArithmetic(node); + visitParameterValue(HParameterValue node) => visitLocalValue(node); + visitRangeConversion(HRangeConversion node) => visitCheck(node); + visitReturn(HReturn node) => visitControlFlow(node); + visitShiftLeft(HShiftLeft node) => visitBinaryBitOp(node); + visitShiftRight(HShiftRight node) => visitBinaryBitOp(node); + visitSubtract(HSubtract node) => visitBinaryArithmetic(node); + visitSwitch(HSwitch node) => visitControlFlow(node); + visitStatic(HStatic node) => visitInstruction(node); + visitStaticStore(HStaticStore node) => visitInstruction(node); + visitStringConcat(HStringConcat node) => visitInstruction(node); + visitStringify(HStringify node) => visitInstruction(node); + visitThis(HThis node) => visitParameterValue(node); + visitThrow(HThrow node) => visitControlFlow(node); + visitThrowExpression(HThrowExpression node) => visitInstruction(node); + visitTruncatingDivide(HTruncatingDivide node) => visitBinaryArithmetic(node); + visitTry(HTry node) => visitControlFlow(node); + visitIs(HIs node) => visitInstruction(node); + visitIsViaInterceptor(HIsViaInterceptor node) => visitInstruction(node); + visitTypeConversion(HTypeConversion node) => visitCheck(node); + visitTypeKnown(HTypeKnown node) => visitCheck(node); + visitReadTypeVariable(HReadTypeVariable node) => visitInstruction(node); + visitFunctionType(HFunctionType node) => visitInstruction(node); + visitVoidType(HVoidType node) => visitInstruction(node); + visitInterfaceType(HInterfaceType node) => visitInstruction(node); + visitDynamicType(HDynamicType node) => visitInstruction(node); +} + +class SubGraph { + // The first and last block of the sub-graph. + final HBasicBlock start; + final HBasicBlock end; + + const SubGraph(this.start, this.end); + + bool contains(HBasicBlock block) { + assert(start != null); + assert(end != null); + assert(block != null); + return start.id <= block.id && block.id <= end.id; + } +} + +class SubExpression extends SubGraph { + const SubExpression(HBasicBlock start, HBasicBlock end) + : super(start, end); + + /** Find the condition expression if this sub-expression is a condition. */ + HInstruction get conditionExpression { + HInstruction last = end.last; + if (last is HConditionalBranch || last is HSwitch) return last.inputs[0]; + return null; + } +} + +class HInstructionList { + HInstruction first = null; + HInstruction last = null; + + bool get isEmpty { + return first == null; + } + + void internalAddAfter(HInstruction cursor, HInstruction instruction) { + if (cursor == null) { + assert(isEmpty); + first = last = instruction; + } else if (identical(cursor, last)) { + last.next = instruction; + instruction.previous = last; + last = instruction; + } else { + instruction.previous = cursor; + instruction.next = cursor.next; + cursor.next.previous = instruction; + cursor.next = instruction; + } + } + + void internalAddBefore(HInstruction cursor, HInstruction instruction) { + if (cursor == null) { + assert(isEmpty); + first = last = instruction; + } else if (identical(cursor, first)) { + first.previous = instruction; + instruction.next = first; + first = instruction; + } else { + instruction.next = cursor; + instruction.previous = cursor.previous; + cursor.previous.next = instruction; + cursor.previous = instruction; + } + } + + void detach(HInstruction instruction) { + assert(contains(instruction)); + assert(instruction.isInBasicBlock()); + if (instruction.previous == null) { + first = instruction.next; + } else { + instruction.previous.next = instruction.next; + } + if (instruction.next == null) { + last = instruction.previous; + } else { + instruction.next.previous = instruction.previous; + } + instruction.previous = null; + instruction.next = null; + } + + void remove(HInstruction instruction) { + assert(instruction.usedBy.isEmpty); + detach(instruction); + } + + /** Linear search for [instruction]. */ + bool contains(HInstruction instruction) { + HInstruction cursor = first; + while (cursor != null) { + if (identical(cursor, instruction)) return true; + cursor = cursor.next; + } + return false; + } +} + +class HBasicBlock extends HInstructionList { + // The [id] must be such that any successor's id is greater than + // this [id]. The exception are back-edges. + int id; + + static const int STATUS_NEW = 0; + static const int STATUS_OPEN = 1; + static const int STATUS_CLOSED = 2; + int status = STATUS_NEW; + + HInstructionList phis; + + HLoopInformation loopInformation = null; + HBlockFlow blockFlow = null; + HBasicBlock parentLoopHeader = null; + bool isLive = true; + + final List predecessors; + List successors; + + HBasicBlock dominator = null; + final List dominatedBlocks; + + HBasicBlock() : this.withId(null); + HBasicBlock.withId(this.id) + : phis = new HInstructionList(), + predecessors = [], + successors = const [], + dominatedBlocks = []; + + int get hashCode => id; + + bool isNew() => status == STATUS_NEW; + bool isOpen() => status == STATUS_OPEN; + bool isClosed() => status == STATUS_CLOSED; + + bool isLoopHeader() { + return loopInformation != null; + } + + void setBlockFlow(HBlockInformation blockInfo, HBasicBlock continuation) { + blockFlow = new HBlockFlow(blockInfo, continuation); + } + + bool isLabeledBlock() => + blockFlow != null && + blockFlow.body is HLabeledBlockInformation; + + HBasicBlock get enclosingLoopHeader { + if (isLoopHeader()) return this; + return parentLoopHeader; + } + + void open() { + assert(isNew()); + status = STATUS_OPEN; + } + + void close(HControlFlow end) { + assert(isOpen()); + addAfter(last, end); + status = STATUS_CLOSED; + } + + void addAtEntry(HInstruction instruction) { + assert(instruction is !HPhi); + internalAddBefore(first, instruction); + instruction.notifyAddedToBlock(this); + } + + void addAtExit(HInstruction instruction) { + assert(isClosed()); + assert(last is HControlFlow); + assert(instruction is !HPhi); + internalAddBefore(last, instruction); + instruction.notifyAddedToBlock(this); + } + + void moveAtExit(HInstruction instruction) { + assert(instruction is !HPhi); + assert(instruction.isInBasicBlock()); + assert(isClosed()); + assert(last is HControlFlow); + internalAddBefore(last, instruction); + instruction.block = this; + assert(isValid()); + } + + void add(HInstruction instruction) { + assert(instruction is !HControlFlow); + assert(instruction is !HPhi); + internalAddAfter(last, instruction); + instruction.notifyAddedToBlock(this); + } + + void addPhi(HPhi phi) { + assert(phi.inputs.length == 0 || phi.inputs.length == predecessors.length); + assert(phi.block == null); + phis.internalAddAfter(phis.last, phi); + phi.notifyAddedToBlock(this); + } + + void removePhi(HPhi phi) { + phis.remove(phi); + assert(phi.block == this); + phi.notifyRemovedFromBlock(); + } + + void addAfter(HInstruction cursor, HInstruction instruction) { + assert(cursor is !HPhi); + assert(instruction is !HPhi); + assert(isOpen() || isClosed()); + internalAddAfter(cursor, instruction); + instruction.notifyAddedToBlock(this); + } + + void addBefore(HInstruction cursor, HInstruction instruction) { + assert(cursor is !HPhi); + assert(instruction is !HPhi); + assert(isOpen() || isClosed()); + internalAddBefore(cursor, instruction); + instruction.notifyAddedToBlock(this); + } + + void remove(HInstruction instruction) { + assert(isOpen() || isClosed()); + assert(instruction is !HPhi); + super.remove(instruction); + assert(instruction.block == this); + instruction.notifyRemovedFromBlock(); + } + + void addSuccessor(HBasicBlock block) { + if (successors.isEmpty) { + successors = [block]; + } else { + successors.add(block); + } + block.predecessors.add(this); + } + + void postProcessLoopHeader() { + assert(isLoopHeader()); + // Only the first entry into the loop is from outside the + // loop. All other entries must be back edges. + for (int i = 1, length = predecessors.length; i < length; i++) { + loopInformation.addBackEdge(predecessors[i]); + } + } + + /** + * Rewrites all uses of the [from] instruction to using the [to] + * instruction instead. + */ + void rewrite(HInstruction from, HInstruction to) { + for (HInstruction use in from.usedBy) { + use.rewriteInput(from, to); + } + to.usedBy.addAll(from.usedBy); + from.usedBy.clear(); + } + + /** + * Rewrites all uses of the [from] instruction to using either the + * [to] instruction, or a [HCheck] instruction that has better type + * information on [to], and that dominates the user. + */ + void rewriteWithBetterUser(HInstruction from, HInstruction to) { + // BUG(11841): Turn this method into a phase to be run after GVN phases. + Link better = const Link(); + for (HInstruction user in to.usedBy) { + if (user == from || user is! HCheck) continue; + HCheck check = user; + if (check.checkedInput == to) { + better = better.prepend(user); + } + } + + if (better.isEmpty) return rewrite(from, to); + + L1: for (HInstruction user in from.usedBy) { + for (HCheck check in better) { + if (check.dominates(user)) { + user.rewriteInput(from, check); + check.usedBy.add(user); + continue L1; + } + } + user.rewriteInput(from, to); + to.usedBy.add(user); + } + from.usedBy.clear(); + } + + bool isExitBlock() { + return identical(first, last) && first is HExit; + } + + void addDominatedBlock(HBasicBlock block) { + assert(isClosed()); + assert(id != null && block.id != null); + assert(dominatedBlocks.indexOf(block) < 0); + // Keep the list of dominated blocks sorted such that if there are two + // succeeding blocks in the list, the predecessor is before the successor. + // Assume that we add the dominated blocks in the right order. + int index = dominatedBlocks.length; + while (index > 0 && dominatedBlocks[index - 1].id > block.id) { + index--; + } + if (index == dominatedBlocks.length) { + dominatedBlocks.add(block); + } else { + dominatedBlocks.insert(index, block); + } + assert(block.dominator == null); + block.dominator = this; + } + + void removeDominatedBlock(HBasicBlock block) { + assert(isClosed()); + assert(id != null && block.id != null); + int index = dominatedBlocks.indexOf(block); + assert(index >= 0); + if (index == dominatedBlocks.length - 1) { + dominatedBlocks.removeLast(); + } else { + dominatedBlocks.removeRange(index, index + 1); + } + assert(identical(block.dominator, this)); + block.dominator = null; + } + + void assignCommonDominator(HBasicBlock predecessor) { + assert(isClosed()); + if (dominator == null) { + // If this basic block doesn't have a dominator yet we use the + // given predecessor as the dominator. + predecessor.addDominatedBlock(this); + } else if (predecessor.dominator != null) { + // If the predecessor has a dominator and this basic block has a + // dominator, we find a common parent in the dominator tree and + // use that as the dominator. + HBasicBlock block0 = dominator; + HBasicBlock block1 = predecessor; + while (!identical(block0, block1)) { + if (block0.id > block1.id) { + block0 = block0.dominator; + } else { + block1 = block1.dominator; + } + assert(block0 != null && block1 != null); + } + if (!identical(dominator, block0)) { + dominator.removeDominatedBlock(this); + block0.addDominatedBlock(this); + } + } + } + + void forEachPhi(void f(HPhi phi)) { + HPhi current = phis.first; + while (current != null) { + HInstruction saved = current.next; + f(current); + current = saved; + } + } + + void forEachInstruction(void f(HInstruction instruction)) { + HInstruction current = first; + while (current != null) { + HInstruction saved = current.next; + f(current); + current = saved; + } + } + + bool isValid() { + assert(isClosed()); + HValidator validator = new HValidator(); + validator.visitBasicBlock(this); + return validator.isValid; + } + + Map dominatesCache; + + bool dominates(HBasicBlock other) { + if (dominatesCache == null) { + dominatesCache = new Map(); + } else { + bool res = dominatesCache[other]; + if (res != null) return res; + } + do { + if (identical(this, other)) return dominatesCache[other] = true; + other = other.dominator; + } while (other != null && other.id >= id); + return dominatesCache[other] = false; + } +} + +abstract class HInstruction implements Spannable { + Element sourceElement; + SourceFileLocation sourcePosition; + + final int id; + static int idCounter; + + final List inputs; + final List usedBy; + + HBasicBlock block; + HInstruction previous = null; + HInstruction next = null; + + SideEffects sideEffects = new SideEffects.empty(); + bool _useGvn = false; + + // Type codes. + static const int UNDEFINED_TYPECODE = -1; + static const int BOOLIFY_TYPECODE = 0; + static const int TYPE_GUARD_TYPECODE = 1; + static const int BOUNDS_CHECK_TYPECODE = 2; + static const int INTEGER_CHECK_TYPECODE = 3; + static const int INTERCEPTOR_TYPECODE = 4; + static const int ADD_TYPECODE = 5; + static const int DIVIDE_TYPECODE = 6; + static const int MULTIPLY_TYPECODE = 7; + static const int SUBTRACT_TYPECODE = 8; + static const int SHIFT_LEFT_TYPECODE = 9; + static const int BIT_OR_TYPECODE = 10; + static const int BIT_AND_TYPECODE = 11; + static const int BIT_XOR_TYPECODE = 12; + static const int NEGATE_TYPECODE = 13; + static const int BIT_NOT_TYPECODE = 14; + static const int NOT_TYPECODE = 15; + static const int IDENTITY_TYPECODE = 16; + static const int GREATER_TYPECODE = 17; + static const int GREATER_EQUAL_TYPECODE = 18; + static const int LESS_TYPECODE = 19; + static const int LESS_EQUAL_TYPECODE = 20; + static const int STATIC_TYPECODE = 21; + static const int STATIC_STORE_TYPECODE = 22; + static const int FIELD_GET_TYPECODE = 23; + static const int TYPE_CONVERSION_TYPECODE = 24; + static const int TYPE_KNOWN_TYPECODE = 25; + static const int INVOKE_STATIC_TYPECODE = 26; + static const int INDEX_TYPECODE = 27; + static const int IS_TYPECODE = 28; + static const int INVOKE_DYNAMIC_TYPECODE = 29; + static const int SHIFT_RIGHT_TYPECODE = 30; + static const int READ_TYPE_VARIABLE_TYPECODE = 31; + static const int FUNCTION_TYPE_TYPECODE = 32; + static const int VOID_TYPE_TYPECODE = 33; + static const int INTERFACE_TYPE_TYPECODE = 34; + static const int DYNAMIC_TYPE_TYPECODE = 35; + static const int TRUNCATING_DIVIDE_TYPECODE = 36; + static const int IS_VIA_INTERCEPTOR_TYPECODE = 37; + + HInstruction(this.inputs, this.instructionType) + : id = idCounter++, usedBy = [] { + assert(inputs.every((e) => e != null)); + } + + int get hashCode => id; + + bool useGvn() => _useGvn; + void setUseGvn() { _useGvn = true; } + void clearUseGvn() { _useGvn = false; } + + bool get isMovable => useGvn(); + + /** + * A pure instruction is an instruction that does not have any side + * effect, nor any dependency. They can be moved anywhere in the + * graph. + */ + bool isPure() { + return !sideEffects.hasSideEffects() + && !sideEffects.dependsOnSomething() + && !canThrow(); + } + + // Overridden by [HCheck] to return the actual non-[HCheck] + // instruction it checks against. + HInstruction nonCheck() => this; + + // Can this node throw an exception? + bool canThrow() => false; + + // Does this node potentially affect control flow. + bool isControlFlow() => false; + + bool isExact() => instructionType.isExact || isNull(); + + bool canBeNull() => instructionType.isNullable; + + bool isNull() => instructionType.isEmpty && instructionType.isNullable; + bool isConflicting() { + return instructionType.isEmpty && !instructionType.isNullable; + } + + bool canBePrimitive(Compiler compiler) { + return canBePrimitiveNumber(compiler) + || canBePrimitiveArray(compiler) + || canBePrimitiveBoolean(compiler) + || canBePrimitiveString(compiler) + || isNull(); + } + + bool canBePrimitiveNumber(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return instructionType.contains(backend.jsNumberClass, compiler) + || instructionType.contains(backend.jsIntClass, compiler) + || instructionType.contains(backend.jsDoubleClass, compiler); + } + + bool canBePrimitiveBoolean(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return instructionType.contains(backend.jsBoolClass, compiler); + } + + bool canBePrimitiveArray(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return instructionType.contains(backend.jsArrayClass, compiler) + || instructionType.contains(backend.jsFixedArrayClass, compiler) + || instructionType.contains(backend.jsExtendableArrayClass, compiler); + } + + bool isIndexablePrimitive(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return instructionType.containsOnlyString(compiler) + || instructionType.satisfies(backend.jsIndexableClass, compiler); + } + + bool isFixedArray(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return instructionType.containsOnly(backend.jsFixedArrayClass); + } + + bool isExtendableArray(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return instructionType.containsOnly(backend.jsExtendableArrayClass); + } + + bool isMutableArray(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return instructionType.satisfies(backend.jsMutableArrayClass, compiler); + } + + bool isReadableArray(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return instructionType.satisfies(backend.jsArrayClass, compiler); + } + + bool isMutableIndexable(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return instructionType.satisfies(backend.jsMutableIndexableClass, compiler); + } + + bool isArray(Compiler compiler) => isReadableArray(compiler); + + bool canBePrimitiveString(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return instructionType.contains(backend.jsStringClass, compiler); + } + + bool isInteger(Compiler compiler) { + return instructionType.containsOnlyInt(compiler) + && !instructionType.isNullable; + } + + bool isUInt32(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return !instructionType.isNullable + && instructionType.satisfies(backend.jsUInt32Class, compiler); + } + + bool isUInt31(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return !instructionType.isNullable + && instructionType.satisfies(backend.jsUInt31Class, compiler); + } + + bool isPositiveInteger(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return !instructionType.isNullable + && instructionType.satisfies(backend.jsPositiveIntClass, compiler); + } + + bool isPositiveIntegerOrNull(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return instructionType.satisfies(backend.jsPositiveIntClass, compiler); + } + + bool isIntegerOrNull(Compiler compiler) { + return instructionType.containsOnlyInt(compiler); + } + + bool isNumber(Compiler compiler) { + return instructionType.containsOnlyNum(compiler) + && !instructionType.isNullable; + } + + bool isNumberOrNull(Compiler compiler) { + return instructionType.containsOnlyNum(compiler); + } + + bool isDouble(Compiler compiler) { + return instructionType.containsOnlyDouble(compiler) + && !instructionType.isNullable; + } + + bool isDoubleOrNull(Compiler compiler) { + return instructionType.containsOnlyDouble(compiler); + } + + bool isBoolean(Compiler compiler) { + return instructionType.containsOnlyBool(compiler) + && !instructionType.isNullable; + } + + bool isBooleanOrNull(Compiler compiler) { + return instructionType.containsOnlyBool(compiler); + } + + bool isString(Compiler compiler) { + return instructionType.containsOnlyString(compiler) + && !instructionType.isNullable; + } + + bool isStringOrNull(Compiler compiler) { + return instructionType.containsOnlyString(compiler); + } + + bool isPrimitive(Compiler compiler) { + return (isPrimitiveOrNull(compiler) && !instructionType.isNullable) + || isNull(); + } + + bool isPrimitiveOrNull(Compiler compiler) { + return isIndexablePrimitive(compiler) + || isNumberOrNull(compiler) + || isBooleanOrNull(compiler) + || isNull(); + } + + /** + * Type of the instruction. + */ + TypeMask instructionType; + + Selector get selector => null; + HInstruction getDartReceiver(Compiler compiler) => null; + bool onlyThrowsNSM() => false; + + bool isInBasicBlock() => block != null; + + bool gvnEquals(HInstruction other) { + assert(useGvn() && other.useGvn()); + // Check that the type and the sideEffects match. + bool hasSameType = typeEquals(other); + assert(hasSameType == (typeCode() == other.typeCode())); + if (!hasSameType) return false; + if (sideEffects != other.sideEffects) return false; + // Check that the inputs match. + final int inputsLength = inputs.length; + final List otherInputs = other.inputs; + if (inputsLength != otherInputs.length) return false; + for (int i = 0; i < inputsLength; i++) { + if (!identical(inputs[i].nonCheck(), otherInputs[i].nonCheck())) { + return false; + } + } + // Check that the data in the instruction matches. + return dataEquals(other); + } + + int gvnHashCode() { + int result = typeCode(); + int length = inputs.length; + for (int i = 0; i < length; i++) { + result = (result * 19) + (inputs[i].nonCheck().id) + (result >> 7); + } + return result; + } + + // These methods should be overwritten by instructions that + // participate in global value numbering. + int typeCode() => HInstruction.UNDEFINED_TYPECODE; + bool typeEquals(HInstruction other) => false; + bool dataEquals(HInstruction other) => false; + + accept(HVisitor visitor); + + void notifyAddedToBlock(HBasicBlock targetBlock) { + assert(!isInBasicBlock()); + assert(block == null); + // Add [this] to the inputs' uses. + for (int i = 0; i < inputs.length; i++) { + assert(inputs[i].isInBasicBlock()); + inputs[i].usedBy.add(this); + } + block = targetBlock; + assert(isValid()); + } + + void notifyRemovedFromBlock() { + assert(isInBasicBlock()); + assert(usedBy.isEmpty); + + // Remove [this] from the inputs' uses. + for (int i = 0; i < inputs.length; i++) { + inputs[i].removeUser(this); + } + this.block = null; + assert(isValid()); + } + + /// Do a in-place change of [from] to [to]. Warning: this function + /// does not update [inputs] and [usedBy]. Use [changeUse] instead. + void rewriteInput(HInstruction from, HInstruction to) { + for (int i = 0; i < inputs.length; i++) { + if (identical(inputs[i], from)) inputs[i] = to; + } + } + + /** Removes all occurrences of [instruction] from [list]. */ + void removeFromList(List list, HInstruction instruction) { + int length = list.length; + int i = 0; + while (i < length) { + if (instruction == list[i]) { + list[i] = list[length - 1]; + length--; + } else { + i++; + } + } + list.length = length; + } + + /** Removes all occurrences of [user] from [usedBy]. */ + void removeUser(HInstruction user) { + removeFromList(usedBy, user); + } + + // Change all uses of [oldInput] by [this] to [newInput]. Also + // updates the [usedBy] of [oldInput] and [newInput]. + void changeUse(HInstruction oldInput, HInstruction newInput) { + assert(newInput != null && !identical(oldInput, newInput)); + for (int i = 0; i < inputs.length; i++) { + if (identical(inputs[i], oldInput)) { + inputs[i] = newInput; + newInput.usedBy.add(this); + } + } + removeFromList(oldInput.usedBy, this); + } + + // Compute the set of users of this instruction that is dominated by + // [other]. If [other] is a user of [this], it is included in the + // returned set. + Setlet dominatedUsers(HInstruction other) { + // Keep track of all instructions that we have to deal with later + // and count the number of them that are in the current block. + Setlet users = new Setlet(); + int usersInCurrentBlock = 0; + + // Run through all the users and see if they are dominated or + // potentially dominated by [other]. + HBasicBlock otherBlock = other.block; + for (int i = 0, length = usedBy.length; i < length; i++) { + HInstruction current = usedBy[i]; + if (otherBlock.dominates(current.block)) { + if (identical(current.block, otherBlock)) usersInCurrentBlock++; + users.add(current); + } + } + + // Run through all the phis in the same block as [other] and remove them + // from the users set. + if (usersInCurrentBlock > 0) { + for (HPhi phi = otherBlock.phis.first; phi != null; phi = phi.next) { + if (users.contains(phi)) { + users.remove(phi); + if (--usersInCurrentBlock == 0) break; + } + } + } + + // Run through all the instructions before [other] and remove them + // from the users set. + if (usersInCurrentBlock > 0) { + HInstruction current = otherBlock.first; + while (!identical(current, other)) { + if (users.contains(current)) { + users.remove(current); + if (--usersInCurrentBlock == 0) break; + } + current = current.next; + } + } + + return users; + } + + void replaceAllUsersDominatedBy(HInstruction cursor, + HInstruction newInstruction) { + Setlet users = dominatedUsers(cursor); + for (HInstruction user in users) { + user.changeUse(this, newInstruction); + } + } + + void moveBefore(HInstruction other) { + assert(this is !HControlFlow); + assert(this is !HPhi); + assert(other is !HPhi); + block.detach(this); + other.block.internalAddBefore(other, this); + block = other.block; + } + + bool isConstant() => false; + bool isConstantBoolean() => false; + bool isConstantNull() => false; + bool isConstantNumber() => false; + bool isConstantInteger() => false; + bool isConstantString() => false; + bool isConstantList() => false; + bool isConstantMap() => false; + bool isConstantFalse() => false; + bool isConstantTrue() => false; + + bool isInterceptor(Compiler compiler) => false; + + bool isValid() { + HValidator validator = new HValidator(); + validator.currentBlock = block; + validator.visitInstruction(this); + return validator.isValid; + } + + bool isCodeMotionInvariant() => false; + + bool isJsStatement() => false; + + bool dominates(HInstruction other) { + // An instruction does not dominates itself. + if (this == other) return false; + if (block != other.block) return block.dominates(other.block); + + HInstruction current = this.next; + while (current != null) { + if (current == other) return true; + current = current.next; + } + return false; + } + + HInstruction convertType(Compiler compiler, DartType type, int kind) { + if (type == null) return this; + type = type.unalias(compiler); + // Only the builder knows how to create [HTypeConversion] + // instructions with generics. It has the generic type context + // available. + assert(type.kind != TypeKind.TYPE_VARIABLE); + assert(type.treatAsRaw || type.kind == TypeKind.FUNCTION); + if (type.isDynamic) return this; + // The type element is either a class or the void element. + Element element = type.element; + if (identical(element, compiler.objectClass)) return this; + JavaScriptBackend backend = compiler.backend; + if (type.kind != TypeKind.INTERFACE) { + return new HTypeConversion(type, kind, backend.dynamicType, this); + } else if (kind == HTypeConversion.BOOLEAN_CONVERSION_CHECK) { + // Boolean conversion checks work on non-nullable booleans. + return new HTypeConversion(type, kind, backend.boolType, this); + } else if (kind == HTypeConversion.CHECKED_MODE_CHECK && !type.treatAsRaw) { + throw 'creating compound check to $type (this = ${this})'; + } else { + TypeMask subtype = new TypeMask.subtype(element.declaration); + return new HTypeConversion(type, kind, subtype, this); + } + } + + /** + * Return whether the instructions do not belong to a loop or + * belong to the same loop. + */ + bool hasSameLoopHeaderAs(HInstruction other) { + return block.enclosingLoopHeader == other.block.enclosingLoopHeader; + } +} + +/** + * Late instructions are used after the main optimization phases. They capture + * codegen decisions just prior to generating JavaScript. + */ +abstract class HLateInstruction extends HInstruction { + HLateInstruction(List inputs, TypeMask type) + : super(inputs, type); +} + +class HBoolify extends HInstruction { + HBoolify(HInstruction value, TypeMask type) + : super([value], type) { + setUseGvn(); + } + + accept(HVisitor visitor) => visitor.visitBoolify(this); + int typeCode() => HInstruction.BOOLIFY_TYPECODE; + bool typeEquals(other) => other is HBoolify; + bool dataEquals(HInstruction other) => true; +} + +/** + * A [HCheck] instruction is an instruction that might do a dynamic + * check at runtime on another instruction. To have proper instruction + * dependencies in the graph, instructions that depend on the check + * being done reference the [HCheck] instruction instead of the + * instruction itself. + */ +abstract class HCheck extends HInstruction { + HCheck(inputs, type) : super(inputs, type) { + setUseGvn(); + } + HInstruction get checkedInput => inputs[0]; + bool isJsStatement() => true; + bool canThrow() => true; + + HInstruction nonCheck() => checkedInput.nonCheck(); +} + +class HBoundsCheck extends HCheck { + static const int ALWAYS_FALSE = 0; + static const int FULL_CHECK = 1; + static const int ALWAYS_ABOVE_ZERO = 2; + static const int ALWAYS_BELOW_LENGTH = 3; + static const int ALWAYS_TRUE = 4; + /** + * Details which tests have been done statically during compilation. + * Default is that all checks must be performed dynamically. + */ + int staticChecks = FULL_CHECK; + + HBoundsCheck(length, index, array, type) + : super([length, index, array], type); + + HInstruction get length => inputs[1]; + HInstruction get index => inputs[0]; + HInstruction get array => inputs[2]; + bool isControlFlow() => true; + + accept(HVisitor visitor) => visitor.visitBoundsCheck(this); + int typeCode() => HInstruction.BOUNDS_CHECK_TYPECODE; + bool typeEquals(other) => other is HBoundsCheck; + bool dataEquals(HInstruction other) => true; +} + +abstract class HConditionalBranch extends HControlFlow { + HConditionalBranch(inputs) : super(inputs); + HInstruction get condition => inputs[0]; + HBasicBlock get trueBranch => block.successors[0]; + HBasicBlock get falseBranch => block.successors[1]; +} + +abstract class HControlFlow extends HInstruction { + HControlFlow(inputs) : super(inputs, const TypeMask.nonNullEmpty()); + bool isControlFlow() => true; + bool isJsStatement() => true; +} + +abstract class HInvoke extends HInstruction { + /** + * The first argument must be the target: either an [HStatic] node, or + * the receiver of a method-call. The remaining inputs are the arguments + * to the invocation. + */ + HInvoke(List inputs, type) : super(inputs, type) { + sideEffects.setAllSideEffects(); + sideEffects.setDependsOnSomething(); + } + static const int ARGUMENTS_OFFSET = 1; + bool canThrow() => true; + + /** + * Returns whether this call is on an intercepted method. + */ + bool get isInterceptedCall { + // We know it's a selector call if it follows the interceptor + // calling convention, which adds the actual receiver as a + // parameter to the call. + return (selector != null) && (inputs.length - 2 == selector.argumentCount); + } +} + +abstract class HInvokeDynamic extends HInvoke { + final InvokeDynamicSpecializer specializer; + Selector selector; + Element element; + + HInvokeDynamic(Selector selector, + this.element, + List inputs, + TypeMask type, + [bool isIntercepted = false]) + : super(inputs, type), + this.selector = selector, + specializer = isIntercepted + ? InvokeDynamicSpecializer.lookupSpecializer(selector) + : const InvokeDynamicSpecializer(); + toString() => 'invoke dynamic: $selector'; + HInstruction get receiver => inputs[0]; + HInstruction getDartReceiver(Compiler compiler) { + return isCallOnInterceptor(compiler) ? inputs[1] : inputs[0]; + } + + /** + * Returns whether this call is on an interceptor object. + */ + bool isCallOnInterceptor(Compiler compiler) { + return isInterceptedCall && receiver.isInterceptor(compiler); + } + + int typeCode() => HInstruction.INVOKE_DYNAMIC_TYPECODE; + bool typeEquals(other) => other is HInvokeDynamic; + bool dataEquals(HInvokeDynamic other) { + // Use the name and the kind instead of [Selector.operator==] + // because we don't need to check the arity (already checked in + // [gvnEquals]), and the receiver types may not be in sync. + return selector.name == other.selector.name + && selector.kind == other.selector.kind; + } +} + +class HInvokeClosure extends HInvokeDynamic { + HInvokeClosure(Selector selector, List inputs, TypeMask type) + : super(selector, null, inputs, type) { + assert(selector.isClosureCall()); + } + accept(HVisitor visitor) => visitor.visitInvokeClosure(this); +} + +class HInvokeDynamicMethod extends HInvokeDynamic { + HInvokeDynamicMethod(Selector selector, + List inputs, + TypeMask type, + [bool isIntercepted = false]) + : super(selector, null, inputs, type, isIntercepted); + + String toString() => 'invoke dynamic method: $selector'; + accept(HVisitor visitor) => visitor.visitInvokeDynamicMethod(this); +} + +abstract class HInvokeDynamicField extends HInvokeDynamic { + HInvokeDynamicField( + Selector selector, Element element, List inputs, + TypeMask type) + : super(selector, element, inputs, type); + toString() => 'invoke dynamic field: $selector'; +} + +class HInvokeDynamicGetter extends HInvokeDynamicField { + HInvokeDynamicGetter(selector, element, inputs, type) + : super(selector, element, inputs, type); + toString() => 'invoke dynamic getter: $selector'; + accept(HVisitor visitor) => visitor.visitInvokeDynamicGetter(this); +} + +class HInvokeDynamicSetter extends HInvokeDynamicField { + HInvokeDynamicSetter(selector, element, inputs, type) + : super(selector, element, inputs, type); + toString() => 'invoke dynamic setter: $selector'; + accept(HVisitor visitor) => visitor.visitInvokeDynamicSetter(this); +} + +class HInvokeStatic extends HInvoke { + final Element element; + + final bool targetCanThrow; + + bool canThrow() => targetCanThrow; + + /// If this instruction is a call to a constructor, [instantiatedTypes] + /// contains the type(s) used in the (Dart) `New` expression(s). The + /// [instructionType] of this node is not enough, because we also need the + /// type arguments. See also [SsaFromAstMixin.currentInlinedInstantiations]. + List instantiatedTypes; + + /** The first input must be the target. */ + HInvokeStatic(this.element, inputs, TypeMask type, + {this.targetCanThrow: true}) + : super(inputs, type); + + toString() => 'invoke static: $element'; + accept(HVisitor visitor) => visitor.visitInvokeStatic(this); + int typeCode() => HInstruction.INVOKE_STATIC_TYPECODE; +} + +class HInvokeSuper extends HInvokeStatic { + /** The class where the call to super is being done. */ + final ClassElement caller; + final bool isSetter; + final Selector selector; + + HInvokeSuper(Element element, + this.caller, + this.selector, + inputs, + type, + {this.isSetter}) + : super(element, inputs, type); + toString() => 'invoke super: ${element.name}'; + accept(HVisitor visitor) => visitor.visitInvokeSuper(this); + + HInstruction get value { + assert(isSetter); + // The 'inputs' are [receiver, value] or [interceptor, receiver, value]. + return inputs.last; + } +} + +class HInvokeConstructorBody extends HInvokeStatic { + // The 'inputs' are + // [receiver, arg1, ..., argN] or + // [interceptor, receiver, arg1, ... argN]. + HInvokeConstructorBody(element, inputs, type) + : super(element, inputs, type); + + String toString() => 'invoke constructor body: ${element.name}'; + accept(HVisitor visitor) => visitor.visitInvokeConstructorBody(this); +} + +abstract class HFieldAccess extends HInstruction { + final Element element; + + HFieldAccess(Element element, List inputs, TypeMask type) + : this.element = element, super(inputs, type); + + HInstruction get receiver => inputs[0]; +} + +class HFieldGet extends HFieldAccess { + final bool isAssignable; + + HFieldGet(Element element, + HInstruction receiver, + TypeMask type, + {bool isAssignable}) + : this.isAssignable = (isAssignable != null) + ? isAssignable + : element.isAssignable(), + super(element, [receiver], type) { + sideEffects.clearAllSideEffects(); + sideEffects.clearAllDependencies(); + setUseGvn(); + if (this.isAssignable) { + sideEffects.setDependsOnInstancePropertyStore(); + } + } + + bool isInterceptor(Compiler compiler) { + if (sourceElement == null) return false; + // In case of a closure inside an interceptor class, [:this:] is + // stored in the generated closure class, and accessed through a + // [HFieldGet]. + JavaScriptBackend backend = compiler.backend; + bool interceptor = + backend.isInterceptorClass(sourceElement.getEnclosingClass()); + return interceptor && sourceElement is ThisElement; + } + + bool canThrow() => receiver.canBeNull(); + + HInstruction getDartReceiver(Compiler compiler) => receiver; + bool onlyThrowsNSM() => true; + bool get isNullCheck => element == null; + + accept(HVisitor visitor) => visitor.visitFieldGet(this); + + int typeCode() => HInstruction.FIELD_GET_TYPECODE; + bool typeEquals(other) => other is HFieldGet; + bool dataEquals(HFieldGet other) => element == other.element; + String toString() => "FieldGet $element"; +} + +class HFieldSet extends HFieldAccess { + HFieldSet(Element element, + HInstruction receiver, + HInstruction value) + : super(element, [receiver, value], + const TypeMask.nonNullEmpty()) { + sideEffects.clearAllSideEffects(); + sideEffects.clearAllDependencies(); + sideEffects.setChangesInstanceProperty(); + } + + bool canThrow() => receiver.canBeNull(); + + HInstruction getDartReceiver(Compiler compiler) => receiver; + bool onlyThrowsNSM() => true; + + HInstruction get value => inputs[1]; + accept(HVisitor visitor) => visitor.visitFieldSet(this); + + bool isJsStatement() => true; + String toString() => "FieldSet $element"; +} + +class HLocalGet extends HFieldAccess { + // No need to use GVN for a [HLocalGet], it is just a local + // access. + HLocalGet(Element element, HLocalValue local, TypeMask type) + : super(element, [local], type); + + accept(HVisitor visitor) => visitor.visitLocalGet(this); + + HLocalValue get local => inputs[0]; +} + +class HLocalSet extends HFieldAccess { + HLocalSet(Element element, HLocalValue local, HInstruction value) + : super(element, [local, value], + const TypeMask.nonNullEmpty()); + + accept(HVisitor visitor) => visitor.visitLocalSet(this); + + HLocalValue get local => inputs[0]; + HInstruction get value => inputs[1]; + bool isJsStatement() => true; +} + +class HForeign extends HInstruction { + final js.Node codeAst; + final bool isStatement; + final bool _canThrow; + final native.NativeBehavior nativeBehavior; + + HForeign(this.codeAst, + TypeMask type, + List inputs, + {this.isStatement: false, + SideEffects effects, + native.NativeBehavior nativeBehavior, + canThrow: false}) + : this.nativeBehavior = nativeBehavior, + this._canThrow = canThrow, + super(inputs, type) { + if (effects == null && nativeBehavior != null) { + effects = nativeBehavior.sideEffects; + } + if (effects != null) sideEffects.add(effects); + } + + HForeign.statement(codeAst, List inputs, + SideEffects effects, + native.NativeBehavior nativeBehavior, + TypeMask type) + : this(codeAst, type, inputs, isStatement: true, + effects: effects, nativeBehavior: nativeBehavior); + + accept(HVisitor visitor) => visitor.visitForeign(this); + + bool isJsStatement() => isStatement; + bool canThrow() { + return _canThrow + || sideEffects.hasSideEffects() + || sideEffects.dependsOnSomething(); + } +} + +class HForeignNew extends HForeign { + ClassElement element; + + /// If this field is not `null`, this call is from an inlined constructor and + /// we have to register the instantiated type in the code generator. The + /// [instructionType] of this node is not enough, because we also need the + /// type arguments. See also [SsaFromAstMixin.currentInlinedInstantiations]. + List instantiatedTypes; + + HForeignNew(this.element, TypeMask type, List inputs, + [this.instantiatedTypes]) + : super(null, type, inputs); + + accept(HVisitor visitor) => visitor.visitForeignNew(this); +} + +abstract class HInvokeBinary extends HInstruction { + final Selector selector; + HInvokeBinary(HInstruction left, HInstruction right, this.selector, type) + : super([left, right], type) { + sideEffects.clearAllSideEffects(); + sideEffects.clearAllDependencies(); + setUseGvn(); + } + + HInstruction get left => inputs[0]; + HInstruction get right => inputs[1]; + + BinaryOperation operation(ConstantSystem constantSystem); +} + +abstract class HBinaryArithmetic extends HInvokeBinary { + HBinaryArithmetic(left, right, selector, type) + : super(left, right, selector, type); + BinaryOperation operation(ConstantSystem constantSystem); +} + +class HAdd extends HBinaryArithmetic { + HAdd(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitAdd(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.add; + int typeCode() => HInstruction.ADD_TYPECODE; + bool typeEquals(other) => other is HAdd; + bool dataEquals(HInstruction other) => true; +} + +class HDivide extends HBinaryArithmetic { + HDivide(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitDivide(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.divide; + int typeCode() => HInstruction.DIVIDE_TYPECODE; + bool typeEquals(other) => other is HDivide; + bool dataEquals(HInstruction other) => true; +} + +class HMultiply extends HBinaryArithmetic { + HMultiply(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitMultiply(this); + + BinaryOperation operation(ConstantSystem operations) + => operations.multiply; + int typeCode() => HInstruction.MULTIPLY_TYPECODE; + bool typeEquals(other) => other is HMultiply; + bool dataEquals(HInstruction other) => true; +} + +class HSubtract extends HBinaryArithmetic { + HSubtract(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitSubtract(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.subtract; + int typeCode() => HInstruction.SUBTRACT_TYPECODE; + bool typeEquals(other) => other is HSubtract; + bool dataEquals(HInstruction other) => true; +} + +class HTruncatingDivide extends HBinaryArithmetic { + HTruncatingDivide(left, right, selector, type) + : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitTruncatingDivide(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.truncatingDivide; + int typeCode() => HInstruction.TRUNCATING_DIVIDE_TYPECODE; + bool typeEquals(other) => other is HTruncatingDivide; + bool dataEquals(HInstruction other) => true; +} + +/** + * An [HSwitch] instruction has one input for the incoming + * value, and one input per constant that it can switch on. + * Its block has one successor per constant, and one for the default. + */ +class HSwitch extends HControlFlow { + HSwitch(List inputs) : super(inputs); + + HConstant constant(int index) => inputs[index + 1]; + HInstruction get expression => inputs[0]; + + /** + * Provides the target to jump to if none of the constants match + * the expression. If the switch had no default case, this is the + * following join-block. + */ + HBasicBlock get defaultTarget => block.successors.last; + + accept(HVisitor visitor) => visitor.visitSwitch(this); + + String toString() => "HSwitch cases = $inputs"; +} + +abstract class HBinaryBitOp extends HInvokeBinary { + HBinaryBitOp(left, right, selector, type) + : super(left, right, selector, type); +} + +class HShiftLeft extends HBinaryBitOp { + HShiftLeft(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitShiftLeft(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.shiftLeft; + int typeCode() => HInstruction.SHIFT_LEFT_TYPECODE; + bool typeEquals(other) => other is HShiftLeft; + bool dataEquals(HInstruction other) => true; +} + +class HShiftRight extends HBinaryBitOp { + HShiftRight(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitShiftRight(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.shiftRight; + int typeCode() => HInstruction.SHIFT_RIGHT_TYPECODE; + bool typeEquals(other) => other is HShiftRight; + bool dataEquals(HInstruction other) => true; +} + +class HBitOr extends HBinaryBitOp { + HBitOr(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitBitOr(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.bitOr; + int typeCode() => HInstruction.BIT_OR_TYPECODE; + bool typeEquals(other) => other is HBitOr; + bool dataEquals(HInstruction other) => true; +} + +class HBitAnd extends HBinaryBitOp { + HBitAnd(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitBitAnd(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.bitAnd; + int typeCode() => HInstruction.BIT_AND_TYPECODE; + bool typeEquals(other) => other is HBitAnd; + bool dataEquals(HInstruction other) => true; +} + +class HBitXor extends HBinaryBitOp { + HBitXor(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitBitXor(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.bitXor; + int typeCode() => HInstruction.BIT_XOR_TYPECODE; + bool typeEquals(other) => other is HBitXor; + bool dataEquals(HInstruction other) => true; +} + +abstract class HInvokeUnary extends HInstruction { + final Selector selector; + HInvokeUnary(HInstruction input, this.selector, type) + : super([input], type) { + sideEffects.clearAllSideEffects(); + sideEffects.clearAllDependencies(); + setUseGvn(); + } + + HInstruction get operand => inputs[0]; + + UnaryOperation operation(ConstantSystem constantSystem); +} + +class HNegate extends HInvokeUnary { + HNegate(input, selector, type) : super(input, selector, type); + accept(HVisitor visitor) => visitor.visitNegate(this); + + UnaryOperation operation(ConstantSystem constantSystem) + => constantSystem.negate; + int typeCode() => HInstruction.NEGATE_TYPECODE; + bool typeEquals(other) => other is HNegate; + bool dataEquals(HInstruction other) => true; +} + +class HBitNot extends HInvokeUnary { + HBitNot(input, selector, type) : super(input, selector, type); + accept(HVisitor visitor) => visitor.visitBitNot(this); + + UnaryOperation operation(ConstantSystem constantSystem) + => constantSystem.bitNot; + int typeCode() => HInstruction.BIT_NOT_TYPECODE; + bool typeEquals(other) => other is HBitNot; + bool dataEquals(HInstruction other) => true; +} + +class HExit extends HControlFlow { + HExit() : super(const []); + toString() => 'exit'; + accept(HVisitor visitor) => visitor.visitExit(this); +} + +class HGoto extends HControlFlow { + HGoto() : super(const []); + toString() => 'goto'; + accept(HVisitor visitor) => visitor.visitGoto(this); +} + +abstract class HJump extends HControlFlow { + final TargetElement target; + final LabelElement label; + HJump(this.target) : label = null, super(const []); + HJump.toLabel(LabelElement label) + : label = label, target = label.target, super(const []); +} + +class HBreak extends HJump { + /** + * Signals that this is a special break instruction for the synthetic loop + * generatedfor a switch statement with continue statements. See + * [SsaFromAstMixin.buildComplexSwitchStatement] for detail. + */ + final bool breakSwitchContinueLoop; + HBreak(TargetElement target, {bool this.breakSwitchContinueLoop: false}) + : super(target); + HBreak.toLabel(LabelElement label) + : breakSwitchContinueLoop = false, super.toLabel(label); + toString() => (label != null) ? 'break ${label.labelName}' : 'break'; + accept(HVisitor visitor) => visitor.visitBreak(this); +} + +class HContinue extends HJump { + HContinue(TargetElement target) : super(target); + HContinue.toLabel(LabelElement label) : super.toLabel(label); + toString() => (label != null) ? 'continue ${label.labelName}' : 'continue'; + accept(HVisitor visitor) => visitor.visitContinue(this); +} + +class HTry extends HControlFlow { + HLocalValue exception; + HBasicBlock catchBlock; + HBasicBlock finallyBlock; + HTry() : super(const []); + toString() => 'try'; + accept(HVisitor visitor) => visitor.visitTry(this); + HBasicBlock get joinBlock => this.block.successors.last; +} + +// An [HExitTry] control flow node is used when the body of a try or +// the body of a catch contains a return, break or continue. To build +// the control flow graph, we explicitly mark the body that +// leads to one of this instruction a predecessor of catch and +// finally. +class HExitTry extends HControlFlow { + HExitTry() : super(const []); + toString() => 'exit try'; + accept(HVisitor visitor) => visitor.visitExitTry(this); + HBasicBlock get bodyTrySuccessor => block.successors[0]; +} + +class HIf extends HConditionalBranch { + HBlockFlow blockInformation = null; + HIf(HInstruction condition) : super([condition]); + toString() => 'if'; + accept(HVisitor visitor) => visitor.visitIf(this); + + HBasicBlock get thenBlock { + assert(identical(block.dominatedBlocks[0], block.successors[0])); + return block.successors[0]; + } + + HBasicBlock get elseBlock { + assert(identical(block.dominatedBlocks[1], block.successors[1])); + return block.successors[1]; + } + + HBasicBlock get joinBlock => blockInformation.continuation; +} + +class HLoopBranch extends HConditionalBranch { + static const int CONDITION_FIRST_LOOP = 0; + static const int DO_WHILE_LOOP = 1; + + final int kind; + HLoopBranch(HInstruction condition, [this.kind = CONDITION_FIRST_LOOP]) + : super([condition]); + toString() => 'loop-branch'; + accept(HVisitor visitor) => visitor.visitLoopBranch(this); +} + +class HConstant extends HInstruction { + final Constant constant; + HConstant.internal(this.constant, TypeMask constantType) + : super([], constantType); + + toString() => 'literal: $constant'; + accept(HVisitor visitor) => visitor.visitConstant(this); + + bool isConstant() => true; + bool isConstantBoolean() => constant.isBool; + bool isConstantNull() => constant.isNull; + bool isConstantNumber() => constant.isNum; + bool isConstantInteger() => constant.isInt; + bool isConstantString() => constant.isString; + bool isConstantList() => constant.isList; + bool isConstantMap() => constant.isMap; + bool isConstantFalse() => constant.isFalse; + bool isConstantTrue() => constant.isTrue; + + bool isInterceptor(Compiler compiler) => constant.isInterceptor; + + // Maybe avoid this if the literal is big? + bool isCodeMotionInvariant() => true; + + set instructionType(type) { + // Only lists can be specialized. The SSA builder uses the + // inferrer for finding the type of a constant list. We should + // have the constant know its type instead. + if (!isConstantList()) return; + super.instructionType = type; + } +} + +class HNot extends HInstruction { + HNot(HInstruction value, TypeMask type) : super([value], type) { + setUseGvn(); + } + + accept(HVisitor visitor) => visitor.visitNot(this); + int typeCode() => HInstruction.NOT_TYPECODE; + bool typeEquals(other) => other is HNot; + bool dataEquals(HInstruction other) => true; +} + +/** + * An [HLocalValue] represents a local. Unlike [HParameterValue]s its + * first use must be in an HLocalSet. That is, [HParameterValue]s have a + * value from the start, whereas [HLocalValue]s need to be initialized first. + */ +class HLocalValue extends HInstruction { + HLocalValue(Element element, TypeMask type) : super([], type) { + sourceElement = element; + } + + toString() => 'local ${sourceElement.name}'; + accept(HVisitor visitor) => visitor.visitLocalValue(this); +} + +class HParameterValue extends HLocalValue { + HParameterValue(Element element, type) : super(element, type); + + toString() => 'parameter ${sourceElement.name}'; + accept(HVisitor visitor) => visitor.visitParameterValue(this); +} + +class HThis extends HParameterValue { + HThis(Element element, TypeMask type) : super(element, type); + toString() => 'this'; + accept(HVisitor visitor) => visitor.visitThis(this); + bool isCodeMotionInvariant() => true; + bool isInterceptor(Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + return backend.isInterceptorClass(sourceElement.getEnclosingClass()); + } +} + +class HPhi extends HInstruction { + static const IS_NOT_LOGICAL_OPERATOR = 0; + static const IS_AND = 1; + static const IS_OR = 2; + + int logicalOperatorType = IS_NOT_LOGICAL_OPERATOR; + + // The order of the [inputs] must correspond to the order of the + // predecessor-edges. That is if an input comes from the first predecessor + // of the surrounding block, then the input must be the first in the [HPhi]. + HPhi(Element element, List inputs, TypeMask type) + : super(inputs, type) { + sourceElement = element; + } + HPhi.noInputs(Element element, TypeMask type) + : this(element, [], type); + HPhi.singleInput(Element element, HInstruction input, TypeMask type) + : this(element, [input], type); + HPhi.manyInputs(Element element, List inputs, TypeMask type) + : this(element, inputs, type); + + void addInput(HInstruction input) { + assert(isInBasicBlock()); + inputs.add(input); + assert(inputs.length <= block.predecessors.length); + input.usedBy.add(this); + } + + toString() => 'phi'; + accept(HVisitor visitor) => visitor.visitPhi(this); +} + +abstract class HRelational extends HInvokeBinary { + bool usesBoolifiedInterceptor = false; + HRelational(left, right, selector, type) : super(left, right, selector, type); +} + +class HIdentity extends HRelational { + // Cached codegen decision. + String singleComparisonOp; // null, '===', '==' + + HIdentity(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitIdentity(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.identity; + int typeCode() => HInstruction.IDENTITY_TYPECODE; + bool typeEquals(other) => other is HIdentity; + bool dataEquals(HInstruction other) => true; +} + +class HGreater extends HRelational { + HGreater(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitGreater(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.greater; + int typeCode() => HInstruction.GREATER_TYPECODE; + bool typeEquals(other) => other is HGreater; + bool dataEquals(HInstruction other) => true; +} + +class HGreaterEqual extends HRelational { + HGreaterEqual(left, right, selector, type) + : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitGreaterEqual(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.greaterEqual; + int typeCode() => HInstruction.GREATER_EQUAL_TYPECODE; + bool typeEquals(other) => other is HGreaterEqual; + bool dataEquals(HInstruction other) => true; +} + +class HLess extends HRelational { + HLess(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitLess(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.less; + int typeCode() => HInstruction.LESS_TYPECODE; + bool typeEquals(other) => other is HLess; + bool dataEquals(HInstruction other) => true; +} + +class HLessEqual extends HRelational { + HLessEqual(left, right, selector, type) : super(left, right, selector, type); + accept(HVisitor visitor) => visitor.visitLessEqual(this); + + BinaryOperation operation(ConstantSystem constantSystem) + => constantSystem.lessEqual; + int typeCode() => HInstruction.LESS_EQUAL_TYPECODE; + bool typeEquals(other) => other is HLessEqual; + bool dataEquals(HInstruction other) => true; +} + +class HReturn extends HControlFlow { + HReturn(value) : super([value]); + toString() => 'return'; + accept(HVisitor visitor) => visitor.visitReturn(this); +} + +class HThrowExpression extends HInstruction { + HThrowExpression(value) + : super([value], const TypeMask.nonNullEmpty()); + toString() => 'throw expression'; + accept(HVisitor visitor) => visitor.visitThrowExpression(this); + bool canThrow() => true; +} + +class HThrow extends HControlFlow { + final bool isRethrow; + HThrow(value, {this.isRethrow: false}) : super([value]); + toString() => 'throw'; + accept(HVisitor visitor) => visitor.visitThrow(this); +} + +class HStatic extends HInstruction { + final Element element; + HStatic(this.element, type) : super([], type) { + assert(element != null); + assert(invariant(this, element.isDeclaration)); + sideEffects.clearAllSideEffects(); + sideEffects.clearAllDependencies(); + if (element.isAssignable()) { + sideEffects.setDependsOnStaticPropertyStore(); + } + setUseGvn(); + } + toString() => 'static ${element.name}'; + accept(HVisitor visitor) => visitor.visitStatic(this); + + int gvnHashCode() => super.gvnHashCode() ^ element.hashCode; + int typeCode() => HInstruction.STATIC_TYPECODE; + bool typeEquals(other) => other is HStatic; + bool dataEquals(HStatic other) => element == other.element; + bool isCodeMotionInvariant() => !element.isAssignable(); +} + +class HInterceptor extends HInstruction { + // This field should originally be null to allow GVN'ing all + // [HInterceptor] on the same input. + Set interceptedClasses; + HInterceptor(HInstruction receiver, TypeMask type) + : super([receiver], type) { + sideEffects.clearAllSideEffects(); + sideEffects.clearAllDependencies(); + setUseGvn(); + } + String toString() => 'interceptor on $interceptedClasses'; + accept(HVisitor visitor) => visitor.visitInterceptor(this); + HInstruction get receiver => inputs[0]; + bool isInterceptor(Compiler compiler) => true; + + int typeCode() => HInstruction.INTERCEPTOR_TYPECODE; + bool typeEquals(other) => other is HInterceptor; + bool dataEquals(HInterceptor other) { + return interceptedClasses == other.interceptedClasses + || (interceptedClasses.length == other.interceptedClasses.length + && interceptedClasses.containsAll(other.interceptedClasses)); + } +} + +/** + * A "one-shot" interceptor is a call to a synthetized method that + * will fetch the interceptor of its first parameter, and make a call + * on a given selector with the remaining parameters. + * + * In order to share the same optimizations with regular interceptor + * calls, this class extends [HInvokeDynamic] and also has the null + * constant as the first input. + */ +class HOneShotInterceptor extends HInvokeDynamic { + Set interceptedClasses; + HOneShotInterceptor(Selector selector, + List inputs, + TypeMask type, + this.interceptedClasses) + : super(selector, null, inputs, type, true) { + assert(inputs[0] is HConstant); + assert(inputs[0].isNull()); + } + bool isCallOnInterceptor(Compiler compiler) => true; + + String toString() => 'one shot interceptor on $selector'; + accept(HVisitor visitor) => visitor.visitOneShotInterceptor(this); +} + +/** An [HLazyStatic] is a static that is initialized lazily at first read. */ +class HLazyStatic extends HInstruction { + final Element element; + HLazyStatic(this.element, type) : super([], type) { + // TODO(4931): The first access has side-effects, but we afterwards we + // should be able to GVN. + sideEffects.setAllSideEffects(); + sideEffects.setDependsOnSomething(); + } + + toString() => 'lazy static ${element.name}'; + accept(HVisitor visitor) => visitor.visitLazyStatic(this); + + int typeCode() => 30; + // TODO(4931): can we do better here? + bool isCodeMotionInvariant() => false; + bool canThrow() => true; +} + +class HStaticStore extends HInstruction { + Element element; + HStaticStore(this.element, HInstruction value) + : super([value], const TypeMask.nonNullEmpty()) { + sideEffects.clearAllSideEffects(); + sideEffects.clearAllDependencies(); + sideEffects.setChangesStaticProperty(); + } + toString() => 'static store ${element.name}'; + accept(HVisitor visitor) => visitor.visitStaticStore(this); + + int typeCode() => HInstruction.STATIC_STORE_TYPECODE; + bool typeEquals(other) => other is HStaticStore; + bool dataEquals(HStaticStore other) => element == other.element; + bool isJsStatement() => true; +} + +class HLiteralList extends HInstruction { + HLiteralList(List inputs, TypeMask type) : super(inputs, type); + toString() => 'literal list'; + accept(HVisitor visitor) => visitor.visitLiteralList(this); +} + +/** + * The primitive array indexing operation. Note that this instruction + * does not throw because we generate the checks explicitly. + */ +class HIndex extends HInstruction { + final Selector selector; + HIndex(HInstruction receiver, HInstruction index, this.selector, type) + : super([receiver, index], type) { + sideEffects.clearAllSideEffects(); + sideEffects.clearAllDependencies(); + sideEffects.setDependsOnIndexStore(); + setUseGvn(); + } + + String toString() => 'index operator'; + accept(HVisitor visitor) => visitor.visitIndex(this); + + HInstruction get receiver => inputs[0]; + HInstruction get index => inputs[1]; + + HInstruction getDartReceiver(Compiler compiler) => receiver; + bool onlyThrowsNSM() => true; + bool canThrow() => receiver.canBeNull(); + + int typeCode() => HInstruction.INDEX_TYPECODE; + bool typeEquals(HInstruction other) => other is HIndex; + bool dataEquals(HIndex other) => true; +} + +/** + * The primitive array assignment operation. Note that this instruction + * does not throw because we generate the checks explicitly. + */ +class HIndexAssign extends HInstruction { + final Selector selector; + HIndexAssign(HInstruction receiver, + HInstruction index, + HInstruction value, + this.selector) + : super([receiver, index, value], + const TypeMask.nonNullEmpty()) { + sideEffects.clearAllSideEffects(); + sideEffects.clearAllDependencies(); + sideEffects.setChangesIndex(); + } + String toString() => 'index assign operator'; + accept(HVisitor visitor) => visitor.visitIndexAssign(this); + + HInstruction get receiver => inputs[0]; + HInstruction get index => inputs[1]; + HInstruction get value => inputs[2]; + + HInstruction getDartReceiver(Compiler compiler) => receiver; + bool onlyThrowsNSM() => true; + bool canThrow() => receiver.canBeNull(); +} + +class HIs extends HInstruction { + /// A check against a raw type: 'o is int', 'o is A'. + static const int RAW_CHECK = 0; + /// A check against a type with type arguments: 'o is List', 'o is C'. + static const int COMPOUND_CHECK = 1; + /// A check against a single type variable: 'o is T'. + static const int VARIABLE_CHECK = 2; + + final DartType typeExpression; + final int kind; + + HIs.direct(DartType typeExpression, + HInstruction expression, + TypeMask type) + : this.internal(typeExpression, [expression], RAW_CHECK, type); + + HIs.raw(DartType typeExpression, + HInstruction expression, + HInterceptor interceptor, + TypeMask type) + : this.internal( + typeExpression, [expression, interceptor], RAW_CHECK, type); + + HIs.compound(DartType typeExpression, + HInstruction expression, + HInstruction call, + TypeMask type) + : this.internal(typeExpression, [expression, call], COMPOUND_CHECK, type); + + HIs.variable(DartType typeExpression, + HInstruction expression, + HInstruction call, + TypeMask type) + : this.internal(typeExpression, [expression, call], VARIABLE_CHECK, type); + + HIs.internal(this.typeExpression, List inputs, this.kind, type) + : super(inputs, type) { + assert(kind >= RAW_CHECK && kind <= VARIABLE_CHECK); + setUseGvn(); + } + + HInstruction get expression => inputs[0]; + + HInstruction get interceptor { + assert(kind == RAW_CHECK); + return inputs.length > 1 ? inputs[1] : null; + } + + HInstruction get checkCall { + assert(kind == VARIABLE_CHECK || kind == COMPOUND_CHECK); + return inputs[1]; + } + + bool get isRawCheck => kind == RAW_CHECK; + bool get isVariableCheck => kind == VARIABLE_CHECK; + bool get isCompoundCheck => kind == COMPOUND_CHECK; + + accept(HVisitor visitor) => visitor.visitIs(this); + + toString() => "$expression is $typeExpression"; + + int typeCode() => HInstruction.IS_TYPECODE; + + bool typeEquals(HInstruction other) => other is HIs; + + bool dataEquals(HIs other) { + return typeExpression == other.typeExpression + && kind == other.kind; + } +} + +/** + * HIsViaInterceptor is a late-stage instruction for a type test that can be + * done entirely on an interceptor. It is not a HCheck because the checked + * input is not one of the inputs. + */ +class HIsViaInterceptor extends HLateInstruction { + final DartType typeExpression; + HIsViaInterceptor(this.typeExpression, HInstruction interceptor, + TypeMask type) + : super([interceptor], type) { + setUseGvn(); + } + + HInstruction get interceptor => inputs[0]; + + accept(HVisitor visitor) => visitor.visitIsViaInterceptor(this); + toString() => "$interceptor is $typeExpression"; + int typeCode() => HInstruction.IS_VIA_INTERCEPTOR_TYPECODE; + bool typeEquals(HInstruction other) => other is HIsViaInterceptor; + bool dataEquals(HIs other) { + return typeExpression == other.typeExpression; + } +} + +class HTypeConversion extends HCheck { + final DartType typeExpression; + final int kind; + final Selector receiverTypeCheckSelector; + final bool contextIsTypeArguments; + TypeMask checkedType; // Not final because we refine it. + + static const int CHECKED_MODE_CHECK = 0; + static const int ARGUMENT_TYPE_CHECK = 1; + static const int CAST_TYPE_CHECK = 2; + static const int BOOLEAN_CONVERSION_CHECK = 3; + static const int RECEIVER_TYPE_CHECK = 4; + + HTypeConversion(this.typeExpression, this.kind, + TypeMask type, HInstruction input, + [this.receiverTypeCheckSelector]) + : contextIsTypeArguments = false, + checkedType = type, + super([input], type) { + assert(!isReceiverTypeCheck || receiverTypeCheckSelector != null); + assert(typeExpression == null || + typeExpression.kind != TypeKind.TYPEDEF); + sourceElement = input.sourceElement; + } + + HTypeConversion.withTypeRepresentation(this.typeExpression, this.kind, + TypeMask type, HInstruction input, + HInstruction typeRepresentation) + : contextIsTypeArguments = false, + checkedType = type, + super([input, typeRepresentation],type), + receiverTypeCheckSelector = null { + assert(typeExpression.kind != TypeKind.TYPEDEF); + sourceElement = input.sourceElement; + } + + HTypeConversion.withContext(this.typeExpression, this.kind, + TypeMask type, HInstruction input, + HInstruction context, + {bool this.contextIsTypeArguments}) + : super([input, context], type), + checkedType = type, + receiverTypeCheckSelector = null { + assert(typeExpression.kind != TypeKind.TYPEDEF); + sourceElement = input.sourceElement; + } + + bool get hasTypeRepresentation { + return typeExpression.kind == TypeKind.INTERFACE && inputs.length > 1; + } + HInstruction get typeRepresentation => inputs[1]; + + bool get hasContext { + return typeExpression.kind == TypeKind.FUNCTION && inputs.length > 1; + } + HInstruction get context => inputs[1]; + + HInstruction convertType(Compiler compiler, DartType type, int kind) { + if (typeExpression == type) return this; + return super.convertType(compiler, type, kind); + } + + bool get isCheckedModeCheck { + return kind == CHECKED_MODE_CHECK + || kind == BOOLEAN_CONVERSION_CHECK; + } + bool get isArgumentTypeCheck => kind == ARGUMENT_TYPE_CHECK; + bool get isReceiverTypeCheck => kind == RECEIVER_TYPE_CHECK; + bool get isCastTypeCheck => kind == CAST_TYPE_CHECK; + bool get isBooleanConversionCheck => kind == BOOLEAN_CONVERSION_CHECK; + + accept(HVisitor visitor) => visitor.visitTypeConversion(this); + + bool isJsStatement() => isControlFlow(); + bool isControlFlow() => isArgumentTypeCheck || isReceiverTypeCheck; + + int typeCode() => HInstruction.TYPE_CONVERSION_TYPECODE; + bool typeEquals(HInstruction other) => other is HTypeConversion; + bool isCodeMotionInvariant() => false; + + bool dataEquals(HTypeConversion other) { + return kind == other.kind + && typeExpression == other.typeExpression + && checkedType == other.checkedType + && receiverTypeCheckSelector == other.receiverTypeCheckSelector; + } +} + +/// The [HTypeKnown] instruction marks a value with a refined type. +class HTypeKnown extends HCheck { + TypeMask knownType; + bool _isMovable; + + HTypeKnown.pinned(TypeMask knownType, HInstruction input) + : this.knownType = knownType, + this._isMovable = false, + super([input], knownType); + + HTypeKnown.witnessed(TypeMask knownType, HInstruction input, + HInstruction witness) + : this.knownType = knownType, + this._isMovable = true, + super([input, witness], knownType); + + toString() => 'TypeKnown $knownType'; + accept(HVisitor visitor) => visitor.visitTypeKnown(this); + + bool isJsStatement() => false; + bool isControlFlow() => false; + bool canThrow() => false; + + int typeCode() => HInstruction.TYPE_KNOWN_TYPECODE; + bool typeEquals(HInstruction other) => other is HTypeKnown; + bool isCodeMotionInvariant() => true; + bool get isMovable => _isMovable && useGvn(); + + bool dataEquals(HTypeKnown other) { + return knownType == other.knownType + && instructionType == other.instructionType; + } +} + +class HRangeConversion extends HCheck { + HRangeConversion(HInstruction input, type) + : super([input], type) { + sourceElement = input.sourceElement; + } + + bool get isMovable => false; + + accept(HVisitor visitor) => visitor.visitRangeConversion(this); +} + +class HStringConcat extends HInstruction { + final ast.Node node; + HStringConcat(HInstruction left, HInstruction right, this.node, TypeMask type) + : super([left, right], type) { + // TODO(sra): Until Issue 9293 is fixed, this false dependency keeps the + // concats bunched with stringified inputs for much better looking code with + // fewer temps. + sideEffects.setDependsOnSomething(); + } + + HInstruction get left => inputs[0]; + HInstruction get right => inputs[1]; + + accept(HVisitor visitor) => visitor.visitStringConcat(this); + toString() => "string concat"; +} + +/** + * The part of string interpolation which converts and interpolated expression + * into a String value. + */ +class HStringify extends HInstruction { + final ast.Node node; + HStringify(HInstruction input, this.node, TypeMask type) + : super([input], type) { + sideEffects.setAllSideEffects(); + sideEffects.setDependsOnSomething(); + } + + accept(HVisitor visitor) => visitor.visitStringify(this); + toString() => "stringify"; +} + +/** Non-block-based (aka. traditional) loop information. */ +class HLoopInformation { + final HBasicBlock header; + final List blocks; + final List backEdges; + final List labels; + final TargetElement target; + + /** Corresponding block information for the loop. */ + HLoopBlockInformation loopBlockInformation; + + HLoopInformation(this.header, this.target, this.labels) + : blocks = new List(), + backEdges = new List(); + + void addBackEdge(HBasicBlock predecessor) { + backEdges.add(predecessor); + List workQueue = [predecessor]; + do { + HBasicBlock current = workQueue.removeLast(); + addBlock(current, workQueue); + } while (!workQueue.isEmpty); + } + + // Adds a block and transitively all its predecessors in the loop as + // loop blocks. + void addBlock(HBasicBlock block, List workQueue) { + if (identical(block, header)) return; + HBasicBlock parentHeader = block.parentLoopHeader; + if (identical(parentHeader, header)) { + // Nothing to do in this case. + } else if (parentHeader != null) { + workQueue.add(parentHeader); + } else { + block.parentLoopHeader = header; + blocks.add(block); + workQueue.addAll(block.predecessors); + } + } +} + + +/** + * Embedding of a [HBlockInformation] for block-structure based traversal + * in a dominator based flow traversal by attaching it to a basic block. + * To go back to dominator-based traversal, a [HSubGraphBlockInformation] + * structure can be added in the block structure. + */ +class HBlockFlow { + final HBlockInformation body; + final HBasicBlock continuation; + HBlockFlow(this.body, this.continuation); +} + + +/** + * Information about a syntactic-like structure. + */ +abstract class HBlockInformation { + HBasicBlock get start; + HBasicBlock get end; + bool accept(HBlockInformationVisitor visitor); +} + + +/** + * Information about a statement-like structure. + */ +abstract class HStatementInformation extends HBlockInformation { + bool accept(HStatementInformationVisitor visitor); +} + + +/** + * Information about an expression-like structure. + */ +abstract class HExpressionInformation extends HBlockInformation { + bool accept(HExpressionInformationVisitor visitor); + HInstruction get conditionExpression; +} + + +abstract class HStatementInformationVisitor { + bool visitLabeledBlockInfo(HLabeledBlockInformation info); + bool visitLoopInfo(HLoopBlockInformation info); + bool visitIfInfo(HIfBlockInformation info); + bool visitTryInfo(HTryBlockInformation info); + bool visitSwitchInfo(HSwitchBlockInformation info); + bool visitSequenceInfo(HStatementSequenceInformation info); + // Pseudo-structure embedding a dominator-based traversal into + // the block-structure traversal. This will eventually go away. + bool visitSubGraphInfo(HSubGraphBlockInformation info); +} + + +abstract class HExpressionInformationVisitor { + bool visitAndOrInfo(HAndOrBlockInformation info); + bool visitSubExpressionInfo(HSubExpressionBlockInformation info); +} + + +abstract class HBlockInformationVisitor + implements HStatementInformationVisitor, HExpressionInformationVisitor { +} + + +/** + * Generic class wrapping a [SubGraph] as a block-information until + * all structures are handled properly. + */ +class HSubGraphBlockInformation implements HStatementInformation { + final SubGraph subGraph; + HSubGraphBlockInformation(this.subGraph); + + HBasicBlock get start => subGraph.start; + HBasicBlock get end => subGraph.end; + + bool accept(HStatementInformationVisitor visitor) => + visitor.visitSubGraphInfo(this); +} + +/** + * Generic class wrapping a [SubExpression] as a block-information until + * expressions structures are handled properly. + */ +class HSubExpressionBlockInformation implements HExpressionInformation { + final SubExpression subExpression; + HSubExpressionBlockInformation(this.subExpression); + + HBasicBlock get start => subExpression.start; + HBasicBlock get end => subExpression.end; + + HInstruction get conditionExpression => subExpression.conditionExpression; + + bool accept(HExpressionInformationVisitor visitor) => + visitor.visitSubExpressionInfo(this); +} + +/** A sequence of separate statements. */ +class HStatementSequenceInformation implements HStatementInformation { + final List statements; + HStatementSequenceInformation(this.statements); + + HBasicBlock get start => statements[0].start; + HBasicBlock get end => statements.last.end; + + bool accept(HStatementInformationVisitor visitor) => + visitor.visitSequenceInfo(this); +} + +class HLabeledBlockInformation implements HStatementInformation { + final HStatementInformation body; + final List labels; + final TargetElement target; + final bool isContinue; + + HLabeledBlockInformation(this.body, + List labels, + {this.isContinue: false}) : + this.labels = labels, this.target = labels[0].target; + + HLabeledBlockInformation.implicit(this.body, + this.target, + {this.isContinue: false}) + : this.labels = const[]; + + HBasicBlock get start => body.start; + HBasicBlock get end => body.end; + + bool accept(HStatementInformationVisitor visitor) => + visitor.visitLabeledBlockInfo(this); +} + +class LoopTypeVisitor extends ast.Visitor { + const LoopTypeVisitor(); + int visitNode(ast.Node node) => HLoopBlockInformation.NOT_A_LOOP; + int visitWhile(ast.While node) => HLoopBlockInformation.WHILE_LOOP; + int visitFor(ast.For node) => HLoopBlockInformation.FOR_LOOP; + int visitDoWhile(ast.DoWhile node) => HLoopBlockInformation.DO_WHILE_LOOP; + int visitForIn(ast.ForIn node) => HLoopBlockInformation.FOR_IN_LOOP; + int visitSwitchStatement(ast.SwitchStatement node) => + HLoopBlockInformation.SWITCH_CONTINUE_LOOP; +} + +class HLoopBlockInformation implements HStatementInformation { + static const int WHILE_LOOP = 0; + static const int FOR_LOOP = 1; + static const int DO_WHILE_LOOP = 2; + static const int FOR_IN_LOOP = 3; + static const int SWITCH_CONTINUE_LOOP = 4; + static const int NOT_A_LOOP = -1; + + final int kind; + final HExpressionInformation initializer; + final HExpressionInformation condition; + final HStatementInformation body; + final HExpressionInformation updates; + final TargetElement target; + final List labels; + final SourceFileLocation sourcePosition; + final SourceFileLocation endSourcePosition; + + HLoopBlockInformation(this.kind, + this.initializer, + this.condition, + this.body, + this.updates, + this.target, + this.labels, + this.sourcePosition, + this.endSourcePosition) { + assert( + (kind == DO_WHILE_LOOP ? body.start : condition.start).isLoopHeader()); + } + + HBasicBlock get start { + if (initializer != null) return initializer.start; + if (kind == DO_WHILE_LOOP) { + return body.start; + } + return condition.start; + } + + HBasicBlock get loopHeader { + return kind == DO_WHILE_LOOP ? body.start : condition.start; + } + + HBasicBlock get end { + if (updates != null) return updates.end; + if (kind == DO_WHILE_LOOP && condition != null) { + return condition.end; + } + return body.end; + } + + static int loopType(ast.Node node) { + return node.accept(const LoopTypeVisitor()); + } + + bool accept(HStatementInformationVisitor visitor) => + visitor.visitLoopInfo(this); +} + +class HIfBlockInformation implements HStatementInformation { + final HExpressionInformation condition; + final HStatementInformation thenGraph; + final HStatementInformation elseGraph; + HIfBlockInformation(this.condition, + this.thenGraph, + this.elseGraph); + + HBasicBlock get start => condition.start; + HBasicBlock get end => elseGraph == null ? thenGraph.end : elseGraph.end; + + bool accept(HStatementInformationVisitor visitor) => + visitor.visitIfInfo(this); +} + +class HAndOrBlockInformation implements HExpressionInformation { + final bool isAnd; + final HExpressionInformation left; + final HExpressionInformation right; + HAndOrBlockInformation(this.isAnd, + this.left, + this.right); + + HBasicBlock get start => left.start; + HBasicBlock get end => right.end; + + // We don't currently use HAndOrBlockInformation. + HInstruction get conditionExpression { + return null; + } + bool accept(HExpressionInformationVisitor visitor) => + visitor.visitAndOrInfo(this); +} + +class HTryBlockInformation implements HStatementInformation { + final HStatementInformation body; + final HLocalValue catchVariable; + final HStatementInformation catchBlock; + final HStatementInformation finallyBlock; + HTryBlockInformation(this.body, + this.catchVariable, + this.catchBlock, + this.finallyBlock); + + HBasicBlock get start => body.start; + HBasicBlock get end => + finallyBlock == null ? catchBlock.end : finallyBlock.end; + + bool accept(HStatementInformationVisitor visitor) => + visitor.visitTryInfo(this); +} + +class HSwitchBlockInformation implements HStatementInformation { + final HExpressionInformation expression; + final List statements; + final TargetElement target; + final List labels; + + HSwitchBlockInformation(this.expression, + this.statements, + this.target, + this.labels); + + HBasicBlock get start => expression.start; + HBasicBlock get end { + // We don't create a switch block if there are no cases. + assert(!statements.isEmpty); + return statements.last.end; + } + + bool accept(HStatementInformationVisitor visitor) => + visitor.visitSwitchInfo(this); +} + +class HReadTypeVariable extends HInstruction { + /// The type variable being read. + final TypeVariableType dartType; + + final bool hasReceiver; + + HReadTypeVariable(this.dartType, + HInstruction receiver, + TypeMask instructionType) + : hasReceiver = true, + super([receiver], instructionType) { + setUseGvn(); + } + + HReadTypeVariable.noReceiver(this.dartType, + HInstruction typeArgument, + TypeMask instructionType) + : hasReceiver = false, + super([typeArgument], instructionType) { + setUseGvn(); + } + + accept(HVisitor visitor) => visitor.visitReadTypeVariable(this); + + bool canThrow() => false; + + int typeCode() => HInstruction.READ_TYPE_VARIABLE_TYPECODE; + bool typeEquals(HInstruction other) => other is HReadTypeVariable; + + bool dataEquals(HReadTypeVariable other) { + return dartType.element == other.dartType.element + && hasReceiver == other.hasReceiver; + } +} + +abstract class HRuntimeType extends HInstruction { + final DartType dartType; + + HRuntimeType(List inputs, + this.dartType, + TypeMask instructionType) + : super(inputs, instructionType) { + setUseGvn(); + } + + bool canThrow() => false; + + int typeCode() { + throw 'abstract method'; + } + + bool typeEquals(HInstruction other) { + throw 'abstract method'; + } + + bool dataEquals(HRuntimeType other) { + return dartType == other.dartType; + } +} + +class HFunctionType extends HRuntimeType { + HFunctionType(List inputs, + FunctionType dartType, + TypeMask instructionType) + : super(inputs, dartType, instructionType); + + accept(HVisitor visitor) => visitor.visitFunctionType(this); + + int typeCode() => HInstruction.FUNCTION_TYPE_TYPECODE; + + bool typeEquals(HInstruction other) => other is HFunctionType; +} + +class HVoidType extends HRuntimeType { + HVoidType(VoidType dartType, TypeMask instructionType) + : super(const [], dartType, instructionType); + + accept(HVisitor visitor) => visitor.visitVoidType(this); + + int typeCode() => HInstruction.VOID_TYPE_TYPECODE; + + bool typeEquals(HInstruction other) => other is HVoidType; +} + +class HInterfaceType extends HRuntimeType { + HInterfaceType(List inputs, + InterfaceType dartType, + TypeMask instructionType) + : super(inputs, dartType, instructionType); + + accept(HVisitor visitor) => visitor.visitInterfaceType(this); + + int typeCode() => HInstruction.INTERFACE_TYPE_TYPECODE; + + bool typeEquals(HInstruction other) => other is HInterfaceType; +} + +class HDynamicType extends HRuntimeType { + HDynamicType(DynamicType dartType, TypeMask instructionType) + : super(const [], dartType, instructionType); + + accept(HVisitor visitor) => visitor.visitDynamicType(this); + + int typeCode() => HInstruction.DYNAMIC_TYPE_TYPECODE; + + bool typeEquals(HInstruction other) => other is HDynamicType; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/optimize.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/optimize.dart new file mode 100644 index 0000000..7068388 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/optimize.dart @@ -0,0 +1,2104 @@ +// Copyright (c) 2012, 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. + +part of ssa; + +abstract class OptimizationPhase { + String get name; + void visitGraph(HGraph graph); +} + +class SsaOptimizerTask extends CompilerTask { + final JavaScriptBackend backend; + SsaOptimizerTask(JavaScriptBackend backend) + : this.backend = backend, + super(backend.compiler); + String get name => 'SSA optimizer'; + Compiler get compiler => backend.compiler; + Map ranges = {}; + + void runPhases(HGraph graph, List phases) { + for (OptimizationPhase phase in phases) { + runPhase(graph, phase); + } + } + + void runPhase(HGraph graph, OptimizationPhase phase) { + phase.visitGraph(graph); + compiler.tracer.traceGraph(phase.name, graph); + assert(graph.isValid()); + } + + void optimize(CodegenWorkItem work, HGraph graph) { + ConstantSystem constantSystem = compiler.backend.constantSystem; + JavaScriptItemCompilationContext context = work.compilationContext; + measure(() { + SsaDeadCodeEliminator dce; + List phases = [ + // Run trivial instruction simplification first to optimize + // some patterns useful for type conversion. + new SsaInstructionSimplifier(constantSystem, backend, work), + new SsaTypeConversionInserter(compiler), + new SsaRedundantPhiEliminator(), + new SsaDeadPhiEliminator(), + new SsaTypePropagator(compiler), + // After type propagation, more instructions can be + // simplified. + new SsaInstructionSimplifier(constantSystem, backend, work), + new SsaCheckInserter(backend, work, context.boundsChecked), + new SsaInstructionSimplifier(constantSystem, backend, work), + new SsaCheckInserter(backend, work, context.boundsChecked), + new SsaTypePropagator(compiler), + // Run a dead code eliminator before LICM because dead + // interceptors are often in the way of LICM'able instructions. + new SsaDeadCodeEliminator(compiler), + new SsaGlobalValueNumberer(compiler), + // After GVN, some instructions might need their type to be + // updated because they now have different inputs. + new SsaTypePropagator(compiler), + new SsaCodeMotion(), + new SsaLoadElimination(compiler), + new SsaDeadPhiEliminator(), + new SsaTypePropagator(compiler), + new SsaValueRangeAnalyzer(compiler, constantSystem, work), + // Previous optimizations may have generated new + // opportunities for instruction simplification. + new SsaInstructionSimplifier(constantSystem, backend, work), + new SsaCheckInserter(backend, work, context.boundsChecked), + new SsaSimplifyInterceptors(compiler, constantSystem, work), + dce = new SsaDeadCodeEliminator(compiler), + new SsaTypePropagator(compiler)]; + runPhases(graph, phases); + if (dce.eliminatedSideEffects) { + phases = [ + new SsaGlobalValueNumberer(compiler), + new SsaCodeMotion(), + new SsaValueRangeAnalyzer(compiler, constantSystem, work), + new SsaInstructionSimplifier(constantSystem, backend, work), + new SsaCheckInserter(backend, work, context.boundsChecked), + new SsaSimplifyInterceptors(compiler, constantSystem, work), + new SsaDeadCodeEliminator(compiler)]; + } else { + phases = [ + // Run the simplifier to remove unneeded type checks inserted + // by type propagation. + new SsaInstructionSimplifier(constantSystem, backend, work)]; + } + runPhases(graph, phases); + }); + } +} + +bool isFixedLength(mask, Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + if (mask.isContainer && mask.length != null) { + // A container on which we have inferred the length. + return true; + } else if (mask.containsOnly(backend.jsFixedArrayClass) + || mask.containsOnlyString(compiler) + || backend.isTypedArray(mask)) { + return true; + } + return false; +} + +/** + * If both inputs to known operations are available execute the operation at + * compile-time. + */ +class SsaInstructionSimplifier extends HBaseVisitor + implements OptimizationPhase { + final String name = "SsaInstructionSimplifier"; + final JavaScriptBackend backend; + final CodegenWorkItem work; + final ConstantSystem constantSystem; + HGraph graph; + Compiler get compiler => backend.compiler; + + SsaInstructionSimplifier(this.constantSystem, this.backend, this.work); + + void visitGraph(HGraph visitee) { + graph = visitee; + visitDominatorTree(visitee); + } + + visitBasicBlock(HBasicBlock block) { + HInstruction instruction = block.first; + while (instruction != null) { + HInstruction next = instruction.next; + HInstruction replacement = instruction.accept(this); + if (replacement != instruction) { + block.rewrite(instruction, replacement); + + // The intersection of double and int return conflicting, and + // because of our number implementation for JavaScript, it + // might be that an operation thought to return double, can be + // simplified to an int. For example: + // `2.5 * 10`. + if (!(replacement.isNumberOrNull(compiler) + && instruction.isNumberOrNull(compiler))) { + // If we can replace [instruction] with [replacement], then + // [replacement]'s type can be narrowed. + TypeMask newType = replacement.instructionType.intersection( + instruction.instructionType, compiler); + replacement.instructionType = newType; + } + + // If the replacement instruction does not know its + // source element, use the source element of the + // instruction. + if (replacement.sourceElement == null) { + replacement.sourceElement = instruction.sourceElement; + } + if (replacement.sourcePosition == null) { + replacement.sourcePosition = instruction.sourcePosition; + } + if (!replacement.isInBasicBlock()) { + // The constant folding can return an instruction that is already + // part of the graph (like an input), so we only add the replacement + // if necessary. + block.addAfter(instruction, replacement); + // Visit the replacement as the next instruction in case it + // can also be constant folded away. + next = replacement; + } + block.remove(instruction); + } + instruction = next; + } + } + + HInstruction visitInstruction(HInstruction node) { + return node; + } + + HInstruction visitBoolify(HBoolify node) { + List inputs = node.inputs; + assert(inputs.length == 1); + HInstruction input = inputs[0]; + if (input.isBoolean(compiler)) return input; + // All values that cannot be 'true' are boolified to false. + TypeMask mask = input.instructionType; + if (!mask.contains(backend.jsBoolClass, compiler)) { + return graph.addConstantBool(false, compiler); + } + return node; + } + + HInstruction visitNot(HNot node) { + List inputs = node.inputs; + assert(inputs.length == 1); + HInstruction input = inputs[0]; + if (input is HConstant) { + HConstant constant = input; + bool isTrue = constant.constant.isTrue; + return graph.addConstantBool(!isTrue, compiler); + } else if (input is HNot) { + return input.inputs[0]; + } + return node; + } + + HInstruction visitInvokeUnary(HInvokeUnary node) { + HInstruction folded = + foldUnary(node.operation(constantSystem), node.operand); + return folded != null ? folded : node; + } + + HInstruction foldUnary(UnaryOperation operation, HInstruction operand) { + if (operand is HConstant) { + HConstant receiver = operand; + Constant folded = operation.fold(receiver.constant); + if (folded != null) return graph.addConstant(folded, compiler); + } + return null; + } + + HInstruction tryOptimizeLengthInterceptedGetter(HInvokeDynamic node) { + HInstruction actualReceiver = node.inputs[1]; + if (actualReceiver.isIndexablePrimitive(compiler)) { + if (actualReceiver.isConstantString()) { + HConstant constantInput = actualReceiver; + StringConstant constant = constantInput.constant; + return graph.addConstantInt(constant.length, compiler); + } else if (actualReceiver.isConstantList()) { + HConstant constantInput = actualReceiver; + ListConstant constant = constantInput.constant; + return graph.addConstantInt(constant.length, compiler); + } + Element element = backend.jsIndexableLength; + bool isFixed = isFixedLength(actualReceiver.instructionType, compiler); + HFieldGet result = new HFieldGet( + element, actualReceiver, backend.positiveIntType, + isAssignable: !isFixed); + return result; + } else if (actualReceiver.isConstantMap()) { + HConstant constantInput = actualReceiver; + MapConstant constant = constantInput.constant; + return graph.addConstantInt(constant.length, compiler); + } + return null; + } + + HInstruction handleInterceptedCall(HInvokeDynamic node) { + // Try constant folding the instruction. + Operation operation = node.specializer.operation(constantSystem); + if (operation != null) { + HInstruction instruction = node.inputs.length == 2 + ? foldUnary(operation, node.inputs[1]) + : foldBinary(operation, node.inputs[1], node.inputs[2]); + if (instruction != null) return instruction; + } + + // Try converting the instruction to a builtin instruction. + HInstruction instruction = + node.specializer.tryConvertToBuiltin(node, compiler); + if (instruction != null) return instruction; + + Selector selector = node.selector; + HInstruction input = node.inputs[1]; + + if (selector.isCall() || selector.isOperator()) { + Element target; + if (input.isExtendableArray(compiler)) { + if (selector.applies(backend.jsArrayRemoveLast, compiler)) { + target = backend.jsArrayRemoveLast; + } else if (selector.applies(backend.jsArrayAdd, compiler)) { + // The codegen special cases array calls, but does not + // inline argument type checks. + if (!compiler.enableTypeAssertions) { + target = backend.jsArrayAdd; + } + } + } else if (input.isStringOrNull(compiler)) { + if (selector.applies(backend.jsStringSplit, compiler)) { + HInstruction argument = node.inputs[2]; + if (argument.isString(compiler)) { + target = backend.jsStringSplit; + } + } else if (selector.applies(backend.jsStringOperatorAdd, compiler)) { + // `operator+` is turned into a JavaScript '+' so we need to + // make sure the receiver and the argument are not null. + HInstruction argument = node.inputs[2]; + if (argument.isString(compiler) + && !input.canBeNull()) { + target = backend.jsStringOperatorAdd; + } + } else if (selector.applies(backend.jsStringToString, compiler) + && !input.canBeNull()) { + return input; + } + } + if (target != null) { + // TODO(ngeoffray): There is a strong dependency between codegen + // and this optimization that the dynamic invoke does not need an + // interceptor. We currently need to keep a + // HInvokeDynamicMethod and not create a HForeign because + // HForeign is too opaque for the SsaCheckInserter (that adds a + // bounds check on removeLast). Once we start inlining, the + // bounds check will become explicit, so we won't need this + // optimization. + HInvokeDynamicMethod result = new HInvokeDynamicMethod( + node.selector, node.inputs.sublist(1), node.instructionType); + result.element = target; + return result; + } + } else if (selector.isGetter()) { + if (selector.asUntyped.applies(backend.jsIndexableLength, compiler)) { + HInstruction optimized = tryOptimizeLengthInterceptedGetter(node); + if (optimized != null) return optimized; + } + } + + return node; + } + + HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) { + if (node.isInterceptedCall) { + HInstruction folded = handleInterceptedCall(node); + if (folded != node) return folded; + } + + TypeMask receiverType = node.getDartReceiver(compiler).instructionType; + Selector selector = new TypedSelector(receiverType, node.selector); + Element element = compiler.world.locateSingleElement(selector); + // TODO(ngeoffray): Also fold if it's a getter or variable. + if (element != null + && element.isFunction() + // If we found out that the only target is a [:noSuchMethod:], + // we just ignore it. + && element.name == selector.name) { + FunctionElement method = element; + + if (method.isNative()) { + HInstruction folded = tryInlineNativeMethod(node, method); + if (folded != null) return folded; + } else { + // TODO(ngeoffray): If the method has optional parameters, + // we should pass the default values. + FunctionSignature parameters = method.functionSignature; + if (parameters.optionalParameterCount == 0 + || parameters.parameterCount == node.selector.argumentCount) { + node.element = element; + } + } + } + return node; + } + + HInstruction tryInlineNativeMethod(HInvokeDynamicMethod node, + FunctionElement method) { + // Enable direct calls to a native method only if we don't run in checked + // mode, where the Dart version may have type annotations on parameters and + // return type that it should check. + // Also check that the parameters are not functions: it's the callee that + // will translate them to JS functions. + // + // TODO(ngeoffray): There are some cases where we could still inline in + // checked mode if we know the arguments have the right type. And we could + // do the closure conversion as well as the return type annotation check. + + if (!node.isInterceptedCall) return null; + + // TODO(sra): Check for legacy methods with bodies in the native strings. + // foo() native 'return something'; + // They should not be used. + + FunctionSignature signature = method.functionSignature; + if (signature.optionalParametersAreNamed) return null; + + // Return types on native methods don't need to be checked, since the + // declaration has to be truthful. + + // The call site might omit optional arguments. The inlined code must + // preserve the number of arguments, so check only the actual arguments. + + List inputs = node.inputs.sublist(1); + int inputPosition = 1; // Skip receiver. + bool canInline = true; + signature.forEachParameter((ParameterElement element) { + if (inputPosition < inputs.length && canInline) { + HInstruction input = inputs[inputPosition++]; + DartType type = element.type.unalias(compiler); + if (type is FunctionType) { + canInline = false; + } + if (compiler.enableTypeAssertions) { + // TODO(sra): Check if [input] is guaranteed to pass the parameter + // type check. Consider using a strengthened type check to avoid + // passing `null` to primitive types since the native methods usually + // have non-nullable primitive parameter types. + canInline = false; + } + } + }); + + if (!canInline) return null; + + // Strengthen instruction type from annotations to help optimize + // dependent instructions. + native.NativeBehavior nativeBehavior = + native.NativeBehavior.ofMethod(method, compiler); + TypeMask returnType = + TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler); + HInvokeDynamicMethod result = + new HInvokeDynamicMethod(node.selector, inputs, returnType); + result.element = method; + return result; + } + + HInstruction visitBoundsCheck(HBoundsCheck node) { + HInstruction index = node.index; + if (index.isInteger(compiler)) return node; + if (index.isConstant()) { + HConstant constantInstruction = index; + assert(!constantInstruction.constant.isInt); + if (!constantSystem.isInt(constantInstruction.constant)) { + // -0.0 is a double but will pass the runtime integer check. + node.staticChecks = HBoundsCheck.ALWAYS_FALSE; + } + } + return node; + } + + HInstruction foldBinary(BinaryOperation operation, + HInstruction left, + HInstruction right) { + if (left is HConstant && right is HConstant) { + HConstant op1 = left; + HConstant op2 = right; + Constant folded = operation.fold(op1.constant, op2.constant); + if (folded != null) return graph.addConstant(folded, compiler); + } + return null; + } + + HInstruction visitAdd(HAdd node) { + HInstruction left = node.left; + HInstruction right = node.right; + // We can only perform this rewriting on Integer, as it is not + // valid for -0.0. + if (left.isInteger(compiler) && right.isInteger(compiler)) { + if (left is HConstant && left.constant.isZero) return right; + if (right is HConstant && right.constant.isZero) return left; + } + return super.visitAdd(node); + } + + HInstruction visitMultiply(HMultiply node) { + HInstruction left = node.left; + HInstruction right = node.right; + if (left.isNumber(compiler) && right.isNumber(compiler)) { + if (left is HConstant && left.constant.isOne) return right; + if (right is HConstant && right.constant.isOne) return left; + } + return super.visitMultiply(node); + } + + HInstruction visitInvokeBinary(HInvokeBinary node) { + HInstruction left = node.left; + HInstruction right = node.right; + BinaryOperation operation = node.operation(constantSystem); + HConstant folded = foldBinary(operation, left, right); + if (folded != null) return folded; + return node; + } + + bool allUsersAreBoolifies(HInstruction instruction) { + List users = instruction.usedBy; + int length = users.length; + for (int i = 0; i < length; i++) { + if (users[i] is! HBoolify) return false; + } + return true; + } + + HInstruction visitRelational(HRelational node) { + if (allUsersAreBoolifies(node)) { + // TODO(ngeoffray): Call a boolified selector. + // This node stays the same, but the Boolify node will go away. + } + // Note that we still have to call [super] to make sure that we end up + // in the remaining optimizations. + return super.visitRelational(node); + } + + HInstruction handleIdentityCheck(HRelational node) { + HInstruction left = node.left; + HInstruction right = node.right; + TypeMask leftType = left.instructionType; + TypeMask rightType = right.instructionType; + + // Intersection of int and double return conflicting, so + // we don't optimize on numbers to preserve the runtime semantics. + if (!(left.isNumberOrNull(compiler) && right.isNumberOrNull(compiler))) { + TypeMask intersection = leftType.intersection(rightType, compiler); + if (intersection.isEmpty && !intersection.isNullable) { + return graph.addConstantBool(false, compiler); + } + } + + if (left.isNull() && right.isNull()) { + return graph.addConstantBool(true, compiler); + } + + if (left.isConstantBoolean() && right.isBoolean(compiler)) { + HConstant constant = left; + if (constant.constant.isTrue) { + return right; + } else { + return new HNot(right, backend.boolType); + } + } + + if (right.isConstantBoolean() && left.isBoolean(compiler)) { + HConstant constant = right; + if (constant.constant.isTrue) { + return left; + } else { + return new HNot(left, backend.boolType); + } + } + + return null; + } + + HInstruction visitIdentity(HIdentity node) { + HInstruction newInstruction = handleIdentityCheck(node); + return newInstruction == null ? super.visitIdentity(node) : newInstruction; + } + + void simplifyCondition(HBasicBlock block, + HInstruction condition, + bool value) { + condition.dominatedUsers(block.first).forEach((user) { + HInstruction newCondition = graph.addConstantBool(value, compiler); + user.changeUse(condition, newCondition); + }); + } + + HInstruction visitIf(HIf node) { + HInstruction condition = node.condition; + if (condition.isConstant()) return node; + bool isNegated = condition is HNot; + + if (isNegated) { + condition = condition.inputs[0]; + } else { + // It is possible for LICM to move a negated version of the + // condition out of the loop where it used. We still want to + // simplify the nested use of the condition in that case, so + // we look for all dominating negated conditions and replace + // nested uses of them with true or false. + Iterable dominating = condition.usedBy.where((user) => + user is HNot && user.dominates(node)); + dominating.forEach((hoisted) { + simplifyCondition(node.thenBlock, hoisted, false); + simplifyCondition(node.elseBlock, hoisted, true); + }); + } + simplifyCondition(node.thenBlock, condition, !isNegated); + simplifyCondition(node.elseBlock, condition, isNegated); + return node; + } + + HInstruction visitIs(HIs node) { + DartType type = node.typeExpression; + Element element = type.element; + + if (!node.isRawCheck) { + return node; + } else if (element.isTypedef()) { + return node; + } else if (element == compiler.functionClass) { + return node; + } + + if (element == compiler.objectClass || type.treatAsDynamic) { + return graph.addConstantBool(true, compiler); + } + + HInstruction expression = node.expression; + if (expression.isInteger(compiler)) { + if (identical(element, compiler.intClass) + || identical(element, compiler.numClass) + || Elements.isNumberOrStringSupertype(element, compiler)) { + return graph.addConstantBool(true, compiler); + } else if (identical(element, compiler.doubleClass)) { + // We let the JS semantics decide for that check. Currently + // the code we emit will always return true. + return node; + } else { + return graph.addConstantBool(false, compiler); + } + } else if (expression.isDouble(compiler)) { + if (identical(element, compiler.doubleClass) + || identical(element, compiler.numClass) + || Elements.isNumberOrStringSupertype(element, compiler)) { + return graph.addConstantBool(true, compiler); + } else if (identical(element, compiler.intClass)) { + // We let the JS semantics decide for that check. Currently + // the code we emit will return true for a double that can be + // represented as a 31-bit integer and for -0.0. + return node; + } else { + return graph.addConstantBool(false, compiler); + } + } else if (expression.isNumber(compiler)) { + if (identical(element, compiler.numClass)) { + return graph.addConstantBool(true, compiler); + } else { + // We cannot just return false, because the expression may be of + // type int or double. + } + } else if (expression.canBePrimitiveNumber(compiler) + && identical(element, compiler.intClass)) { + // We let the JS semantics decide for that check. + return node; + // We need the [:hasTypeArguments:] check because we don't have + // the notion of generics in the backend. For example, [:this:] in + // a class [:A:], is currently always considered to have the + // raw type. + } else if (!RuntimeTypes.hasTypeArguments(type)) { + TypeMask expressionMask = expression.instructionType; + TypeMask typeMask = (element == compiler.nullClass) + ? new TypeMask.subtype(element) + : new TypeMask.nonNullSubtype(element); + if (expressionMask.union(typeMask, compiler) == typeMask) { + return graph.addConstantBool(true, compiler); + } else if (expressionMask.intersection(typeMask, compiler).isEmpty) { + return graph.addConstantBool(false, compiler); + } + } + return node; + } + + HInstruction visitTypeConversion(HTypeConversion node) { + HInstruction value = node.inputs[0]; + DartType type = node.typeExpression; + if (type != null) { + if (type.kind == TypeKind.MALFORMED_TYPE) { + // Malformed types are treated as dynamic statically, but should + // throw a type error at runtime. + return node; + } + if (!type.treatAsRaw || type.kind == TypeKind.TYPE_VARIABLE) { + return node; + } + if (type.kind == TypeKind.FUNCTION) { + // TODO(johnniwinther): Optimize function type conversions. + return node; + } + } + return removeIfCheckAlwaysSucceeds(node, node.checkedType); + } + + HInstruction visitTypeKnown(HTypeKnown node) { + return removeIfCheckAlwaysSucceeds(node, node.knownType); + } + + HInstruction removeIfCheckAlwaysSucceeds(HCheck node, TypeMask checkedType) { + if (checkedType.containsAll(compiler)) return node; + HInstruction input = node.checkedInput; + TypeMask inputType = input.instructionType; + return inputType.isInMask(checkedType, compiler) ? input : node; + } + + VariableElement findConcreteFieldForDynamicAccess(HInstruction receiver, + Selector selector) { + TypeMask receiverType = receiver.instructionType; + return compiler.world.locateSingleField( + new TypedSelector(receiverType, selector)); + } + + HInstruction visitFieldGet(HFieldGet node) { + var receiver = node.receiver; + if (node.element == backend.jsIndexableLength) { + JavaScriptItemCompilationContext context = work.compilationContext; + if (context.allocatedFixedLists.contains(receiver)) { + // TODO(ngeoffray): checking if the second input is an integer + // should not be necessary but it currently makes it easier for + // other optimizations to reason about a fixed length constructor + // that we know takes an int. + if (receiver.inputs[0].isInteger(compiler)) { + return receiver.inputs[0]; + } + } else if (receiver.isConstantList() || receiver.isConstantString()) { + return graph.addConstantInt(receiver.constant.length, compiler); + } else { + var type = receiver.instructionType; + if (type.isContainer && type.length != null) { + HInstruction constant = graph.addConstantInt(type.length, compiler); + if (type.isNullable) { + // If the container can be null, we update all uses of the + // length access to use the constant instead, but keep the + // length access in the graph, to ensure we still have a + // null check. + node.block.rewrite(node, constant); + return node; + } else { + return constant; + } + } + } + } + return node; + } + + HInstruction visitIndex(HIndex node) { + if (node.receiver.isConstantList() && node.index.isConstantInteger()) { + var instruction = node.receiver; + List entries = instruction.constant.entries; + instruction = node.index; + int index = instruction.constant.value; + if (index >= 0 && index < entries.length) { + return graph.addConstant(entries[index], compiler); + } + } + return node; + } + + HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { + if (node.isInterceptedCall) { + HInstruction folded = handleInterceptedCall(node); + if (folded != node) return folded; + } + HInstruction receiver = node.getDartReceiver(compiler); + Element field = findConcreteFieldForDynamicAccess(receiver, node.selector); + if (field == null) return node; + return directFieldGet(receiver, field); + } + + HInstruction directFieldGet(HInstruction receiver, Element field) { + ast.Modifiers modifiers = field.modifiers; + bool isAssignable = !compiler.world.fieldNeverChanges(field); + + TypeMask type; + if (field.getEnclosingClass().isNative()) { + type = TypeMaskFactory.fromNativeBehavior( + native.NativeBehavior.ofFieldLoad(field, compiler), + compiler); + } else { + type = TypeMaskFactory.inferredTypeForElement(field, compiler); + } + + return new HFieldGet( + field, receiver, type, isAssignable: isAssignable); + } + + HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { + if (node.isInterceptedCall) { + HInstruction folded = handleInterceptedCall(node); + if (folded != node) return folded; + } + + HInstruction receiver = node.getDartReceiver(compiler); + VariableElement field = + findConcreteFieldForDynamicAccess(receiver, node.selector); + if (field == null || !field.isAssignable()) return node; + // Use [:node.inputs.last:] in case the call follows the + // interceptor calling convention, but is not a call on an + // interceptor. + HInstruction value = node.inputs.last; + if (compiler.enableTypeAssertions) { + DartType type = field.type; + if (!type.treatAsRaw || type.kind == TypeKind.TYPE_VARIABLE) { + // We cannot generate the correct type representation here, so don't + // inline this access. + return node; + } + HInstruction other = value.convertType( + compiler, + type, + HTypeConversion.CHECKED_MODE_CHECK); + if (other != value) { + node.block.addBefore(node, other); + value = other; + } + } + return new HFieldSet(field, receiver, value); + } + + HInstruction visitStringConcat(HStringConcat node) { + // Simplify string concat: + // + // "" + R -> R + // L + "" -> L + // "L" + "R" -> "LR" + // (prefix + "L") + "R" -> prefix + "LR" + // + StringConstant getString(HInstruction instruction) { + if (!instruction.isConstantString()) return null; + HConstant constant = instruction; + return constant.constant; + } + + StringConstant leftString = getString(node.left); + if (leftString != null && leftString.value.length == 0) return node.right; + + StringConstant rightString = getString(node.right); + if (rightString == null) return node; + if (rightString.value.length == 0) return node.left; + + HInstruction prefix = null; + if (leftString == null) { + if (node.left is! HStringConcat) return node; + HStringConcat leftConcat = node.left; + // Don't undo CSE. + if (leftConcat.usedBy.length != 1) return node; + prefix = leftConcat.left; + leftString = getString(leftConcat.right); + if (leftString == null) return node; + } + + HInstruction folded = graph.addConstant( + constantSystem.createString( + new ast.DartString.concat(leftString.value, rightString.value)), + compiler); + if (prefix == null) return folded; + return new HStringConcat(prefix, folded, node.node, backend.stringType); + } + + HInstruction visitStringify(HStringify node) { + HInstruction input = node.inputs[0]; + if (input.isString(compiler)) return input; + if (input.isConstant()) { + HConstant constant = input; + if (!constant.constant.isPrimitive) return node; + PrimitiveConstant primitive = constant.constant; + return graph.addConstant(constantSystem.createString( + primitive.toDartString()), compiler); + } + return node; + } + + HInstruction visitOneShotInterceptor(HOneShotInterceptor node) { + return handleInterceptedCall(node); + } +} + +class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase { + final Set boundsChecked; + final CodegenWorkItem work; + final JavaScriptBackend backend; + final String name = "SsaCheckInserter"; + HGraph graph; + + SsaCheckInserter(this.backend, + this.work, + this.boundsChecked); + + void visitGraph(HGraph graph) { + this.graph = graph; + visitDominatorTree(graph); + } + + void visitBasicBlock(HBasicBlock block) { + HInstruction instruction = block.first; + while (instruction != null) { + HInstruction next = instruction.next; + instruction = instruction.accept(this); + instruction = next; + } + } + + HBoundsCheck insertBoundsCheck(HInstruction indexNode, + HInstruction array, + HInstruction indexArgument) { + Compiler compiler = backend.compiler; + HFieldGet length = new HFieldGet( + backend.jsIndexableLength, array, backend.positiveIntType, + isAssignable: !isFixedLength(array.instructionType, compiler)); + indexNode.block.addBefore(indexNode, length); + + TypeMask type = indexArgument.isPositiveInteger(compiler) + ? indexArgument.instructionType + : backend.positiveIntType; + HBoundsCheck check = new HBoundsCheck( + indexArgument, length, array, type); + indexNode.block.addBefore(indexNode, check); + // If the index input to the bounds check was not known to be an integer + // then we replace its uses with the bounds check, which is known to be an + // integer. However, if the input was already an integer we don't do this + // because putting in a check instruction might obscure the real nature of + // the index eg. if it is a constant. The range information from the + // BoundsCheck instruction is attached to the input directly by + // visitBoundsCheck in the SsaValueRangeAnalyzer. + if (!indexArgument.isInteger(compiler)) { + indexArgument.replaceAllUsersDominatedBy(indexNode, check); + } + boundsChecked.add(indexNode); + return check; + } + + void visitIndex(HIndex node) { + if (boundsChecked.contains(node)) return; + HInstruction index = node.index; + index = insertBoundsCheck(node, node.receiver, index); + } + + void visitIndexAssign(HIndexAssign node) { + if (boundsChecked.contains(node)) return; + HInstruction index = node.index; + index = insertBoundsCheck(node, node.receiver, index); + } + + void visitInvokeDynamicMethod(HInvokeDynamicMethod node) { + Element element = node.element; + if (node.isInterceptedCall) return; + if (element != backend.jsArrayRemoveLast) return; + if (boundsChecked.contains(node)) return; + insertBoundsCheck( + node, node.receiver, graph.addConstantInt(0, backend.compiler)); + } +} + +class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase { + final String name = "SsaDeadCodeEliminator"; + + final Compiler compiler; + SsaLiveBlockAnalyzer analyzer; + bool eliminatedSideEffects = false; + SsaDeadCodeEliminator(this.compiler); + + HInstruction zapInstructionCache; + HInstruction get zapInstruction { + if (zapInstructionCache == null) { + // A constant with no type does not pollute types at phi nodes. + Constant constant = new DummyConstant(const TypeMask.nonNullEmpty()); + zapInstructionCache = analyzer.graph.addConstant(constant, compiler); + } + return zapInstructionCache; + } + + /// Returns whether the next throwing instruction that may have side + /// effects after [instruction], throws [NoSuchMethodError] on the + /// same receiver of [instruction]. + bool hasFollowingThrowingNSM(HInstruction instruction) { + HInstruction receiver = instruction.getDartReceiver(compiler); + HInstruction current = instruction.next; + do { + if ((current.getDartReceiver(compiler) == receiver) + && current.canThrow()) { + return true; + } + if (current.canThrow() || current.sideEffects.hasSideEffects()) { + return false; + } + if (current.next == null && current is HGoto) { + // We do not merge blocks in our SSA graph, so if this block + // just jumps to a single predecessor, visit this predecessor. + assert(current.block.successors.length == 1); + current = current.block.successors[0].first; + } else { + current = current.next; + } + } while (current != null); + return false; + } + + bool isDeadCode(HInstruction instruction) { + if (!instruction.usedBy.isEmpty) return false; + if (instruction.sideEffects.hasSideEffects()) return false; + if (instruction.canThrow() + && instruction.onlyThrowsNSM() + && hasFollowingThrowingNSM(instruction)) { + return true; + } + return !instruction.canThrow() + && instruction is !HParameterValue + && instruction is !HLocalSet; + } + + void visitGraph(HGraph graph) { + analyzer = new SsaLiveBlockAnalyzer(graph, compiler); + analyzer.analyze(); + visitPostDominatorTree(graph); + cleanPhis(graph); + } + + void visitBasicBlock(HBasicBlock block) { + bool isDeadBlock = analyzer.isDeadBlock(block); + block.isLive = !isDeadBlock; + // Start from the last non-control flow instruction in the block. + HInstruction instruction = block.last.previous; + while (instruction != null) { + var previous = instruction.previous; + if (isDeadBlock) { + eliminatedSideEffects = eliminatedSideEffects || + instruction.sideEffects.hasSideEffects(); + removeUsers(instruction); + block.remove(instruction); + } else if (isDeadCode(instruction)) { + block.remove(instruction); + } + instruction = previous; + } + } + + void cleanPhis(HGraph graph) { + L: for (HBasicBlock block in graph.blocks) { + List predecessors = block.predecessors; + // Zap all inputs to phis that correspond to dead blocks. + block.forEachPhi((HPhi phi) { + for (int i = 0; i < phi.inputs.length; ++i) { + if (!predecessors[i].isLive && phi.inputs[i] != zapInstruction) { + replaceInput(i, phi, zapInstruction); + } + } + }); + if (predecessors.length < 2) continue L; + // Find the index of the single live predecessor if it exists. + int indexOfLive = -1; + for (int i = 0; i < predecessors.length; i++) { + if (predecessors[i].isLive) { + if (indexOfLive >= 0) continue L; + indexOfLive = i; + } + } + // Run through the phis of the block and replace them with their input + // that comes from the only live predecessor if that dominates the phi. + block.forEachPhi((HPhi phi) { + HInstruction replacement = (indexOfLive >= 0) + ? phi.inputs[indexOfLive] : zapInstruction; + if (replacement.dominates(phi)) { + block.rewrite(phi, replacement); + block.removePhi(phi); + } + }); + } + } + + void replaceInput(int i, HInstruction from, HInstruction by) { + from.inputs[i].usedBy.remove(from); + from.inputs[i] = by; + by.usedBy.add(from); + } + + void removeUsers(HInstruction instruction) { + instruction.usedBy.forEach((user) { + removeInput(user, instruction); + }); + instruction.usedBy.clear(); + } + + void removeInput(HInstruction user, HInstruction input) { + List inputs = user.inputs; + for (int i = 0, length = inputs.length; i < length; i++) { + if (input == inputs[i]) { + user.inputs[i] = zapInstruction; + zapInstruction.usedBy.add(user); + } + } + } +} + +class SsaLiveBlockAnalyzer extends HBaseVisitor { + final HGraph graph; + final Compiler compiler; + final Set live = new Set(); + final List worklist = []; + + SsaLiveBlockAnalyzer(this.graph, this.compiler); + + JavaScriptBackend get backend => compiler.backend; + Map get ranges => backend.optimizer.ranges; + + bool isDeadBlock(HBasicBlock block) => !live.contains(block); + + void analyze() { + markBlockLive(graph.entry); + while (!worklist.isEmpty) { + HBasicBlock live = worklist.removeLast(); + live.last.accept(this); + } + } + + void markBlockLive(HBasicBlock block) { + if (!live.contains(block)) { + worklist.add(block); + live.add(block); + } + } + + void visitControlFlow(HControlFlow instruction) { + instruction.block.successors.forEach(markBlockLive); + } + + void visitIf(HIf instruction) { + HInstruction condition = instruction.condition; + if (condition.isConstant()) { + if (condition.isConstantTrue()) { + markBlockLive(instruction.thenBlock); + } else { + markBlockLive(instruction.elseBlock); + } + } else { + visitControlFlow(instruction); + } + } + + void visitSwitch(HSwitch node) { + if (node.expression.isInteger(compiler)) { + Range switchRange = ranges[node.expression]; + if (switchRange != null && + switchRange.lower is IntValue && + switchRange.upper is IntValue) { + IntValue lowerValue = switchRange.lower; + IntValue upperValue = switchRange.upper; + int lower = lowerValue.value; + int upper = upperValue.value; + Set liveLabels = new Set(); + for (int pos = 1; pos < node.inputs.length; pos++) { + HConstant input = node.inputs[pos]; + if (!input.isConstantInteger()) continue; + IntConstant constant = input.constant; + int label = constant.value; + if (!liveLabels.contains(label) && + label <= upper && + label >= lower) { + markBlockLive(node.block.successors[pos - 1]); + liveLabels.add(label); + } + } + if (liveLabels.length != upper - lower + 1) { + markBlockLive(node.defaultTarget); + } + return; + } + } + visitControlFlow(node); + } +} + +class SsaDeadPhiEliminator implements OptimizationPhase { + final String name = "SsaDeadPhiEliminator"; + + void visitGraph(HGraph graph) { + final List worklist = []; + // A set to keep track of the live phis that we found. + final Set livePhis = new Set(); + + // Add to the worklist all live phis: phis referenced by non-phi + // instructions. + for (final block in graph.blocks) { + block.forEachPhi((HPhi phi) { + for (final user in phi.usedBy) { + if (user is !HPhi) { + worklist.add(phi); + livePhis.add(phi); + break; + } + } + }); + } + + // Process the worklist by propagating liveness to phi inputs. + while (!worklist.isEmpty) { + HPhi phi = worklist.removeLast(); + for (final input in phi.inputs) { + if (input is HPhi && !livePhis.contains(input)) { + worklist.add(input); + livePhis.add(input); + } + } + } + + // Remove phis that are not live. + // Traverse in reverse order to remove phis with no uses before the + // phis that they might use. + // NOTICE: Doesn't handle circular references, but we don't currently + // create any. + List blocks = graph.blocks; + for (int i = blocks.length - 1; i >= 0; i--) { + HBasicBlock block = blocks[i]; + HPhi current = block.phis.first; + HPhi next = null; + while (current != null) { + next = current.next; + if (!livePhis.contains(current) + // TODO(ahe): Not sure the following is correct. + && current.usedBy.isEmpty) { + block.removePhi(current); + } + current = next; + } + } + } +} + +class SsaRedundantPhiEliminator implements OptimizationPhase { + final String name = "SsaRedundantPhiEliminator"; + + void visitGraph(HGraph graph) { + final List worklist = []; + + // Add all phis in the worklist. + for (final block in graph.blocks) { + block.forEachPhi((HPhi phi) => worklist.add(phi)); + } + + while (!worklist.isEmpty) { + HPhi phi = worklist.removeLast(); + + // If the phi has already been processed, continue. + if (!phi.isInBasicBlock()) continue; + + // Find if the inputs of the phi are the same instruction. + // The builder ensures that phi.inputs[0] cannot be the phi + // itself. + assert(!identical(phi.inputs[0], phi)); + HInstruction candidate = phi.inputs[0]; + for (int i = 1; i < phi.inputs.length; i++) { + HInstruction input = phi.inputs[i]; + // If the input is the phi, the phi is still candidate for + // elimination. + if (!identical(input, candidate) && !identical(input, phi)) { + candidate = null; + break; + } + } + + // If the inputs are not the same, continue. + if (candidate == null) continue; + + // Because we're updating the users of this phi, we may have new + // phis candidate for elimination. Add phis that used this phi + // to the worklist. + for (final user in phi.usedBy) { + if (user is HPhi) worklist.add(user); + } + phi.block.rewrite(phi, candidate); + phi.block.removePhi(phi); + } + } +} + +class GvnWorkItem { + final HBasicBlock block; + final ValueSet valueSet; + GvnWorkItem(this.block, this.valueSet); +} + +class SsaGlobalValueNumberer implements OptimizationPhase { + final String name = "SsaGlobalValueNumberer"; + final Compiler compiler; + final Set visited; + + List blockChangesFlags; + List loopChangesFlags; + + SsaGlobalValueNumberer(this.compiler) : visited = new Set(); + + void visitGraph(HGraph graph) { + computeChangesFlags(graph); + moveLoopInvariantCode(graph); + List workQueue = + [new GvnWorkItem(graph.entry, new ValueSet())]; + do { + GvnWorkItem item = workQueue.removeLast(); + visitBasicBlock(item.block, item.valueSet, workQueue); + } while (!workQueue.isEmpty); + } + + void moveLoopInvariantCode(HGraph graph) { + for (int i = graph.blocks.length - 1; i >= 0; i--) { + HBasicBlock block = graph.blocks[i]; + if (block.isLoopHeader()) { + int changesFlags = loopChangesFlags[block.id]; + HLoopInformation info = block.loopInformation; + // Iterate over all blocks of this loop. Note that blocks in + // inner loops are not visited here, but we know they + // were visited before because we are iterating in post-order. + // So instructions that are GVN'ed in an inner loop are in their + // loop entry, and [info.blocks] contains this loop entry. + moveLoopInvariantCodeFromBlock(block, block, changesFlags); + for (HBasicBlock other in info.blocks) { + moveLoopInvariantCodeFromBlock(other, block, changesFlags); + } + } + } + } + + void moveLoopInvariantCodeFromBlock(HBasicBlock block, + HBasicBlock loopHeader, + int changesFlags) { + assert(block.parentLoopHeader == loopHeader || block == loopHeader); + HBasicBlock preheader = loopHeader.predecessors[0]; + int dependsFlags = SideEffects.computeDependsOnFlags(changesFlags); + HInstruction instruction = block.first; + bool isLoopAlwaysTaken() { + HInstruction instruction = loopHeader.last; + assert(instruction is HGoto || instruction is HLoopBranch); + return instruction is HGoto + || instruction.inputs[0].isConstantTrue(); + } + bool firstInstructionInLoop = block == loopHeader + // Compensate for lack of code motion. + || (blockChangesFlags[loopHeader.id] == 0 + && isLoopAlwaysTaken() + && loopHeader.successors[0] == block); + while (instruction != null) { + HInstruction next = instruction.next; + if (instruction.useGvn() && instruction.isMovable + && (!instruction.canThrow() || firstInstructionInLoop) + && !instruction.sideEffects.dependsOn(dependsFlags)) { + bool loopInvariantInputs = true; + List inputs = instruction.inputs; + for (int i = 0, length = inputs.length; i < length; i++) { + if (isInputDefinedAfterDominator(inputs[i], preheader)) { + loopInvariantInputs = false; + break; + } + } + + // If the inputs are loop invariant, we can move the + // instruction from the current block to the pre-header block. + if (loopInvariantInputs) { + block.detach(instruction); + preheader.moveAtExit(instruction); + } else { + firstInstructionInLoop = false; + } + } + int oldChangesFlags = changesFlags; + changesFlags |= instruction.sideEffects.getChangesFlags(); + if (oldChangesFlags != changesFlags) { + dependsFlags = SideEffects.computeDependsOnFlags(changesFlags); + } + instruction = next; + } + } + + bool isInputDefinedAfterDominator(HInstruction input, + HBasicBlock dominator) { + return input.block.id > dominator.id; + } + + void visitBasicBlock( + HBasicBlock block, ValueSet values, List workQueue) { + HInstruction instruction = block.first; + if (block.isLoopHeader()) { + int flags = loopChangesFlags[block.id]; + values.kill(flags); + } + while (instruction != null) { + HInstruction next = instruction.next; + int flags = instruction.sideEffects.getChangesFlags(); + assert(flags == 0 || !instruction.useGvn()); + values.kill(flags); + if (instruction.useGvn()) { + HInstruction other = values.lookup(instruction); + if (other != null) { + assert(other.gvnEquals(instruction) && instruction.gvnEquals(other)); + block.rewriteWithBetterUser(instruction, other); + block.remove(instruction); + } else { + values.add(instruction); + } + } + instruction = next; + } + + List dominatedBlocks = block.dominatedBlocks; + for (int i = 0, length = dominatedBlocks.length; i < length; i++) { + HBasicBlock dominated = dominatedBlocks[i]; + // No need to copy the value set for the last child. + ValueSet successorValues = (i == length - 1) ? values : values.copy(); + // If we have no values in our set, we do not have to kill + // anything. Also, if the range of block ids from the current + // block to the dominated block is empty, there is no blocks on + // any path from the current block to the dominated block so we + // don't have to do anything either. + assert(block.id < dominated.id); + if (!successorValues.isEmpty && block.id + 1 < dominated.id) { + visited.clear(); + List workQueue = [dominated]; + int changesFlags = 0; + do { + HBasicBlock current = workQueue.removeLast(); + changesFlags |= + getChangesFlagsForDominatedBlock(block, current, workQueue); + } while (!workQueue.isEmpty); + successorValues.kill(changesFlags); + } + workQueue.add(new GvnWorkItem(dominated, successorValues)); + } + } + + void computeChangesFlags(HGraph graph) { + // Create the changes flags lists. Make sure to initialize the + // loop changes flags list to zero so we can use bitwise or when + // propagating loop changes upwards. + final int length = graph.blocks.length; + blockChangesFlags = new List(length); + loopChangesFlags = new List(length); + for (int i = 0; i < length; i++) loopChangesFlags[i] = 0; + + // Run through all the basic blocks in the graph and fill in the + // changes flags lists. + for (int i = length - 1; i >= 0; i--) { + final HBasicBlock block = graph.blocks[i]; + final int id = block.id; + + // Compute block changes flags for the block. + int changesFlags = 0; + HInstruction instruction = block.first; + while (instruction != null) { + changesFlags |= instruction.sideEffects.getChangesFlags(); + instruction = instruction.next; + } + assert(blockChangesFlags[id] == null); + blockChangesFlags[id] = changesFlags; + + // Loop headers are part of their loop, so update the loop + // changes flags accordingly. + if (block.isLoopHeader()) { + loopChangesFlags[id] |= changesFlags; + } + + // Propagate loop changes flags upwards. + HBasicBlock parentLoopHeader = block.parentLoopHeader; + if (parentLoopHeader != null) { + loopChangesFlags[parentLoopHeader.id] |= (block.isLoopHeader()) + ? loopChangesFlags[id] + : changesFlags; + } + } + } + + int getChangesFlagsForDominatedBlock(HBasicBlock dominator, + HBasicBlock dominated, + List workQueue) { + int changesFlags = 0; + List predecessors = dominated.predecessors; + for (int i = 0, length = predecessors.length; i < length; i++) { + HBasicBlock block = predecessors[i]; + int id = block.id; + // If the current predecessor block is on the path from the + // dominator to the dominated, it must have an id that is in the + // range from the dominator to the dominated. + if (dominator.id < id && id < dominated.id && !visited.contains(id)) { + visited.add(id); + changesFlags |= blockChangesFlags[id]; + // Loop bodies might not be on the path from dominator to dominated, + // but they can invalidate values. + changesFlags |= loopChangesFlags[id]; + workQueue.add(block); + } + } + return changesFlags; + } +} + +// This phase merges equivalent instructions on different paths into +// one instruction in a dominator block. It runs through the graph +// post dominator order and computes a ValueSet for each block of +// instructions that can be moved to a dominator block. These +// instructions are the ones that: +// 1) can be used for GVN, and +// 2) do not use definitions of their own block. +// +// A basic block looks at its sucessors and finds the intersection of +// these computed ValueSet. It moves all instructions of the +// intersection into its own list of instructions. +class SsaCodeMotion extends HBaseVisitor implements OptimizationPhase { + final String name = "SsaCodeMotion"; + + List values; + + void visitGraph(HGraph graph) { + values = new List(graph.blocks.length); + for (int i = 0; i < graph.blocks.length; i++) { + values[graph.blocks[i].id] = new ValueSet(); + } + visitPostDominatorTree(graph); + } + + void visitBasicBlock(HBasicBlock block) { + List successors = block.successors; + + // Phase 1: get the ValueSet of all successors (if there are more than one), + // compute the intersection and move the instructions of the intersection + // into this block. + if (successors.length > 1) { + ValueSet instructions = values[successors[0].id]; + for (int i = 1; i < successors.length; i++) { + ValueSet other = values[successors[i].id]; + instructions = instructions.intersection(other); + } + + if (!instructions.isEmpty) { + List list = instructions.toList(); + for (HInstruction instruction in list) { + // Move the instruction to the current block. + instruction.block.detach(instruction); + block.moveAtExit(instruction); + // Go through all successors and rewrite their instruction + // to the shared one. + for (final successor in successors) { + HInstruction toRewrite = values[successor.id].lookup(instruction); + if (toRewrite != instruction) { + successor.rewriteWithBetterUser(toRewrite, instruction); + successor.remove(toRewrite); + } + } + } + } + } + + // Don't try to merge instructions to a dominator if we have + // multiple predecessors. + if (block.predecessors.length != 1) return; + + // Phase 2: Go through all instructions of this block and find + // which instructions can be moved to a dominator block. + ValueSet set_ = values[block.id]; + HInstruction instruction = block.first; + int flags = 0; + while (instruction != null) { + int dependsFlags = SideEffects.computeDependsOnFlags(flags); + flags |= instruction.sideEffects.getChangesFlags(); + + HInstruction current = instruction; + instruction = instruction.next; + if (!current.useGvn() || !current.isMovable) continue; + // TODO(sra): We could move throwing instructions provided we keep the + // exceptions in the same order. This requires they are in the same order + // in all successors, which is not tracked by the ValueSet. + if (current.canThrow()) continue; + if (current.sideEffects.dependsOn(dependsFlags)) continue; + + bool canBeMoved = true; + for (final HInstruction input in current.inputs) { + if (input.block == block) { + canBeMoved = false; + break; + } + } + if (!canBeMoved) continue; + + HInstruction existing = set_.lookup(current); + if (existing == null) { + set_.add(current); + } else { + block.rewriteWithBetterUser(current, existing); + block.remove(current); + } + } + } +} + +class SsaTypeConversionInserter extends HBaseVisitor + implements OptimizationPhase { + final String name = "SsaTypeconversionInserter"; + final Compiler compiler; + + SsaTypeConversionInserter(this.compiler); + + void visitGraph(HGraph graph) { + visitDominatorTree(graph); + } + + // Update users of [input] that are dominated by [:dominator.first:] + // to use [TypeKnown] of [input] instead. As the type information depends + // on the control flow, we mark the inserted [HTypeKnown] nodes as + // non-movable. + void insertTypePropagationForDominatedUsers(HBasicBlock dominator, + HInstruction input, + TypeMask convertedType) { + Setlet dominatedUsers = input.dominatedUsers(dominator.first); + if (dominatedUsers.isEmpty) return; + + HTypeKnown newInput = new HTypeKnown.pinned(convertedType, input); + dominator.addBefore(dominator.first, newInput); + dominatedUsers.forEach((HInstruction user) { + user.changeUse(input, newInput); + }); + } + + void visitIs(HIs instruction) { + DartType type = instruction.typeExpression; + Element element = type.element; + if (!instruction.isRawCheck) { + return; + } else if (element.isTypedef()) { + return; + } + + List ifUsers = []; + List notIfUsers = []; + + collectIfUsers(instruction, ifUsers, notIfUsers); + + if (ifUsers.isEmpty && notIfUsers.isEmpty) return; + + TypeMask convertedType = new TypeMask.nonNullSubtype(element); + HInstruction input = instruction.expression; + + for (HIf ifUser in ifUsers) { + insertTypePropagationForDominatedUsers(ifUser.thenBlock, input, + convertedType); + // TODO(ngeoffray): Also change uses for the else block on a type + // that knows it is not of a specific type. + } + for (HIf ifUser in notIfUsers) { + insertTypePropagationForDominatedUsers(ifUser.elseBlock, input, + convertedType); + // TODO(ngeoffray): Also change uses for the then block on a type + // that knows it is not of a specific type. + } + } + + void visitIdentity(HIdentity instruction) { + // At HIf(HIdentity(x, null)) strengthens x to non-null on else branch. + HInstruction left = instruction.left; + HInstruction right = instruction.right; + HInstruction input; + + if (left.isConstantNull()) { + input = right; + } else if (right.isConstantNull()) { + input = left; + } else { + return; + } + + if (!input.instructionType.isNullable) return; + + List ifUsers = []; + List notIfUsers = []; + + collectIfUsers(instruction, ifUsers, notIfUsers); + + if (ifUsers.isEmpty && notIfUsers.isEmpty) return; + + TypeMask nonNullType = input.instructionType.nonNullable(); + + for (HIf ifUser in ifUsers) { + insertTypePropagationForDominatedUsers(ifUser.elseBlock, input, + nonNullType); + // Uses in thenBlock are `null`, but probably not common. + } + for (HIf ifUser in notIfUsers) { + insertTypePropagationForDominatedUsers(ifUser.thenBlock, input, + nonNullType); + // Uses in elseBlock are `null`, but probably not common. + } + } + + collectIfUsers(HInstruction instruction, + List ifUsers, + List notIfUsers) { + for (HInstruction user in instruction.usedBy) { + if (user is HIf) { + ifUsers.add(user); + } else if (user is HNot) { + collectIfUsers(user, notIfUsers, ifUsers); + } + } + } +} + +/** + * Optimization phase that tries to eliminate memory loads (for + * example [HFieldGet]), when it knows the value stored in that memory + * location. + */ +class SsaLoadElimination extends HBaseVisitor implements OptimizationPhase { + final Compiler compiler; + final String name = "SsaLoadElimination"; + MemorySet memorySet; + List memories; + + SsaLoadElimination(this.compiler); + + void visitGraph(HGraph graph) { + memories = new List(graph.blocks.length); + List blocks = graph.blocks; + for (int i = 0; i < blocks.length; i++) { + HBasicBlock block = blocks[i]; + visitBasicBlock(block); + if (block.successors.isNotEmpty && block.successors[0].isLoopHeader()) { + // We've reached the ending block of a loop. Iterate over the + // blocks of the loop again to take values that flow from that + // ending block into account. + for (int j = block.successors[0].id; j <= block.id; j++) { + visitBasicBlock(blocks[j]); + } + } + } + } + + void visitBasicBlock(HBasicBlock block) { + if (block.predecessors.length == 0) { + // Entry block. + memorySet = new MemorySet(compiler); + } else if (block.predecessors.length == 1 + && block.predecessors[0].successors.length == 1) { + // No need to clone, there is no other successor for + // `block.predecessors[0]`, and this block has only one + // predecessor. Since we are not going to visit + // `block.predecessors[0]` again, we can just re-use its + // [memorySet]. + memorySet = memories[block.predecessors[0].id]; + } else if (block.predecessors.length == 1) { + // Clone the memorySet of the predecessor, because it is also used + // by other successors of it. + memorySet = memories[block.predecessors[0].id].clone(); + } else { + // Compute the intersection of all predecessors. + memorySet = memories[block.predecessors[0].id]; + for (int i = 1; i < block.predecessors.length; i++) { + memorySet = memorySet.intersectionFor( + memories[block.predecessors[i].id], block, i); + } + } + + memories[block.id] = memorySet; + HInstruction instruction = block.first; + while (instruction != null) { + HInstruction next = instruction.next; + instruction.accept(this); + instruction = next; + } + } + + void visitFieldGet(HFieldGet instruction) { + if (instruction.isNullCheck) return; + Element element = instruction.element; + HInstruction receiver = + instruction.getDartReceiver(compiler).nonCheck(); + HInstruction existing = memorySet.lookupFieldValue(element, receiver); + if (existing != null) { + instruction.block.rewriteWithBetterUser(instruction, existing); + instruction.block.remove(instruction); + } else { + memorySet.registerFieldValue(element, receiver, instruction); + } + } + + void visitFieldSet(HFieldSet instruction) { + HInstruction receiver = + instruction.getDartReceiver(compiler).nonCheck(); + memorySet.registerFieldValueUpdate( + instruction.element, receiver, instruction.inputs.last); + } + + void visitForeignNew(HForeignNew instruction) { + memorySet.registerAllocation(instruction); + int argumentIndex = 0; + instruction.element.forEachInstanceField((_, Element member) { + memorySet.registerFieldValue( + member, instruction, instruction.inputs[argumentIndex++]); + }, includeSuperAndInjectedMembers: true); + // In case this instruction has as input non-escaping objects, we + // need to mark these objects as escaping. + memorySet.killAffectedBy(instruction); + } + + void visitInstruction(HInstruction instruction) { + memorySet.killAffectedBy(instruction); + } + + void visitLazyStatic(HLazyStatic instruction) { + handleStaticLoad(instruction.element, instruction); + } + + void handleStaticLoad(Element element, HInstruction instruction) { + HInstruction existing = memorySet.lookupFieldValue(element, null); + if (existing != null) { + instruction.block.rewriteWithBetterUser(instruction, existing); + instruction.block.remove(instruction); + } else { + memorySet.registerFieldValue(element, null, instruction); + } + } + + void visitStatic(HStatic instruction) { + handleStaticLoad(instruction.element, instruction); + } + + void visitStaticStore(HStaticStore instruction) { + memorySet.registerFieldValueUpdate( + instruction.element, null, instruction.inputs.last); + } + + void visitLiteralList(HLiteralList instruction) { + memorySet.registerAllocation(instruction); + memorySet.killAffectedBy(instruction); + } + + void visitIndex(HIndex instruction) { + HInstruction receiver = instruction.receiver.nonCheck(); + HInstruction existing = + memorySet.lookupKeyedValue(receiver, instruction.index); + if (existing != null) { + instruction.block.rewriteWithBetterUser(instruction, existing); + instruction.block.remove(instruction); + } else { + memorySet.registerKeyedValue(receiver, instruction.index, instruction); + } + } + + void visitIndexAssign(HIndexAssign instruction) { + HInstruction receiver = instruction.receiver.nonCheck(); + memorySet.registerKeyedValueUpdate( + receiver, instruction.index, instruction.value); + } +} + +/** + * Holds values of memory places. + */ +class MemorySet { + final Compiler compiler; + + /** + * Maps a field to a map of receiver to value. + */ + final Map> fieldValues = + > {}; + + /** + * Maps a receiver to a map of keys to value. + */ + final Map> keyedValues = + > {}; + + /** + * Set of objects that we know don't escape the current function. + */ + final Setlet nonEscapingReceivers = new Setlet(); + + MemorySet(this.compiler); + + /** + * Returns whether [first] and [second] always alias to the same object. + */ + bool mustAlias(HInstruction first, HInstruction second) { + return first == second; + } + + /** + * Returns whether [first] and [second] may alias to the same object. + */ + bool mayAlias(HInstruction first, HInstruction second) { + if (mustAlias(first, second)) return true; + if (isConcrete(first) && isConcrete(second)) return false; + if (nonEscapingReceivers.contains(first)) return false; + if (nonEscapingReceivers.contains(second)) return false; + // Typed arrays of different types might have a shared buffer. + if (couldBeTypedArray(first) && couldBeTypedArray(second)) return true; + TypeMask intersection = first.instructionType.intersection( + second.instructionType, compiler); + if (intersection.isEmpty) return false; + return true; + } + + bool isFinal(Element element) { + return compiler.world.fieldNeverChanges(element); + } + + bool isConcrete(HInstruction instruction) { + return instruction is HForeignNew + || instruction is HConstant + || instruction is HLiteralList; + } + + bool couldBeTypedArray(HInstruction receiver) { + JavaScriptBackend backend = compiler.backend; + return backend.couldBeTypedArray(receiver.instructionType); + } + + /** + * Returns whether [receiver] escapes the current function. + */ + bool escapes(HInstruction receiver) { + return !nonEscapingReceivers.contains(receiver); + } + + void registerAllocation(HInstruction instruction) { + nonEscapingReceivers.add(instruction); + } + + /** + * Sets `receiver.element` to contain [value]. Kills all potential + * places that may be affected by this update. + */ + void registerFieldValueUpdate(Element element, + HInstruction receiver, + HInstruction value) { + if (element.isNative()) return; // TODO(14955): Remove this restriction? + // [value] is being set in some place in memory, we remove it from + // the non-escaping set. + nonEscapingReceivers.remove(value); + Map map = fieldValues.putIfAbsent( + element, () => {}); + map.forEach((key, value) { + if (mayAlias(receiver, key)) map[key] = null; + }); + map[receiver] = value; + } + + /** + * Registers that `receiver.element` is now [value]. + */ + void registerFieldValue(Element element, + HInstruction receiver, + HInstruction value) { + if (element.isNative()) return; // TODO(14955): Remove this restriction? + Map map = fieldValues.putIfAbsent( + element, () => {}); + map[receiver] = value; + } + + /** + * Returns the value stored in `receiver.element`. Returns null if + * we don't know. + */ + HInstruction lookupFieldValue(Element element, HInstruction receiver) { + Map map = fieldValues[element]; + return (map == null) ? null : map[receiver]; + } + + /** + * Kill all places that may be affected by this [instruction]. Also + * update the set of non-escaping objects in case [instruction] has + * non-escaping objects in its inputs. + */ + void killAffectedBy(HInstruction instruction) { + // Even if [instruction] does not have side effects, it may use + // non-escaping objects and store them in a new object, which + // make these objects escaping. + // TODO(ngeoffray): We need a new side effect flag to know whether + // an instruction allocates an object. + instruction.inputs.forEach((input) { + nonEscapingReceivers.remove(input); + }); + + if (instruction.sideEffects.changesInstanceProperty() + || instruction.sideEffects.changesStaticProperty()) { + fieldValues.forEach((element, map) { + if (isFinal(element)) return; + map.forEach((receiver, value) { + if (escapes(receiver)) { + map[receiver] = null; + } + }); + }); + } + + if (instruction.sideEffects.changesIndex()) { + keyedValues.forEach((receiver, map) { + if (escapes(receiver)) { + map.forEach((index, value) { + map[index] = null; + }); + } + }); + } + } + + /** + * Returns the value stored in `receiver[index]`. Returns null if + * we don't know. + */ + HInstruction lookupKeyedValue(HInstruction receiver, HInstruction index) { + Map map = keyedValues[receiver]; + return (map == null) ? null : map[index]; + } + + /** + * Registers that `receiver[index]` is now [value]. + */ + void registerKeyedValue(HInstruction receiver, + HInstruction index, + HInstruction value) { + Map map = keyedValues.putIfAbsent( + receiver, () => {}); + map[index] = value; + } + + /** + * Sets `receiver[index]` to contain [value]. Kills all potential + * places that may be affected by this update. + */ + void registerKeyedValueUpdate(HInstruction receiver, + HInstruction index, + HInstruction value) { + nonEscapingReceivers.remove(value); + keyedValues.forEach((key, values) { + if (mayAlias(receiver, key)) { + // Typed arrays that are views of the same buffer may have different + // offsets or element sizes, unless they are the same typed array. + bool weakIndex = couldBeTypedArray(key) && !mustAlias(receiver, key); + values.forEach((otherIndex, otherValue) { + if (weakIndex || mayAlias(index, otherIndex)) { + values[otherIndex] = null; + } + }); + } + }); + + // Typed arrays may narrow incoming values. + if (couldBeTypedArray(receiver)) return; + + Map map = keyedValues.putIfAbsent( + receiver, () => {}); + map[index] = value; + } + + /** + * Returns null if either [first] or [second] is null. Otherwise + * returns [first] if [first] and [second] are equal. Otherwise + * creates or re-uses a phi in [block] that holds [first] and [second]. + */ + HInstruction findCommonInstruction(HInstruction first, + HInstruction second, + HBasicBlock block, + int predecessorIndex) { + if (first == null || second == null) return null; + if (first == second) return first; + TypeMask phiType = second.instructionType.union( + first.instructionType, compiler); + if (first is HPhi && first.block == block) { + HPhi phi = first; + phi.addInput(second); + phi.instructionType = phiType; + return phi; + } else { + HPhi phi = new HPhi.noInputs(null, phiType); + block.addPhi(phi); + // Previous predecessors had the same input. A phi must have + // the same number of inputs as its block has predecessors. + for (int i = 0; i < predecessorIndex; i++) { + phi.addInput(first); + } + phi.addInput(second); + return phi; + } + } + + /** + * Returns the intersection between [this] and [other]. + */ + MemorySet intersectionFor(MemorySet other, + HBasicBlock block, + int predecessorIndex) { + MemorySet result = new MemorySet(compiler); + if (other == null) return result; + + fieldValues.forEach((element, values) { + var otherValues = other.fieldValues[element]; + if (otherValues == null) return; + values.forEach((receiver, value) { + HInstruction instruction = findCommonInstruction( + value, otherValues[receiver], block, predecessorIndex); + if (instruction != null) { + result.registerFieldValue(element, receiver, instruction); + } + }); + }); + + keyedValues.forEach((receiver, values) { + var otherValues = other.keyedValues[receiver]; + if (otherValues == null) return; + values.forEach((index, value) { + HInstruction instruction = findCommonInstruction( + value, otherValues[index], block, predecessorIndex); + if (instruction != null) { + result.registerKeyedValue(receiver, index, instruction); + } + }); + }); + + nonEscapingReceivers.forEach((receiver) { + if (other.nonEscapingReceivers.contains(receiver)) { + result.nonEscapingReceivers.add(receiver); + } + }); + return result; + } + + /** + * Returns a copy of [this]. + */ + MemorySet clone() { + MemorySet result = new MemorySet(compiler); + + fieldValues.forEach((element, values) { + result.fieldValues[element] = + new Map.from(values); + }); + + keyedValues.forEach((receiver, values) { + result.keyedValues[receiver] = + new Map.from(values); + }); + + result.nonEscapingReceivers.addAll(nonEscapingReceivers); + return result; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/ssa.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/ssa.dart new file mode 100644 index 0000000..6d3a236 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/ssa.dart @@ -0,0 +1,48 @@ +// Copyright (c) 2012, 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. + +library ssa; + +import 'dart:collection'; + +import '../closure.dart'; +import '../js/js.dart' as js; +import '../dart2jslib.dart' hide Selector, TypedSelector; +import '../dart_types.dart'; +import '../source_file.dart'; +import '../source_map_builder.dart'; +import '../elements/elements.dart'; +import '../js_backend/js_backend.dart'; +import '../native_handler.dart' as native; +import '../tree/tree.dart' as ast; +import '../types/types.dart'; +import '../universe/universe.dart'; +import '../util/util.dart'; + +import '../scanner/scannerlib.dart' + show PartialFunctionElement, Token, PLUS_TOKEN; + +import '../elements/visitor.dart' + show ElementVisitor; + +import '../elements/modelx.dart' + show ElementX, + VariableElementX, + ConstructorBodyElementX; + +import '../js_emitter/js_emitter.dart' show CodeEmitterTask; + +part 'builder.dart'; +part 'codegen.dart'; +part 'codegen_helpers.dart'; +part 'interceptor_simplifier.dart'; +part 'invoke_dynamic_specializers.dart'; +part 'nodes.dart'; +part 'optimize.dart'; +part 'types.dart'; +part 'types_propagation.dart'; +part 'validate.dart'; +part 'variable_allocator.dart'; +part 'value_range_analyzer.dart'; +part 'value_set.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/tracer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/tracer.dart new file mode 100644 index 0000000..4c86cb7 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/tracer.dart @@ -0,0 +1,558 @@ +// Copyright (c) 2013, 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. + +library tracer; + +import 'dart:async' show EventSink; + +import 'ssa.dart'; +import '../js_backend/js_backend.dart'; +import '../dart2jslib.dart'; + +const bool GENERATE_SSA_TRACE = false; +const String SSA_TRACE_FILTER = null; + +class HTracer extends HGraphVisitor implements Tracer { + Compiler compiler; + JavaScriptItemCompilationContext context; + int indent = 0; + final EventSink output; + final bool enabled = GENERATE_SSA_TRACE; + bool traceActive = false; + + HTracer(this.output); + + void close() { + if (enabled) output.close(); + } + + void traceCompilation(String methodName, + JavaScriptItemCompilationContext compilationContext, + Compiler compiler) { + if (!enabled) return; + this.context = compilationContext; + this.compiler = compiler; + traceActive = + SSA_TRACE_FILTER == null || methodName.contains(SSA_TRACE_FILTER); + if (!traceActive) return; + tag("compilation", () { + printProperty("name", methodName); + printProperty("method", methodName); + printProperty("date", new DateTime.now().millisecondsSinceEpoch); + }); + } + + void traceGraph(String name, HGraph graph) { + if (!traceActive) return; + tag("cfg", () { + printProperty("name", name); + visitDominatorTree(graph); + }); + } + + void addPredecessors(HBasicBlock block) { + if (block.predecessors.isEmpty) { + printEmptyProperty("predecessors"); + } else { + addIndent(); + add("predecessors"); + for (HBasicBlock predecessor in block.predecessors) { + add(' "B${predecessor.id}"'); + } + add("\n"); + } + } + + void addSuccessors(HBasicBlock block) { + if (block.successors.isEmpty) { + printEmptyProperty("successors"); + } else { + addIndent(); + add("successors"); + for (HBasicBlock successor in block.successors) { + add(' "B${successor.id}"'); + } + add("\n"); + } + } + + void addInstructions(HInstructionStringifier stringifier, + HInstructionList list) { + for (HInstruction instruction = list.first; + instruction != null; + instruction = instruction.next) { + int bci = 0; + int uses = instruction.usedBy.length; + String changes = instruction.sideEffects.hasSideEffects() ? '!' : ' '; + String depends = instruction.sideEffects.dependsOnSomething() ? '?' : ''; + addIndent(); + String temporaryId = stringifier.temporaryId(instruction); + String instructionString = stringifier.visit(instruction); + add("$bci $uses $temporaryId $instructionString $changes $depends <|@\n"); + } + } + + void visitBasicBlock(HBasicBlock block) { + HInstructionStringifier stringifier = + new HInstructionStringifier(context, block, compiler); + assert(block.id != null); + tag("block", () { + printProperty("name", "B${block.id}"); + printProperty("from_bci", -1); + printProperty("to_bci", -1); + addPredecessors(block); + addSuccessors(block); + printEmptyProperty("xhandlers"); + printEmptyProperty("flags"); + if (block.dominator != null) { + printProperty("dominator", "B${block.dominator.id}"); + } + tag("states", () { + tag("locals", () { + printProperty("size", 0); + printProperty("method", "None"); + block.forEachPhi((phi) { + String phiId = stringifier.temporaryId(phi); + StringBuffer inputIds = new StringBuffer(); + for (int i = 0; i < phi.inputs.length; i++) { + inputIds.write(stringifier.temporaryId(phi.inputs[i])); + inputIds.write(" "); + } + println("${phi.id} $phiId [ $inputIds]"); + }); + }); + }); + tag("HIR", () { + addInstructions(stringifier, block.phis); + addInstructions(stringifier, block); + }); + }); + } + + void tag(String tagName, Function f) { + println("begin_$tagName"); + indent++; + f(); + indent--; + println("end_$tagName"); + } + + void println(String string) { + addIndent(); + add(string); + add("\n"); + } + + void printEmptyProperty(String propertyName) { + println(propertyName); + } + + void printProperty(String propertyName, var value) { + if (value is num) { + println("$propertyName $value"); + } else { + println('$propertyName "$value"'); + } + } + + void add(String string) { + output.add(string); + } + + void addIndent() { + for (int i = 0; i < indent; i++) { + add(" "); + } + } +} + +class HInstructionStringifier implements HVisitor { + final Compiler compiler; + final JavaScriptItemCompilationContext context; + final HBasicBlock currentBlock; + + HInstructionStringifier(this.context, this.currentBlock, this.compiler); + + visit(HInstruction node) => '${node.accept(this)} ${node.instructionType}'; + + String temporaryId(HInstruction instruction) { + String prefix; + if (instruction.isNull()) { + prefix = 'u'; + } else if (instruction.isConflicting()) { + prefix = 'c'; + } else if (instruction.isExtendableArray(compiler)) { + prefix = 'e'; + } else if (instruction.isFixedArray(compiler)) { + prefix = 'f'; + } else if (instruction.isMutableArray(compiler)) { + prefix = 'm'; + } else if (instruction.isReadableArray(compiler)) { + prefix = 'a'; + } else if (instruction.isString(compiler)) { + prefix = 's'; + } else if (instruction.isIndexablePrimitive(compiler)) { + prefix = 'r'; + } else if (instruction.isBoolean(compiler)) { + prefix = 'b'; + } else if (instruction.isInteger(compiler)) { + prefix = 'i'; + } else if (instruction.isDouble(compiler)) { + prefix = 'd'; + } else if (instruction.isNumber(compiler)) { + prefix = 'n'; + } else if (instruction.instructionType.containsAll(compiler)) { + prefix = 'v'; + } else { + prefix = 'U'; + } + return "$prefix${instruction.id}"; + } + + String visitBoolify(HBoolify node) { + return "Boolify: ${temporaryId(node.inputs[0])}"; + } + + String handleInvokeBinary(HInvokeBinary node, String op) { + String left = temporaryId(node.left); + String right= temporaryId(node.right); + return '$left $op $right'; + } + + String visitAdd(HAdd node) => handleInvokeBinary(node, '+'); + + String visitBitAnd(HBitAnd node) => handleInvokeBinary(node, '&'); + + String visitBitNot(HBitNot node) { + String operand = temporaryId(node.operand); + return "~$operand"; + } + + String visitBitOr(HBitOr node) => handleInvokeBinary(node, '|'); + + String visitBitXor(HBitXor node) => handleInvokeBinary(node, '^'); + + String visitBoundsCheck(HBoundsCheck node) { + String lengthId = temporaryId(node.length); + String indexId = temporaryId(node.index); + return "Bounds check: length = $lengthId, index = $indexId"; + } + + String visitBreak(HBreak node) { + HBasicBlock target = currentBlock.successors[0]; + if (node.label != null) { + return "Break ${node.label.labelName}: (B${target.id})"; + } + return "Break: (B${target.id})"; + } + + String visitConstant(HConstant constant) => "Constant ${constant.constant}"; + + String visitContinue(HContinue node) { + HBasicBlock target = currentBlock.successors[0]; + if (node.label != null) { + return "Continue ${node.label.labelName}: (B${target.id})"; + } + return "Continue: (B${target.id})"; + } + + String visitDivide(HDivide node) => handleInvokeBinary(node, '/'); + + String visitExit(HExit node) => "exit"; + + String visitFieldGet(HFieldGet node) { + if (node.isNullCheck) { + return 'null check on ${temporaryId(node.receiver)}'; + } + String fieldName = node.element.name; + return 'field get ${temporaryId(node.receiver)}.$fieldName'; + } + + String visitFieldSet(HFieldSet node) { + String valueId = temporaryId(node.value); + String fieldName = node.element.name; + return 'field set ${temporaryId(node.receiver)}.$fieldName to $valueId'; + } + + String visitLocalGet(HLocalGet node) { + String localName = node.element.name; + return 'local get ${temporaryId(node.local)}.$localName'; + } + + String visitLocalSet(HLocalSet node) { + String valueId = temporaryId(node.value); + String localName = node.element.name; + return 'local set ${temporaryId(node.local)}.$localName to $valueId'; + } + + String visitGoto(HGoto node) { + HBasicBlock target = currentBlock.successors[0]; + return "Goto: (B${target.id})"; + } + + String visitGreater(HGreater node) => handleInvokeBinary(node, '>'); + String visitGreaterEqual(HGreaterEqual node) { + return handleInvokeBinary(node, '>='); + } + String visitIdentity(HIdentity node) => handleInvokeBinary(node, '==='); + + String visitIf(HIf node) { + HBasicBlock thenBlock = currentBlock.successors[0]; + HBasicBlock elseBlock = currentBlock.successors[1]; + String conditionId = temporaryId(node.inputs[0]); + return "If ($conditionId): (B${thenBlock.id}) else (B${elseBlock.id})"; + } + + String visitGenericInvoke(String invokeType, String functionName, + List arguments) { + StringBuffer argumentsString = new StringBuffer(); + for (int i = 0; i < arguments.length; i++) { + if (i != 0) argumentsString.write(", "); + argumentsString.write(temporaryId(arguments[i])); + } + return "$invokeType: $functionName($argumentsString)"; + } + + String visitIndex(HIndex node) { + String receiver = temporaryId(node.receiver); + String index = temporaryId(node.index); + return "Index: $receiver[$index]"; + } + + String visitIndexAssign(HIndexAssign node) { + String receiver = temporaryId(node.receiver); + String index = temporaryId(node.index); + String value = temporaryId(node.value); + return "IndexAssign: $receiver[$index] = $value"; + } + + String visitInterceptor(HInterceptor node) { + String value = temporaryId(node.inputs[0]); + if (node.interceptedClasses != null) { + JavaScriptBackend backend = compiler.backend; + String cls = backend.namer.getInterceptorSuffix(node.interceptedClasses); + return "Intercept ($cls): $value"; + } + return "Intercept: $value"; + } + + String visitInvokeClosure(HInvokeClosure node) + => visitInvokeDynamic(node, "closure"); + + String visitInvokeDynamic(HInvokeDynamic invoke, String kind) { + String receiver = temporaryId(invoke.receiver); + String name = invoke.selector.name; + String target = "($kind) $receiver.$name"; + int offset = HInvoke.ARGUMENTS_OFFSET; + List arguments = invoke.inputs.sublist(offset); + return visitGenericInvoke("Invoke", target, arguments); + } + + String visitInvokeDynamicMethod(HInvokeDynamicMethod node) + => visitInvokeDynamic(node, "method"); + String visitInvokeDynamicGetter(HInvokeDynamicGetter node) + => visitInvokeDynamic(node, "get"); + String visitInvokeDynamicSetter(HInvokeDynamicSetter node) + => visitInvokeDynamic(node, "set"); + + String visitInvokeStatic(HInvokeStatic invoke) { + String target = invoke.element.name; + return visitGenericInvoke("Invoke", target, invoke.inputs); + } + + String visitInvokeSuper(HInvokeSuper invoke) { + String target = invoke.element.name; + return visitGenericInvoke("Invoke super", target, invoke.inputs); + } + + String visitInvokeConstructorBody(HInvokeConstructorBody invoke) { + String target = invoke.element.name; + return visitGenericInvoke("Invoke constructor body", target, invoke.inputs); + } + + String visitForeign(HForeign foreign) { + return visitGenericInvoke("Foreign", "${foreign.codeAst}", foreign.inputs); + } + + String visitForeignNew(HForeignNew node) { + return visitGenericInvoke("New", + "${node.element.name}", + node.inputs); + } + + String visitLess(HLess node) => handleInvokeBinary(node, '<'); + String visitLessEqual(HLessEqual node) => handleInvokeBinary(node, '<='); + + String visitLiteralList(HLiteralList node) { + StringBuffer elementsString = new StringBuffer(); + for (int i = 0; i < node.inputs.length; i++) { + if (i != 0) elementsString.write(", "); + elementsString.write(temporaryId(node.inputs[i])); + } + return "Literal list: [$elementsString]"; + } + + String visitLoopBranch(HLoopBranch branch) { + HBasicBlock bodyBlock = currentBlock.successors[0]; + HBasicBlock exitBlock = currentBlock.successors[1]; + String conditionId = temporaryId(branch.inputs[0]); + return "While ($conditionId): (B${bodyBlock.id}) then (B${exitBlock.id})"; + } + + String visitMultiply(HMultiply node) => handleInvokeBinary(node, '*'); + + String visitNegate(HNegate node) { + String operand = temporaryId(node.operand); + return "-$operand"; + } + + String visitNot(HNot node) => "Not: ${temporaryId(node.inputs[0])}"; + + String visitParameterValue(HParameterValue node) { + return "p${node.sourceElement.name}"; + } + + String visitLocalValue(HLocalValue node) { + return "l${node.sourceElement.name}"; + } + + String visitPhi(HPhi phi) { + StringBuffer buffer = new StringBuffer(); + buffer.write("Phi("); + for (int i = 0; i < phi.inputs.length; i++) { + if (i > 0) buffer.write(", "); + buffer.write(temporaryId(phi.inputs[i])); + } + buffer.write(")"); + return buffer.toString(); + } + + String visitReturn(HReturn node) => "Return ${temporaryId(node.inputs[0])}"; + + String visitShiftLeft(HShiftLeft node) => handleInvokeBinary(node, '<<'); + String visitShiftRight(HShiftRight node) => handleInvokeBinary(node, '>>'); + + String visitStatic(HStatic node) + => "Static ${node.element.name}"; + + String visitLazyStatic(HLazyStatic node) + => "LazyStatic ${node.element.name}"; + + String visitOneShotInterceptor(HOneShotInterceptor node) + => visitInvokeDynamic(node, "one shot interceptor"); + + String visitStaticStore(HStaticStore node) { + String lhs = node.element.name; + return "Static $lhs = ${temporaryId(node.inputs[0])}"; + } + + String visitStringConcat(HStringConcat node) { + var leftId = temporaryId(node.left); + var rightId = temporaryId(node.right); + return "StringConcat: $leftId + $rightId"; + } + + String visitStringify(HStringify node) { + return "Stringify ${temporaryId(node.inputs[0])}"; + } + + String visitSubtract(HSubtract node) => handleInvokeBinary(node, '-'); + + String visitSwitch(HSwitch node) { + StringBuffer buf = new StringBuffer(); + buf.write("Switch: ("); + buf.write(temporaryId(node.inputs[0])); + buf.write(") "); + for (int i = 1; i < node.inputs.length; i++) { + buf.write(temporaryId(node.inputs[i])); + buf.write(": B"); + buf.write(node.block.successors[i - 1].id); + buf.write(", "); + } + buf.write("default: B"); + buf.write(node.defaultTarget.id); + return buf.toString(); + } + + String visitThis(HThis node) => "this"; + + String visitThrow(HThrow node) => "Throw ${temporaryId(node.inputs[0])}"; + + String visitThrowExpression(HThrowExpression node) { + return "ThrowExpression ${temporaryId(node.inputs[0])}"; + } + + String visitTruncatingDivide(HTruncatingDivide node) { + return handleInvokeBinary(node, '~/'); + } + + String visitExitTry(HExitTry node) { + return "Exit try"; + } + + String visitTry(HTry node) { + List successors = currentBlock.successors; + String tryBlock = 'B${successors[0].id}'; + String catchBlock = 'none'; + if (node.catchBlock != null) { + catchBlock = 'B${successors[1].id}'; + } + + String finallyBlock = 'none'; + if (node.finallyBlock != null) { + finallyBlock = 'B${node.finallyBlock.id}'; + } + + return "Try: $tryBlock, Catch: $catchBlock, Finally: $finallyBlock, " + "Join: B${successors.last.id}"; + } + + String visitIs(HIs node) { + String type = node.typeExpression.toString(); + return "TypeTest: ${temporaryId(node.expression)} is $type"; + } + + String visitIsViaInterceptor(HIsViaInterceptor node) { + String type = node.typeExpression.toString(); + return "TypeTest: ${temporaryId(node.inputs[0])} is $type"; + } + + String visitTypeConversion(HTypeConversion node) { + assert(node.inputs.length <= 2); + String otherInput = (node.inputs.length == 2) + ? temporaryId(node.inputs[1]) + : ''; + return "TypeConversion: ${temporaryId(node.checkedInput)} to " + "${node.instructionType} $otherInput"; + } + + String visitTypeKnown(HTypeKnown node) { + assert(node.inputs.length == 1); + return "TypeKnown: ${temporaryId(node.checkedInput)} is ${node.knownType}"; + } + + String visitRangeConversion(HRangeConversion node) { + return "RangeConversion: ${node.checkedInput}"; + } + + String visitReadTypeVariable(HReadTypeVariable node) { + return "ReadTypeVariable: ${node.dartType} ${node.hasReceiver}"; + } + + String visitFunctionType(HFunctionType node) { + return "FunctionType: ${node.dartType}"; + } + + String visitVoidType(HVoidType node) { + return "VoidType"; + } + + String visitInterfaceType(HInterfaceType node) { + return "InterfaceType: ${node.dartType}"; + } + + String visitDynamicType(HDynamicType node) { + return "DynamicType"; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/types.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/types.dart new file mode 100644 index 0000000..c88e521 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/types.dart @@ -0,0 +1,72 @@ +// Copyright (c) 2012, 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. + +part of ssa; + +class TypeMaskFactory { + static TypeMask fromInferredType(TypeMask mask, Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + if (mask == null) return backend.dynamicType; + return mask; + } + + static TypeMask inferredReturnTypeForElement( + Element element, Compiler compiler) { + return fromInferredType( + compiler.typesTask.getGuaranteedReturnTypeOfElement(element), + compiler); + } + + static TypeMask inferredTypeForElement(Element element, Compiler compiler) { + return fromInferredType( + compiler.typesTask.getGuaranteedTypeOfElement(element), + compiler); + } + + static TypeMask inferredTypeForSelector(Selector selector, Compiler compiler) { + return fromInferredType( + compiler.typesTask.getGuaranteedTypeOfSelector(selector), + compiler); + } + + static TypeMask inferredForNode(Element owner, ast.Node node, + Compiler compiler) { + return fromInferredType( + compiler.typesTask.getGuaranteedTypeOfNode(owner, node), + compiler); + } + + static TypeMask fromNativeBehavior(native.NativeBehavior nativeBehavior, + Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + if (nativeBehavior.typesReturned.isEmpty) return backend.dynamicType; + + TypeMask result = nativeBehavior.typesReturned + .map((type) => fromNativeType(type, compiler)) + .reduce((t1, t2) => t1.union(t2, compiler)); + assert(!(result.isEmpty && !result.isNullable)); + return result; + } + + // [type] is either an instance of [DartType] or special objects + // like [native.SpecialType.JsObject]. + static TypeMask fromNativeType(type, Compiler compiler) { + JavaScriptBackend backend = compiler.backend; + if (type == native.SpecialType.JsObject) { + return new TypeMask.nonNullExact(compiler.objectClass); + } else if (type.isVoid) { + return backend.nullType; + } else if (type.element == compiler.nullClass) { + return backend.nullType; + } else if (type.treatAsDynamic) { + return backend.dynamicType; + } else if (compiler.world.hasAnySubtype(type.element)) { + return new TypeMask.nonNullSubtype(type.element); + } else if (compiler.world.hasAnySubclass(type.element)) { + return new TypeMask.nonNullSubclass(type.element); + } else { + return new TypeMask.nonNullExact(type.element); + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart new file mode 100644 index 0000000..75fe4b6 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart @@ -0,0 +1,357 @@ +// Copyright (c) 2012, 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. + +part of ssa; + +class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase { + final Map workmap = new Map(); + final List worklist = new List(); + final Map pendingOptimizations = + new Map(); + + final Compiler compiler; + JavaScriptBackend get backend => compiler.backend; + String get name => 'type propagator'; + + SsaTypePropagator(this.compiler); + + TypeMask computeType(HInstruction instruction) { + return instruction.accept(this); + } + + // Re-compute and update the type of the instruction. Returns + // whether or not the type was changed. + bool updateType(HInstruction instruction) { + // Compute old and new types. + TypeMask oldType = instruction.instructionType; + TypeMask newType = computeType(instruction); + assert(newType != null); + // We unconditionally replace the propagated type with the new type. The + // computeType must make sure that we eventually reach a stable state. + instruction.instructionType = newType; + return oldType != newType; + } + + void visitGraph(HGraph graph) { + visitDominatorTree(graph); + processWorklist(); + } + + visitBasicBlock(HBasicBlock block) { + if (block.isLoopHeader()) { + block.forEachPhi((HPhi phi) { + // Set the initial type for the phi. We're not using the type + // the phi thinks it has because new optimizations may imply + // changing it. + // In theory we would need to mark + // the type of all other incoming edges as "unitialized" and take this + // into account when doing the propagation inside the phis. Just + // setting the propagated type is however easier. + phi.instructionType = phi.inputs[0].instructionType; + addToWorkList(phi); + }); + } else { + block.forEachPhi((HPhi phi) { + if (updateType(phi)) { + addDependentInstructionsToWorkList(phi); + } + }); + } + + HInstruction instruction = block.first; + while (instruction != null) { + if (updateType(instruction)) { + addDependentInstructionsToWorkList(instruction); + } + instruction = instruction.next; + } + } + + void processWorklist() { + do { + while (!worklist.isEmpty) { + int id = worklist.removeLast(); + HInstruction instruction = workmap[id]; + assert(instruction != null); + workmap.remove(id); + if (updateType(instruction)) { + addDependentInstructionsToWorkList(instruction); + } + } + // While processing the optimizable arithmetic instructions, we + // may discover better type information for dominated users of + // replaced operands, so we may need to take another stab at + // emptying the worklist afterwards. + processPendingOptimizations(); + } while (!worklist.isEmpty); + } + + + void addToWorkList(HInstruction instruction) { + final int id = instruction.id; + + if (!workmap.containsKey(id)) { + worklist.add(id); + workmap[id] = instruction; + } + } + + TypeMask visitBinaryArithmetic(HBinaryArithmetic instruction) { + HInstruction left = instruction.left; + HInstruction right = instruction.right; + if (left.isInteger(compiler) && right.isInteger(compiler)) { + return backend.intType; + } + if (left.isDouble(compiler)) return backend.doubleType; + return backend.numType; + } + + TypeMask checkPositiveInteger(HBinaryArithmetic instruction) { + HInstruction left = instruction.left; + HInstruction right = instruction.right; + if (left.isPositiveInteger(compiler) && right.isPositiveInteger(compiler)) { + return backend.positiveIntType; + } + return visitBinaryArithmetic(instruction); + } + + TypeMask visitMultiply(HMultiply instruction) { + return checkPositiveInteger(instruction); + } + + TypeMask visitAdd(HAdd instruction) { + return checkPositiveInteger(instruction); + } + + TypeMask visitNegate(HNegate instruction) { + HInstruction operand = instruction.operand; + // We have integer subclasses that represent ranges, so widen any int + // subclass to full integer. + if (operand.isInteger(compiler)) return backend.intType; + return instruction.operand.instructionType; + } + + TypeMask visitInstruction(HInstruction instruction) { + assert(instruction.instructionType != null); + return instruction.instructionType; + } + + TypeMask visitPhi(HPhi phi) { + TypeMask candidateType = backend.emptyType; + for (int i = 0, length = phi.inputs.length; i < length; i++) { + TypeMask inputType = phi.inputs[i].instructionType; + candidateType = candidateType.union(inputType, compiler); + } + return candidateType; + } + + TypeMask visitTypeConversion(HTypeConversion instruction) { + HInstruction input = instruction.checkedInput; + TypeMask inputType = input.instructionType; + TypeMask checkedType = instruction.checkedType; + if (instruction.isArgumentTypeCheck || instruction.isReceiverTypeCheck) { + // We must make sure a type conversion for receiver or argument check + // does not try to do an int check, because an int check is not enough. + // We only do an int check if the input is integer or null. + if (checkedType.containsOnlyNum(compiler) + && !checkedType.containsOnlyDouble(compiler) + && input.isIntegerOrNull(compiler)) { + instruction.checkedType = backend.intType; + } else if (checkedType.containsOnlyInt(compiler) + && !input.isIntegerOrNull(compiler)) { + instruction.checkedType = backend.numType; + } + } + + TypeMask outputType = checkedType.intersection(inputType, compiler); + if (outputType.isEmpty && !outputType.isNullable) { + // Intersection of double and integer conflicts (is empty), but JS numbers + // can be both int and double at the same time. For example, the input + // can be a literal double '8.0' that is marked as an integer (because 'is + // int' will return 'true'). What we really need to do is make the + // overlap between int and double values explicit in the TypeMask system. + if (inputType.containsOnlyInt(compiler) + && checkedType.containsOnlyDouble(compiler)) { + if (inputType.isNullable && checkedType.isNullable) { + outputType = backend.doubleType.nullable(); + } else { + outputType = backend.doubleType; + } + } + } + return outputType; + } + + TypeMask visitTypeKnown(HTypeKnown instruction) { + HInstruction input = instruction.checkedInput; + return instruction.knownType.intersection(input.instructionType, compiler); + } + + void convertInput(HInvokeDynamic instruction, + HInstruction input, + TypeMask type, + int kind) { + Selector selector = (kind == HTypeConversion.RECEIVER_TYPE_CHECK) + ? instruction.selector + : null; + HTypeConversion converted = new HTypeConversion( + null, kind, type, input, selector); + instruction.block.addBefore(instruction, converted); + input.replaceAllUsersDominatedBy(instruction, converted); + } + + bool isCheckEnoughForNsmOrAe(HInstruction instruction, + TypeMask type) { + // In some cases, we want the receiver to be an integer, + // but that does not mean we will get a NoSuchMethodError + // if it's not: the receiver could be a double. + if (type.containsOnlyInt(compiler)) { + // If the instruction's type is integer or null, the codegen + // will emit a null check, which is enough to know if it will + // hit a noSuchMethod. + return instruction.isIntegerOrNull(compiler); + } + return true; + } + + // Add a receiver type check when the call can only hit + // [noSuchMethod] if the receiver is not of a specific type. + // Return true if the receiver type check was added. + bool checkReceiver(HInvokeDynamic instruction) { + assert(instruction.isInterceptedCall); + HInstruction receiver = instruction.inputs[1]; + if (receiver.isNumber(compiler)) return false; + if (receiver.isNumberOrNull(compiler)) { + convertInput(instruction, + receiver, + receiver.instructionType.nonNullable(), + HTypeConversion.RECEIVER_TYPE_CHECK); + return true; + } else if (instruction.element == null) { + Iterable targets = + compiler.world.allFunctions.filter(instruction.selector); + if (targets.length == 1) { + Element target = targets.first; + ClassElement cls = target.getEnclosingClass(); + TypeMask type = new TypeMask.nonNullSubclass(cls.declaration); + // TODO(ngeoffray): We currently only optimize on primitive + // types. + if (!type.satisfies(backend.jsIndexableClass, compiler) + && !type.containsOnlyNum(compiler) + && !type.containsOnlyBool(compiler)) { + return false; + } + if (!isCheckEnoughForNsmOrAe(receiver, type)) return false; + instruction.element = target; + convertInput(instruction, + receiver, + type, + HTypeConversion.RECEIVER_TYPE_CHECK); + return true; + } + } + return false; + } + + // Add an argument type check if the argument is not of a type + // expected by the call. + // Return true if the argument type check was added. + bool checkArgument(HInvokeDynamic instruction) { + // We want the right error in checked mode. + if (compiler.enableTypeAssertions) return false; + HInstruction left = instruction.inputs[1]; + HInstruction right = instruction.inputs[2]; + + Selector selector = instruction.selector; + if (selector.isOperator() && left.isNumber(compiler)) { + if (right.isNumber(compiler)) return false; + TypeMask type = right.isIntegerOrNull(compiler) + ? right.instructionType.nonNullable() + : backend.numType; + // TODO(ngeoffray): Some number operations don't have a builtin + // variant and will do the check in their method anyway. We + // still add a check because it allows to GVN these operations, + // but we should find a better way. + convertInput(instruction, + right, + type, + HTypeConversion.ARGUMENT_TYPE_CHECK); + return true; + } + return false; + } + + void processPendingOptimizations() { + pendingOptimizations.forEach((instruction, action) => action()); + pendingOptimizations.clear(); + } + + void addDependentInstructionsToWorkList(HInstruction instruction) { + for (int i = 0, length = instruction.usedBy.length; i < length; i++) { + // The type propagator only propagates types forward. We + // thus only need to add the users of the [instruction] to the list. + addToWorkList(instruction.usedBy[i]); + } + } + + void addAllUsersBut(HInvokeDynamic invoke, HInstruction instruction) { + instruction.usedBy.forEach((HInstruction user) { + if (user != invoke) addToWorkList(user); + }); + } + + TypeMask visitInvokeDynamic(HInvokeDynamic instruction) { + if (instruction.isInterceptedCall) { + // We cannot do the following optimization now, because we have + // to wait for the type propagation to be stable. The receiver + // of [instruction] might move from number to dynamic. + pendingOptimizations.putIfAbsent(instruction, () => () { + Selector selector = instruction.selector; + if (selector.isOperator() && selector.name != '==') { + if (checkReceiver(instruction)) { + addAllUsersBut(instruction, instruction.inputs[1]); + } + if (!selector.isUnaryOperator() && checkArgument(instruction)) { + addAllUsersBut(instruction, instruction.inputs[2]); + } + } + }); + } + + HInstruction receiver = instruction.getDartReceiver(compiler); + TypeMask receiverType = receiver.instructionType; + Selector selector = new TypedSelector(receiverType, instruction.selector); + instruction.selector = selector; + + // Try to specialize the receiver after this call. + if (receiver.dominatedUsers(instruction).length != 1 + && !selector.isClosureCall()) { + TypeMask newType = compiler.world.allFunctions.receiverType(selector); + newType = newType.intersection(receiverType, compiler); + var next = instruction.next; + if (next is HTypeKnown && next.checkedInput == receiver) { + // We already have refined [receiver]. We still update the + // type of the [HTypeKnown] instruction because it may have + // been refined with a correct type at the time, but + // incorrect now. + if (next.instructionType != newType) { + next.knownType = next.instructionType = newType; + addDependentInstructionsToWorkList(next); + } + } else if (newType != receiverType) { + // Insert a refinement node after the call and update all + // users dominated by the call to use that node instead of + // [receiver]. + HTypeKnown converted = + new HTypeKnown.witnessed(newType, receiver, instruction); + instruction.block.addBefore(instruction.next, converted); + receiver.replaceAllUsersDominatedBy(converted.next, converted); + addDependentInstructionsToWorkList(converted); + } + } + + return instruction.specializer.computeTypeFromInputTypes( + instruction, compiler); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/validate.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/validate.dart new file mode 100644 index 0000000..e751b4d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/validate.dart @@ -0,0 +1,180 @@ +// Copyright (c) 2011, 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. + +part of ssa; + +class HValidator extends HInstructionVisitor { + bool isValid = true; + HGraph graph; + + void visitGraph(HGraph visitee) { + graph = visitee; + visitDominatorTree(visitee); + } + + void markInvalid(String reason) { + print(reason); + isValid = false; + } + + // Note that during construction of the Ssa graph the basic blocks are + // not required to be valid yet. + void visitBasicBlock(HBasicBlock block) { + currentBlock = block; + if (!isValid) return; // Don't need to continue if we are already invalid. + + // Test that the last instruction is a branching instruction and that the + // basic block contains the branch-target. + if (block.first == null || block.last == null) { + markInvalid("empty block"); + } + if (block.last is !HControlFlow) { + markInvalid("block ends with non-tail node."); + } + if (block.last is HIf && block.successors.length != 2) { + markInvalid("If node without two successors"); + } + if (block.last is HConditionalBranch && block.successors.length != 2) { + markInvalid("Conditional node without two successors"); + } + if (block.last is HGoto && block.successors.length != 1) { + markInvalid("Goto node with not exactly one successor"); + } + if (block.last is HJump && block.successors.length != 1) { + markInvalid("Break or continue node without one successor"); + } + if ((block.last is HReturn || block.last is HThrow) && + (block.successors.length != 1 || !block.successors[0].isExitBlock())) { + markInvalid("Return or throw node with > 1 successor " + "or not going to exit-block"); + } + if (block.last is HExit && !block.successors.isEmpty) { + markInvalid("Exit block with successor"); + } + + if (block.successors.isEmpty && !block.isExitBlock()) { + markInvalid("Non-exit block without successor"); + } + + // Check that successors ids are always higher than the current one. + // TODO(floitsch): this is, of course, not true for back-branches. + if (block.id == null) markInvalid("block without id"); + for (HBasicBlock successor in block.successors) { + if (!isValid) break; + if (successor.id == null) markInvalid("successor without id"); + if (successor.id <= block.id && !successor.isLoopHeader()) { + markInvalid("successor with lower id, but not a loop-header"); + } + } + + // Check that the entries in the dominated-list are sorted. + int lastId = 0; + for (HBasicBlock dominated in block.dominatedBlocks) { + if (!isValid) break; + if (!identical(dominated.dominator, block)) { + markInvalid("dominated block not pointing back"); + } + if (dominated.id == null || dominated.id <= lastId) { + markInvalid("dominated.id == null or dominated has <= id"); + } + lastId = dominated.id; + } + + if (!isValid) return; + block.forEachPhi(visitInstruction); + + // Check that the blocks of the parameters of a phi are dominating the + // corresponding predecessor block. Note that a block dominates + // itself. + block.forEachPhi((HPhi phi) { + assert(phi.inputs.length <= block.predecessors.length); + for (int i = 0; i < phi.inputs.length; i++) { + HInstruction input = phi.inputs[i]; + if (!input.block.dominates(block.predecessors[i])) { + markInvalid("Definition does not dominate use"); + } + } + }); + + // Check that the blocks of the inputs of an instruction dominate the + // instruction's block. + block.forEachInstruction((HInstruction instruction) { + for (HInstruction input in instruction.inputs) { + if (!input.block.dominates(block)) { + markInvalid("Definition does not dominate use"); + } + } + }); + + super.visitBasicBlock(block); + } + + /** Returns how often [instruction] is contained in [instructions]. */ + static int countInstruction(List instructions, + HInstruction instruction) { + int result = 0; + for (int i = 0; i < instructions.length; i++) { + if (identical(instructions[i], instruction)) result++; + } + return result; + } + + /** + * Returns true if the predicate returns true for every instruction in the + * list. The argument to [f] is an instruction with the count of how often + * it appeared in the list [instructions]. + */ + static bool everyInstruction(List instructions, Function f) { + var copy = new List.from(instructions); + // TODO(floitsch): there is currently no way to sort HInstructions before + // we have assigned an ID. The loop is therefore O(n^2) for now. + for (int i = 0; i < copy.length; i++) { + var current = copy[i]; + if (current == null) continue; + int count = 1; + for (int j = i + 1; j < copy.length; j++) { + if (identical(copy[j], current)) { + copy[j] = null; + count++; + } + } + if (!f(current, count)) return false; + } + return true; + } + + void visitInstruction(HInstruction instruction) { + // Verifies that we are in the use list of our inputs. + bool hasCorrectInputs() { + bool inBasicBlock = instruction.isInBasicBlock(); + return everyInstruction(instruction.inputs, (input, count) { + if (inBasicBlock) { + return input.isInBasicBlock() + && countInstruction(input.usedBy, instruction) == count; + } else { + return countInstruction(input.usedBy, instruction) == 0; + } + }); + } + + // Verifies that all our uses have us in their inputs. + bool hasCorrectUses() { + if (!instruction.isInBasicBlock()) return true; + return everyInstruction(instruction.usedBy, (use, count) { + return use.isInBasicBlock() + && countInstruction(use.inputs, instruction) == count; + }); + } + + if (!identical(instruction.block, currentBlock)) { + markInvalid("Instruction in wrong block"); + } + if (!hasCorrectInputs()) { + markInvalid("Incorrect inputs"); + } + if (!hasCorrectUses()) { + markInvalid("Incorrect uses"); + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart new file mode 100644 index 0000000..f8dcf42 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart @@ -0,0 +1,1053 @@ +// Copyright (c) 2012, 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. + +part of ssa; + + +class ValueRangeInfo { + final ConstantSystem constantSystem; + + IntValue intZero; + IntValue intOne; + + ValueRangeInfo(this.constantSystem) { + intZero = newIntValue(0); + intOne = newIntValue(1); + } + + Value newIntValue(int value) { + return new IntValue(value, this); + } + + Value newInstructionValue(HInstruction instruction) { + return new InstructionValue(instruction, this); + } + + Value newPositiveValue(HInstruction instruction) { + return new PositiveValue(instruction, this); + } + + Value newAddValue(Value left, Value right) { + return new AddValue(left, right, this); + } + + Value newSubtractValue(Value left, Value right) { + return new SubtractValue(left, right, this); + } + + Value newNegateValue(Value value) { + return new NegateValue(value, this); + } + + Range newUnboundRange() { + return new Range.unbound(this); + } + + Range newNormalizedRange(Value low, Value up) { + return new Range.normalize(low, up, this); + } + + Range newMarkerRange() { + return new Range(new MarkerValue(false, this), + new MarkerValue(true, this), + this); + } +} + +/** + * A [Value] represents both symbolic values like the value of a + * parameter, or the length of an array, and concrete values, like + * constants. + */ +abstract class Value { + final ValueRangeInfo info; + const Value(this.info); + + Value operator +(Value other) => const UnknownValue(); + Value operator -(Value other) => const UnknownValue(); + Value operator -() => const UnknownValue(); + Value operator &(Value other) => const UnknownValue(); + + Value min(Value other) { + if (this == other) return this; + if (other == const MinIntValue()) return other; + if (other == const MaxIntValue()) return this; + Value value = this - other; + if (value.isPositive) return other; + if (value.isNegative) return this; + return const UnknownValue(); + } + + Value max(Value other) { + if (this == other) return this; + if (other == const MinIntValue()) return this; + if (other == const MaxIntValue()) return other; + Value value = this - other; + if (value.isPositive) return this; + if (value.isNegative) return other; + return const UnknownValue(); + } + + bool get isNegative => false; + bool get isPositive => false; + bool get isZero => false; +} + +/** + * The [MarkerValue] class is used to recognize ranges of loop + * updates. + */ +class MarkerValue extends Value { + /// If [positive] is true (respectively false), the marker goes + /// to [MaxIntValue] (respectively [MinIntValue]) when being added + /// to a positive (respectively negative) value. + final bool positive; + + const MarkerValue(this.positive, info) : super(info); + + Value operator +(Value other) { + if (other.isPositive && positive) return const MaxIntValue(); + if (other.isNegative && !positive) return const MinIntValue(); + if (other is IntValue) return this; + return const UnknownValue(); + } + + Value operator -(Value other) { + if (other.isPositive && !positive) return const MinIntValue(); + if (other.isNegative && positive) return const MaxIntValue(); + if (other is IntValue) return this; + return const UnknownValue(); + } +} + +/** + * An [IntValue] contains a constant integer value. + */ +class IntValue extends Value { + final int value; + + const IntValue(this.value, info) : super(info); + + Value operator +(other) { + if (other.isZero) return this; + if (other is !IntValue) return other + this; + ConstantSystem constantSystem = info.constantSystem; + var constant = constantSystem.add.fold( + constantSystem.createInt(value), constantSystem.createInt(other.value)); + if (!constant.isInt) return const UnknownValue(); + return info.newIntValue(constant.value); + } + + Value operator -(other) { + if (other.isZero) return this; + if (other is !IntValue) return -other + this; + ConstantSystem constantSystem = info.constantSystem; + var constant = constantSystem.subtract.fold( + constantSystem.createInt(value), constantSystem.createInt(other.value)); + if (!constant.isInt) return const UnknownValue(); + return info.newIntValue(constant.value); + } + + Value operator -() { + if (isZero) return this; + ConstantSystem constantSystem = info.constantSystem; + var constant = constantSystem.negate.fold( + constantSystem.createInt(value)); + if (!constant.isInt) return const UnknownValue(); + return info.newIntValue(constant.value); + } + + Value operator &(other) { + if (other is !IntValue) return const UnknownValue(); + ConstantSystem constantSystem = info.constantSystem; + var constant = constantSystem.bitAnd.fold( + constantSystem.createInt(value), constantSystem.createInt(other.value)); + return info.newIntValue(constant.value); + } + + Value min(other) { + if (other is !IntValue) return other.min(this); + return this.value < other.value ? this : other; + } + + Value max(other) { + if (other is !IntValue) return other.max(this); + return this.value < other.value ? other : this; + } + + bool operator ==(other) { + if (other is !IntValue) return false; + return this.value == other.value; + } + + int get hashCode => throw new UnsupportedError('IntValue.hashCode'); + + String toString() => 'IntValue $value'; + bool get isNegative => value < 0; + bool get isPositive => value >= 0; + bool get isZero => value == 0; +} + +/** + * The [MaxIntValue] represents the maximum value an integer can have, + * which is currently +infinity. + */ +class MaxIntValue extends Value { + const MaxIntValue() : super(null); + Value operator +(Value other) => this; + Value operator -(Value other) => this; + Value operator -() => const MinIntValue(); + Value min(Value other) => other; + Value max(Value other) => this; + String toString() => 'Max'; + bool get isNegative => false; + bool get isPositive => true; +} + +/** + * The [MinIntValue] represents the minimum value an integer can have, + * which is currently -infinity. + */ +class MinIntValue extends Value { + const MinIntValue() : super(null); + Value operator +(Value other) => this; + Value operator -(Value other) => this; + Value operator -() => const MaxIntValue(); + Value min(Value other) => this; + Value max(Value other) => other; + String toString() => 'Min'; + bool get isNegative => true; + bool get isPositive => false; +} + +/** + * The [UnknownValue] is the sentinel in our analysis to mark an + * operation that could not be done because of too much complexity. + */ +class UnknownValue extends Value { + const UnknownValue() : super(null); + Value operator +(Value other) => const UnknownValue(); + Value operator -(Value other) => const UnknownValue(); + Value operator -() => const UnknownValue(); + Value min(Value other) => const UnknownValue(); + Value max(Value other) => const UnknownValue(); + bool get isNegative => false; + bool get isPositive => false; + String toString() => 'Unknown'; +} + +/** + * A symbolic value representing an [HInstruction]. + */ +class InstructionValue extends Value { + final HInstruction instruction; + InstructionValue(this.instruction, info) : super(info); + + bool operator ==(other) { + if (other is !InstructionValue) return false; + return this.instruction == other.instruction; + } + + int get hashCode => throw new UnsupportedError('InstructionValue.hashCode'); + + Value operator +(Value other) { + if (other.isZero) return this; + if (other is IntValue) { + if (other.isNegative) { + return info.newSubtractValue(this, -other); + } + return info.newAddValue(this, other); + } + if (other is InstructionValue) { + return info.newAddValue(this, other); + } + return other + this; + } + + Value operator -(Value other) { + if (other.isZero) return this; + if (this == other) return info.intZero; + if (other is IntValue) { + if (other.isNegative) { + return info.newAddValue(this, -other); + } + return info.newSubtractValue(this, other); + } + if (other is InstructionValue) { + return info.newSubtractValue(this, other); + } + return -other + this; + } + + Value operator -() { + return info.newNegateValue(this); + } + + bool get isNegative => false; + bool get isPositive => false; + + String toString() => 'Instruction: $instruction'; +} + +/** + * Special value for instructions whose type is a positive integer. + */ +class PositiveValue extends InstructionValue { + PositiveValue(HInstruction instruction, info) : super(instruction, info); + bool get isPositive => true; +} + +/** + * Represents a binary operation on two [Value], where the operation + * did not yield a canonical value. + */ +class BinaryOperationValue extends Value { + final Value left; + final Value right; + BinaryOperationValue(this.left, this.right, info) : super(info); +} + +class AddValue extends BinaryOperationValue { + AddValue(left, right, info) : super(left, right, info); + + bool operator ==(other) { + if (other is !AddValue) return false; + return (left == other.left && right == other.right) + || (left == other.right && right == other.left); + } + + int get hashCode => throw new UnsupportedError('AddValue.hashCode'); + + Value operator -() => -left - right; + + Value operator +(Value other) { + if (other.isZero) return this; + Value value = left + other; + if (value != const UnknownValue() && value is! BinaryOperationValue) { + return value + right; + } + // If the result is not simple enough, we try the same approach + // with [right]. + value = right + other; + if (value != const UnknownValue() && value is! BinaryOperationValue) { + return left + value; + } + return const UnknownValue(); + } + + Value operator -(Value other) { + if (other.isZero) return this; + Value value = left - other; + if (value != const UnknownValue() && value is! BinaryOperationValue) { + return value + right; + } + // If the result is not simple enough, we try the same approach + // with [right]. + value = right - other; + if (value != const UnknownValue() && value is! BinaryOperationValue) { + return left + value; + } + return const UnknownValue(); + } + + bool get isNegative => left.isNegative && right.isNegative; + bool get isPositive => left.isPositive && right.isPositive; + String toString() => '$left + $right'; +} + +class SubtractValue extends BinaryOperationValue { + SubtractValue(left, right, info) : super(left, right, info); + + bool operator ==(other) { + if (other is !SubtractValue) return false; + return left == other.left && right == other.right; + } + + int get hashCode => throw new UnsupportedError('SubtractValue.hashCode'); + + Value operator -() => right - left; + + Value operator +(Value other) { + if (other.isZero) return this; + Value value = left + other; + if (value != const UnknownValue() && value is! BinaryOperationValue) { + return value - right; + } + // If the result is not simple enough, we try the same approach + // with [right]. + value = other - right; + if (value != const UnknownValue() && value is! BinaryOperationValue) { + return left + value; + } + return const UnknownValue(); + } + + Value operator -(Value other) { + if (other.isZero) return this; + Value value = left - other; + if (value != const UnknownValue() && value is! BinaryOperationValue) { + return value - right; + } + // If the result is not simple enough, we try the same approach + // with [right]. + value = right + other; + if (value != const UnknownValue() && value is! BinaryOperationValue) { + return left - value; + } + return const UnknownValue(); + } + + bool get isNegative => left.isNegative && right.isPositive; + bool get isPositive => left.isPositive && right.isNegative; + String toString() => '$left - $right'; +} + +class NegateValue extends Value { + final Value value; + NegateValue(this.value, info) : super(info); + + bool operator ==(other) { + if (other is !NegateValue) return false; + return value == other.value; + } + + int get hashCode => throw new UnsupportedError('Negate.hashCode'); + + Value operator +(other) { + if (other.isZero) return this; + if (other == value) return info.intZero; + if (other is NegateValue) return this - other.value; + if (other is IntValue) { + if (other.isNegative) { + return info.newSubtractValue(this, -other); + } + return info.newSubtractValue(other, value); + } + if (other is InstructionValue) { + return info.newSubtractValue(other, value); + } + return other - value; + } + + Value operator &(Value other) => const UnknownValue(); + + Value operator -(other) { + if (other.isZero) return this; + if (other is IntValue) { + if (other.isNegative) { + return info.newSubtractValue(-other, value); + } + return info.newSubtractValue(this, other); + } + if (other is InstructionValue) { + return info.newSubtractValue(this, other); + } + if (other is NegateValue) return this + other.value; + return -other - value; + } + + Value operator -() => value; + + bool get isNegative => value.isPositive; + bool get isPositive => value.isNegative; + String toString() => '-$value'; +} + +/** + * A [Range] represents the possible integer values an instruction + * can have, from its [lower] bound to its [upper] bound, both + * included. + */ +class Range { + final Value lower; + final Value upper; + final ValueRangeInfo info; + Range(this.lower, this.upper, this.info) { + assert(lower != const UnknownValue()); + assert(upper != const UnknownValue()); + } + + Range.unbound(info) : this(const MinIntValue(), const MaxIntValue(), info); + + /** + * Checks if the given values are unknown, and creates a + * range that does not have any unknown values. + */ + Range.normalize(Value low, Value up, info) : this( + low == const UnknownValue() ? const MinIntValue() : low, + up == const UnknownValue() ? const MaxIntValue() : up, + info); + + Range union(Range other) { + return info.newNormalizedRange( + lower.min(other.lower), upper.max(other.upper)); + } + + Range intersection(Range other) { + Value low = lower.max(other.lower); + Value up = upper.min(other.upper); + // If we could not compute max or min, pick a value in the two + // ranges, with priority to [IntValue]s because they are simpler. + if (low == const UnknownValue()) { + if (lower is IntValue) low = lower; + else if (other.lower is IntValue) low = other.lower; + else low = lower; + } + if (up == const UnknownValue()) { + if (upper is IntValue) up = upper; + else if (other.upper is IntValue) up = other.upper; + else up = upper; + } + return info.newNormalizedRange(low, up); + } + + Range operator +(Range other) { + return info.newNormalizedRange(lower + other.lower, upper + other.upper); + } + + Range operator -(Range other) { + return info.newNormalizedRange(lower - other.upper, upper - other.lower); + } + + Range operator -() { + return info.newNormalizedRange(-upper, -lower); + } + + Range operator &(Range other) { + if (isSingleValue + && other.isSingleValue + && lower is IntValue + && other.lower is IntValue) { + return info.newNormalizedRange(lower & other.lower, upper & other.upper); + } + if (isPositive && other.isPositive) { + Value up = upper.min(other.upper); + if (up == const UnknownValue()) { + // If we could not find a trivial bound, just try to use the + // one that is an int. + up = upper is IntValue ? upper : other.upper; + // Make sure we get the same upper bound, whether it's a & b + // or b & a. + if (up is! IntValue && upper != other.upper) up = const MaxIntValue(); + } + return info.newNormalizedRange(info.intZero, up); + } else if (isPositive) { + return info.newNormalizedRange(info.intZero, upper); + } else if (other.isPositive) { + return info.newNormalizedRange(info.intZero, other.upper); + } else { + return info.newUnboundRange(); + } + } + + bool operator ==(other) { + if (other is! Range) return false; + return other.lower == lower && other.upper == upper; + } + + int get hashCode => throw new UnsupportedError('Range.hashCode'); + + bool operator <(Range other) { + return upper != other.lower && upper.min(other.lower) == upper; + } + + bool operator >(Range other) { + return lower != other.upper && lower.max(other.upper) == lower; + } + + bool operator <=(Range other) { + return upper.min(other.lower) == upper; + } + + bool operator >=(Range other) { + return lower.max(other.upper) == lower; + } + + bool get isNegative => upper.isNegative; + bool get isPositive => lower.isPositive; + bool get isSingleValue => lower == upper; + + String toString() => '[$lower, $upper]'; +} + +/** + * Visits the graph in dominator order, and computes value ranges for + * integer instructions. While visiting the graph, this phase also + * removes unnecessary bounds checks, and comparisons that are proven + * to be true or false. + */ +class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { + String get name => 'SSA value range builder'; + + /** + * List of [HRangeConversion] instructions created by the phase. We + * save them here in order to remove them once the phase is done. + */ + final List conversions = []; + + /** + * Value ranges for integer instructions. This map gets populated by + * the dominator tree visit. + */ + final Map ranges = new Map(); + + final Compiler compiler; + final ConstantSystem constantSystem; + final ValueRangeInfo info; + + CodegenWorkItem work; + HGraph graph; + + SsaValueRangeAnalyzer(this.compiler, constantSystem, this.work) + : info = new ValueRangeInfo(constantSystem), + this.constantSystem = constantSystem; + + void visitGraph(HGraph graph) { + this.graph = graph; + visitDominatorTree(graph); + // We remove the range conversions after visiting the graph so + // that the graph does not get polluted with these instructions + // only necessary for this phase. + removeRangeConversion(); + JavaScriptBackend backend = compiler.backend; + // TODO(herhut): Find a cleaner way to pass around ranges. + backend.optimizer.ranges = ranges; + } + + void removeRangeConversion() { + conversions.forEach((HRangeConversion instruction) { + instruction.block.rewrite(instruction, instruction.inputs[0]);; + instruction.block.remove(instruction); + }); + } + + void visitBasicBlock(HBasicBlock block) { + + void visit(HInstruction instruction) { + Range range = instruction.accept(this); + if (instruction.isInteger(compiler)) { + assert(range != null); + ranges[instruction] = range; + } + } + + block.forEachPhi(visit); + block.forEachInstruction(visit); + } + + Range visitInstruction(HInstruction instruction) { + if (instruction.isPositiveInteger(compiler)) { + return info.newNormalizedRange( + info.intZero, info.newPositiveValue(instruction)); + } else if (instruction.isInteger(compiler)) { + InstructionValue value = info.newInstructionValue(instruction); + return info.newNormalizedRange(value, value); + } else { + return info.newUnboundRange(); + } + } + + Range visitPhi(HPhi phi) { + if (!phi.isInteger(compiler)) return info.newUnboundRange(); + // Some phases may replace instructions that change the inputs of + // this phi. Only the [SsaTypesPropagation] phase will update the + // phi type. Play it safe by assuming the [SsaTypesPropagation] + // phase is not necessarily run before the [ValueRangeAnalyzer]. + if (phi.inputs.any((i) => !i.isInteger(compiler))) { + return info.newUnboundRange(); + } + if (phi.block.isLoopHeader()) { + Range range = new LoopUpdateRecognizer(compiler, ranges, info).run(phi); + if (range == null) return info.newUnboundRange(); + return range; + } + + Range range = ranges[phi.inputs[0]]; + for (int i = 1; i < phi.inputs.length; i++) { + range = range.union(ranges[phi.inputs[i]]); + } + return range; + } + + Range visitConstant(HConstant constant) { + if (!constant.isInteger(compiler)) return info.newUnboundRange(); + NumConstant constantNum = constant.constant; + if (constantNum.isMinusZero) constantNum = new IntConstant(0); + Value value = info.newIntValue(constantNum.value); + return info.newNormalizedRange(value, value); + } + + Range visitFieldGet(HFieldGet fieldGet) { + if (!fieldGet.isInteger(compiler)) return info.newUnboundRange(); + if (!fieldGet.receiver.isIndexablePrimitive(compiler)) { + return visitInstruction(fieldGet); + } + JavaScriptBackend backend = compiler.backend; + assert(fieldGet.element == backend.jsIndexableLength); + PositiveValue value = info.newPositiveValue(fieldGet); + // We know this range is above zero. To simplify the analysis, we + // put the zero value as the lower bound of this range. This + // allows to easily remove the second bound check in the following + // expression: a[1] + a[0]. + return info.newNormalizedRange(info.intZero, value); + } + + Range visitBoundsCheck(HBoundsCheck check) { + // Save the next instruction, in case the check gets removed. + HInstruction next = check.next; + Range indexRange = ranges[check.index]; + Range lengthRange = ranges[check.length]; + if (indexRange == null) { + indexRange = info.newUnboundRange(); + assert(!check.index.isInteger(compiler)); + } + assert(check.length.isInteger(compiler)); + + // Check if the index is strictly below the upper bound of the length + // range. + Value maxIndex = lengthRange.upper - info.intOne; + bool belowLength = maxIndex != const MaxIntValue() + && indexRange.upper.min(maxIndex) == indexRange.upper; + + // Check if the index is strictly below the lower bound of the length + // range. + belowLength = belowLength + || (indexRange.upper != lengthRange.lower + && indexRange.upper.min(lengthRange.lower) == indexRange.upper); + if (indexRange.isPositive && belowLength) { + check.block.rewrite(check, check.index); + check.block.remove(check); + } else if (indexRange.isNegative || lengthRange < indexRange) { + check.staticChecks = HBoundsCheck.ALWAYS_FALSE; + // The check is always false, and whatever instruction it + // dominates is dead code. + return indexRange; + } else if (indexRange.isPositive) { + check.staticChecks = HBoundsCheck.ALWAYS_ABOVE_ZERO; + } else if (belowLength) { + check.staticChecks = HBoundsCheck.ALWAYS_BELOW_LENGTH; + } + + if (indexRange.isPositive) { + // If the test passes, we know the lower bound of the length is + // greater or equal than the lower bound of the index. + Value low = lengthRange.lower.max(indexRange.lower); + if (low != const UnknownValue()) { + HInstruction instruction = + createRangeConversion(next, check.length); + ranges[instruction] = info.newNormalizedRange(low, lengthRange.upper); + } + } + + // Update the range of the index if using the maximum index + // narrows it. Use that new range for this instruction as well. + Range newIndexRange = indexRange.intersection( + info.newNormalizedRange(info.intZero, maxIndex)); + if (indexRange == newIndexRange) return indexRange; + // Explicitly attach the range information to the index instruction, + // which may be used by other instructions. Returning the new range will + // attach it to this instruction. + HInstruction instruction = createRangeConversion(next, check.index); + ranges[instruction] = newIndexRange; + return newIndexRange; + } + + Range visitRelational(HRelational relational) { + HInstruction right = relational.right; + HInstruction left = relational.left; + if (!left.isInteger(compiler)) return info.newUnboundRange(); + if (!right.isInteger(compiler)) return info.newUnboundRange(); + BinaryOperation operation = relational.operation(constantSystem); + Range rightRange = ranges[relational.right]; + Range leftRange = ranges[relational.left]; + + if (relational is HIdentity) { + handleEqualityCheck(relational); + } else if (operation.apply(leftRange, rightRange)) { + relational.block.rewrite( + relational, graph.addConstantBool(true, compiler)); + relational.block.remove(relational); + } else if (reverseOperation(operation).apply(leftRange, rightRange)) { + relational.block.rewrite( + relational, graph.addConstantBool(false, compiler)); + relational.block.remove(relational); + } + return info.newUnboundRange(); + } + + void handleEqualityCheck(HRelational node) { + Range right = ranges[node.right]; + Range left = ranges[node.left]; + if (left.isSingleValue && right.isSingleValue && left == right) { + node.block.rewrite( + node, graph.addConstantBool(true, compiler)); + node.block.remove(node); + } + } + + Range handleInvokeModulo(HInvokeDynamicMethod invoke) { + HInstruction left = invoke.inputs[1]; + HInstruction right = invoke.inputs[2]; + Range divisor = ranges[right]; + if (divisor != null) { + // For Integer values we can be precise in the upper bound, + // so special case those. + if (left.isInteger(compiler) && right.isInteger(compiler)) { + if (divisor.isPositive) { + return info.newNormalizedRange(info.intZero, divisor.upper - + info.intOne); + } else if (divisor.isNegative) { + return info.newNormalizedRange(info.intZero, info.newNegateValue( + divisor.lower) - info.intOne); + } + } else if (left.isNumber(compiler) && right.isNumber(compiler)) { + if (divisor.isPositive) { + return info.newNormalizedRange(info.intZero, divisor.upper); + } else if (divisor.isNegative) { + return info.newNormalizedRange(info.intZero, info.newNegateValue( + divisor.lower)); + } + } + } + return info.newUnboundRange(); + } + + Range visitInvokeDynamicMethod(HInvokeDynamicMethod invoke) { + if ((invoke.inputs.length == 3) && (invoke.selector.name == "%")) + return handleInvokeModulo(invoke); + return super.visitInvokeDynamicMethod(invoke); + } + + Range handleBinaryOperation(HBinaryArithmetic instruction) { + if (!instruction.isInteger(compiler)) return info.newUnboundRange(); + return instruction.operation(constantSystem).apply( + ranges[instruction.left], ranges[instruction.right]); + } + + Range visitAdd(HAdd add) { + return handleBinaryOperation(add); + } + + Range visitSubtract(HSubtract sub) { + return handleBinaryOperation(sub); + } + + Range visitBitAnd(HBitAnd node) { + if (!node.isInteger(compiler)) return info.newUnboundRange(); + HInstruction right = node.right; + HInstruction left = node.left; + if (left.isInteger(compiler) && right.isInteger(compiler)) { + return ranges[left] & ranges[right]; + } + + Range tryComputeRange(HInstruction instruction) { + Range range = ranges[instruction]; + if (range.isPositive) { + return info.newNormalizedRange(info.intZero, range.upper); + } else if (range.isNegative) { + return info.newNormalizedRange(range.lower, info.intZero); + } + return info.newUnboundRange(); + } + + if (left.isInteger(compiler)) { + return tryComputeRange(left); + } else if (right.isInteger(compiler)) { + return tryComputeRange(right); + } + return info.newUnboundRange(); + } + + Range visitCheck(HCheck instruction) { + if (ranges[instruction.checkedInput] == null) { + return visitInstruction(instruction); + } + return ranges[instruction.checkedInput]; + } + + HInstruction createRangeConversion(HInstruction cursor, + HInstruction instruction) { + JavaScriptBackend backend = compiler.backend; + HRangeConversion newInstruction = + new HRangeConversion(instruction, backend.intType); + conversions.add(newInstruction); + cursor.block.addBefore(cursor, newInstruction); + // Update the users of the instruction dominated by [cursor] to + // use the new instruction, that has an narrower range. + instruction.replaceAllUsersDominatedBy(cursor, newInstruction); + return newInstruction; + } + + static BinaryOperation reverseOperation(BinaryOperation operation) { + if (operation == const LessOperation()) { + return const GreaterEqualOperation(); + } else if (operation == const LessEqualOperation()) { + return const GreaterOperation(); + } else if (operation == const GreaterOperation()) { + return const LessEqualOperation(); + } else if (operation == const GreaterEqualOperation()) { + return const LessOperation(); + } else { + return null; + } + } + + Range computeConstrainedRange(BinaryOperation operation, + Range leftRange, + Range rightRange) { + Range range; + if (operation == const LessOperation()) { + range = info.newNormalizedRange( + const MinIntValue(), rightRange.upper - info.intOne); + } else if (operation == const LessEqualOperation()) { + range = info.newNormalizedRange(const MinIntValue(), rightRange.upper); + } else if (operation == const GreaterOperation()) { + range = info.newNormalizedRange( + rightRange.lower + info.intOne, const MaxIntValue()); + } else if (operation == const GreaterEqualOperation()) { + range = info.newNormalizedRange(rightRange.lower, const MaxIntValue()); + } else { + range = info.newUnboundRange(); + } + return range.intersection(leftRange); + } + + Range visitConditionalBranch(HConditionalBranch branch) { + var condition = branch.condition; + // TODO(ngeoffray): Handle complex conditions. + if (condition is !HRelational) return info.newUnboundRange(); + if (condition is HIdentity) return info.newUnboundRange(); + HInstruction right = condition.right; + HInstruction left = condition.left; + if (!left.isInteger(compiler)) return info.newUnboundRange(); + if (!right.isInteger(compiler)) return info.newUnboundRange(); + + Range rightRange = ranges[right]; + Range leftRange = ranges[left]; + Operation operation = condition.operation(constantSystem); + Operation reverse = reverseOperation(operation); + // Only update the true branch if this block is the only + // predecessor. + if (branch.trueBranch.predecessors.length == 1) { + assert(branch.trueBranch.predecessors[0] == branch.block); + // Update the true branch to use narrower ranges for [left] and + // [right]. + Range range = computeConstrainedRange(operation, leftRange, rightRange); + if (leftRange != range) { + HInstruction instruction = + createRangeConversion(branch.trueBranch.first, left); + ranges[instruction] = range; + } + + range = computeConstrainedRange(reverse, rightRange, leftRange); + if (rightRange != range) { + HInstruction instruction = + createRangeConversion(branch.trueBranch.first, right); + ranges[instruction] = range; + } + } + + // Only update the false branch if this block is the only + // predecessor. + if (branch.falseBranch.predecessors.length == 1) { + assert(branch.falseBranch.predecessors[0] == branch.block); + // Update the false branch to use narrower ranges for [left] and + // [right]. + Range range = computeConstrainedRange(reverse, leftRange, rightRange); + if (leftRange != range) { + HInstruction instruction = + createRangeConversion(branch.falseBranch.first, left); + ranges[instruction] = range; + } + + range = computeConstrainedRange(operation, rightRange, leftRange); + if (rightRange != range) { + HInstruction instruction = + createRangeConversion(branch.falseBranch.first, right); + ranges[instruction] = range; + } + } + + return info.newUnboundRange(); + } + + Range visitRangeConversion(HRangeConversion conversion) { + return ranges[conversion]; + } +} + +/** + * Tries to find a range for the update instruction of a loop phi. + */ +class LoopUpdateRecognizer extends HBaseVisitor { + final Compiler compiler; + final Map ranges; + final ValueRangeInfo info; + LoopUpdateRecognizer(this.compiler, this.ranges, this.info); + + Range run(HPhi loopPhi) { + // Create a marker range for the loop phi, so that if the update + // uses the loop phi, it has a range to use. + ranges[loopPhi] = info.newMarkerRange(); + Range updateRange = visit(loopPhi.inputs[1]); + ranges[loopPhi] = null; + if (updateRange == null) return null; + Range startRange = ranges[loopPhi.inputs[0]]; + // If the lower (respectively upper) value is the marker, we know + // the loop does not change it, so we can just use the + // [startRange]'s lower (upper) value. Otherwise the lower (upper) value + // is the minimum of the [startRange]'s lower (upper) and the + // [updateRange]'s lower (upper). + Value low = updateRange.lower is MarkerValue + ? startRange.lower + : updateRange.lower.min(startRange.lower); + Value up = updateRange.upper is MarkerValue + ? startRange.upper + : updateRange.upper.max(startRange.upper); + return info.newNormalizedRange(low, up); + } + + Range visit(HInstruction instruction) { + if (!instruction.isInteger(compiler)) return null; + if (ranges[instruction] != null) return ranges[instruction]; + return instruction.accept(this); + } + + Range visitPhi(HPhi phi) { + // If the update of a loop phi involves another loop phi, we give + // up. + if (phi.block.isLoopHeader()) return null; + Range phiRange; + for (HInstruction input in phi.inputs) { + Range inputRange = visit(input); + if (inputRange == null) return null; + if (phiRange == null) { + phiRange = inputRange; + } else { + phiRange = phiRange.union(inputRange); + } + } + return phiRange; + } + + Range visitCheck(HCheck instruction) { + return visit(instruction.checkedInput); + } + + Range visitAdd(HAdd operation) { + return handleBinaryOperation(operation); + } + + Range visitSubtract(HSubtract operation) { + return handleBinaryOperation(operation); + } + + Range handleBinaryOperation(HBinaryArithmetic instruction) { + Range leftRange = visit(instruction.left); + Range rightRange = visit(instruction.right); + if (leftRange == null || rightRange == null) return null; + BinaryOperation operation = instruction.operation(info.constantSystem); + return operation.apply(leftRange, rightRange); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/value_set.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/value_set.dart new file mode 100644 index 0000000..07681b6 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/value_set.dart @@ -0,0 +1,157 @@ +// Copyright (c) 2012, 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. + +part of ssa; + +class ValueSet { + int size = 0; + List table; + ValueSetNode collisions; + ValueSet() : table = new List(8); + + bool get isEmpty => size == 0; + int get length => size; + + void add(HInstruction instruction) { + assert(lookup(instruction) == null); + int hashCode = instruction.gvnHashCode(); + int capacity = table.length; + // Resize when half of the hash table is in use. + if (size >= capacity >> 1) { + capacity = capacity << 1; + resize(capacity); + } + // Try to insert in the hash table first. + int index = hashCode % capacity; + if (table[index] == null) { + table[index] = instruction; + } else { + collisions = new ValueSetNode(instruction, hashCode, collisions); + } + size++; + } + + HInstruction lookup(HInstruction instruction) { + int hashCode = instruction.gvnHashCode(); + int index = hashCode % table.length; + // Look in the hash table. + HInstruction probe = table[index]; + if (probe != null && probe.gvnEquals(instruction)) return probe; + // Look in the collisions list. + for (ValueSetNode node = collisions; node != null; node = node.next) { + if (node.hashCode == hashCode) { + HInstruction cached = node.value; + if (cached.gvnEquals(instruction)) return cached; + } + } + return null; + } + + void kill(int flags) { + if (flags == 0) return; + int depends = SideEffects.computeDependsOnFlags(flags); + // Kill in the hash table. + for (int index = 0, length = table.length; index < length; index++) { + HInstruction instruction = table[index]; + if (instruction != null && instruction.sideEffects.dependsOn(depends)) { + table[index] = null; + size--; + } + } + // Kill in the collisions list. + ValueSetNode previous = null; + ValueSetNode current = collisions; + while (current != null) { + ValueSetNode next = current.next; + HInstruction cached = current.value; + if (cached.sideEffects.dependsOn(depends)) { + if (previous == null) { + collisions = next; + } else { + previous.next = next; + } + size--; + } else { + previous = current; + } + current = next; + } + } + + ValueSet copy() { + return copyTo(new ValueSet(), table, collisions); + } + + List toList() { + return copyTo([], table, collisions); + } + + // Copy the instructions in value set defined by [table] and + // [collisions] into [other] and returns [other]. The copy is done + // by iterating through the hash table and the collisions list and + // calling [:other.add:]. + static copyTo(var other, List table, ValueSetNode collisions) { + // Copy elements from the hash table. + for (int index = 0, length = table.length; index < length; index++) { + HInstruction instruction = table[index]; + if (instruction != null) other.add(instruction); + } + // Copy elements from the collision list. + ValueSetNode current = collisions; + while (current != null) { + // TODO(kasperl): Maybe find a way of reusing the hash code + // rather than recomputing it every time. + other.add(current.value); + current = current.next; + } + return other; + } + + ValueSet intersection(ValueSet other) { + if (size > other.size) return other.intersection(this); + ValueSet result = new ValueSet(); + // Look in the hash table. + for (int index = 0, length = table.length; index < length; index++) { + HInstruction instruction = table[index]; + if (instruction != null && other.lookup(instruction) != null) { + result.add(instruction); + } + } + // Look in the collision list. + ValueSetNode current = collisions; + while (current != null) { + HInstruction value = current.value; + if (other.lookup(value) != null) { + result.add(value); + } + current = current.next; + } + return result; + } + + void resize(int capacity) { + var oldSize = size; + var oldTable = table; + var oldCollisions = collisions; + // Reset the table with a bigger capacity. + assert(capacity > table.length); + size = 0; + table = new List(capacity); + collisions = null; + // Add the old instructions to the new table. + copyTo(this, oldTable, oldCollisions); + // Make sure we preserved all elements and that no resizing + // happened as part of this resizing. + assert(size == oldSize); + assert(table.length == capacity); + } +} + +class ValueSetNode { + final HInstruction value; + final int hash; + int get hashCode => hash; + ValueSetNode next; + ValueSetNode(this.value, this.hash, this.next); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/variable_allocator.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/variable_allocator.dart new file mode 100644 index 0000000..dc1ac26 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/ssa/variable_allocator.dart @@ -0,0 +1,702 @@ +// Copyright (c) 2012, 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. + +part of ssa; + +/** + * The [LiveRange] class covers a range where an instruction is live. + */ +class LiveRange { + final int start; + // [end] is not final because it can be updated due to loops. + int end; + LiveRange(this.start, this.end) { + assert(start <= end); + } + + String toString() => '[$start $end['; +} + +/** + * The [LiveInterval] class contains the list of ranges where an + * instruction is live. + */ +class LiveInterval { + /** + * The id where the instruction is defined. + */ + int start; + final List ranges; + LiveInterval() : ranges = []; + + // We want [HCheck] instructions to have the same name as the + // instruction it checks, so both instructions should share the same + // live ranges. + LiveInterval.forCheck(this.start, LiveInterval checkedInterval) + : ranges = checkedInterval.ranges; + + /** + * Update all ranges that are contained in [from, to[ to + * die at [to]. + */ + void loopUpdate(int from, int to) { + for (LiveRange range in ranges) { + if (from <= range.start && range.end < to) { + range.end = to; + } + } + } + + /** + * Add a new range to this interval. + */ + void add(LiveRange interval) { + ranges.add(interval); + } + + /** + * Returns true if one of the ranges of this interval dies at [at]. + */ + bool diesAt(int at) { + for (LiveRange range in ranges) { + if (range.end == at) return true; + } + return false; + } + + String toString() { + List res = new List(); + for (final interval in ranges) res.add(interval.toString()); + return '(${res.join(", ")})'; + } +} + +/** + * The [LiveEnvironment] class contains the liveIn set of a basic + * block. A liveIn set of a block contains the instructions that are + * live when entering that block. + */ +class LiveEnvironment { + /** + * The instruction id where the basic block starts. See + * [SsaLiveIntervalBuilder.instructionId]. + */ + int startId; + + /** + * The instruction id where the basic block ends. + */ + final int endId; + + /** + * Loop markers that will be updated once the loop header is + * visited. The liveIn set of the loop header will be merged into this + * environment. [loopMarkers] is a mapping from block header to the + * end instruction id of the loop exit block. + */ + final Map loopMarkers; + + /** + * The instructions that are live in this basic block. The values of + * the map contain the instruction ids where the instructions die. + * It will be used when adding a range to the live interval of an + * instruction. + */ + final Map liveInstructions; + + /** + * Map containing the live intervals of instructions. + */ + final Map liveIntervals; + + LiveEnvironment(this.liveIntervals, this.endId) + : liveInstructions = new Map(), + loopMarkers = new Map(); + + /** + * Remove an instruction from the liveIn set. This method also + * updates the live interval of [instruction] to contain the new + * range: [id, / id contained in [liveInstructions] /]. + */ + void remove(HInstruction instruction, int id) { + LiveInterval interval = liveIntervals.putIfAbsent( + instruction, () => new LiveInterval()); + int lastId = liveInstructions[instruction]; + // If [lastId] is null, then this instruction is not being used. + interval.add(new LiveRange(id, lastId == null ? id : lastId)); + // The instruction is defined at [id]. + interval.start = id; + liveInstructions.remove(instruction); + } + + /** + * Add [instruction] to the liveIn set. If the instruction is not + * already in the set, we save the id where it dies. + */ + void add(HInstruction instruction, int userId) { + // Note that we are visiting the graph in post-dominator order, so + // the first time we see a variable is when it dies. + liveInstructions.putIfAbsent(instruction, () => userId); + } + + /** + * Merge this environment with [other]. Update the end id of + * instructions in case they are different between this and [other]. + */ + void mergeWith(LiveEnvironment other) { + other.liveInstructions.forEach((HInstruction instruction, int existingId) { + // If both environments have the same instruction id of where + // [instruction] dies, there is no need to update the live + // interval of [instruction]. For example the if block and the + // else block have the same end id for an instruction that is + // being used in the join block and defined before the if/else. + if (existingId == endId) return; + LiveInterval range = liveIntervals.putIfAbsent( + instruction, () => new LiveInterval()); + range.add(new LiveRange(other.startId, existingId)); + liveInstructions[instruction] = endId; + }); + other.loopMarkers.forEach((k, v) { loopMarkers[k] = v; }); + } + + void addLoopMarker(HBasicBlock header, int id) { + assert(!loopMarkers.containsKey(header)); + loopMarkers[header] = id; + } + + void removeLoopMarker(HBasicBlock header) { + assert(loopMarkers.containsKey(header)); + loopMarkers.remove(header); + } + + bool get isEmpty => liveInstructions.isEmpty && loopMarkers.isEmpty; + bool contains(HInstruction instruction) => + liveInstructions.containsKey(instruction); + String toString() => liveInstructions.toString(); +} + +/** + * Builds the live intervals of each instruction. The algorithm visits + * the graph post-dominator tree to find the last uses of an + * instruction, and computes the liveIns of each basic block. + */ +class SsaLiveIntervalBuilder extends HBaseVisitor { + final Compiler compiler; + final Set generateAtUseSite; + final Set controlFlowOperators; + + /** + * A counter to assign start and end ids to live ranges. The initial + * value is not relevant. Note that instructionId goes downward to ease + * reasoning about live ranges (the first instruction of a graph has + * the lowest id). + */ + int instructionId = 0; + + /** + * The liveIns of basic blocks. + */ + final Map liveInstructions; + + /** + * The live intervals of instructions. + */ + final Map liveIntervals; + + SsaLiveIntervalBuilder( + this.compiler, this.generateAtUseSite, this.controlFlowOperators) + : liveInstructions = new Map(), + liveIntervals = new Map(); + + void visitGraph(HGraph graph) { + visitPostDominatorTree(graph); + if (!liveInstructions[graph.entry].isEmpty) { + compiler.internalError(CURRENT_ELEMENT_SPANNABLE, 'LiveIntervalBuilder.'); + } + } + + void markInputsAsLiveInEnvironment(HInstruction instruction, + LiveEnvironment environment) { + for (int i = 0, len = instruction.inputs.length; i < len; i++) { + markAsLiveInEnvironment(instruction.inputs[i], environment); + } + } + + // Returns the non-HCheck instruction, or the last [HCheck] in the + // check chain that is not generate at use site. + HInstruction checkedInstructionOrNonGenerateAtUseSite(HCheck check) { + var checked = check.checkedInput; + while (checked is HCheck) { + HInstruction next = checked.checkedInput; + if (generateAtUseSite.contains(next)) break; + checked = next; + } + return checked; + } + + void markAsLiveInEnvironment(HInstruction instruction, + LiveEnvironment environment) { + if (generateAtUseSite.contains(instruction)) { + markInputsAsLiveInEnvironment(instruction, environment); + } else { + environment.add(instruction, instructionId); + // Special case the HCheck instruction to mark the actual + // checked instruction live. The checked instruction and the + // [HCheck] will share the same live ranges. + if (instruction is HCheck) { + HCheck check = instruction; + HInstruction checked = checkedInstructionOrNonGenerateAtUseSite(check); + if (!generateAtUseSite.contains(checked)) { + environment.add(checked, instructionId); + } + } + } + } + + void removeFromEnvironment(HInstruction instruction, + LiveEnvironment environment) { + environment.remove(instruction, instructionId); + // Special case the HCheck instruction to have the same live + // interval as the instruction it is checking. + if (instruction is HCheck) { + HCheck check = instruction; + HInstruction checked = checkedInstructionOrNonGenerateAtUseSite(check); + if (!generateAtUseSite.contains(checked)) { + liveIntervals.putIfAbsent(checked, () => new LiveInterval()); + // Unconditionally force the live ranges of the HCheck to + // be the live ranges of the instruction it is checking. + liveIntervals[instruction] = + new LiveInterval.forCheck(instructionId, liveIntervals[checked]); + } + } + } + + void visitBasicBlock(HBasicBlock block) { + LiveEnvironment environment = + new LiveEnvironment(liveIntervals, instructionId); + + // If the control flow instruction in this block will actually be + // inlined in the codegen in the join block, we need to make + // whatever is used by that control flow instruction as live in + // the join block. + if (controlFlowOperators.contains(block.last)) { + HIf ifInstruction = block.last; + HBasicBlock joinBlock = ifInstruction.joinBlock; + if (generateAtUseSite.contains(joinBlock.phis.first)) { + markInputsAsLiveInEnvironment( + ifInstruction, liveInstructions[joinBlock]); + } + } + + // Add to the environment the liveIn of its successor, as well as + // the inputs of the phis of the successor that flow from this block. + for (int i = 0; i < block.successors.length; i++) { + HBasicBlock successor = block.successors[i]; + LiveEnvironment successorEnv = liveInstructions[successor]; + if (successorEnv != null) { + environment.mergeWith(successorEnv); + } else { + environment.addLoopMarker(successor, instructionId); + } + + int index = successor.predecessors.indexOf(block); + for (HPhi phi = successor.phis.first; phi != null; phi = phi.next) { + markAsLiveInEnvironment(phi.inputs[index], environment); + } + } + + // Iterate over all instructions to remove an instruction from the + // environment and add its inputs. + HInstruction instruction = block.last; + while (instruction != null) { + if (!generateAtUseSite.contains(instruction)) { + removeFromEnvironment(instruction, environment); + markInputsAsLiveInEnvironment(instruction, environment); + } + instructionId--; + instruction = instruction.previous; + } + + // We just remove the phis from the environment. The inputs of the + // phis will be put in the environment of the predecessors. + for (HPhi phi = block.phis.first; phi != null; phi = phi.next) { + if (!generateAtUseSite.contains(phi)) { + environment.remove(phi, instructionId); + } + } + + // Save the liveInstructions of that block. + environment.startId = instructionId + 1; + liveInstructions[block] = environment; + + // If the block is a loop header, we can remove the loop marker, + // because it will just recompute the loop phis. + // We also check if this loop header has any back edges. If not, + // we know there is no loop marker for it. + if (block.isLoopHeader() && block.predecessors.length > 1) { + updateLoopMarker(block); + } + } + + void updateLoopMarker(HBasicBlock header) { + LiveEnvironment env = liveInstructions[header]; + int lastId = env.loopMarkers[header]; + // Update all instructions that are liveIns in [header] to have a + // range that covers the loop. + env.liveInstructions.forEach((HInstruction instruction, int id) { + LiveInterval range = env.liveIntervals.putIfAbsent( + instruction, () => new LiveInterval()); + range.loopUpdate(env.startId, lastId); + env.liveInstructions[instruction] = lastId; + }); + + env.removeLoopMarker(header); + + // Update all liveIns set to contain the liveIns of [header]. + liveInstructions.forEach((HBasicBlock block, LiveEnvironment other) { + if (other.loopMarkers.containsKey(header)) { + env.liveInstructions.forEach((HInstruction instruction, int id) { + other.liveInstructions[instruction] = id; + }); + other.removeLoopMarker(header); + env.loopMarkers.forEach((k, v) { other.loopMarkers[k] = v; }); + } + }); + } +} + +/** + * Represents a copy from one instruction to another. The codegen + * also uses this class to represent a copy from one variable to + * another. + */ +class Copy { + final source; + final destination; + Copy(this.source, this.destination); + String toString() => '$destination <- $source'; +} + +/** + * A copy handler contains the copies that a basic block needs to do + * after executing all its instructions. + */ +class CopyHandler { + /** + * The copies from an instruction to a phi of the successor. + */ + final List copies; + + /** + * Assignments from an instruction that does not need a name (e.g. a + * constant) to the phi of a successor. + */ + final List assignments; + + CopyHandler() + : copies = new List(), + assignments = new List(); + + void addCopy(HInstruction source, HInstruction destination) { + copies.add(new Copy(source, destination)); + } + + void addAssignment(HInstruction source, HInstruction destination) { + assignments.add(new Copy(source, destination)); + } + + String toString() => 'Copies: $copies, assignments: $assignments'; + bool get isEmpty => copies.isEmpty && assignments.isEmpty; +} + +/** + * Contains the mapping between instructions and their names for code + * generation, as well as the [CopyHandler] for each basic block. + */ +class VariableNames { + final Map ownName; + final Map copyHandlers; + + // Used to control heuristic that determines how local variables are declared. + final Set allUsedNames; + /** + * Name that is used as a temporary to break cycles in + * parallel copies. We make sure this name is not being used + * anywhere by reserving it when we allocate names for instructions. + */ + final String swapTemp; + + String getSwapTemp() { + allUsedNames.add(swapTemp); + return swapTemp; + } + + VariableNames() + : ownName = new Map(), + copyHandlers = new Map(), + allUsedNames = new Set(), + swapTemp = 't0'; + + int get numberOfVariables => allUsedNames.length; + + String getName(HInstruction instruction) { + return ownName[instruction]; + } + + CopyHandler getCopyHandler(HBasicBlock block) { + return copyHandlers[block]; + } + + void addNameUsed(String name) { + allUsedNames.add(name); + } + + bool hasName(HInstruction instruction) => ownName.containsKey(instruction); + + void addCopy(HBasicBlock block, HInstruction source, HPhi destination) { + CopyHandler handler = + copyHandlers.putIfAbsent(block, () => new CopyHandler()); + handler.addCopy(source, destination); + } + + void addAssignment(HBasicBlock block, HInstruction source, HPhi destination) { + CopyHandler handler = + copyHandlers.putIfAbsent(block, () => new CopyHandler()); + handler.addAssignment(source, destination); + } +} + +/** + * Allocates variable names for instructions, making sure they don't collide. + */ +class VariableNamer { + final VariableNames names; + final Compiler compiler; + final Set usedNames; + final List freeTemporaryNames; + int temporaryIndex = 0; + static final RegExp regexp = new RegExp('t[0-9]+'); + + VariableNamer(LiveEnvironment environment, + this.names, + this.compiler) + : usedNames = new Set(), + freeTemporaryNames = new List() { + // [VariableNames.swapTemp] is used when there is a cycle in a copy handler. + // Therefore we make sure no one uses it. + usedNames.add(names.swapTemp); + + // All liveIns instructions must have a name at this point, so we + // add them to the list of used names. + environment.liveInstructions.forEach((HInstruction instruction, int index) { + String name = names.getName(instruction); + if (name != null) { + usedNames.add(name); + names.addNameUsed(name); + } + }); + } + + String allocateWithHint(String originalName) { + int i = 0; + JavaScriptBackend backend = compiler.backend; + String name = backend.namer.safeVariableName(originalName); + while (usedNames.contains(name)) { + name = backend.namer.safeVariableName('$originalName${i++}'); + } + return name; + } + + String allocateTemporary() { + while (!freeTemporaryNames.isEmpty) { + String name = freeTemporaryNames.removeLast(); + if (!usedNames.contains(name)) return name; + } + String name = 't${temporaryIndex++}'; + while (usedNames.contains(name)) name = 't${temporaryIndex++}'; + return name; + } + + HPhi firstPhiUserWithElement(HInstruction instruction) { + for (HInstruction user in instruction.usedBy) { + if (user is HPhi && user.sourceElement != null) { + return user; + } + } + return null; + } + + String allocateName(HInstruction instruction) { + String name; + if (instruction is HCheck) { + // Special case this instruction to use the name of its + // input if it has one. + var temp = instruction; + do { + temp = temp.checkedInput; + name = names.ownName[temp]; + } while (name == null && temp is HCheck); + if (name != null) return addAllocatedName(instruction, name); + } + + if (instruction.sourceElement != null) { + name = allocateWithHint(instruction.sourceElement.name); + } else { + // We could not find an element for the instruction. If the + // instruction is used by a phi, try to use the name of the phi. + // Otherwise, just allocate a temporary name. + HPhi phi = firstPhiUserWithElement(instruction); + if (phi != null) { + name = allocateWithHint(phi.sourceElement.name); + } else { + name = allocateTemporary(); + } + } + return addAllocatedName(instruction, name); + } + + String addAllocatedName(HInstruction instruction, String name) { + usedNames.add(name); + names.addNameUsed(name); + names.ownName[instruction] = name; + return name; + } + + /** + * Frees [instruction]'s name so it can be used for other instructions. + */ + void freeName(HInstruction instruction) { + String ownName = names.ownName[instruction]; + if (ownName != null) { + // We check if we have already looked for temporary names + // because if we haven't, chances are the temporary we allocate + // in this block can match a phi with the same name in the + // successor block. + if (temporaryIndex != 0 && regexp.hasMatch(ownName)) { + freeTemporaryNames.add(ownName); + } + usedNames.remove(ownName); + } + } +} + +/** + * Visits all blocks in the graph, sets names to instructions, and + * creates the [CopyHandler] for each block. This class needs to have + * the liveIns set as well as all the live intervals of instructions. + * It visits the graph in dominator order, so that at each entry of a + * block, the instructions in its liveIns set have names. + * + * When visiting a block, it goes through all instructions. For each + * instruction, it frees the names of the inputs that die at that + * instruction, and allocates a name to the instruction. For each phi, + * it adds a copy to the CopyHandler of the corresponding predecessor. + */ +class SsaVariableAllocator extends HBaseVisitor { + + final Compiler compiler; + final Map liveInstructions; + final Map liveIntervals; + final Set generateAtUseSite; + + final VariableNames names; + + SsaVariableAllocator(this.compiler, + this.liveInstructions, + this.liveIntervals, + this.generateAtUseSite) + : this.names = new VariableNames(); + + void visitGraph(HGraph graph) { + visitDominatorTree(graph); + } + + void visitBasicBlock(HBasicBlock block) { + VariableNamer namer = new VariableNamer( + liveInstructions[block], names, compiler); + + block.forEachPhi((HPhi phi) { + handlePhi(phi, namer); + }); + + block.forEachInstruction((HInstruction instruction) { + handleInstruction(instruction, namer); + }); + } + + /** + * Returns whether [instruction] needs a name. Instructions that + * have no users or that are generated at use site do not need a name. + */ + bool needsName(instruction) { + if (instruction is HThis) return false; + if (instruction is HParameterValue) return true; + if (instruction.usedBy.isEmpty) return false; + if (generateAtUseSite.contains(instruction)) return false; + return !instruction.nonCheck().isCodeMotionInvariant(); + } + + /** + * Returns whether [instruction] dies at the instruction [at]. + */ + bool diesAt(HInstruction instruction, HInstruction at) { + LiveInterval atInterval = liveIntervals[at]; + LiveInterval instructionInterval = liveIntervals[instruction]; + int start = atInterval.start; + return instructionInterval.diesAt(start); + } + + void freeUsedNamesAt(HInstruction instruction, + HInstruction at, + VariableNamer namer) { + if (needsName(instruction)) { + if (diesAt(instruction, at)) { + namer.freeName(instruction); + } + } else if (generateAtUseSite.contains(instruction)) { + // If the instruction is generated at use site, then all its + // inputs may also die at [at]. + for (int i = 0, len = instruction.inputs.length; i < len; i++) { + HInstruction input = instruction.inputs[i]; + freeUsedNamesAt(input, at, namer); + } + } + } + + void handleInstruction(HInstruction instruction, VariableNamer namer) { + if (generateAtUseSite.contains(instruction)) { + assert(!liveIntervals.containsKey(instruction)); + return; + } + + for (int i = 0, len = instruction.inputs.length; i < len; i++) { + HInstruction input = instruction.inputs[i]; + freeUsedNamesAt(input, instruction, namer); + } + + if (needsName(instruction)) { + namer.allocateName(instruction); + } + } + + void handlePhi(HPhi phi, VariableNamer namer) { + if (!needsName(phi)) return; + + for (int i = 0; i < phi.inputs.length; i++) { + HInstruction input = phi.inputs[i]; + HBasicBlock predecessor = phi.block.predecessors[i]; + // A [HTypeKnown] instruction never has a name, but its checked + // input might, therefore we need to do a copy instead of an + // assignment. + while (input is HTypeKnown) input = input.inputs[0]; + if (!needsName(input)) { + names.addAssignment(predecessor, input, phi); + } else { + names.addCopy(predecessor, input, phi); + } + } + + namer.allocateName(phi); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/string_validator.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/string_validator.dart new file mode 100644 index 0000000..140134b --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/string_validator.dart @@ -0,0 +1,215 @@ +// Copyright (c) 2012, 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. + +// Check the validity of string literals. + +library stringvalidator; + +import "dart:collection"; + +import "dart2jslib.dart"; +import "tree/tree.dart"; +import "util/characters.dart"; +import "scanner/scannerlib.dart" show Token; + +class StringValidator { + final DiagnosticListener listener; + + StringValidator(this.listener); + + DartString validateInterpolationPart(Token token, StringQuoting quoting, + {bool isFirst: false, + bool isLast: false}) { + String source = token.value; + int leftQuote = 0; + int rightQuote = 0; + if (isFirst) leftQuote = quoting.leftQuoteLength; + if (isLast) rightQuote = quoting.rightQuoteLength; + String content = copyWithoutQuotes(source, leftQuote, rightQuote); + return validateString(token, + token.charOffset + leftQuote, + content, + quoting); + } + + static StringQuoting quotingFromString(String sourceString) { + Iterator source = sourceString.codeUnits.iterator; + bool raw = false; + int quoteLength = 1; + source.moveNext(); + int quoteChar = source.current; + if (quoteChar == $r) { + raw = true; + source.moveNext(); + quoteChar = source.current; + } + assert(quoteChar == $SQ || quoteChar == $DQ); + // String has at least one quote. Check it if has three. + // If it only have two, the string must be an empty string literal, + // and end after the second quote. + bool multiline = false; + if (source.moveNext() && source.current == quoteChar && source.moveNext()) { + int code = source.current; + assert(code == quoteChar); // If not, there is a bug in the parser. + quoteLength = 3; + // Check if a multiline string starts with a newline (CR, LF or CR+LF). + if (source.moveNext()) { + code = source.current; + if (code == $CR) { + quoteLength += 1; + if (source.moveNext() && source.current == $LF) { + quoteLength += 1; + } + } else if (code == $LF) { + quoteLength += 1; + } + } + } + return StringQuoting.getQuoting(quoteChar, raw, quoteLength); + } + + /** + * Return the string [string] witout its [initial] first and [terminal] last + * characters. This is intended to be used to remove quotes from string + * literals (including an initial 'r' for raw strings). + */ + String copyWithoutQuotes(String string, int initial, int terminal) { + assert(0 <= initial); + assert(0 <= terminal); + assert(initial + terminal <= string.length); + return string.substring(initial, string.length - terminal); + } + + void stringParseError(String message, Token token, int offset) { + listener.reportFatalError( + token, MessageKind.GENERIC, {'text': "$message @ $offset"}); + } + + /** + * Validates the escape sequences and special characters of a string literal. + * Returns a DartString if valid, and null if not. + */ + DartString validateString(Token token, + int startOffset, + String string, + StringQuoting quoting) { + // We need to check for invalid x and u escapes, for line + // terminators in non-multiline strings, and for invalid Unicode + // scalar values (either directly or as u-escape values). We also check + // for unpaired UTF-16 surrogates. + int length = 0; + int index = startOffset; + bool containsEscape = false; + bool previousWasLeadSurrogate = false; + bool invalidUtf16 = false; + var stringIter = string.codeUnits.iterator; + for(HasNextIterator iter = new HasNextIterator(stringIter); + iter.hasNext; + length++) { + index++; + int code = iter.next(); + if (code == $BACKSLASH) { + if (quoting.raw) continue; + containsEscape = true; + if (!iter.hasNext) { + stringParseError("Incomplete escape sequence",token, index); + return null; + } + index++; + code = iter.next(); + if (code == $x) { + for (int i = 0; i < 2; i++) { + if (!iter.hasNext) { + stringParseError("Incomplete escape sequence", token, index); + return null; + } + index++; + code = iter.next(); + if (!isHexDigit(code)) { + stringParseError("Invalid character in escape sequence", + token, index); + return null; + } + } + // A two-byte hex escape can't generate an invalid value. + continue; + } else if (code == $u) { + int escapeStart = index - 1; + index++; + code = iter.hasNext ? iter.next() : 0; + int value = 0; + if (code == $OPEN_CURLY_BRACKET) { + // expect 1-6 hex digits. + int count = 0; + while (iter.hasNext) { + code = iter.next(); + index++; + if (code == $CLOSE_CURLY_BRACKET) { + break; + } + if (!isHexDigit(code)) { + stringParseError("Invalid character in escape sequence", + token, index); + return null; + } + count++; + value = value * 16 + hexDigitValue(code); + } + if (code != $CLOSE_CURLY_BRACKET || count == 0 || count > 6) { + int errorPosition = index - count; + if (count > 6) errorPosition += 6; + stringParseError("Invalid character in escape sequence", + token, errorPosition); + return null; + } + } else { + // Expect four hex digits, including the one just read. + for (int i = 0; i < 4; i++) { + if (i > 0) { + if (iter.hasNext) { + index++; + code = iter.next(); + } else { + code = 0; + } + } + if (!isHexDigit(code)) { + stringParseError("Invalid character in escape sequence", + token, index); + return null; + } + value = value * 16 + hexDigitValue(code); + } + } + code = value; + } + } + if (code >= 0x10000) length++; + // This handles both unescaped characters and the value of unicode + // escapes. + if (previousWasLeadSurrogate) { + if (!isUtf16TrailSurrogate(code)) { + invalidUtf16 = true; + break; + } + previousWasLeadSurrogate = false; + } else if (isUtf16LeadSurrogate(code)) { + previousWasLeadSurrogate = true; + } else if (!isUnicodeScalarValue(code)) { + invalidUtf16 = true; + break; + } + } + if (previousWasLeadSurrogate || invalidUtf16) { + stringParseError("Invalid Utf16 surrogate", token, index); + return null; + } + // String literal successfully validated. + if (quoting.raw || !containsEscape) { + // A string without escapes could just as well have been raw. + return new DartString.rawString(string, length); + } + return new DartString.escapedString(string, length); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/dartstring.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/dartstring.dart new file mode 100644 index 0000000..72d0b0c --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/dartstring.dart @@ -0,0 +1,251 @@ +// Copyright (c) 2012, 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. + +part of tree; + +/** + * The [DartString] type represents a Dart string value as a sequence of Unicode + * Scalar Values. + * After parsing, any valid [LiteralString] will contain a [DartString] + * representing its content after removing quotes and resolving escapes in + * its source. + */ +abstract class DartString extends IterableBase { + factory DartString.empty() => const LiteralDartString(""); + // This is a convenience constructor. If you need a const literal DartString, + // use [const LiteralDartString(string)] directly. + factory DartString.literal(String string) => new LiteralDartString(string); + factory DartString.rawString(String source, int length) => + new RawSourceDartString(source, length); + factory DartString.escapedString(String source, int length) => + new EscapedSourceDartString(source, length); + factory DartString.concat(DartString first, DartString second) { + if (first.isEmpty) return second; + if (second.isEmpty) return first; + return new ConsDartString(first, second); + } + const DartString(); + + /** + * The length of this [DartString], which is the string length after + * escapes have been resolved. + */ + int get length; + bool get isEmpty => length == 0; + + Iterator get iterator; + + /** + * The string represented by this [DartString]. + */ + String slowToString(); + + bool operator ==(var other) { + if (other is !DartString) return false; + DartString otherString = other; + if (length != otherString.length) return false; + Iterator it1 = iterator; + Iterator it2 = otherString.iterator; + while (it1.moveNext()) { + if (!it2.moveNext()) return false; + if (it1.current != it2.current) return false; + } + return true; + } + + int get hashCode => throw new UnsupportedError('DartString.hashCode'); + + /** + * A textual representation of this [DartString] with some debugging + * information. + */ + String toString() => "DartString#${length}:${slowToString()}"; +} + + +/** + * A [DartString] where the content is represented by an actual [String]. + */ +class LiteralDartString extends DartString { + final String string; + const LiteralDartString(this.string); + int get length => string.length; + Iterator get iterator => string.codeUnits.iterator; + String slowToString() => string; +} + +/** + * A [DartString] whereSource the content comes from a slice of the program + * source. + */ +abstract class SourceBasedDartString extends DartString { + /** + * The source string containing explicit escapes from which this [DartString] + * is built. + */ + final String source; + final int length; + SourceBasedDartString(this.source, this.length); + Iterator get iterator; +} + +/** + * Special case of a [SourceBasedDartString] where we know the source doesn't + * contain any escapes. + */ +class RawSourceDartString extends SourceBasedDartString { + RawSourceDartString(source, length) : super(source, length); + Iterator get iterator => source.codeUnits.iterator; + String slowToString() => source; +} + +/** + * General case of a [SourceBasedDartString] where the source might contain + * escapes. + */ +class EscapedSourceDartString extends SourceBasedDartString { + String toStringCache; + EscapedSourceDartString(source, length) : super(source, length); + Iterator get iterator { + if (toStringCache != null) return toStringCache.codeUnits.iterator; + return new StringEscapeIterator(source); + } + String slowToString() { + if (toStringCache != null) return toStringCache; + StringBuffer buffer = new StringBuffer(); + StringEscapeIterator it = new StringEscapeIterator(source); + while (it.moveNext()) { + buffer.writeCharCode(it.current); + } + toStringCache = buffer.toString(); + return toStringCache; + } +} + +/** + * The concatenation of two [DartString]s. + */ +class ConsDartString extends DartString { + final DartString left; + final DartString right; + final int length; + String toStringCache; + ConsDartString(DartString left, DartString right) + : this.left = left, + this.right = right, + length = left.length + right.length; + + Iterator get iterator => new ConsDartStringIterator(this); + + String slowToString() { + if (toStringCache != null) return toStringCache; + toStringCache = left.slowToString() + right.slowToString(); + return toStringCache; + } + String get source => slowToString(); +} + +class ConsDartStringIterator implements Iterator { + HasNextIterator currentIterator; + DartString right; + bool hasNextLookAhead; + int _current = null; + + ConsDartStringIterator(ConsDartString cons) + : currentIterator = new HasNextIterator(cons.left.iterator), + right = cons.right { + hasNextLookAhead = currentIterator.hasNext; + if (!hasNextLookAhead) { + nextPart(); + } + } + + int get current => _current; + + bool moveNext() { + if (!hasNextLookAhead) { + _current = null; + return false; + } + _current = currentIterator.next(); + hasNextLookAhead = currentIterator.hasNext; + if (!hasNextLookAhead) { + nextPart(); + } + return true; + } + void nextPart() { + if (right != null) { + currentIterator = new HasNextIterator(right.iterator); + right = null; + hasNextLookAhead = currentIterator.hasNext; + } + } +} + +/** + *Iterator that returns the actual string contents of a string with escapes. + */ +class StringEscapeIterator implements Iterator{ + final Iterator source; + int _current = null; + + StringEscapeIterator(String source) : this.source = source.codeUnits.iterator; + + int get current => _current; + + bool moveNext() { + if (!source.moveNext()) { + _current = null; + return false; + } + int code = source.current; + if (code != $BACKSLASH) { + _current = code; + return true; + } + source.moveNext(); + code = source.current; + switch (code) { + case $n: _current = $LF; break; + case $r: _current = $CR; break; + case $t: _current = $TAB; break; + case $b: _current = $BS; break; + case $f: _current = $FF; break; + case $v: _current = $VTAB; break; + case $x: + source.moveNext(); + int value = hexDigitValue(source.current); + source.moveNext(); + value = value * 16 + hexDigitValue(source.current); + _current = value; + break; + case $u: + int value = 0; + source.moveNext(); + code = source.current; + if (code == $OPEN_CURLY_BRACKET) { + source.moveNext(); + while (source.current != $CLOSE_CURLY_BRACKET) { + value = value * 16 + hexDigitValue(source.current); + source.moveNext(); + } + _current = value; + break; + } + // Four digit hex value. + value = hexDigitValue(code); + for (int i = 0; i < 3; i++) { + source.moveNext(); + value = value * 16 + hexDigitValue(source.current); + } + _current = value; + break; + default: + _current = code; + } + return true; + } +} + diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/nodes.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/nodes.dart new file mode 100644 index 0000000..d74f51f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/nodes.dart @@ -0,0 +1,2116 @@ +// Copyright (c) 2012, 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. + +part of tree; + +abstract class Visitor { + const Visitor(); + + R visitNode(Node node); + + R visitBlock(Block node) => visitStatement(node); + R visitBreakStatement(BreakStatement node) => visitGotoStatement(node); + R visitCascade(Cascade node) => visitExpression(node); + R visitCascadeReceiver(CascadeReceiver node) => visitExpression(node); + R visitCaseMatch(CaseMatch node) => visitNode(node); + R visitCatchBlock(CatchBlock node) => visitNode(node); + R visitClassNode(ClassNode node) => visitNode(node); + R visitCombinator(Combinator node) => visitNode(node); + R visitConditional(Conditional node) => visitExpression(node); + R visitContinueStatement(ContinueStatement node) => visitGotoStatement(node); + R visitDoWhile(DoWhile node) => visitLoop(node); + R visitEmptyStatement(EmptyStatement node) => visitStatement(node); + R visitExport(Export node) => visitLibraryDependency(node); + R visitExpression(Expression node) => visitNode(node); + R visitExpressionStatement(ExpressionStatement node) => visitStatement(node); + R visitFor(For node) => visitLoop(node); + R visitForIn(ForIn node) => visitLoop(node); + R visitFunctionDeclaration(FunctionDeclaration node) => visitStatement(node); + R visitFunctionExpression(FunctionExpression node) => visitExpression(node); + R visitGotoStatement(GotoStatement node) => visitStatement(node); + R visitIdentifier(Identifier node) => visitExpression(node); + R visitIf(If node) => visitStatement(node); + R visitImport(Import node) => visitLibraryDependency(node); + R visitLabel(Label node) => visitNode(node); + R visitLabeledStatement(LabeledStatement node) => visitStatement(node); + R visitLibraryDependency(LibraryDependency node) => visitLibraryTag(node); + R visitLibraryName(LibraryName node) => visitLibraryTag(node); + R visitLibraryTag(LibraryTag node) => visitNode(node); + R visitLiteral(Literal node) => visitExpression(node); + R visitLiteralBool(LiteralBool node) => visitLiteral(node); + R visitLiteralDouble(LiteralDouble node) => visitLiteral(node); + R visitLiteralInt(LiteralInt node) => visitLiteral(node); + R visitLiteralList(LiteralList node) => visitExpression(node); + R visitLiteralMap(LiteralMap node) => visitExpression(node); + R visitLiteralMapEntry(LiteralMapEntry node) => visitNode(node); + R visitLiteralNull(LiteralNull node) => visitLiteral(node); + R visitLiteralString(LiteralString node) => visitStringNode(node); + R visitStringJuxtaposition(StringJuxtaposition node) => visitStringNode(node); + R visitLoop(Loop node) => visitStatement(node); + R visitMetadata(Metadata node) => visitNode(node); + R visitMixinApplication(MixinApplication node) => visitNode(node); + R visitModifiers(Modifiers node) => visitNode(node); + R visitNamedArgument(NamedArgument node) => visitExpression(node); + R visitNamedMixinApplication(NamedMixinApplication node) { + return visitMixinApplication(node); + } + R visitNewExpression(NewExpression node) => visitExpression(node); + R visitNodeList(NodeList node) => visitNode(node); + R visitOperator(Operator node) => visitIdentifier(node); + R visitParenthesizedExpression(ParenthesizedExpression node) { + return visitExpression(node); + } + R visitPart(Part node) => visitLibraryTag(node); + R visitPartOf(PartOf node) => visitNode(node); + R visitPostfix(Postfix node) => visitNodeList(node); + R visitPrefix(Prefix node) => visitNodeList(node); + R visitRethrow(Rethrow node) => visitStatement(node); + R visitReturn(Return node) => visitStatement(node); + R visitSend(Send node) => visitExpression(node); + R visitSendSet(SendSet node) => visitSend(node); + R visitStatement(Statement node) => visitNode(node); + R visitStringNode(StringNode node) => visitExpression(node); + R visitStringInterpolation(StringInterpolation node) => visitStringNode(node); + R visitStringInterpolationPart(StringInterpolationPart node) { + return visitNode(node); + } + R visitSwitchCase(SwitchCase node) => visitNode(node); + R visitSwitchStatement(SwitchStatement node) => visitStatement(node); + R visitLiteralSymbol(LiteralSymbol node) => visitExpression(node); + R visitThrow(Throw node) => visitExpression(node); + R visitTryStatement(TryStatement node) => visitStatement(node); + R visitTypeAnnotation(TypeAnnotation node) => visitNode(node); + R visitTypedef(Typedef node) => visitNode(node); + R visitTypeVariable(TypeVariable node) => visitNode(node); + R visitVariableDefinitions(VariableDefinitions node) => visitStatement(node); + R visitWhile(While node) => visitLoop(node); +} + +Token firstBeginToken(Node first, Node second) { + Token token = null; + if (first != null) { + token = first.getBeginToken(); + } + if (token == null && second != null) { + // [token] might be null even when [first] is not, e.g. for empty Modifiers. + token = second.getBeginToken(); + } + return token; +} + +/** + * A node in a syntax tree. + * + * The abstract part of "abstract syntax tree" is invalidated when + * supporting tools such as code formatting. These tools need concrete + * syntax such as parentheses and no constant folding. + * + * We support these tools by storing additional references back to the + * token stream. These references are stored in fields ending with + * "Token". + */ +abstract class Node extends TreeElementMixin implements Spannable { + final int hashCode; + static int _HASH_COUNTER = 0; + + Node() : hashCode = ++_HASH_COUNTER; + + accept(Visitor visitor); + + visitChildren(Visitor visitor); + + /** + * Returns this node unparsed to Dart source string. + */ + toString() => unparse(this); + + /** + * Returns Xml-like tree representation of this node. + */ + toDebugString() { + return PrettyPrinter.prettyPrint(this); + } + + String getObjectDescription() => super.toString(); + + Token getBeginToken(); + + Token getEndToken(); + + Block asBlock() => null; + BreakStatement asBreakStatement() => null; + Cascade asCascade() => null; + CascadeReceiver asCascadeReceiver() => null; + CaseMatch asCaseMatch() => null; + CatchBlock asCatchBlock() => null; + ClassNode asClassNode() => null; + Combinator asCombinator() => null; + Conditional asConditional() => null; + ContinueStatement asContinueStatement() => null; + DoWhile asDoWhile() => null; + EmptyStatement asEmptyStatement() => null; + ErrorExpression asErrorExpression() => null; + Export asExport() => null; + Expression asExpression() => null; + ExpressionStatement asExpressionStatement() => null; + For asFor() => null; + ForIn asForIn() => null; + FunctionDeclaration asFunctionDeclaration() => null; + FunctionExpression asFunctionExpression() => null; + Identifier asIdentifier() => null; + If asIf() => null; + Import asImport() => null; + Label asLabel() => null; + LabeledStatement asLabeledStatement() => null; + LibraryName asLibraryName() => null; + LibraryDependency asLibraryDependency() => null; + LiteralBool asLiteralBool() => null; + LiteralDouble asLiteralDouble() => null; + LiteralInt asLiteralInt() => null; + LiteralList asLiteralList() => null; + LiteralMap asLiteralMap() => null; + LiteralMapEntry asLiteralMapEntry() => null; + LiteralNull asLiteralNull() => null; + LiteralString asLiteralString() => null; + LiteralSymbol asLiteralSymbol() => null; + Metadata asMetadata() => null; + MixinApplication asMixinApplication() => null; + Modifiers asModifiers() => null; + NamedArgument asNamedArgument() => null; + NamedMixinApplication asNamedMixinApplication() => null; + NewExpression asNewExpression() => null; + NodeList asNodeList() => null; + Operator asOperator() => null; + ParenthesizedExpression asParenthesizedExpression() => null; + Part asPart() => null; + PartOf asPartOf() => null; + Rethrow asRethrow() => null; + Return asReturn() => null; + Send asSend() => null; + SendSet asSendSet() => null; + Statement asStatement() => null; + StringInterpolation asStringInterpolation() => null; + StringInterpolationPart asStringInterpolationPart() => null; + StringJuxtaposition asStringJuxtaposition() => null; + StringNode asStringNode() => null; + SwitchCase asSwitchCase() => null; + SwitchStatement asSwitchStatement() => null; + Throw asThrow() => null; + TryStatement asTryStatement() => null; + TypeAnnotation asTypeAnnotation() => null; + TypeVariable asTypeVariable() => null; + Typedef asTypedef() => null; + VariableDefinitions asVariableDefinitions() => null; + While asWhile() => null; + + bool isValidBreakTarget() => false; + bool isValidContinueTarget() => false; + bool isThis() => false; + bool isSuper() => false; +} + +class ClassNode extends Node { + final Modifiers modifiers; + final Identifier name; + final Node superclass; + final NodeList interfaces; + final NodeList typeParameters; + final NodeList body; + + final Token beginToken; + final Token extendsKeyword; + final Token endToken; + + ClassNode(this.modifiers, this.name, this.typeParameters, this.superclass, + this.interfaces, this.beginToken, + this.extendsKeyword, this.body, this.endToken); + + ClassNode asClassNode() => this; + + accept(Visitor visitor) => visitor.visitClassNode(this); + + visitChildren(Visitor visitor) { + if (name != null) name.accept(visitor); + if (typeParameters != null) typeParameters.accept(visitor); + if (superclass != null) superclass.accept(visitor); + if (interfaces != null) interfaces.accept(visitor); + if (body != null) body.accept(visitor); + } + + Token getBeginToken() => beginToken; + + Token getEndToken() => endToken; +} + +class MixinApplication extends Node { + final TypeAnnotation superclass; + final NodeList mixins; + + MixinApplication(this.superclass, this.mixins); + + MixinApplication asMixinApplication() => this; + + accept(Visitor visitor) => visitor.visitMixinApplication(this); + + visitChildren(Visitor visitor) { + if (superclass != null) superclass.accept(visitor); + if (mixins != null) mixins.accept(visitor); + } + + Token getBeginToken() => superclass.getBeginToken(); + Token getEndToken() => mixins.getEndToken(); +} + +// TODO(kasperl): Let this share some structure with the typedef for function +// type aliases? +class NamedMixinApplication extends Node implements MixinApplication { + final Identifier name; + final NodeList typeParameters; + + final Modifiers modifiers; + final MixinApplication mixinApplication; + final NodeList interfaces; + + final Token classKeyword; + final Token endToken; + + NamedMixinApplication(this.name, this.typeParameters, + this.modifiers, this.mixinApplication, this.interfaces, + this.classKeyword, this.endToken); + + TypeAnnotation get superclass => mixinApplication.superclass; + NodeList get mixins => mixinApplication.mixins; + + MixinApplication asMixinApplication() => this; + NamedMixinApplication asNamedMixinApplication() => this; + + accept(Visitor visitor) => visitor.visitNamedMixinApplication(this); + + visitChildren(Visitor visitor) { + name.accept(visitor); + if (typeParameters != null) typeParameters.accept(visitor); + if (modifiers != null) modifiers.accept(visitor); + if (interfaces != null) interfaces.accept(visitor); + mixinApplication.accept(visitor); + } + + Token getBeginToken() => classKeyword; + Token getEndToken() => endToken; +} + +abstract class Expression extends Node { + Expression(); + + Expression asExpression() => this; + + // TODO(ahe): make class abstract instead of adding an abstract method. + accept(Visitor visitor); +} + +abstract class Statement extends Node { + Statement(); + + Statement asStatement() => this; + + // TODO(ahe): make class abstract instead of adding an abstract method. + accept(Visitor visitor); + + bool isValidBreakTarget() => true; +} + +/// Errorneous expression that behaves as a literal integer (0). +class ErrorExpression extends LiteralInt { + ErrorExpression(token) + : super(token, null); + + ErrorExpression asErrorExpression() => this; + + int get value => 0; +} + +/** + * A message send aka method invocation. In Dart, most operations can + * (and should) be considered as message sends. Getters and setters + * are just methods with a special syntax. Consequently, we model + * property access, assignment, operators, and method calls with this + * one node. + */ +class Send extends Expression { + final Node receiver; + final Node selector; + final NodeList argumentsNode; + Link get arguments => argumentsNode.nodes; + + Send([this.receiver, this.selector, this.argumentsNode]); + Send.postfix(this.receiver, this.selector, [Node argument = null]) + : argumentsNode = (argument == null) + ? new Postfix() + : new Postfix.singleton(argument); + Send.prefix(this.receiver, this.selector, [Node argument = null]) + : argumentsNode = (argument == null) + ? new Prefix() + : new Prefix.singleton(argument); + + Send asSend() => this; + + accept(Visitor visitor) => visitor.visitSend(this); + + visitChildren(Visitor visitor) { + if (receiver != null) receiver.accept(visitor); + if (selector != null) selector.accept(visitor); + if (argumentsNode != null) argumentsNode.accept(visitor); + } + + int argumentCount() { + return (argumentsNode == null) ? -1 : argumentsNode.slowLength(); + } + + bool get isSuperCall { + return receiver != null && receiver.isSuper(); + } + bool get isOperator => selector is Operator; + bool get isPropertyAccess => argumentsNode == null; + bool get isFunctionObjectInvocation => selector == null; + bool get isPrefix => argumentsNode is Prefix; + bool get isPostfix => argumentsNode is Postfix; + bool get isCall => !isOperator && !isPropertyAccess; + bool get isIndex => + isOperator && identical(selector.asOperator().source, '[]'); + bool get isLogicalAnd => + isOperator && identical(selector.asOperator().source, '&&'); + bool get isLogicalOr => + isOperator && identical(selector.asOperator().source, '||'); + + bool get isTypeCast { + return isOperator + && identical(selector.asOperator().source, 'as'); + } + + bool get isTypeTest { + return isOperator + && identical(selector.asOperator().source, 'is'); + } + + bool get isIsNotCheck { + return isTypeTest && arguments.head.asSend() != null; + } + + TypeAnnotation get typeAnnotationFromIsCheckOrCast { + assert(isOperator); + assert(identical(selector.asOperator().source, 'is') || + identical(selector.asOperator().source, 'as')); + return isIsNotCheck + ? arguments.head.asSend().receiver + : arguments.head; + } + + Token getBeginToken() { + if (isPrefix && !isIndex) return selector.getBeginToken(); + return firstBeginToken(receiver, selector); + } + + Token getEndToken() { + if (isPrefix) { + if (receiver != null) return receiver.getEndToken(); + if (selector != null) return selector.getEndToken(); + return null; + } + if (!isPostfix && argumentsNode != null) { + return argumentsNode.getEndToken(); + } + if (selector != null) return selector.getEndToken(); + return receiver.getBeginToken(); + } + + Send copyWithReceiver(Node newReceiver) { + assert(receiver == null); + return new Send(newReceiver, selector, argumentsNode); + } +} + +class Postfix extends NodeList { + Postfix() : super(null, const Link()); + Postfix.singleton(Node argument) : super.singleton(argument); +} + +class Prefix extends NodeList { + Prefix() : super(null, const Link()); + Prefix.singleton(Node argument) : super.singleton(argument); +} + +class SendSet extends Send { + final Operator assignmentOperator; + SendSet(receiver, selector, this.assignmentOperator, argumentsNode) + : super(receiver, selector, argumentsNode); + SendSet.postfix(receiver, + selector, + this.assignmentOperator, + [Node argument = null]) + : super.postfix(receiver, selector, argument); + SendSet.prefix(receiver, + selector, + this.assignmentOperator, + [Node argument = null]) + : super.prefix(receiver, selector, argument); + + SendSet asSendSet() => this; + + accept(Visitor visitor) => visitor.visitSendSet(this); + + visitChildren(Visitor visitor) { + super.visitChildren(visitor); + if (assignmentOperator != null) assignmentOperator.accept(visitor); + } + + Send copyWithReceiver(Node newReceiver) { + assert(receiver == null); + return new SendSet(newReceiver, selector, assignmentOperator, + argumentsNode); + } + + Token getBeginToken() { + if (isPrefix) return assignmentOperator.getBeginToken(); + return super.getBeginToken(); + } + + Token getEndToken() { + if (isPostfix) return assignmentOperator.getEndToken(); + return super.getEndToken(); + } +} + +class NewExpression extends Expression { + /** The token NEW or CONST or `null` for metadata */ + final Token newToken; + + // Note: we expect that send.receiver is null. + final Send send; + + NewExpression([this.newToken, this.send]); + + NewExpression asNewExpression() => this; + + accept(Visitor visitor) => visitor.visitNewExpression(this); + + visitChildren(Visitor visitor) { + if (send != null) send.accept(visitor); + } + + bool isConst() { + return newToken == null || identical(newToken.stringValue, 'const'); + } + + Token getBeginToken() => newToken != null ? newToken : send.getBeginToken(); + + Token getEndToken() => send.getEndToken(); +} + +class NodeList extends Node { + final Link nodes; + final Token beginToken; + final Token endToken; + final String delimiter; + bool get isEmpty => nodes.isEmpty; + + NodeList([this.beginToken, this.nodes, this.endToken, this.delimiter]); + + Iterator get iterator => nodes.iterator; + + NodeList.singleton(Node node) : this(null, const Link().prepend(node)); + NodeList.empty() : this(null, const Link()); + + NodeList asNodeList() => this; + + int slowLength() { + int result = 0; + for (Link cursor = nodes; !cursor.isEmpty; cursor = cursor.tail) { + result++; + } + return result; + } + + accept(Visitor visitor) => visitor.visitNodeList(this); + + visitChildren(Visitor visitor) { + if (nodes == null) return; + for (Link link = nodes; !link.isEmpty; link = link.tail) { + if (link.head != null) link.head.accept(visitor); + } + } + + Token getBeginToken() { + if (beginToken != null) return beginToken; + if (nodes != null) { + for (Link link = nodes; !link.isEmpty; link = link.tail) { + if (link.head.getBeginToken() != null) { + return link.head.getBeginToken(); + } + if (link.head.getEndToken() != null) { + return link.head.getEndToken(); + } + } + } + return endToken; + } + + Token getEndToken() { + if (endToken != null) return endToken; + if (nodes != null) { + Link link = nodes; + if (link.isEmpty) return beginToken; + while (!link.tail.isEmpty) link = link.tail; + if (link.head.getEndToken() != null) return link.head.getEndToken(); + if (link.head.getBeginToken() != null) return link.head.getBeginToken(); + } + return beginToken; + } +} + +class Block extends Statement { + final NodeList statements; + + Block(this.statements); + + Block asBlock() => this; + + accept(Visitor visitor) => visitor.visitBlock(this); + + visitChildren(Visitor visitor) { + if (statements != null) statements.accept(visitor); + } + + Token getBeginToken() => statements.getBeginToken(); + + Token getEndToken() => statements.getEndToken(); +} + +class If extends Statement { + final ParenthesizedExpression condition; + final Statement thenPart; + final Statement elsePart; + + final Token ifToken; + final Token elseToken; + + If(this.condition, this.thenPart, this.elsePart, + this.ifToken, this.elseToken); + + If asIf() => this; + + bool get hasElsePart => elsePart != null; + + accept(Visitor visitor) => visitor.visitIf(this); + + visitChildren(Visitor visitor) { + if (condition != null) condition.accept(visitor); + if (thenPart != null) thenPart.accept(visitor); + if (elsePart != null) elsePart.accept(visitor); + } + + Token getBeginToken() => ifToken; + + Token getEndToken() { + if (elsePart == null) return thenPart.getEndToken(); + return elsePart.getEndToken(); + } +} + +class Conditional extends Expression { + final Expression condition; + final Expression thenExpression; + final Expression elseExpression; + + final Token questionToken; + final Token colonToken; + + Conditional(this.condition, this.thenExpression, + this.elseExpression, this.questionToken, this.colonToken); + + Conditional asConditional() => this; + + accept(Visitor visitor) => visitor.visitConditional(this); + + visitChildren(Visitor visitor) { + condition.accept(visitor); + thenExpression.accept(visitor); + elseExpression.accept(visitor); + } + + Token getBeginToken() => condition.getBeginToken(); + + Token getEndToken() => elseExpression.getEndToken(); +} + +class For extends Loop { + /** Either a variable declaration or an expression. */ + final Node initializer; + /** Either an expression statement or an empty statement. */ + final Statement conditionStatement; + final NodeList update; + + final Token forToken; + + For(this.initializer, this.conditionStatement, this.update, body, + this.forToken) : super(body); + + For asFor() => this; + + Expression get condition { + if (conditionStatement is ExpressionStatement) { + return conditionStatement.asExpressionStatement().expression; + } else { + return null; + } + } + + accept(Visitor visitor) => visitor.visitFor(this); + + visitChildren(Visitor visitor) { + if (initializer != null) initializer.accept(visitor); + if (conditionStatement != null) conditionStatement.accept(visitor); + if (update != null) update.accept(visitor); + if (body != null) body.accept(visitor); + } + + Token getBeginToken() => forToken; + + Token getEndToken() { + return body.getEndToken(); + } +} + +class FunctionDeclaration extends Statement { + final FunctionExpression function; + + FunctionDeclaration(this.function); + + FunctionDeclaration asFunctionDeclaration() => this; + + accept(Visitor visitor) => visitor.visitFunctionDeclaration(this); + + visitChildren(Visitor visitor) => function.accept(visitor); + + Token getBeginToken() => function.getBeginToken(); + Token getEndToken() => function.getEndToken(); +} + +class FunctionExpression extends Expression { + final Node name; + + /** + * List of VariableDefinitions or NodeList. + * + * A NodeList can only occur at the end and holds named parameters. + */ + final NodeList parameters; + + final Statement body; + final TypeAnnotation returnType; + final Modifiers modifiers; + final NodeList initializers; + + final Token getOrSet; + + FunctionExpression(this.name, this.parameters, this.body, this.returnType, + this.modifiers, this.initializers, this.getOrSet) { + assert(modifiers != null); + } + + FunctionExpression asFunctionExpression() => this; + + accept(Visitor visitor) => visitor.visitFunctionExpression(this); + + bool get isRedirectingFactory { + return body != null && body.asReturn() != null && + body.asReturn().isRedirectingFactoryBody; + } + + visitChildren(Visitor visitor) { + if (modifiers != null) modifiers.accept(visitor); + if (returnType != null) returnType.accept(visitor); + if (name != null) name.accept(visitor); + if (parameters != null) parameters.accept(visitor); + if (initializers != null) initializers.accept(visitor); + if (body != null) body.accept(visitor); + } + + bool hasBody() => body.asEmptyStatement() == null; + + bool hasEmptyBody() { + Block block = body.asBlock(); + if (block == null) return false; + return block.statements.isEmpty; + } + + Token getBeginToken() { + Token token = firstBeginToken(modifiers, returnType); + if (token != null) return token; + if (getOrSet != null) return getOrSet; + return firstBeginToken(name, parameters); + } + + Token getEndToken() { + Token token = (body == null) ? null : body.getEndToken(); + token = (token == null) ? parameters.getEndToken() : token; + return (token == null) ? name.getEndToken() : token; + } +} + +typedef void DecodeErrorHandler(Token token, var error); + +abstract class Literal extends Expression { + final Token token; + final DecodeErrorHandler handler; + + Literal(Token this.token, DecodeErrorHandler this.handler); + + T get value; + + visitChildren(Visitor visitor) {} + + Token getBeginToken() => token; + + Token getEndToken() => token; +} + +class LiteralInt extends Literal { + LiteralInt(Token token, DecodeErrorHandler handler) : super(token, handler); + + LiteralInt asLiteralInt() => this; + + int get value { + try { + Token valueToken = token; + if (identical(valueToken.kind, PLUS_TOKEN)) valueToken = valueToken.next; + return int.parse(valueToken.value); + } on FormatException catch (ex) { + (this.handler)(token, ex); + } + } + + accept(Visitor visitor) => visitor.visitLiteralInt(this); +} + +class LiteralDouble extends Literal { + LiteralDouble(Token token, DecodeErrorHandler handler) + : super(token, handler); + + LiteralDouble asLiteralDouble() => this; + + double get value { + try { + Token valueToken = token; + if (identical(valueToken.kind, PLUS_TOKEN)) valueToken = valueToken.next; + return double.parse(valueToken.value); + } on FormatException catch (ex) { + (this.handler)(token, ex); + } + } + + accept(Visitor visitor) => visitor.visitLiteralDouble(this); +} + +class LiteralBool extends Literal { + LiteralBool(Token token, DecodeErrorHandler handler) : super(token, handler); + + LiteralBool asLiteralBool() => this; + + bool get value { + if (identical(token.stringValue, 'true')) return true; + if (identical(token.stringValue, 'false')) return false; + (this.handler)(token, "not a bool ${token.value}"); + throw false; + } + + accept(Visitor visitor) => visitor.visitLiteralBool(this); +} + + +class StringQuoting { + static const StringQuoting SINGLELINE_DQ = + const StringQuoting($DQ, raw: false, leftQuoteLength: 1); + static const StringQuoting RAW_SINGLELINE_DQ = + const StringQuoting($DQ, raw: true, leftQuoteLength: 1); + static const StringQuoting MULTILINE_DQ = + const StringQuoting($DQ, raw: false, leftQuoteLength: 3); + static const StringQuoting RAW_MULTILINE_DQ = + const StringQuoting($DQ, raw: true, leftQuoteLength: 3); + static const StringQuoting MULTILINE_NL_DQ = + const StringQuoting($DQ, raw: false, leftQuoteLength: 4); + static const StringQuoting RAW_MULTILINE_NL_DQ = + const StringQuoting($DQ, raw: true, leftQuoteLength: 4); + static const StringQuoting MULTILINE_NL2_DQ = + const StringQuoting($DQ, raw: false, leftQuoteLength: 5); + static const StringQuoting RAW_MULTILINE_NL2_DQ = + const StringQuoting($DQ, raw: true, leftQuoteLength: 5); + static const StringQuoting SINGLELINE_SQ = + const StringQuoting($SQ, raw: false, leftQuoteLength: 1); + static const StringQuoting RAW_SINGLELINE_SQ = + const StringQuoting($SQ, raw: true, leftQuoteLength: 1); + static const StringQuoting MULTILINE_SQ = + const StringQuoting($SQ, raw: false, leftQuoteLength: 3); + static const StringQuoting RAW_MULTILINE_SQ = + const StringQuoting($SQ, raw: true, leftQuoteLength: 3); + static const StringQuoting MULTILINE_NL_SQ = + const StringQuoting($SQ, raw: false, leftQuoteLength: 4); + static const StringQuoting RAW_MULTILINE_NL_SQ = + const StringQuoting($SQ, raw: true, leftQuoteLength: 4); + static const StringQuoting MULTILINE_NL2_SQ = + const StringQuoting($SQ, raw: false, leftQuoteLength: 5); + static const StringQuoting RAW_MULTILINE_NL2_SQ = + const StringQuoting($SQ, raw: true, leftQuoteLength: 5); + + + static const List mapping = const [ + SINGLELINE_DQ, + RAW_SINGLELINE_DQ, + MULTILINE_DQ, + RAW_MULTILINE_DQ, + MULTILINE_NL_DQ, + RAW_MULTILINE_NL_DQ, + MULTILINE_NL2_DQ, + RAW_MULTILINE_NL2_DQ, + SINGLELINE_SQ, + RAW_SINGLELINE_SQ, + MULTILINE_SQ, + RAW_MULTILINE_SQ, + MULTILINE_NL_SQ, + RAW_MULTILINE_NL_SQ, + MULTILINE_NL2_SQ, + RAW_MULTILINE_NL2_SQ + ]; + final bool raw; + final int leftQuoteCharCount; + final int quote; + const StringQuoting(this.quote, {bool raw, int leftQuoteLength}) + : this.raw = raw, this.leftQuoteCharCount = leftQuoteLength; + String get quoteChar => identical(quote, $DQ) ? '"' : "'"; + + int get leftQuoteLength => (raw ? 1 : 0) + leftQuoteCharCount; + int get rightQuoteLength => (leftQuoteCharCount > 2) ? 3 : 1; + static StringQuoting getQuoting(int quote, bool raw, int quoteLength) { + int index = quoteLength - 1; + if (quoteLength > 2) index -= 1; + return mapping[(raw ? 1 : 0) + index * 2 + (identical(quote, $SQ) ? 8 : 0)]; + } +} + +/** + * Superclass for classes representing string literals. + */ +abstract class StringNode extends Expression { + DartString get dartString; + bool get isInterpolation; + + StringNode asStringNode() => this; +} + +class LiteralString extends StringNode { + final Token token; + /** Non-null on validated string literals. */ + final DartString dartString; + + LiteralString(this.token, this.dartString); + + LiteralString asLiteralString() => this; + + void visitChildren(Visitor visitor) {} + + bool get isInterpolation => false; + + Token getBeginToken() => token; + Token getEndToken() => token; + + accept(Visitor visitor) => visitor.visitLiteralString(this); +} + +class LiteralNull extends Literal { + LiteralNull(Token token) : super(token, null); + + LiteralNull asLiteralNull() => this; + + String get value => null; + + accept(Visitor visitor) => visitor.visitLiteralNull(this); +} + +class LiteralList extends Expression { + final NodeList typeArguments; + final NodeList elements; + + final Token constKeyword; + + LiteralList(this.typeArguments, this.elements, this.constKeyword); + + bool isConst() => constKeyword != null; + + LiteralList asLiteralList() => this; + accept(Visitor visitor) => visitor.visitLiteralList(this); + + visitChildren(Visitor visitor) { + if (typeArguments != null) typeArguments.accept(visitor); + elements.accept(visitor); + } + + Token getBeginToken() { + if (constKeyword != null) return constKeyword; + return firstBeginToken(typeArguments, elements); + } + + Token getEndToken() => elements.getEndToken(); +} + +class LiteralSymbol extends Expression { + final Token hashToken; + final NodeList identifiers; + + LiteralSymbol(this.hashToken, this.identifiers); + + LiteralSymbol asLiteralSymbol() => this; + + void visitChildren(Visitor visitor) { + if (identifiers != null) identifiers.accept(visitor); + } + + accept(Visitor visitor) => visitor.visitLiteralSymbol(this); + + Token getBeginToken() => hashToken; + + Token getEndToken() => identifiers.getEndToken(); + + String get slowNameString => '${identifiers}'; +} + +class Identifier extends Expression { + final Token token; + + String get source => token.value; + + Identifier(Token this.token); + + bool isThis() => identical(source, 'this'); + + bool isSuper() => identical(source, 'super'); + + Identifier asIdentifier() => this; + + accept(Visitor visitor) => visitor.visitIdentifier(this); + + visitChildren(Visitor visitor) {} + + Token getBeginToken() => token; + + Token getEndToken() => token; +} + +class Operator extends Identifier { + Operator(Token token) : super(token); + + Operator asOperator() => this; + + accept(Visitor visitor) => visitor.visitOperator(this); +} + +class Return extends Statement { + final Node expression; + final Token beginToken; + final Token endToken; + + Return(this.beginToken, this.endToken, this.expression); + + Return asReturn() => this; + + bool get hasExpression => expression != null; + + bool get isRedirectingFactoryBody => beginToken.stringValue == '='; + + accept(Visitor visitor) => visitor.visitReturn(this); + + visitChildren(Visitor visitor) { + if (expression != null) expression.accept(visitor); + } + + Token getBeginToken() => beginToken; + + Token getEndToken() { + if (endToken == null) return expression.getEndToken(); + return endToken; + } +} + +class ExpressionStatement extends Statement { + final Expression expression; + final Token endToken; + + ExpressionStatement(this.expression, this.endToken); + + ExpressionStatement asExpressionStatement() => this; + + accept(Visitor visitor) => visitor.visitExpressionStatement(this); + + visitChildren(Visitor visitor) { + if (expression != null) expression.accept(visitor); + } + + Token getBeginToken() => expression.getBeginToken(); + + Token getEndToken() => endToken; +} + +class Throw extends Expression { + final Expression expression; + + final Token throwToken; + final Token endToken; + + Throw(this.expression, this.throwToken, this.endToken); + + Throw asThrow() => this; + + accept(Visitor visitor) => visitor.visitThrow(this); + + visitChildren(Visitor visitor) { + expression.accept(visitor); + } + + Token getBeginToken() => throwToken; + + Token getEndToken() => endToken; +} + +class Rethrow extends Statement { + final Token throwToken; + final Token endToken; + + Rethrow(this.throwToken, this.endToken); + + Rethrow asRethrow() => this; + + accept(Visitor visitor) => visitor.visitRethrow(this); + visitChildren(Visitor visitor) { } + + Token getBeginToken() => throwToken; + Token getEndToken() => endToken; +} + +class TypeAnnotation extends Node { + final Expression typeName; + final NodeList typeArguments; + + TypeAnnotation(Expression this.typeName, NodeList this.typeArguments); + + TypeAnnotation asTypeAnnotation() => this; + + accept(Visitor visitor) => visitor.visitTypeAnnotation(this); + + visitChildren(Visitor visitor) { + typeName.accept(visitor); + if (typeArguments != null) typeArguments.accept(visitor); + } + + Token getBeginToken() => typeName.getBeginToken(); + + Token getEndToken() => typeName.getEndToken(); +} + +class TypeVariable extends Node { + final Identifier name; + final TypeAnnotation bound; + TypeVariable(Identifier this.name, TypeAnnotation this.bound); + + accept(Visitor visitor) => visitor.visitTypeVariable(this); + + visitChildren(Visitor visitor) { + name.accept(visitor); + if (bound != null) { + bound.accept(visitor); + } + } + + TypeVariable asTypeVariable() => this; + + Token getBeginToken() => name.getBeginToken(); + + Token getEndToken() { + return (bound != null) ? bound.getEndToken() : name.getEndToken(); + } +} + +class VariableDefinitions extends Statement { + final NodeList metadata; + final TypeAnnotation type; + final Modifiers modifiers; + final NodeList definitions; + + VariableDefinitions(this.type, + this.modifiers, + this.definitions) + : this.metadata = null { + assert(modifiers != null); + } + + // TODO(johnniwinther): Make this its own node type. + VariableDefinitions.forParameter(this.metadata, + this.type, + this.modifiers, + this.definitions) { + assert(modifiers != null); + } + + VariableDefinitions asVariableDefinitions() => this; + + accept(Visitor visitor) => visitor.visitVariableDefinitions(this); + + visitChildren(Visitor visitor) { + if (type != null) type.accept(visitor); + if (definitions != null) definitions.accept(visitor); + } + + Token getBeginToken() { + var token = firstBeginToken(modifiers, type); + if (token == null) { + token = definitions.getBeginToken(); + } + return token; + } + + Token getEndToken() => definitions.getEndToken(); +} + +abstract class Loop extends Statement { + Expression get condition; + final Statement body; + + Loop(this.body); + + bool isValidContinueTarget() => true; +} + +class DoWhile extends Loop { + final Token doKeyword; + final Token whileKeyword; + final Token endToken; + + final Expression condition; + + DoWhile(Statement body, Expression this.condition, + Token this.doKeyword, Token this.whileKeyword, Token this.endToken) + : super(body); + + DoWhile asDoWhile() => this; + + accept(Visitor visitor) => visitor.visitDoWhile(this); + + visitChildren(Visitor visitor) { + if (condition != null) condition.accept(visitor); + if (body != null) body.accept(visitor); + } + + Token getBeginToken() => doKeyword; + + Token getEndToken() => endToken; +} + +class While extends Loop { + final Token whileKeyword; + final Expression condition; + + While(Expression this.condition, Statement body, + Token this.whileKeyword) : super(body); + + While asWhile() => this; + + accept(Visitor visitor) => visitor.visitWhile(this); + + visitChildren(Visitor visitor) { + if (condition != null) condition.accept(visitor); + if (body != null) body.accept(visitor); + } + + Token getBeginToken() => whileKeyword; + + Token getEndToken() => body.getEndToken(); +} + +class ParenthesizedExpression extends Expression { + final Expression expression; + final BeginGroupToken beginToken; + + ParenthesizedExpression(Expression this.expression, + BeginGroupToken this.beginToken); + + ParenthesizedExpression asParenthesizedExpression() => this; + + accept(Visitor visitor) => visitor.visitParenthesizedExpression(this); + + visitChildren(Visitor visitor) { + if (expression != null) expression.accept(visitor); + } + + Token getBeginToken() => beginToken; + + Token getEndToken() => beginToken.endGroup; +} + +/** Representation of modifiers such as static, abstract, final, etc. */ +class Modifiers extends Node { + /** + * Pseudo-constant for empty modifiers. + */ + static final Modifiers EMPTY = new Modifiers(new NodeList.empty()); + + /* TODO(ahe): The following should be validated relating to modifiers: + * 1. The nodes must come in a certain order. + * 2. The keywords "var" and "final" may not be used at the same time. + * 3. The keywords "abstract" and "external" may not be used at the same time. + * 4. The type of an element must be null if isVar() is true. + */ + + final NodeList nodes; + /** Bit pattern to easy check what modifiers are present. */ + final int flags; + + static const int FLAG_STATIC = 1; + static const int FLAG_ABSTRACT = FLAG_STATIC << 1; + static const int FLAG_FINAL = FLAG_ABSTRACT << 1; + static const int FLAG_VAR = FLAG_FINAL << 1; + static const int FLAG_CONST = FLAG_VAR << 1; + static const int FLAG_FACTORY = FLAG_CONST << 1; + static const int FLAG_EXTERNAL = FLAG_FACTORY << 1; + + Modifiers(NodeList nodes) : this.withFlags(nodes, computeFlags(nodes.nodes)); + + Modifiers.withFlags(this.nodes, this.flags); + + static int computeFlags(Link nodes) { + int flags = 0; + for (; !nodes.isEmpty; nodes = nodes.tail) { + String value = nodes.head.asIdentifier().source; + if (identical(value, 'static')) flags |= FLAG_STATIC; + else if (identical(value, 'abstract')) flags |= FLAG_ABSTRACT; + else if (identical(value, 'final')) flags |= FLAG_FINAL; + else if (identical(value, 'var')) flags |= FLAG_VAR; + else if (identical(value, 'const')) flags |= FLAG_CONST; + else if (identical(value, 'factory')) flags |= FLAG_FACTORY; + else if (identical(value, 'external')) flags |= FLAG_EXTERNAL; + else throw 'internal error: ${nodes.head}'; + } + return flags; + } + + Node findModifier(String modifier) { + Link nodeList = nodes.nodes; + for (; !nodeList.isEmpty; nodeList = nodeList.tail) { + String value = nodeList.head.asIdentifier().source; + if(identical(value, modifier)) { + return nodeList.head; + } + } + return null; + } + + Modifiers asModifiers() => this; + Token getBeginToken() => nodes.getBeginToken(); + Token getEndToken() => nodes.getEndToken(); + accept(Visitor visitor) => visitor.visitModifiers(this); + visitChildren(Visitor visitor) => nodes.accept(visitor); + + bool isStatic() => (flags & FLAG_STATIC) != 0; + bool isAbstract() => (flags & FLAG_ABSTRACT) != 0; + bool isFinal() => (flags & FLAG_FINAL) != 0; + bool isVar() => (flags & FLAG_VAR) != 0; + bool isConst() => (flags & FLAG_CONST) != 0; + bool isFactory() => (flags & FLAG_FACTORY) != 0; + bool isExternal() => (flags & FLAG_EXTERNAL) != 0; + + Node getStatic() => findModifier('static'); + + /** + * Use this to check if the declaration is either explicitly or implicitly + * final. + */ + bool isFinalOrConst() => isFinal() || isConst(); + + String toString() { + LinkBuilder builder = new LinkBuilder(); + if (isStatic()) builder.addLast('static'); + if (isAbstract()) builder.addLast('abstract'); + if (isFinal()) builder.addLast('final'); + if (isVar()) builder.addLast('var'); + if (isConst()) builder.addLast('const'); + if (isFactory()) builder.addLast('factory'); + if (isExternal()) builder.addLast('external'); + StringBuffer buffer = new StringBuffer(); + builder.toLink().printOn(buffer, ', '); + return buffer.toString(); + } +} + +class StringInterpolation extends StringNode { + final LiteralString string; + final NodeList parts; + + StringInterpolation(this.string, this.parts); + + StringInterpolation asStringInterpolation() => this; + + DartString get dartString => null; + bool get isInterpolation => true; + + accept(Visitor visitor) => visitor.visitStringInterpolation(this); + + visitChildren(Visitor visitor) { + string.accept(visitor); + parts.accept(visitor); + } + + Token getBeginToken() => string.getBeginToken(); + Token getEndToken() => parts.getEndToken(); +} + +class StringInterpolationPart extends Node { + final Expression expression; + final LiteralString string; + + StringInterpolationPart(this.expression, this.string); + + StringInterpolationPart asStringInterpolationPart() => this; + + accept(Visitor visitor) => visitor.visitStringInterpolationPart(this); + + visitChildren(Visitor visitor) { + expression.accept(visitor); + string.accept(visitor); + } + + Token getBeginToken() => expression.getBeginToken(); + + Token getEndToken() => string.getEndToken(); +} + +/** + * A class representing juxtaposed string literals. + * The string literals can be both plain literals and string interpolations. + */ +class StringJuxtaposition extends StringNode { + final Expression first; + final Expression second; + + /** + * Caches the check for whether this juxtaposition contains a string + * interpolation + */ + bool isInterpolationCache = null; + + /** + * Caches a Dart string representation of the entire juxtaposition's + * content. Only juxtapositions that don't (transitively) contains + * interpolations have a static representation. + */ + DartString dartStringCache = null; + + StringJuxtaposition(this.first, this.second); + + StringJuxtaposition asStringJuxtaposition() => this; + + bool get isInterpolation { + if (isInterpolationCache == null) { + isInterpolationCache = (first.accept(const IsInterpolationVisitor()) || + second.accept(const IsInterpolationVisitor())); + } + return isInterpolationCache; + } + + /** + * Retrieve a single DartString that represents this entire juxtaposition + * of string literals. + * Should only be called if [isInterpolation] returns false. + */ + DartString get dartString { + if (isInterpolation) { + throw new SpannableAssertionFailure( + this, "Getting dartString on interpolation;"); + } + if (dartStringCache == null) { + DartString firstString = first.accept(const GetDartStringVisitor()); + DartString secondString = second.accept(const GetDartStringVisitor()); + if (firstString == null || secondString == null) { + return null; + } + dartStringCache = new DartString.concat(firstString, secondString); + } + return dartStringCache; + } + + accept(Visitor visitor) => visitor.visitStringJuxtaposition(this); + + void visitChildren(Visitor visitor) { + first.accept(visitor); + second.accept(visitor); + } + + Token getBeginToken() => first.getBeginToken(); + + Token getEndToken() => second.getEndToken(); +} + +class EmptyStatement extends Statement { + final Token semicolonToken; + + EmptyStatement(this.semicolonToken); + + EmptyStatement asEmptyStatement() => this; + + accept(Visitor visitor) => visitor.visitEmptyStatement(this); + + visitChildren(Visitor visitor) {} + + Token getBeginToken() => semicolonToken; + + Token getEndToken() => semicolonToken; +} + +class LiteralMap extends Expression { + final NodeList typeArguments; + final NodeList entries; + + final Token constKeyword; + + LiteralMap(this.typeArguments, this.entries, this.constKeyword); + + bool isConst() => constKeyword != null; + + LiteralMap asLiteralMap() => this; + + accept(Visitor visitor) => visitor.visitLiteralMap(this); + + visitChildren(Visitor visitor) { + if (typeArguments != null) typeArguments.accept(visitor); + entries.accept(visitor); + } + + Token getBeginToken() { + if (constKeyword != null) return constKeyword; + return firstBeginToken(typeArguments, entries); + } + + Token getEndToken() => entries.getEndToken(); +} + +class LiteralMapEntry extends Node { + final Expression key; + final Expression value; + + final Token colonToken; + + LiteralMapEntry(this.key, this.colonToken, this.value); + + LiteralMapEntry asLiteralMapEntry() => this; + + accept(Visitor visitor) => visitor.visitLiteralMapEntry(this); + + visitChildren(Visitor visitor) { + key.accept(visitor); + value.accept(visitor); + } + + Token getBeginToken() => key.getBeginToken(); + + Token getEndToken() => value.getEndToken(); +} + +class NamedArgument extends Expression { + final Identifier name; + final Expression expression; + + final Token colonToken; + + NamedArgument(this.name, this.colonToken, this.expression); + + NamedArgument asNamedArgument() => this; + + accept(Visitor visitor) => visitor.visitNamedArgument(this); + + visitChildren(Visitor visitor) { + name.accept(visitor); + expression.accept(visitor); + } + + Token getBeginToken() => name.getBeginToken(); + + Token getEndToken() => expression.getEndToken(); +} + +class SwitchStatement extends Statement { + final ParenthesizedExpression parenthesizedExpression; + final NodeList cases; + + final Token switchKeyword; + + SwitchStatement(this.parenthesizedExpression, this.cases, + this.switchKeyword); + + SwitchStatement asSwitchStatement() => this; + + Expression get expression => parenthesizedExpression.expression; + + accept(Visitor visitor) => visitor.visitSwitchStatement(this); + + visitChildren(Visitor visitor) { + parenthesizedExpression.accept(visitor); + cases.accept(visitor); + } + + Token getBeginToken() => switchKeyword; + + Token getEndToken() => cases.getEndToken(); +} + +class CaseMatch extends Node { + final Token caseKeyword; + final Expression expression; + final Token colonToken; + CaseMatch(this.caseKeyword, this.expression, this.colonToken); + + CaseMatch asCaseMatch() => this; + Token getBeginToken() => caseKeyword; + Token getEndToken() => colonToken; + accept(Visitor visitor) => visitor.visitCaseMatch(this); + visitChildren(Visitor visitor) => expression.accept(visitor); +} + +class SwitchCase extends Node { + // The labels and case patterns are collected in [labelsAndCases]. + // The default keyword, if present, is collected in [defaultKeyword]. + // Any actual switch case must have at least one 'case' or 'default' + // clause. + // Notice: The labels and cases can occur interleaved in the source. + // They are separated here, since the order is irrelevant to the meaning + // of the switch. + + /** List of [Label] and [CaseMatch] nodes. */ + final NodeList labelsAndCases; + /** A "default" keyword token, if applicable. */ + final Token defaultKeyword; + /** List of statements, the body of the case. */ + final NodeList statements; + + final Token startToken; + + SwitchCase(this.labelsAndCases, this.defaultKeyword, + this.statements, this.startToken); + + SwitchCase asSwitchCase() => this; + + bool get isDefaultCase => defaultKeyword != null; + + bool isValidContinueTarget() => true; + + accept(Visitor visitor) => visitor.visitSwitchCase(this); + + visitChildren(Visitor visitor) { + labelsAndCases.accept(visitor); + statements.accept(visitor); + } + + Token getBeginToken() { + return startToken; + } + + Token getEndToken() { + if (statements.nodes.isEmpty) { + // All cases must have at least one expression or be the default. + if (defaultKeyword != null) { + // The colon after 'default'. + return defaultKeyword.next; + } + // The colon after the last expression. + return labelsAndCases.getEndToken(); + } else { + return statements.getEndToken(); + } + } +} + +abstract class GotoStatement extends Statement { + final Identifier target; + final Token keywordToken; + final Token semicolonToken; + + GotoStatement(this.target, this.keywordToken, this.semicolonToken); + + visitChildren(Visitor visitor) { + if (target != null) target.accept(visitor); + } + + Token getBeginToken() => keywordToken; + + Token getEndToken() => semicolonToken; + + // TODO(ahe): make class abstract instead of adding an abstract method. + accept(Visitor visitor); +} + +class BreakStatement extends GotoStatement { + BreakStatement(Identifier target, Token keywordToken, Token semicolonToken) + : super(target, keywordToken, semicolonToken); + + BreakStatement asBreakStatement() => this; + + accept(Visitor visitor) => visitor.visitBreakStatement(this); +} + +class ContinueStatement extends GotoStatement { + ContinueStatement(Identifier target, Token keywordToken, Token semicolonToken) + : super(target, keywordToken, semicolonToken); + + ContinueStatement asContinueStatement() => this; + + accept(Visitor visitor) => visitor.visitContinueStatement(this); +} + +class ForIn extends Loop { + final Node declaredIdentifier; + final Expression expression; + + final Token forToken; + final Token inToken; + + ForIn(this.declaredIdentifier, this.expression, + Statement body, this.forToken, this.inToken) : super(body); + + Expression get condition => null; + + ForIn asForIn() => this; + + accept(Visitor visitor) => visitor.visitForIn(this); + + visitChildren(Visitor visitor) { + declaredIdentifier.accept(visitor); + expression.accept(visitor); + body.accept(visitor); + } + + Token getBeginToken() => forToken; + + Token getEndToken() => body.getEndToken(); +} + +class Label extends Node { + final Identifier identifier; + final Token colonToken; + + Label(this.identifier, this.colonToken); + + String get labelName => identifier.source; + + Label asLabel() => this; + + accept(Visitor visitor) => visitor.visitLabel(this); + + void visitChildren(Visitor visitor) { + identifier.accept(visitor); + } + + Token getBeginToken() => identifier.token; + Token getEndToken() => colonToken; +} + +class LabeledStatement extends Statement { + final NodeList labels; + final Statement statement; + + LabeledStatement(this.labels, this.statement); + + LabeledStatement asLabeledStatement() => this; + + accept(Visitor visitor) => visitor.visitLabeledStatement(this); + + visitChildren(Visitor visitor) { + labels.accept(visitor); + statement.accept(visitor); + } + + Token getBeginToken() => labels.getBeginToken(); + + Token getEndToken() => statement.getEndToken(); + + bool isValidContinueTarget() => statement.isValidContinueTarget(); +} + +abstract class LibraryTag extends Node { + final Link metadata; + + LibraryTag(this.metadata); + + bool get isLibraryName => false; + bool get isImport => false; + bool get isExport => false; + bool get isPart => false; + bool get isPartOf => false; +} + +class LibraryName extends LibraryTag { + final Expression name; + + final Token libraryKeyword; + + LibraryName(this.libraryKeyword, + this.name, + Link metadata) + : super(metadata); + + bool get isLibraryName => true; + + LibraryName asLibraryName() => this; + + accept(Visitor visitor) => visitor.visitLibraryName(this); + + visitChildren(Visitor visitor) => name.accept(visitor); + + Token getBeginToken() => libraryKeyword; + + Token getEndToken() => name.getEndToken().next; +} + +/** + * This tag describes a dependency between one library and the exported + * identifiers of another library. The other library is specified by the [uri]. + * Combinators filter away some identifiers from the other library. + */ +abstract class LibraryDependency extends LibraryTag { + final StringNode uri; + final NodeList combinators; + + LibraryDependency(this.uri, + this.combinators, + Link metadata) + : super(metadata); + + LibraryDependency asLibraryDependency() => this; +} + +/** + * An [:import:] library tag. + * + * An import tag is dependency on another library where the exported identifiers + * are put into the import scope of the importing library. The import scope is + * only visible inside the library. + */ +class Import extends LibraryDependency { + final Identifier prefix; + final Token importKeyword; + final bool isDeferred; + + Import(this.importKeyword, StringNode uri, + this.prefix, NodeList combinators, + Link metadata, + {this.isDeferred}) + : super(uri, combinators, metadata); + + bool get isImport => true; + + Import asImport() => this; + + accept(Visitor visitor) => visitor.visitImport(this); + + visitChildren(Visitor visitor) { + uri.accept(visitor); + if (prefix != null) prefix.accept(visitor); + if (combinators != null) combinators.accept(visitor); + } + + Token getBeginToken() => importKeyword; + + Token getEndToken() { + if (combinators != null) return combinators.getEndToken().next; + if (prefix != null) return prefix.getEndToken().next; + return uri.getEndToken().next; + } +} + +/** + * An [:export:] library tag. + * + * An export tag is dependency on another library where the exported identifiers + * are put into the export scope of the exporting library. The export scope is + * not visible inside the library. + */ +class Export extends LibraryDependency { + final Token exportKeyword; + + Export(this.exportKeyword, + StringNode uri, + NodeList combinators, + Link metadata) + : super(uri, combinators, metadata); + + bool get isExport => true; + + Export asExport() => this; + + accept(Visitor visitor) => visitor.visitExport(this); + + visitChildren(Visitor visitor) { + uri.accept(visitor); + if (combinators != null) combinators.accept(visitor); + } + + Token getBeginToken() => exportKeyword; + + Token getEndToken() { + if (combinators != null) return combinators.getEndToken().next; + return uri.getEndToken().next; + } +} + +class Part extends LibraryTag { + final StringNode uri; + + final Token partKeyword; + + Part(this.partKeyword, this.uri, Link metadata) + : super(metadata); + + bool get isPart => true; + + Part asPart() => this; + + accept(Visitor visitor) => visitor.visitPart(this); + + visitChildren(Visitor visitor) => uri.accept(visitor); + + Token getBeginToken() => partKeyword; + + Token getEndToken() => uri.getEndToken().next; +} + +class PartOf extends Node { + final Expression name; + + final Token partKeyword; + + final Link metadata; + + PartOf(this.partKeyword, this.name, this.metadata); + + Token get ofKeyword => partKeyword.next; + + bool get isPartOf => true; + + PartOf asPartOf() => this; + + accept(Visitor visitor) => visitor.visitPartOf(this); + + visitChildren(Visitor visitor) => name.accept(visitor); + + Token getBeginToken() => partKeyword; + + Token getEndToken() => name.getEndToken().next; +} + +class Combinator extends Node { + final NodeList identifiers; + + final Token keywordToken; + + Combinator(this.identifiers, this.keywordToken); + + bool get isShow => identical(keywordToken.stringValue, 'show'); + + bool get isHide => identical(keywordToken.stringValue, 'hide'); + + Combinator asCombinator() => this; + + accept(Visitor visitor) => visitor.visitCombinator(this); + + visitChildren(Visitor visitor) => identifiers.accept(visitor); + + Token getBeginToken() => keywordToken; + + Token getEndToken() => identifiers.getEndToken(); +} + +class Typedef extends Node { + final TypeAnnotation returnType; + final Identifier name; + final NodeList typeParameters; + final NodeList formals; + + final Token typedefKeyword; + final Token endToken; + + Typedef(this.returnType, this.name, this.typeParameters, this.formals, + this.typedefKeyword, this.endToken); + + Typedef asTypedef() => this; + + accept(Visitor visitor) => visitor.visitTypedef(this); + + visitChildren(Visitor visitor) { + if (returnType != null) returnType.accept(visitor); + name.accept(visitor); + if (typeParameters != null) typeParameters.accept(visitor); + formals.accept(visitor); + } + + Token getBeginToken() => typedefKeyword; + + Token getEndToken() => endToken; +} + +class TryStatement extends Statement { + final Block tryBlock; + final NodeList catchBlocks; + final Block finallyBlock; + + final Token tryKeyword; + final Token finallyKeyword; + + TryStatement(this.tryBlock, this.catchBlocks, this.finallyBlock, + this.tryKeyword, this.finallyKeyword); + + TryStatement asTryStatement() => this; + + accept(Visitor visitor) => visitor.visitTryStatement(this); + + visitChildren(Visitor visitor) { + tryBlock.accept(visitor); + catchBlocks.accept(visitor); + if (finallyBlock != null) finallyBlock.accept(visitor); + } + + Token getBeginToken() => tryKeyword; + + Token getEndToken() { + if (finallyBlock != null) return finallyBlock.getEndToken(); + if (!catchBlocks.isEmpty) return catchBlocks.getEndToken(); + return tryBlock.getEndToken(); + } +} + +class Cascade extends Expression { + final Expression expression; + Cascade(this.expression); + + Cascade asCascade() => this; + accept(Visitor visitor) => visitor.visitCascade(this); + + void visitChildren(Visitor visitor) { + expression.accept(visitor); + } + + Token getBeginToken() => expression.getBeginToken(); + + Token getEndToken() => expression.getEndToken(); +} + +class CascadeReceiver extends Expression { + final Expression expression; + final Token cascadeOperator; + CascadeReceiver(this.expression, this.cascadeOperator); + + CascadeReceiver asCascadeReceiver() => this; + accept(Visitor visitor) => visitor.visitCascadeReceiver(this); + + void visitChildren(Visitor visitor) { + expression.accept(visitor); + } + + Token getBeginToken() => expression.getBeginToken(); + + Token getEndToken() => expression.getEndToken(); +} + +class CatchBlock extends Node { + final TypeAnnotation type; + final NodeList formals; + final Block block; + + final Token onKeyword; + final Token catchKeyword; + + CatchBlock(this.type, this.formals, this.block, + this.onKeyword, this.catchKeyword); + + CatchBlock asCatchBlock() => this; + + accept(Visitor visitor) => visitor.visitCatchBlock(this); + + Node get exception { + if (formals == null || formals.nodes.isEmpty) return null; + VariableDefinitions declarations = formals.nodes.head; + return declarations.definitions.nodes.head; + } + + Node get trace { + if (formals == null || formals.nodes.isEmpty) return null; + Link declarations = formals.nodes.tail; + if (declarations.isEmpty) return null; + VariableDefinitions head = declarations.head; + return head.definitions.nodes.head; + } + + visitChildren(Visitor visitor) { + if (type != null) type.accept(visitor); + if (formals != null) formals.accept(visitor); + block.accept(visitor); + } + + Token getBeginToken() => onKeyword != null ? onKeyword : catchKeyword; + + Token getEndToken() => block.getEndToken(); +} + +class Metadata extends Node { + final Token token; + final Expression expression; + + Metadata(this.token, this.expression); + + Metadata asMetadata() => this; + + accept(Visitor visitor) => visitor.visitMetadata(this); + + visitChildren(Visitor visitor) { + expression.accept(visitor); + } + + Token getBeginToken() => token; + + Token getEndToken() => expression.getEndToken(); +} + +class Initializers { + static bool isSuperConstructorCall(Send node) { + return (node.receiver == null && node.selector.isSuper()) || + (node.receiver != null && + node.receiver.isSuper() && + node.selector.asIdentifier() != null); + } + + static bool isConstructorRedirect(Send node) { + return (node.receiver == null && node.selector.isThis()) || + (node.receiver != null && + node.receiver.isThis() && + node.selector.asIdentifier() != null); + } +} + +class GetDartStringVisitor extends Visitor { + const GetDartStringVisitor(); + DartString visitNode(Node node) => null; + DartString visitStringJuxtaposition(StringJuxtaposition node) + => node.dartString; + DartString visitLiteralString(LiteralString node) => node.dartString; +} + +class IsInterpolationVisitor extends Visitor { + const IsInterpolationVisitor(); + bool visitNode(Node node) => false; + bool visitStringInterpolation(StringInterpolation node) => true; + bool visitStringJuxtaposition(StringJuxtaposition node) + => node.isInterpolation; +} + diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/prettyprint.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/prettyprint.dart new file mode 100644 index 0000000..c7c1a32 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/prettyprint.dart @@ -0,0 +1,510 @@ +// Copyright (c) 2012, 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. + +part of tree; + +/** + * Pretty-prints Node tree in XML-like format. + * + * TODO(smok): Add main() to run from command-line to print out tree for given + * .dart file. + */ +class PrettyPrinter implements Visitor { + + /** String used to represent one level of indent. */ + static const String INDENT = " "; + + StringBuffer sb; + Link tagStack; + + PrettyPrinter() : + sb = new StringBuffer(), + tagStack = const Link(); + + void pushTag(String tag) { + tagStack = tagStack.prepend(tag); + } + + String popTag() { + assert(!tagStack.isEmpty); + String tag = tagStack.head; + tagStack = tagStack.tail; + return tag; + } + + /** + * Adds given string to result string. + */ + void add(String string) { + sb.write(string); + } + + void addBeginAndEndTokensToParams(Node node, Map params) { + params['getBeginToken'] = tokenToStringOrNull(node.getBeginToken()); + params['getEndToken'] = tokenToStringOrNull(node.getEndToken()); + } + + /** + * Adds given node type to result string. + * The method "opens" the node, meaning that all output after calling + * this method and before calling closeNode() will represent contents + * of given node. + */ + void openNode(Node node, String type, [Map params]) { + if (params == null) params = new Map(); + addCurrentIndent(); + sb.write("<"); + addBeginAndEndTokensToParams(node, params); + addTypeWithParams(type, params); + sb.write(">\n"); + pushTag(type); + } + + /** + * Adds given node to result string. + */ + void openAndCloseNode(Node node, String type, [Map params]) { + if (params == null) params = new Map(); + addCurrentIndent(); + sb.write("<"); + addBeginAndEndTokensToParams(node, params); + addTypeWithParams(type, params); + sb.write("/>\n"); + } + + /** + * Closes current node type. + */ + void closeNode() { + String tag = popTag(); + addCurrentIndent(); + sb.write("\n"); + } + + void addTypeWithParams(String type, [Map params]) { + if (params == null) params = new Map(); + sb.write("${type}"); + params.forEach((k, v) { + String value; + if (v != null) { + var str = v; + if (v is Token) str = v.value; + value = str + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', "'"); + } else { + value = "[null]"; + } + sb.write(' $k="$value"'); + }); + } + + void addCurrentIndent() { + tagStack.forEach((_) { sb.write(INDENT); }); + } + + /** + * Pretty-prints given node tree into string. + */ + static String prettyPrint(Node node) { + var p = new PrettyPrinter(); + node.accept(p); + return p.sb.toString(); + } + + visitNodeWithChildren(Node node, String type) { + openNode(node, type); + node.visitChildren(this); + closeNode(); + } + + visitBlock(Block node) { + visitNodeWithChildren(node, "Block"); + } + + visitBreakStatement(BreakStatement node) { + visitNodeWithChildren(node, "BreakStatement"); + } + + visitCascade(Cascade node) { + visitNodeWithChildren(node, "Cascade"); + } + + visitCascadeReceiver(CascadeReceiver node) { + visitNodeWithChildren(node, "CascadeReceiver"); + } + + visitCaseMatch(CaseMatch node) { + visitNodeWithChildren(node, "CaseMatch"); + } + + visitCatchBlock(CatchBlock node) { + visitNodeWithChildren(node, "CatchBlock"); + } + + visitClassNode(ClassNode node) { + openNode(node, "ClassNode", { + "extendsKeyword" : tokenToStringOrNull(node.extendsKeyword) + }); + visitChildNode(node.name, "name"); + visitChildNode(node.superclass, "superclass"); + visitChildNode(node.interfaces, "interfaces"); + visitChildNode(node.typeParameters, "typeParameters"); + closeNode(); + } + + visitConditional(Conditional node) { + visitNodeWithChildren(node, "Conditional"); + } + + visitContinueStatement(ContinueStatement node) { + visitNodeWithChildren(node, "ContinueStatement"); + } + + visitDoWhile(DoWhile node) { + visitNodeWithChildren(node, "DoWhile"); + } + + visitEmptyStatement(EmptyStatement node) { + visitNodeWithChildren(node, "EmptyStatement"); + } + + visitExpressionStatement(ExpressionStatement node) { + visitNodeWithChildren(node, "ExpressionStatement"); + } + + visitFor(For node) { + visitNodeWithChildren(node, "For"); + } + + visitForIn(ForIn node) { + visitNodeWithChildren(node, "ForIn"); + } + + visitFunctionDeclaration(FunctionDeclaration node) { + visitNodeWithChildren(node, "FunctionDeclaration"); + } + + visitFunctionExpression(FunctionExpression node) { + openNode(node, "FunctionExpression", { + "getOrSet" : tokenToStringOrNull(node.getOrSet) + }); + visitChildNode(node.modifiers, "modifiers"); + visitChildNode(node.returnType, "returnType"); + visitChildNode(node.name, "name"); + visitChildNode(node.parameters, "parameters"); + visitChildNode(node.initializers, "initializers"); + visitChildNode(node.body, "body"); + closeNode(); + } + + visitIdentifier(Identifier node) { + openAndCloseNode(node, "Identifier", {"token" : node.token}); + } + + visitIf(If node) { + visitNodeWithChildren(node, "If"); + } + + visitLabel(Label node) { + visitNodeWithChildren(node, "Label"); + } + + visitLabeledStatement(LabeledStatement node) { + visitNodeWithChildren(node, "LabeledStatement"); + } + + // Custom. + printLiteral(Literal node, String type) { + openAndCloseNode(node, type, {"value" : node.value.toString()}); + } + + visitLiteralBool(LiteralBool node) { + printLiteral(node, "LiteralBool"); + } + + visitLiteralDouble(LiteralDouble node) { + printLiteral(node, "LiteralDouble"); + } + + visitLiteralInt(LiteralInt node) { + printLiteral(node, "LiteralInt"); + } + + /** Returns token string value or [:null:] if token is [:null:]. */ + tokenToStringOrNull(Token token) => token == null ? null : token.stringValue; + + visitLiteralList(LiteralList node) { + openNode(node, "LiteralList", { + "constKeyword" : tokenToStringOrNull(node.constKeyword) + }); + visitChildNode(node.typeArguments, "typeArguments"); + visitChildNode(node.elements, "elements"); + closeNode(); + } + + visitLiteralMap(LiteralMap node) { + visitNodeWithChildren(node, "LiteralMap"); + } + + visitLiteralMapEntry(LiteralMapEntry node) { + visitNodeWithChildren(node, "LiteralMapEntry"); + } + + visitLiteralNull(LiteralNull node) { + printLiteral(node, "LiteralNull"); + } + + visitLiteralString(LiteralString node) { + openAndCloseNode(node, "LiteralString", + {"value" : node.token}); + } + + visitMixinApplication(MixinApplication node) { + visitNodeWithChildren(node, "MixinApplication"); + } + + visitModifiers(Modifiers node) { + visitNodeWithChildren(node, "Modifiers"); + } + + visitNamedArgument(NamedArgument node) { + visitNodeWithChildren(node, "NamedArgument"); + } + + visitNamedMixinApplication(NamedMixinApplication node) { + visitNodeWithChildren(node, "NamedMixinApplication"); + } + + visitNewExpression(NewExpression node) { + visitNodeWithChildren(node, "NewExpression"); + } + + visitNodeList(NodeList node) { + var params = { "delimiter" : node.delimiter }; + if (node.nodes.toList().length == 0) { + openAndCloseNode(node, "NodeList", params); + } else { + openNode(node, "NodeList", params); + node.visitChildren(this); + closeNode(); + } + } + + visitOperator(Operator node) { + openAndCloseNode(node, "Operator", {"value" : node.token}); + } + + visitParenthesizedExpression(ParenthesizedExpression node) { + visitNodeWithChildren(node, "ParenthesizedExpression"); + } + + visitRethrow(Rethrow node) { + visitNodeWithChildren(node, "Rethrow"); + } + + visitReturn(Return node) { + openNode(node, "Return"); + visitChildNode(node.expression, "expression"); + closeNode(); + } + + visitChildNode(Node node, String fieldName) { + if (node == null) return; + addCurrentIndent(); + sb.write("<$fieldName>\n"); + pushTag(fieldName); + node.accept(this); + popTag(); + addCurrentIndent(); + sb.write("\n"); + } + + openSendNodeWithFields(Send node, String type) { + openNode(node, type, { + "isPrefix" : "${node.isPrefix}", + "isPostfix" : "${node.isPostfix}", + "isIndex" : "${node.isIndex}" + }); + visitChildNode(node.receiver, "receiver"); + visitChildNode(node.selector, "selector"); + visitChildNode(node.argumentsNode, "argumentsNode"); + } + + visitSend(Send node) { + openSendNodeWithFields(node, "Send"); + closeNode(); + } + + visitSendSet(SendSet node) { + openSendNodeWithFields(node, "SendSet"); + visitChildNode(node.assignmentOperator, "assignmentOperator"); + closeNode(); + } + + visitStringInterpolation(StringInterpolation node) { + visitNodeWithChildren(node, "StringInterpolation"); + } + + visitStringInterpolationPart(StringInterpolationPart node) { + visitNodeWithChildren(node, "StringInterpolationPart"); + } + + visitStringJuxtaposition(StringJuxtaposition node) { + visitNodeWithChildren(node, "StringJuxtaposition"); + } + + visitSwitchCase(SwitchCase node) { + visitNodeWithChildren(node, "SwitchCase"); + } + + visitSwitchStatement(SwitchStatement node) { + visitNodeWithChildren(node, "SwitchStatement"); + } + + visitLiteralSymbol(LiteralSymbol node) { + openNode(node, "LiteralSymbol"); + visitChildNode(node.identifiers, "identifiers"); + closeNode(); + } + + visitThrow(Throw node) { + visitNodeWithChildren(node, "Throw"); + } + + visitTryStatement(TryStatement node) { + visitNodeWithChildren(node, "TryStatement"); + } + + visitTypeAnnotation(TypeAnnotation node) { + openNode(node, "TypeAnnotation"); + visitChildNode(node.typeName, "typeName"); + visitChildNode(node.typeArguments, "typeArguments"); + closeNode(); + } + + visitTypedef(Typedef node) { + visitNodeWithChildren(node, "Typedef"); + } + + visitTypeVariable(TypeVariable node) { + openNode(node, "TypeVariable"); + visitChildNode(node.name, "name"); + visitChildNode(node.bound, "bound"); + closeNode(); + } + + visitVariableDefinitions(VariableDefinitions node) { + openNode(node, "VariableDefinitions"); + visitChildNode(node.type, "type"); + visitChildNode(node.modifiers, "modifiers"); + visitChildNode(node.definitions, "definitions"); + closeNode(); + } + + visitWhile(While node) { + visitNodeWithChildren(node, "While"); + } + + visitMetadata(Metadata node) { + openNode(node, "Metadata", { + "token": node.token + }); + visitChildNode(node.expression, "expression"); + closeNode(); + } + + visitCombinator(Combinator node) { + openNode(node, "Combinator", {"isShow" : "${node.isShow}", + "isHide" : "${node.isHide}"}); + closeNode(); + } + + visitExport(Export node) { + openNode(node, "Export"); + visitChildNode(node.uri, "uri"); + visitChildNode(node.combinators, "combinators"); + closeNode(); + } + + visitImport(Import node) { + openNode(node, "Import", { + "isDeferred" : "${node.isDeferred}"}); + visitChildNode(node.uri, "uri"); + visitChildNode(node.combinators, "combinators"); + if (node.prefix != null) { + visitChildNode(node.prefix, "prefix"); + } + closeNode(); + } + + visitPart(Part node) { + openNode(node, "Part"); + visitChildNode(node.uri, "uri"); + closeNode(); + } + + visitPartOf(PartOf node) { + openNode(node, "PartOf"); + visitChildNode(node.name, "name"); + closeNode(); + } + + visitLibraryName(LibraryName node) { + openNode(node, "LibraryName"); + visitChildNode(node.name, "name"); + closeNode(); + } + + visitNode(Node node) { + unimplemented('visitNode', node: node); + } + + visitLibraryDependency(Node node) { + unimplemented('visitNode', node: node); + } + + visitLibraryTag(LibraryTag node) { + unimplemented('visitNode', node: node); + } + + visitLiteral(Literal node) { + unimplemented('visitNode', node: node); + } + + visitLoop(Loop node) { + unimplemented('visitNode', node: node); + } + + visitPostfix(Postfix node) { + unimplemented('visitNode', node: node); + } + + visitPrefix(Prefix node) { + unimplemented('visitNode', node: node); + } + + visitStringNode(StringNode node) { + unimplemented('visitNode', node: node); + } + + visitStatement(Statement node) { + unimplemented('visitNode', node: node); + } + + visitExpression(Expression node) { + unimplemented('visitNode', node: node); + } + + visitGotoStatement(GotoStatement node) { + unimplemented('visitNode', node: node); + } + + unimplemented(String message, {Node node}) { + throw message; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/tree.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/tree.dart new file mode 100644 index 0000000..cc1ceb0 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/tree.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2012, 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. + +library tree; + +import 'dart:collection'; + +import '../scanner/scannerlib.dart'; +import '../util/util.dart'; +import '../util/characters.dart'; + +import '../resolution/secret_tree_element.dart' show TreeElementMixin; + +import '../elements/elements.dart' show MetadataAnnotation; + +part 'dartstring.dart'; +part 'nodes.dart'; +part 'prettyprint.dart'; +part 'unparser.dart'; +part 'visitors.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/unparser.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/unparser.dart new file mode 100644 index 0000000..88fa1d7 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/unparser.dart @@ -0,0 +1,626 @@ +// Copyright (c) 2012, 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. + +part of tree; + +String unparse(Node node) { + Unparser unparser = new Unparser(); + unparser.unparse(node); + return unparser.result; +} + +class Unparser implements Visitor { + final StringBuffer sb; + + String get result => sb.toString(); + + Unparser() : sb = new StringBuffer(); + + void add(String string) { + sb.write(string); + } + + void addToken(Token token) { + if (token == null) return; + add(token.value); + if (identical(token.kind, KEYWORD_TOKEN) + || identical(token.kind, IDENTIFIER_TOKEN)) { + sb.write(' '); + } + } + + unparse(Node node) { visit(node); } + + visit(Node node) { + if (node != null) node.accept(this); + } + + visitBlock(Block node) { + visit(node.statements); + } + + visitCascade(Cascade node) { + visit(node.expression); + } + + visitCascadeReceiver(CascadeReceiver node) { + visit(node.expression); + } + + unparseClassWithBody(ClassNode node, members) { + addToken(node.beginToken); + if (node.beginToken.stringValue == 'abstract') { + addToken(node.beginToken.next); + } + visit(node.name); + if (node.typeParameters != null) { + visit(node.typeParameters); + } + if (node.extendsKeyword != null) { + sb.write(' '); + addToken(node.extendsKeyword); + visit(node.superclass); + } + if (!node.interfaces.isEmpty) { + sb.write(' '); + visit(node.interfaces); + } + sb.write('{'); + for (final member in members) { + visit(member); + } + sb.write('}'); + } + + visitClassNode(ClassNode node) { + unparseClassWithBody(node, node.body.nodes); + } + + visitMixinApplication(MixinApplication node) { + visit(node.superclass); + sb.write(' with '); + visit(node.mixins); + } + + visitNamedMixinApplication(NamedMixinApplication node) { + if (!node.modifiers.nodes.isEmpty) { + visit(node.modifiers); + sb.write(' '); + } + sb.write('class '); + visit(node.name); + if (node.typeParameters != null) { + visit(node.typeParameters); + } + sb.write(' = '); + visit(node.mixinApplication); + if (node.interfaces != null) { + sb.write(' implements '); + visit(node.interfaces); + } + sb.write(';'); + } + + visitConditional(Conditional node) { + visit(node.condition); + add(node.questionToken.value); + visit(node.thenExpression); + add(node.colonToken.value); + visit(node.elseExpression); + } + + visitExpressionStatement(ExpressionStatement node) { + visit(node.expression); + add(node.endToken.value); + } + + visitFor(For node) { + add(node.forToken.value); + sb.write('('); + visit(node.initializer); + sb.write(';'); + visit(node.conditionStatement); + visit(node.update); + sb.write(')'); + visit(node.body); + } + + visitFunctionDeclaration(FunctionDeclaration node) { + visit(node.function); + } + + void unparseFunctionName(Node name) { + // TODO(antonm): that's a workaround as currently FunctionExpression + // names are modelled with Send and it emits operator[] as only + // operator, without [] which are expected to be emitted with + // arguments. + if (name is Send) { + Send send = name; + assert(send is !SendSet); + if (!send.isOperator) { + // Looks like a factory method. + visit(send.receiver); + sb.write('.'); + } else { + visit(send.receiver); + Identifier identifier = send.selector.asIdentifier(); + if (identical(identifier.token.kind, KEYWORD_TOKEN)) { + sb.write(' '); + } else if (identifier.source == 'negate') { + // TODO(ahe): Remove special case for negate. + sb.write(' '); + } + } + visit(send.selector); + } else { + visit(name); + } + } + + visitFunctionExpression(FunctionExpression node) { + if (!node.modifiers.nodes.isEmpty) { + visit(node.modifiers); + sb.write(' '); + } + if (node.returnType != null) { + visit(node.returnType); + sb.write(' '); + } + if (node.getOrSet != null) { + add(node.getOrSet.value); + sb.write(' '); + } + unparseFunctionName(node.name); + visit(node.parameters); + visit(node.initializers); + visit(node.body); + } + + visitIdentifier(Identifier node) { + add(node.token.value); + } + + visitIf(If node) { + add(node.ifToken.value); + visit(node.condition); + visit(node.thenPart); + if (node.hasElsePart) { + add(node.elseToken.value); + if (node.elsePart is !Block) sb.write(' '); + visit(node.elsePart); + } + } + + visitLiteralBool(LiteralBool node) { + add(node.token.value); + } + + visitLiteralDouble(LiteralDouble node) { + add(node.token.value); + // -Lit is represented as a send. + if (node.token.kind == PLUS_TOKEN) add(node.token.next.value); + } + + visitLiteralInt(LiteralInt node) { + add(node.token.value); + // -Lit is represented as a send. + if (node.token.kind == PLUS_TOKEN) add(node.token.next.value); + } + + visitLiteralString(LiteralString node) { + add(node.token.value); + } + + visitStringJuxtaposition(StringJuxtaposition node) { + visit(node.first); + sb.write(" "); + visit(node.second); + } + + visitLiteralNull(LiteralNull node) { + add(node.token.value); + } + + visitLiteralSymbol(LiteralSymbol node) { + add(node.hashToken.value); + visit(node.identifiers); + } + + visitNewExpression(NewExpression node) { + addToken(node.newToken); + visit(node.send); + } + + visitLiteralList(LiteralList node) { + if (node.constKeyword != null) add(node.constKeyword.value); + visit(node.typeArguments); + visit(node.elements); + // If list is empty, emit space after [] to disambiguate cases like []==[]. + if (node.elements.isEmpty) sb.write(' '); + } + + visitModifiers(Modifiers node) => node.visitChildren(this); + + /** + * Unparses given NodeList starting from specific node. + */ + unparseNodeListFrom(NodeList node, Link from) { + if (from.isEmpty) return; + String delimiter = (node.delimiter == null) ? "" : "${node.delimiter}"; + visit(from.head); + for (Link link = from.tail; !link.isEmpty; link = link.tail) { + sb.write(delimiter); + visit(link.head); + } + } + + visitNodeList(NodeList node) { + addToken(node.beginToken); + if (node.nodes != null) { + unparseNodeListFrom(node, node.nodes); + } + if (node.endToken != null) add(node.endToken.value); + } + + visitOperator(Operator node) { + visitIdentifier(node); + } + + visitRethrow(Rethrow node) { + sb.write('rethrow;'); + } + + visitReturn(Return node) { + if (node.isRedirectingFactoryBody) { + sb.write(' '); + } + add(node.beginToken.value); + if (node.hasExpression && node.beginToken.stringValue != '=>') { + sb.write(' '); + } + visit(node.expression); + if (node.endToken != null) add(node.endToken.value); + } + + unparseSendReceiver(Send node, {bool spacesNeeded: false}) { + if (node.receiver == null) return; + visit(node.receiver); + CascadeReceiver asCascadeReceiver = node.receiver.asCascadeReceiver(); + if (asCascadeReceiver != null) { + add(asCascadeReceiver.cascadeOperator.value); + } else if (node.selector.asOperator() == null) { + sb.write('.'); + } else if (spacesNeeded) { + sb.write(' '); + } + } + + visitSend(Send node) { + Operator op = node.selector.asOperator(); + String opString = op != null ? op.source : null; + bool spacesNeeded = identical(opString, 'is') || identical(opString, 'as'); + + if (node.isPrefix) visit(node.selector); + unparseSendReceiver(node, spacesNeeded: spacesNeeded); + if (!node.isPrefix && !node.isIndex) visit(node.selector); + if (spacesNeeded) sb.write(' '); + // Also add a space for sequences like x + +1 and y - -y. + // TODO(ahe): remove case for '+' when we drop the support for it. + if (node.argumentsNode != null && (identical(opString, '-') + || identical(opString, '+'))) { + Token beginToken = node.argumentsNode.getBeginToken(); + if (beginToken != null && identical(beginToken.stringValue, opString)) { + sb.write(' '); + } + } + visit(node.argumentsNode); + } + + visitSendSet(SendSet node) { + if (node.isPrefix) { + sb.write(' '); + visit(node.assignmentOperator); + } + unparseSendReceiver(node); + if (node.isIndex) { + sb.write('['); + visit(node.arguments.head); + sb.write(']'); + if (!node.isPrefix) visit(node.assignmentOperator); + unparseNodeListFrom(node.argumentsNode, node.argumentsNode.nodes.tail); + } else { + visit(node.selector); + if (!node.isPrefix) { + visit(node.assignmentOperator); + if (node.assignmentOperator.source != '=') sb.write(' '); + } + visit(node.argumentsNode); + } + } + + visitThrow(Throw node) { + add(node.throwToken.value); + sb.write(' '); + visit(node.expression); + } + + visitTypeAnnotation(TypeAnnotation node) { + visit(node.typeName); + visit(node.typeArguments); + } + + visitTypeVariable(TypeVariable node) { + visit(node.name); + if (node.bound != null) { + sb.write(' extends '); + visit(node.bound); + } + } + + visitVariableDefinitions(VariableDefinitions node) { + if (node.metadata != null) { + visit(node.metadata); + sb.write(' '); + } + visit(node.modifiers); + if (!node.modifiers.nodes.isEmpty) { + sb.write(' '); + } + if (node.type != null) { + visit(node.type); + sb.write(' '); + } + visit(node.definitions); + } + + visitDoWhile(DoWhile node) { + add(node.doKeyword.value); + if (node.body is !Block) sb.write(' '); + visit(node.body); + add(node.whileKeyword.value); + visit(node.condition); + sb.write(node.endToken.value); + } + + visitWhile(While node) { + addToken(node.whileKeyword); + visit(node.condition); + visit(node.body); + } + + visitParenthesizedExpression(ParenthesizedExpression node) { + add(node.getBeginToken().value); + visit(node.expression); + add(node.getEndToken().value); + } + + visitStringInterpolation(StringInterpolation node) { + visit(node.string); + visit(node.parts); + } + + visitStringInterpolationPart(StringInterpolationPart node) { + sb.write('\${'); // TODO(ahe): Preserve the real tokens. + visit(node.expression); + sb.write('}'); + visit(node.string); + } + + visitEmptyStatement(EmptyStatement node) { + add(node.semicolonToken.value); + } + + visitGotoStatement(GotoStatement node) { + add(node.keywordToken.value); + if (node.target != null) { + sb.write(' '); + visit(node.target); + } + add(node.semicolonToken.value); + } + + visitBreakStatement(BreakStatement node) { + visitGotoStatement(node); + } + + visitContinueStatement(ContinueStatement node) { + visitGotoStatement(node); + } + + visitForIn(ForIn node) { + add(node.forToken.value); + sb.write('('); + visit(node.declaredIdentifier); + sb.write(' '); + addToken(node.inToken); + visit(node.expression); + sb.write(')'); + visit(node.body); + } + + visitLabel(Label node) { + visit(node.identifier); + add(node.colonToken.value); + } + + visitLabeledStatement(LabeledStatement node) { + visit(node.labels); + visit(node.statement); + } + + visitLiteralMap(LiteralMap node) { + if (node.constKeyword != null) add(node.constKeyword.value); + if (node.typeArguments != null) visit(node.typeArguments); + visit(node.entries); + } + + visitLiteralMapEntry(LiteralMapEntry node) { + visit(node.key); + add(node.colonToken.value); + visit(node.value); + } + + visitNamedArgument(NamedArgument node) { + visit(node.name); + add(node.colonToken.value); + visit(node.expression); + } + + visitSwitchStatement(SwitchStatement node) { + addToken(node.switchKeyword); + visit(node.parenthesizedExpression); + visit(node.cases); + } + + visitSwitchCase(SwitchCase node) { + visit(node.labelsAndCases); + if (node.isDefaultCase) { + sb.write('default:'); + } + visit(node.statements); + } + + unparseImportTag(String uri, [String prefix]) { + final suffix = prefix == null ? '' : ' as $prefix'; + sb.write('import "$uri"$suffix;'); + } + + visitTryStatement(TryStatement node) { + addToken(node.tryKeyword); + visit(node.tryBlock); + visit(node.catchBlocks); + if (node.finallyKeyword != null) { + addToken(node.finallyKeyword); + visit(node.finallyBlock); + } + } + + visitCaseMatch(CaseMatch node) { + add(node.caseKeyword.value); + sb.write(" "); + visit(node.expression); + add(node.colonToken.value); + } + + visitCatchBlock(CatchBlock node) { + addToken(node.onKeyword); + if (node.type != null) { + visit(node.type); + sb.write(' '); + } + addToken(node.catchKeyword); + visit(node.formals); + visit(node.block); + } + + visitTypedef(Typedef node) { + addToken(node.typedefKeyword); + if (node.returnType != null) { + visit(node.returnType); + sb.write(' '); + } + visit(node.name); + if (node.typeParameters != null) { + visit(node.typeParameters); + } + visit(node.formals); + add(node.endToken.value); + } + + visitLibraryName(LibraryName node) { + addToken(node.libraryKeyword); + node.visitChildren(this); + add(node.getEndToken().value); + } + + visitImport(Import node) { + addToken(node.importKeyword); + visit(node.uri); + if (node.isDeferred) { + sb.write(' deferred'); + } + if (node.prefix != null) { + sb.write(' as '); + visit(node.prefix); + } + if (node.combinators != null) { + sb.write(' '); + visit(node.combinators); + } + add(node.getEndToken().value); + } + + visitExport(Export node) { + addToken(node.exportKeyword); + visit(node.uri); + if (node.combinators != null) { + sb.write(' '); + visit(node.combinators); + } + add(node.getEndToken().value); + } + + visitPart(Part node) { + addToken(node.partKeyword); + visit(node.uri); + add(node.getEndToken().value); + } + + visitPartOf(PartOf node) { + addToken(node.partKeyword); + addToken(node.ofKeyword); + visit(node.name); + add(node.getEndToken().value); + } + + visitCombinator(Combinator node) { + addToken(node.keywordToken); + visit(node.identifiers); + } + + visitMetadata(Metadata node) { + addToken(node.token); + visit(node.expression); + } + + visitNode(Node node) { + throw 'internal error'; // Should not be called. + } + + visitExpression(Expression node) { + throw 'internal error'; // Should not be called. + } + + visitLibraryTag(LibraryTag node) { + throw 'internal error'; // Should not be called. + } + + visitLibraryDependency(Node node) { + throw 'internal error'; // Should not be called. + } + + visitLiteral(Literal node) { + throw 'internal error'; // Should not be called. + } + + visitLoop(Loop node) { + throw 'internal error'; // Should not be called. + } + + visitPostfix(Postfix node) { + throw 'internal error'; // Should not be called. + } + + visitPrefix(Prefix node) { + throw 'internal error'; // Should not be called. + } + + visitStatement(Statement node) { + throw 'internal error'; // Should not be called. + } + + visitStringNode(StringNode node) { + throw 'internal error'; // Should not be called. + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/visitors.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/visitors.dart new file mode 100644 index 0000000..9096cff --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree/visitors.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2011, 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. + +part of tree; + +/** + * This visitor takes another visitor and applies it to every + * node in the tree. There is currently no way to control the + * traversal. + */ +class TraversingVisitor extends Visitor { + final Visitor visitor; + + TraversingVisitor(Visitor this.visitor); + + visitNode(Node node) { + node.accept(visitor); + node.visitChildren(this); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree_validator.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree_validator.dart new file mode 100644 index 0000000..399ad60 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/tree_validator.dart @@ -0,0 +1,80 @@ +// Copyright (c) 2011, 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. + +part of dart2js; + +class TreeValidatorTask extends CompilerTask { + TreeValidatorTask(Compiler compiler) : super(compiler); + + void validate(Node tree) { + assert(check(tree)); + } + + bool check(Node tree) { + List errors = []; + void report(node, message) { + final error = new InvalidNodeError(node, message); + errors.add(error); + compiler.reportWarning(node, MessageKind.GENERIC, {'text': message}); + }; + final validator = new ValidatorVisitor(report); + tree.accept(new TraversingVisitor(validator)); + + return errors.isEmpty; + } +} + +class ValidatorVisitor extends Visitor { + final Function reportInvalidNode; + + ValidatorVisitor(Function this.reportInvalidNode); + + expect(Node node, bool test, [message]) { + if (!test) reportInvalidNode(node, message); + } + + visitNode(Node node) {} + + visitSendSet(SendSet node) { + final selector = node.selector; + final name = node.assignmentOperator.source; + final arguments = node.arguments; + + expect(node, arguments != null); + expect(node, + selector is Identifier || selector is FunctionExpression, + 'selector is not assignable'); + if (identical(name, '++') || identical(name, '--')) { + expect(node, node.assignmentOperator is Operator); + if (node.isIndex) { + expect(node.arguments.tail.head, node.arguments.tail.isEmpty); + } else { + expect(node.arguments.head, node.arguments.isEmpty); + } + } else { + expect(node, !node.arguments.isEmpty); + } + } + + visitReturn(Return node) { + if (!node.isRedirectingFactoryBody && node.hasExpression) { + // We allow non-expression expressions in Return nodes, but only when + // using them to hold redirecting factory constructors. + expect(node, node.expression.asExpression() != null); + } + } +} + +class InvalidNodeError { + final Node node; + final String message; + InvalidNodeError(this.node, [this.message]); + + toString() { + String nodeString = node.toDebugString(); + String result = 'invalid node: $nodeString'; + if (message != null) result = '$result ($message)'; + return result; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/typechecker.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/typechecker.dart new file mode 100644 index 0000000..c52c9fe --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/typechecker.dart @@ -0,0 +1,1750 @@ +// Copyright (c) 2012, 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. + +part of dart2js; + +class TypeCheckerTask extends CompilerTask { + TypeCheckerTask(Compiler compiler) : super(compiler); + String get name => "Type checker"; + + void check(TreeElements elements) { + Element element = elements.currentElement; + compiler.withCurrentElement(element, () { + measure(() { + Node tree = element.parseNode(compiler); + TypeCheckerVisitor visitor = + new TypeCheckerVisitor(compiler, elements, compiler.types); + if (element.isField()) { + visitor.analyzingInitializer = true; + } + tree.accept(visitor); + }); + }); + } +} + +/** + * Class used to report different warnings for differrent kinds of members. + */ +class MemberKind { + static const MemberKind METHOD = const MemberKind("method"); + static const MemberKind OPERATOR = const MemberKind("operator"); + static const MemberKind GETTER = const MemberKind("getter"); + static const MemberKind SETTER = const MemberKind("setter"); + + final String name; + + const MemberKind(this.name); + + String toString() => name; +} + +/** + * [ElementAccess] represents the access of [element], either as a property + * access or invocation. + */ +abstract class ElementAccess { + Element get element; + + DartType computeType(Compiler compiler); + + /// Returns [: true :] if the element can be access as an invocation. + bool isCallable(Compiler compiler) { + if (element.isAbstractField()) { + AbstractFieldElement abstractFieldElement = element; + if (abstractFieldElement.getter == null) { + // Setters cannot be invoked as function invocations. + return false; + } + } + return compiler.types.isAssignable( + computeType(compiler), compiler.functionClass.computeType(compiler)); + } +} + +/// An access of a instance member. +class MemberAccess extends ElementAccess { + final MemberSignature member; + + MemberAccess(MemberSignature this.member); + + Element get element => member.declarations.first.element; + + DartType computeType(Compiler compiler) => member.type; + + String toString() => 'MemberAccess($member)'; +} + +/// An access of an unresolved element. +class DynamicAccess implements ElementAccess { + const DynamicAccess(); + + Element get element => null; + + DartType computeType(Compiler compiler) => compiler.types.dynamicType; + + bool isCallable(Compiler compiler) => true; + + String toString() => 'DynamicAccess'; +} + +/** + * An access of a resolved top-level or static property or function, or an + * access of a resolved element through [:this:]. + */ +class ResolvedAccess extends ElementAccess { + final Element element; + + ResolvedAccess(Element this.element) { + assert(element != null); + } + + DartType computeType(Compiler compiler) { + if (element.isGetter()) { + FunctionType functionType = element.computeType(compiler); + return functionType.returnType; + } else if (element.isSetter()) { + FunctionType functionType = element.computeType(compiler); + return functionType.parameterTypes.head; + } else { + return element.computeType(compiler); + } + } + + String toString() => 'ResolvedAccess($element)'; +} + +/// An access to a promoted variable. +class PromotedAccess extends ElementAccess { + final VariableElement element; + final DartType type; + + PromotedAccess(VariableElement this.element, DartType this.type) { + assert(element != null); + assert(type != null); + } + + DartType computeType(Compiler compiler) => type; + + String toString() => 'PromotedAccess($element,$type)'; +} + +/** + * An access of a resolved top-level or static property or function, or an + * access of a resolved element through [:this:]. + */ +class TypeAccess extends ElementAccess { + final DartType type; + TypeAccess(DartType this.type) { + assert(type != null); + } + + Element get element => type.element; + + DartType computeType(Compiler compiler) => type; + + String toString() => 'TypeAccess($type)'; +} + +/** + * An access of a type literal. + */ +class TypeLiteralAccess extends ElementAccess { + final Element element; + TypeLiteralAccess(Element this.element) { + assert(element != null); + } + + DartType computeType(Compiler compiler) => compiler.typeClass.rawType; + + String toString() => 'TypeLiteralAccess($element)'; +} + + +/// An access to the 'call' method of a function type. +class FunctionCallAccess implements ElementAccess { + final Element element; + final DartType type; + + const FunctionCallAccess(this.element, this.type); + + DartType computeType(Compiler compiler) => type; + + bool isCallable(Compiler compiler) => true; + + String toString() => 'FunctionAccess($element, $type)'; +} + + +/// An is-expression that potentially promotes a variable. +class TypePromotion { + final Send node; + final VariableElement variable; + final DartType type; + final List messages = []; + + TypePromotion(this.node, this.variable, this.type); + + bool get isValid => messages.isEmpty; + + TypePromotion copy() { + return new TypePromotion(node, variable, type)..messages.addAll(messages); + } + + void addHint(Spannable spannable, MessageKind kind, [Map arguments]) { + messages.add(new TypePromotionMessage(api.Diagnostic.HINT, + spannable, kind, arguments)); + } + + void addInfo(Spannable spannable, MessageKind kind, [Map arguments]) { + messages.add(new TypePromotionMessage(api.Diagnostic.INFO, + spannable, kind, arguments)); + } + + String toString() { + return 'Promote ${variable} to ${type}${isValid ? '' : ' (invalid)'}'; + } +} + +/// A hint or info message attached to a type promotion. +class TypePromotionMessage { + api.Diagnostic diagnostic; + Spannable spannable; + MessageKind messageKind; + Map messageArguments; + + TypePromotionMessage(this.diagnostic, this.spannable, this.messageKind, + [this.messageArguments]); +} + +class TypeCheckerVisitor extends Visitor { + final Compiler compiler; + final TreeElements elements; + final Types types; + + Node lastSeenNode; + DartType expectedReturnType; + + final ClassElement currentClass; + + InterfaceType thisType; + InterfaceType superType; + + Link cascadeTypes = const Link(); + + bool analyzingInitializer = false; + + DartType intType; + DartType doubleType; + DartType boolType; + DartType stringType; + DartType objectType; + DartType listType; + + Map> shownTypePromotionsMap = + new Map>(); + + Map> typePromotionsMap = + new Map>(); + + Set reportedTypePromotions = new Set(); + + void showTypePromotion(Node node, TypePromotion typePromotion) { + List shownTypePromotions = + shownTypePromotionsMap.putIfAbsent(node, () => []); + shownTypePromotions.add(typePromotion); + } + + void registerKnownTypePromotion(TypePromotion typePromotion) { + VariableElement variable = typePromotion.variable; + Link knownTypes = + typePromotionsMap.putIfAbsent(variable, + () => const Link()); + typePromotionsMap[variable] = knownTypes.prepend(typePromotion); + } + + void unregisterKnownTypePromotion(TypePromotion typePromotion) { + VariableElement variable = typePromotion.variable; + Link knownTypes = typePromotionsMap[variable].tail; + if (knownTypes.isEmpty) { + typePromotionsMap.remove(variable); + } else { + typePromotionsMap[variable] = knownTypes; + } + } + + List getShownTypePromotionsFor(Node node) { + List shownTypePromotions = shownTypePromotionsMap[node]; + return shownTypePromotions != null ? shownTypePromotions : const []; + } + + TypePromotion getKnownTypePromotion(VariableElement element) { + Link promotions = typePromotionsMap[element]; + if (promotions != null) { + while (!promotions.isEmpty) { + TypePromotion typePromotion = promotions.head; + if (typePromotion.isValid) { + return typePromotion; + } + promotions = promotions.tail; + } + } + return null; + } + + DartType getKnownType(VariableElement element) { + TypePromotion typePromotion = getKnownTypePromotion(element); + if (typePromotion != null) return typePromotion.type; + return element.type; + } + + TypeCheckerVisitor(this.compiler, TreeElements elements, this.types) + : this.elements = elements, + currentClass = elements.currentElement != null + ? elements.currentElement.getEnclosingClass() : null { + intType = compiler.intClass.computeType(compiler); + doubleType = compiler.doubleClass.computeType(compiler); + boolType = compiler.boolClass.computeType(compiler); + stringType = compiler.stringClass.computeType(compiler); + objectType = compiler.objectClass.computeType(compiler); + listType = compiler.listClass.computeType(compiler); + + if (currentClass != null) { + thisType = currentClass.thisType; + superType = currentClass.supertype; + } + } + + LibraryElement get currentLibrary => elements.currentElement.getLibrary(); + + reportTypeWarning(Spannable spannable, MessageKind kind, + [Map arguments = const {}]) { + compiler.reportWarning(spannable, kind, arguments); + } + + reportTypeInfo(Spannable spannable, MessageKind kind, + [Map arguments = const {}]) { + compiler.reportInfo(spannable, kind, arguments); + } + + reportTypePromotionHint(TypePromotion typePromotion) { + if (!reportedTypePromotions.contains(typePromotion)) { + reportedTypePromotions.add(typePromotion); + for (TypePromotionMessage message in typePromotion.messages) { + switch (message.diagnostic) { + case api.Diagnostic.HINT: + compiler.reportHint(message.spannable, + message.messageKind, + message.messageArguments); + break; + case api.Diagnostic.INFO: + compiler.reportInfo(message.spannable, + message.messageKind, + message.messageArguments); + break; + } + } + } + } + + // TODO(karlklose): remove these functions. + DartType unhandledExpression() => types.dynamicType; + + DartType analyzeNonVoid(Node node) { + DartType type = analyze(node); + if (type == types.voidType) { + reportTypeWarning(node, MessageKind.VOID_EXPRESSION); + } + return type; + } + + DartType analyzeWithDefault(Node node, DartType defaultValue) { + return node != null ? analyze(node) : defaultValue; + } + + /// If [inInitializer] is true, assignment should be interpreted as write to + /// a field and not to a setter. + DartType analyze(Node node, {bool inInitializer: false}) { + if (node == null) { + final String error = 'Unexpected node: null'; + if (lastSeenNode != null) { + compiler.internalError(lastSeenNode, error); + } else { + compiler.internalError(elements.currentElement, error); + } + } else { + lastSeenNode = node; + } + bool previouslyInitializer = analyzingInitializer; + analyzingInitializer = inInitializer; + DartType result = node.accept(this); + analyzingInitializer = previouslyInitializer; + if (result == null) { + compiler.internalError(node, 'Type is null.'); + } + return result; + } + + void checkTypePromotion(Node node, TypePromotion typePromotion, + {bool checkAccesses: false}) { + VariableElement variable = typePromotion.variable; + String variableName = variable.name; + List potentialMutationsIn = + elements.getPotentialMutationsIn(node, variable); + if (!potentialMutationsIn.isEmpty) { + typePromotion.addHint(typePromotion.node, + MessageKind.POTENTIAL_MUTATION, + {'variableName': variableName, 'shownType': typePromotion.type}); + for (Node mutation in potentialMutationsIn) { + typePromotion.addInfo(mutation, + MessageKind.POTENTIAL_MUTATION_HERE, + {'variableName': variableName}); + } + } + List potentialMutationsInClosures = + elements.getPotentialMutationsInClosure(variable); + if (!potentialMutationsInClosures.isEmpty) { + typePromotion.addHint(typePromotion.node, + MessageKind.POTENTIAL_MUTATION_IN_CLOSURE, + {'variableName': variableName, 'shownType': typePromotion.type}); + for (Node mutation in potentialMutationsInClosures) { + typePromotion.addInfo(mutation, + MessageKind.POTENTIAL_MUTATION_IN_CLOSURE_HERE, + {'variableName': variableName}); + } + } + if (checkAccesses) { + List accesses = elements.getAccessesByClosureIn(node, variable); + List mutations = elements.getPotentialMutations(variable); + if (!accesses.isEmpty && !mutations.isEmpty) { + typePromotion.addHint(typePromotion.node, + MessageKind.ACCESSED_IN_CLOSURE, + {'variableName': variableName, 'shownType': typePromotion.type}); + for (Node access in accesses) { + typePromotion.addInfo(access, + MessageKind.ACCESSED_IN_CLOSURE_HERE, + {'variableName': variableName}); + } + for (Node mutation in mutations) { + typePromotion.addInfo(mutation, + MessageKind.POTENTIAL_MUTATION_HERE, + {'variableName': variableName}); + } + } + } + } + + /// Show type promotions from [left] and [right] in [node] given that the + /// promoted variables are not potentially mutated in [right]. + void reshowTypePromotions(Node node, Node left, Node right) { + for (TypePromotion typePromotion in getShownTypePromotionsFor(left)) { + typePromotion = typePromotion.copy(); + checkTypePromotion(right, typePromotion); + showTypePromotion(node, typePromotion); + } + + for (TypePromotion typePromotion in getShownTypePromotionsFor(right)) { + typePromotion = typePromotion.copy(); + checkTypePromotion(right, typePromotion); + showTypePromotion(node, typePromotion); + } + } + + /// Analyze [node] in the context of the known types shown in [context]. + DartType analyzeInPromotedContext(Node context, Node node) { + Link knownForNode = const Link(); + for (TypePromotion typePromotion in getShownTypePromotionsFor(context)) { + typePromotion = typePromotion.copy(); + checkTypePromotion(node, typePromotion, checkAccesses: true); + knownForNode = knownForNode.prepend(typePromotion); + registerKnownTypePromotion(typePromotion); + } + + final DartType type = analyze(node); + + while (!knownForNode.isEmpty) { + unregisterKnownTypePromotion(knownForNode.head); + knownForNode = knownForNode.tail; + } + + return type; + } + + /** + * Check if a value of type [from] can be assigned to a variable, parameter or + * return value of type [to]. If `isConst == true`, an error is emitted in + * checked mode, otherwise a warning is issued. + */ + bool checkAssignable(Spannable spannable, DartType from, DartType to, + {bool isConst: false}) { + if (!types.isAssignable(from, to)) { + if (compiler.enableTypeAssertions && isConst) { + compiler.reportError(spannable, MessageKind.NOT_ASSIGNABLE, + {'fromType': from, 'toType': to}); + } else { + reportTypeWarning(spannable, MessageKind.NOT_ASSIGNABLE, + {'fromType': from, 'toType': to}); + } + return false; + } + return true; + } + + checkCondition(Expression condition) { + checkAssignable(condition, analyze(condition), boolType); + } + + void pushCascadeType(DartType type) { + cascadeTypes = cascadeTypes.prepend(type); + } + + DartType popCascadeType() { + DartType type = cascadeTypes.head; + cascadeTypes = cascadeTypes.tail; + return type; + } + + DartType visitBlock(Block node) { + return analyze(node.statements); + } + + DartType visitCascade(Cascade node) { + analyze(node.expression); + if (node.expression.asCascadeReceiver() == null) { + // TODO(karlklose): bug: expressions of the form e..x = y do not have + // a CascadeReceiver as expression currently. + return types.dynamicType; + } + return popCascadeType(); + } + + DartType visitCascadeReceiver(CascadeReceiver node) { + DartType type = analyze(node.expression); + pushCascadeType(type); + return type; + } + + DartType visitDoWhile(DoWhile node) { + StatementType bodyType = analyze(node.body); + checkCondition(node.condition); + return bodyType.join(StatementType.NOT_RETURNING); + } + + DartType visitExpressionStatement(ExpressionStatement node) { + Expression expression = node.expression; + analyze(expression); + return (expression.asThrow() != null) + ? StatementType.RETURNING + : StatementType.NOT_RETURNING; + } + + /** Dart Programming Language Specification: 11.5.1 For Loop */ + DartType visitFor(For node) { + analyzeWithDefault(node.initializer, StatementType.NOT_RETURNING); + if (node.condition != null) { + checkCondition(node.condition); + } + analyzeWithDefault(node.update, StatementType.NOT_RETURNING); + StatementType bodyType = analyze(node.body); + return bodyType.join(StatementType.NOT_RETURNING); + } + + DartType visitFunctionDeclaration(FunctionDeclaration node) { + analyze(node.function); + return StatementType.NOT_RETURNING; + } + + DartType visitFunctionExpression(FunctionExpression node) { + DartType type; + DartType returnType; + DartType previousType; + final FunctionElement element = elements[node]; + assert(invariant(node, element != null, + message: 'FunctionExpression with no element')); + if (Elements.isUnresolved(element)) return types.dynamicType; + if (identical(element.kind, ElementKind.GENERATIVE_CONSTRUCTOR) || + identical(element.kind, ElementKind.GENERATIVE_CONSTRUCTOR_BODY)) { + type = types.dynamicType; + returnType = types.voidType; + + element.functionSignature.forEachParameter((ParameterElement parameter) { + if (parameter.isFieldParameter()) { + FieldParameterElement fieldParameter = parameter; + checkAssignable(parameter, parameter.type, + fieldParameter.fieldElement.computeType(compiler)); + } + }); + if (node.initializers != null) { + analyze(node.initializers, inInitializer: true); + } + } else { + FunctionType functionType = element.computeType(compiler); + returnType = functionType.returnType; + type = functionType; + } + DartType previous = expectedReturnType; + expectedReturnType = returnType; + StatementType bodyType = analyze(node.body); + if (returnType != types.voidType && !returnType.treatAsDynamic + && bodyType != StatementType.RETURNING) { + MessageKind kind; + if (bodyType == StatementType.MAYBE_RETURNING) { + kind = MessageKind.MAYBE_MISSING_RETURN; + } else { + kind = MessageKind.MISSING_RETURN; + } + reportTypeWarning(node.name, kind); + } + expectedReturnType = previous; + return type; + } + + DartType visitIdentifier(Identifier node) { + if (node.isThis()) { + return thisType; + } else if (node.isSuper()) { + return superType; + } else { + Element element = elements[node]; + assert(invariant(node, element != null, + message: 'Missing element for identifier')); + assert(invariant(node, element.isVariable() || + element.isParameter() || + element.isField(), + message: 'Unexpected context element ${element}')); + return element.computeType(compiler); + } + } + + DartType visitIf(If node) { + Expression condition = node.condition.expression; + Statement thenPart = node.thenPart; + + checkCondition(node.condition); + + StatementType thenType = analyzeInPromotedContext(condition, thenPart); + + StatementType elseType = node.hasElsePart ? analyze(node.elsePart) + : StatementType.NOT_RETURNING; + return thenType.join(elseType); + } + + void checkPrivateAccess(Node node, Element element, String name) { + if (name != null && + isPrivateName(name) && + element.getLibrary() != currentLibrary) { + reportTypeWarning( + node, + MessageKind.PRIVATE_ACCESS, + {'name': name, + 'libraryName': element.getLibrary().getLibraryOrScriptName()}); + } + + } + + ElementAccess lookupMember(Node node, DartType receiverType, String name, + MemberKind memberKind, Element receiverElement, + {bool lookupClassMember: false}) { + if (receiverType.treatAsDynamic) { + return const DynamicAccess(); + } + + Name memberName = new Name(name, currentLibrary, + isSetter: memberKind == MemberKind.SETTER); + + // Compute the unaliased type of the first non type variable bound of + // [type]. + DartType computeUnaliasedBound(DartType type) { + DartType originalType = type; + while (identical(type.kind, TypeKind.TYPE_VARIABLE)) { + TypeVariableType variable = type; + type = variable.element.bound; + if (type == originalType) { + type = compiler.objectClass.rawType; + } + } + return type.unalias(compiler); + } + + // Compute the interface type of [type]. For type variable it is the + // interface type of the bound, for function types and typedefs it is the + // `Function` type. + InterfaceType computeInterfaceType(DartType type) { + if (type.kind == TypeKind.FUNCTION) { + type = compiler.functionClass.rawType; + } + assert(invariant(node, type.kind == TypeKind.INTERFACE, + message: "unexpected type kind ${type.kind}.")); + return type; + } + + // Lookup the class or interface member [name] in [interface]. + MemberSignature lookupMemberSignature(Name name, InterfaceType interface) { + MembersCreator.computeClassMembers(compiler, interface.element); + return lookupClassMember || analyzingInitializer + ? interface.lookupClassMember(name) + : interface.lookupInterfaceMember(name); + } + + // Compute the access of [name] on [type]. This function takes the special + // 'call' method into account. + ElementAccess getAccess(Name name, + DartType unaliasedBound, InterfaceType interface) { + MemberSignature member = lookupMemberSignature(memberName, interface); + if (member != null) { + return new MemberAccess(member); + } + if (name == const PublicName('call')) { + if (unaliasedBound.kind == TypeKind.FUNCTION) { + // This is an access the implicit 'call' method of a function type. + return new FunctionCallAccess(receiverElement, unaliasedBound); + } + if (types.isSubtype(interface, compiler.functionClass.rawType)) { + // This is an access of the special 'call' method implicitly defined + // on 'Function'. This method can be called with any arguments, which + // we ensure by giving it the type 'dynamic'. + return new FunctionCallAccess(null, types.dynamicType); + } + } + return null; + } + + DartType unaliasedBound = computeUnaliasedBound(receiverType); + InterfaceType interface = computeInterfaceType(unaliasedBound); + ElementAccess access = getAccess(memberName, unaliasedBound, interface); + if (access != null) { + return access; + } + if (receiverElement != null && + (receiverElement.isVariable() || receiverElement.isParameter())) { + Link typePromotions = typePromotionsMap[receiverElement]; + if (typePromotions != null) { + while (!typePromotions.isEmpty) { + TypePromotion typePromotion = typePromotions.head; + if (!typePromotion.isValid) { + DartType unaliasedBound = computeUnaliasedBound(typePromotion.type); + InterfaceType interface = computeInterfaceType(unaliasedBound); + if (getAccess(memberName, unaliasedBound, interface) != null) { + reportTypePromotionHint(typePromotion); + } + } + typePromotions = typePromotions.tail; + } + } + } + if (!interface.element.isProxy) { + bool foundPrivateMember = false; + if (memberName.isPrivate) { + void findPrivateMember(MemberSignature member) { + if (memberName.isSimilarTo(member.name)) { + PrivateName privateName = member.name; + reportTypeWarning( + node, + MessageKind.PRIVATE_ACCESS, + {'name': name, + 'libraryName': privateName.library.getLibraryOrScriptName()}); + foundPrivateMember = true; + } + } + if (lookupClassMember) { + interface.element.forEachClassMember(findPrivateMember); + } else { + interface.element.forEachInterfaceMember(findPrivateMember); + } + + } + if (!foundPrivateMember) { + switch (memberKind) { + case MemberKind.METHOD: + reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND, + {'className': receiverType.name, 'memberName': name}); + break; + case MemberKind.OPERATOR: + reportTypeWarning(node, MessageKind.OPERATOR_NOT_FOUND, + {'className': receiverType.name, 'memberName': name}); + break; + case MemberKind.GETTER: + if (lookupMemberSignature(memberName.setter, interface) != null) { + // A setter is present so warn explicitly about the missing + // getter. + reportTypeWarning(node, MessageKind.GETTER_NOT_FOUND, + {'className': receiverType.name, 'memberName': name}); + } else { + reportTypeWarning(node, MessageKind.MEMBER_NOT_FOUND, + {'className': receiverType.name, 'memberName': name}); + } + break; + case MemberKind.SETTER: + reportTypeWarning(node, MessageKind.SETTER_NOT_FOUND, + {'className': receiverType.name, 'memberName': name}); + break; + } + } + } + return const DynamicAccess(); + } + + DartType lookupMemberType(Node node, DartType type, String name, + MemberKind memberKind) { + return lookupMember(node, type, name, memberKind, null) + .computeType(compiler); + } + + void analyzeArguments(Send send, Element element, DartType type, + [LinkBuilder argumentTypes]) { + Link arguments = send.arguments; + DartType unaliasedType = type.unalias(compiler); + if (identical(unaliasedType.kind, TypeKind.FUNCTION)) { + bool error = false; + FunctionType funType = unaliasedType; + Link parameterTypes = funType.parameterTypes; + Link optionalParameterTypes = funType.optionalParameterTypes; + while (!arguments.isEmpty) { + Node argument = arguments.head; + NamedArgument namedArgument = argument.asNamedArgument(); + if (namedArgument != null) { + argument = namedArgument.expression; + String argumentName = namedArgument.name.source; + DartType namedParameterType = + funType.getNamedParameterType(argumentName); + if (namedParameterType == null) { + error = true; + // TODO(johnniwinther): Provide better information on the called + // function. + reportTypeWarning(argument, MessageKind.NAMED_ARGUMENT_NOT_FOUND, + {'argumentName': argumentName}); + + DartType argumentType = analyze(argument); + if (argumentTypes != null) argumentTypes.addLast(argumentType); + } else { + DartType argumentType = analyze(argument); + if (argumentTypes != null) argumentTypes.addLast(argumentType); + if (!checkAssignable(argument, argumentType, namedParameterType)) { + error = true; + } + } + } else { + if (parameterTypes.isEmpty) { + if (optionalParameterTypes.isEmpty) { + error = true; + // TODO(johnniwinther): Provide better information on the + // called function. + reportTypeWarning(argument, MessageKind.ADDITIONAL_ARGUMENT); + + DartType argumentType = analyze(argument); + if (argumentTypes != null) argumentTypes.addLast(argumentType); + } else { + DartType argumentType = analyze(argument); + if (argumentTypes != null) argumentTypes.addLast(argumentType); + if (!checkAssignable(argument, + argumentType, optionalParameterTypes.head)) { + error = true; + } + optionalParameterTypes = optionalParameterTypes.tail; + } + } else { + DartType argumentType = analyze(argument); + if (argumentTypes != null) argumentTypes.addLast(argumentType); + if (!checkAssignable(argument, argumentType, parameterTypes.head)) { + error = true; + } + parameterTypes = parameterTypes.tail; + } + } + arguments = arguments.tail; + } + if (!parameterTypes.isEmpty) { + error = true; + // TODO(johnniwinther): Provide better information on the called + // function. + reportTypeWarning(send, MessageKind.MISSING_ARGUMENT, + {'argumentType': parameterTypes.head}); + } + if (error) { + // TODO(johnniwinther): Improve access to declaring element and handle + // synthesized member signatures. Currently function typed instance + // members provide no access to there own name. + if (element == null) { + element = type.element; + } else if (type.element.isTypedef()) { + if (element != null) { + reportTypeInfo(element, + MessageKind.THIS_IS_THE_DECLARATION, + {'name': element.name}); + } + element = type.element; + } + reportTypeInfo(element, MessageKind.THIS_IS_THE_METHOD); + } + } else { + while(!arguments.isEmpty) { + DartType argumentType = analyze(arguments.head); + if (argumentTypes != null) argumentTypes.addLast(argumentType); + arguments = arguments.tail; + } + } + } + + // Analyze the invocation [node] of [elementAccess]. + // + // If provided [argumentTypes] is filled with the argument types during + // analysis. + DartType analyzeInvocation(Send node, ElementAccess elementAccess, + [LinkBuilder argumentTypes]) { + DartType type = elementAccess.computeType(compiler); + if (elementAccess.isCallable(compiler)) { + analyzeArguments(node, elementAccess.element, type, argumentTypes); + } else { + reportTypeWarning(node, MessageKind.NOT_CALLABLE, + {'elementName': elementAccess.element.name}); + analyzeArguments(node, elementAccess.element, types.dynamicType, + argumentTypes); + } + type = type.unalias(compiler); + if (identical(type.kind, TypeKind.FUNCTION)) { + FunctionType funType = type; + return funType.returnType; + } else { + return types.dynamicType; + } + } + + /** + * Computes the [ElementAccess] for [name] on the [node] possibly using the + * [element] provided for [node] by the resolver. + */ + ElementAccess computeAccess(Send node, String name, Element element, + MemberKind memberKind, + {bool lookupClassMember: false}) { + if (element != null && element.isErroneous()) { + // An error has already been reported for this node. + return const DynamicAccess(); + } + if (node.receiver != null) { + Element receiverElement = elements[node.receiver]; + if (receiverElement != null) { + if (receiverElement.isPrefix()) { + assert(invariant(node, element != null, + message: 'Prefixed node has no element.')); + return computeResolvedAccess(node, name, element, memberKind); + } + } + // e.foo() for some expression e. + DartType receiverType = analyze(node.receiver); + if (receiverType.treatAsDynamic || receiverType.isVoid) { + return const DynamicAccess(); + } + TypeKind receiverKind = receiverType.kind; + return lookupMember(node, receiverType, name, memberKind, + elements[node.receiver], + lookupClassMember: lookupClassMember || + element != null && element.modifiers.isStatic()); + } else { + return computeResolvedAccess(node, name, element, memberKind); + } + } + + /** + * Computes the [ElementAccess] for [name] on the [node] using the [element] + * provided for [node] by the resolver. + */ + ElementAccess computeResolvedAccess(Send node, String name, + Element element, MemberKind memberKind) { + if (element == null) { + // foo() where foo is unresolved. + return lookupMember(node, thisType, name, memberKind, null); + } else if (element.isErroneous()) { + // foo() where foo is erroneous. + return const DynamicAccess(); + } else if (element.impliesType()) { + // The literal `Foo` where Foo is a class, a typedef, or a type variable. + if (elements.isTypeLiteral(node)) { + assert(invariant(node, identical(compiler.typeClass, + elements.getType(node).element), + message: 'Expected type literal type: ' + '${elements.getType(node)}')); + return new TypeLiteralAccess(element); + } + return createResolvedAccess(node, name, element); + } else if (element.isMember()) { + // foo() where foo is a member. + return lookupMember(node, thisType, name, memberKind, null, + lookupClassMember: element.modifiers.isStatic()); + } else if (element.isFunction()) { + // foo() where foo is a method in the same class. + return createResolvedAccess(node, name, element); + } else if (element.isVariable() || + element.isParameter() || + element.isField()) { + // foo() where foo is a field in the same class. + return createResolvedAccess(node, name, element); + } else if (element.isGetter() || element.isSetter()) { + return createResolvedAccess(node, name, element); + } else { + compiler.internalError(element, + 'Unexpected element kind ${element.kind}.'); + return null; + } + } + + ElementAccess createResolvedAccess(Send node, String name, + Element element) { + checkPrivateAccess(node, element, name); + return createPromotedAccess(element); + } + + ElementAccess createPromotedAccess(Element element) { + if (element.isVariable() || element.isParameter()) { + TypePromotion typePromotion = getKnownTypePromotion(element); + if (typePromotion != null) { + return new PromotedAccess(element, typePromotion.type); + } + } + return new ResolvedAccess(element); + } + + /** + * Computes the type of the access of [name] on the [node] possibly using the + * [element] provided for [node] by the resolver. + */ + DartType computeAccessType(Send node, String name, Element element, + MemberKind memberKind, + {bool lookupClassMember: false}) { + DartType type = + computeAccess(node, name, element, memberKind, + lookupClassMember: lookupClassMember).computeType(compiler); + if (type == null) { + compiler.internalError(node, 'Type is null on access of $name on $node.'); + } + return type; + } + + /// Compute a version of [shownType] that is more specific that [knownType]. + /// This is used to provided better hints when trying to promote a supertype + /// to a raw subtype. For instance trying to promote `Iterable` to `List` + /// we suggest the use of `List`, which would make promotion valid. + DartType computeMoreSpecificType(DartType shownType, + DartType knownType) { + if (knownType.kind == TypeKind.INTERFACE && + shownType.kind == TypeKind.INTERFACE && + types.isSubtype(shownType.asRaw(), knownType)) { + // For the comments in the block, assume the hierarchy: + // class A {} + // class B extends A {} + // and a promotion from a [knownType] of `A` to a + // [shownType] of `B`. + InterfaceType knownInterfaceType = knownType; + ClassElement shownClass = shownType.element; + + // Compute `B` as the subtype of `A` using + // the relation between `A` and `A`. + MoreSpecificSubtypeVisitor visitor = + new MoreSpecificSubtypeVisitor(compiler); + InterfaceType shownTypeGeneric = visitor.computeMoreSpecific( + shownClass, knownInterfaceType); + + if (shownTypeGeneric != null && + types.isMoreSpecific(shownTypeGeneric, knownType)) { + // This should be the case but we double-check. + // TODO(johnniwinther): Ensure that we don't suggest malbounded types. + return shownTypeGeneric; + } + } + return null; + + } + + DartType visitSend(Send node) { + Element element = elements[node]; + + if (element != null && element.isConstructor()) { + DartType receiverType; + if (node.receiver != null) { + receiverType = analyze(node.receiver); + } else if (node.selector.isSuper()) { + // TODO(johnniwinther): Lookup super-member in class members. + receiverType = superType; + } else { + assert(node.selector.isThis()); + receiverType = thisType; + } + DartType constructorType = computeConstructorType(element, receiverType); + analyzeArguments(node, element, constructorType); + return types.dynamicType; + } + + if (Elements.isClosureSend(node, element)) { + if (element != null) { + // foo() where foo is a local or a parameter. + return analyzeInvocation(node, createPromotedAccess(element)); + } else { + // exp() where exp is some complex expression like (o) or foo(). + DartType type = analyze(node.selector); + return analyzeInvocation(node, new TypeAccess(type)); + } + } + + Identifier selector = node.selector.asIdentifier(); + String name = selector.source; + + if (node.isOperator && identical(name, 'is')) { + analyze(node.receiver); + if (!node.isIsNotCheck) { + Element variable = elements[node.receiver]; + if (variable == null) { + // Look for the variable element within parenthesized expressions. + ParenthesizedExpression parentheses = + node.receiver.asParenthesizedExpression(); + while (parentheses != null) { + variable = elements[parentheses.expression]; + if (variable != null) break; + parentheses = parentheses.expression.asParenthesizedExpression(); + } + } + + if (variable != null && + (variable.isVariable() || variable.isParameter())) { + DartType knownType = getKnownType(variable); + if (!knownType.isDynamic) { + DartType shownType = elements.getType(node.arguments.head); + TypePromotion typePromotion = + new TypePromotion(node, variable, shownType); + if (!types.isMoreSpecific(shownType, knownType)) { + String variableName = variable.name; + if (!types.isSubtype(shownType, knownType)) { + typePromotion.addHint(node, + MessageKind.NOT_MORE_SPECIFIC_SUBTYPE, + {'variableName': variableName, + 'shownType': shownType, + 'knownType': knownType}); + } else { + DartType shownTypeSuggestion = + computeMoreSpecificType(shownType, knownType); + if (shownTypeSuggestion != null) { + typePromotion.addHint(node, + MessageKind.NOT_MORE_SPECIFIC_SUGGESTION, + {'variableName': variableName, + 'shownType': shownType, + 'shownTypeSuggestion': shownTypeSuggestion, + 'knownType': knownType}); + } else { + typePromotion.addHint(node, + MessageKind.NOT_MORE_SPECIFIC, + {'variableName': variableName, + 'shownType': shownType, + 'knownType': knownType}); + } + } + } + showTypePromotion(node, typePromotion); + } + } + } + return boolType; + } if (node.isOperator && identical(name, 'as')) { + analyze(node.receiver); + return elements.getType(node.arguments.head); + } else if (node.isOperator) { + final Node receiver = node.receiver; + final DartType receiverType = analyze(receiver); + if (identical(name, '==') || identical(name, '!=') + // TODO(johnniwinther): Remove these. + || identical(name, '===') || identical(name, '!==')) { + // Analyze argument. + analyze(node.arguments.head); + return boolType; + } else if (identical(name, '||')) { + checkAssignable(receiver, receiverType, boolType); + final Node argument = node.arguments.head; + final DartType argumentType = analyze(argument); + checkAssignable(argument, argumentType, boolType); + return boolType; + } else if (identical(name, '&&')) { + checkAssignable(receiver, receiverType, boolType); + final Node argument = node.arguments.head; + + final DartType argumentType = + analyzeInPromotedContext(receiver, argument); + + reshowTypePromotions(node, receiver, argument); + + checkAssignable(argument, argumentType, boolType); + return boolType; + } else if (identical(name, '!')) { + checkAssignable(receiver, receiverType, boolType); + return boolType; + } else if (identical(name, '?')) { + return boolType; + } + String operatorName = selector.source; + if (identical(name, '-') && node.arguments.isEmpty) { + operatorName = 'unary-'; + } + assert(invariant(node, + identical(name, '+') || identical(name, '=') || + identical(name, '-') || identical(name, '*') || + identical(name, '/') || identical(name, '%') || + identical(name, '~/') || identical(name, '|') || + identical(name, '&') || identical(name, '^') || + identical(name, '~')|| identical(name, '<<') || + identical(name, '>>') || + identical(name, '<') || identical(name, '>') || + identical(name, '<=') || identical(name, '>=') || + identical(name, '[]'), + message: 'Unexpected operator $name')); + + // TODO(karlklose): handle `void` in expression context by calling + // [analyzeNonVoid] instead of [analyze]. + ElementAccess access = receiverType.isVoid ? const DynamicAccess() + : lookupMember(node, receiverType, operatorName, + MemberKind.OPERATOR, null); + LinkBuilder argumentTypesBuilder = new LinkBuilder(); + DartType resultType = + analyzeInvocation(node, access, argumentTypesBuilder); + if (identical(receiverType.element, compiler.intClass)) { + if (identical(name, '+') || + identical(operatorName, '-') || + identical(name, '*') || + identical(name, '%')) { + DartType argumentType = argumentTypesBuilder.toLink().head; + if (identical(argumentType.element, compiler.intClass)) { + return intType; + } else if (identical(argumentType.element, compiler.doubleClass)) { + return doubleType; + } + } + } + return resultType; + } else if (node.isPropertyAccess) { + ElementAccess access = + computeAccess(node, selector.source, element, MemberKind.GETTER); + return access.computeType(compiler); + } else if (node.isFunctionObjectInvocation) { + return unhandledExpression(); + } else { + ElementAccess access = + computeAccess(node, selector.source, element, MemberKind.METHOD); + return analyzeInvocation(node, access); + } + } + + /// Returns the first type in the list or [:dynamic:] if the list is empty. + DartType firstType(Link link) { + return link.isEmpty ? types.dynamicType : link.head; + } + + /** + * Returns the second type in the list or [:dynamic:] if the list is too + * short. + */ + DartType secondType(Link link) { + return link.isEmpty || link.tail.isEmpty + ? types.dynamicType : link.tail.head; + } + + /** + * Checks [: target o= value :] for some operator o, and returns the type + * of the result. This method also handles increment/decrement expressions + * like [: target++ :]. + */ + DartType checkAssignmentOperator(SendSet node, + String operatorName, + Node valueNode, + DartType value) { + assert(invariant(node, !node.isIndex)); + Element setterElement = elements[node]; + Element getterElement = elements[node.selector]; + Identifier selector = node.selector; + DartType getter = computeAccessType( + node, selector.source, getterElement, MemberKind.GETTER); + DartType setter = computeAccessType( + node, selector.source, setterElement, MemberKind.SETTER); + // [operator] is the type of operator+ or operator- on [target]. + DartType operator = + lookupMemberType(node, getter, operatorName, MemberKind.OPERATOR); + if (operator is FunctionType) { + FunctionType operatorType = operator; + // [result] is the type of target o value. + DartType result = operatorType.returnType; + DartType operatorArgument = firstType(operatorType.parameterTypes); + // Check target o value. + bool validValue = checkAssignable(valueNode, value, operatorArgument); + if (validValue || !(node.isPrefix || node.isPostfix)) { + // Check target = result. + checkAssignable(node.assignmentOperator, result, setter); + } + return node.isPostfix ? getter : result; + } + return types.dynamicType; + } + + /** + * Checks [: base[key] o= value :] for some operator o, and returns the type + * of the result. This method also handles increment/decrement expressions + * like [: base[key]++ :]. + */ + DartType checkIndexAssignmentOperator(SendSet node, + String operatorName, + Node valueNode, + DartType value) { + assert(invariant(node, node.isIndex)); + final DartType base = analyze(node.receiver); + final Node keyNode = node.arguments.head; + final DartType key = analyze(keyNode); + + // [indexGet] is the type of operator[] on [base]. + DartType indexGet = lookupMemberType( + node, base, '[]', MemberKind.OPERATOR); + if (indexGet is FunctionType) { + FunctionType indexGetType = indexGet; + DartType indexGetKey = firstType(indexGetType.parameterTypes); + // Check base[key]. + bool validKey = checkAssignable(keyNode, key, indexGetKey); + + // [element] is the type of base[key]. + DartType element = indexGetType.returnType; + // [operator] is the type of operator o on [element]. + DartType operator = lookupMemberType( + node, element, operatorName, MemberKind.OPERATOR); + if (operator is FunctionType) { + FunctionType operatorType = operator; + + // Check base[key] o value. + DartType operatorArgument = firstType(operatorType.parameterTypes); + bool validValue = checkAssignable(valueNode, value, operatorArgument); + + // [result] is the type of base[key] o value. + DartType result = operatorType.returnType; + + // [indexSet] is the type of operator[]= on [base]. + DartType indexSet = lookupMemberType( + node, base, '[]=', MemberKind.OPERATOR); + if (indexSet is FunctionType) { + FunctionType indexSetType = indexSet; + DartType indexSetKey = firstType(indexSetType.parameterTypes); + DartType indexSetValue = secondType(indexSetType.parameterTypes); + + if (validKey || indexGetKey != indexSetKey) { + // Only check base[key] on []= if base[key] was valid for [] or + // if the key types differ. + checkAssignable(keyNode, key, indexSetKey); + } + // Check base[key] = result + if (validValue || !(node.isPrefix || node.isPostfix)) { + checkAssignable(node.assignmentOperator, result, indexSetValue); + } + } + return node.isPostfix ? element : result; + } + } + return types.dynamicType; + } + + visitSendSet(SendSet node) { + Element element = elements[node]; + Identifier selector = node.selector; + final name = node.assignmentOperator.source; + if (identical(name, '=')) { + // e1 = value + if (node.isIndex) { + // base[key] = value + final DartType base = analyze(node.receiver); + final Node keyNode = node.arguments.head; + final DartType key = analyze(keyNode); + final Node valueNode = node.arguments.tail.head; + final DartType value = analyze(valueNode); + DartType indexSet = lookupMemberType( + node, base, '[]=', MemberKind.OPERATOR); + if (indexSet is FunctionType) { + FunctionType indexSetType = indexSet; + DartType indexSetKey = firstType(indexSetType.parameterTypes); + checkAssignable(keyNode, key, indexSetKey); + DartType indexSetValue = secondType(indexSetType.parameterTypes); + checkAssignable(node.assignmentOperator, value, indexSetValue); + } + return value; + } else { + // target = value + DartType target; + if (analyzingInitializer) { + // Field declaration `Foo target = value;` or initializer + // `this.target = value`. Lookup the getter `target` in the class + // members. + target = computeAccessType(node, selector.source, element, + MemberKind.GETTER, lookupClassMember: true); + } else { + // Normal assignment `target = value`. + target = computeAccessType( + node, selector.source, element, MemberKind.SETTER); + } + final Node valueNode = node.arguments.head; + final DartType value = analyze(valueNode); + checkAssignable(node.assignmentOperator, value, target); + return value; + } + } else if (identical(name, '++') || identical(name, '--')) { + // e++ or e-- + String operatorName = identical(name, '++') ? '+' : '-'; + if (node.isIndex) { + // base[key]++, base[key]--, ++base[key], or --base[key] + return checkIndexAssignmentOperator( + node, operatorName, node.assignmentOperator, intType); + } else { + // target++, target--, ++target, or --target + return checkAssignmentOperator( + node, operatorName, node.assignmentOperator, intType); + } + } else { + // e1 o= e2 for some operator o. + String operatorName; + switch (name) { + case '+=': operatorName = '+'; break; + case '-=': operatorName = '-'; break; + case '*=': operatorName = '*'; break; + case '/=': operatorName = '/'; break; + case '%=': operatorName = '%'; break; + case '~/=': operatorName = '~/'; break; + case '&=': operatorName = '&'; break; + case '|=': operatorName = '|'; break; + case '^=': operatorName = '^'; break; + case '<<=': operatorName = '<<'; break; + case '>>=': operatorName = '>>'; break; + default: + compiler.internalError(node, 'Unexpected assignment operator $name.'); + } + if (node.isIndex) { + // base[key] o= value for some operator o. + final Node valueNode = node.arguments.tail.head; + final DartType value = analyze(valueNode); + return checkIndexAssignmentOperator( + node, operatorName, valueNode, value); + } else { + // target o= value for some operator o. + final Node valueNode = node.arguments.head; + final DartType value = analyze(valueNode); + return checkAssignmentOperator(node, operatorName, valueNode, value); + } + } + } + + DartType visitLiteralInt(LiteralInt node) { + return intType; + } + + DartType visitLiteralDouble(LiteralDouble node) { + return doubleType; + } + + DartType visitLiteralBool(LiteralBool node) { + return boolType; + } + + DartType visitLiteralString(LiteralString node) { + return stringType; + } + + DartType visitStringJuxtaposition(StringJuxtaposition node) { + analyze(node.first); + analyze(node.second); + return stringType; + } + + DartType visitLiteralNull(LiteralNull node) { + return types.dynamicType; + } + + DartType visitLiteralSymbol(LiteralSymbol node) { + return compiler.symbolClass.rawType; + } + + DartType computeConstructorType(Element constructor, DartType type) { + if (Elements.isUnresolved(constructor)) return types.dynamicType; + DartType constructorType = constructor.computeType(compiler); + if (identical(type.kind, TypeKind.INTERFACE)) { + if (constructor.isSynthesized) { + // TODO(johnniwinther): Remove this when synthesized constructors handle + // type variables correctly. + InterfaceType interfaceType = type; + ClassElement receiverElement = interfaceType.element; + while (receiverElement.isMixinApplication) { + receiverElement = receiverElement.supertype.element; + } + constructorType = constructorType.substByContext( + interfaceType.asInstanceOf(receiverElement)); + } else { + constructorType = constructorType.substByContext(type); + } + } + return constructorType; + } + + DartType visitNewExpression(NewExpression node) { + Element element = elements[node.send]; + if (Elements.isUnresolved(element)) return types.dynamicType; + + checkPrivateAccess(node, element, element.name); + + DartType newType = elements.getType(node); + DartType constructorType = computeConstructorType(element, newType); + analyzeArguments(node.send, element, constructorType); + return newType; + } + + DartType visitLiteralList(LiteralList node) { + InterfaceType listType = elements.getType(node); + DartType listElementType = firstType(listType.typeArguments); + for (Link link = node.elements.nodes; + !link.isEmpty; + link = link.tail) { + Node element = link.head; + DartType elementType = analyze(element); + checkAssignable(element, elementType, listElementType, + isConst: node.isConst()); + } + return listType; + } + + DartType visitNodeList(NodeList node) { + DartType type = StatementType.NOT_RETURNING; + bool reportedDeadCode = false; + for (Link link = node.nodes; !link.isEmpty; link = link.tail) { + DartType nextType = + analyze(link.head, inInitializer: analyzingInitializer); + if (type == StatementType.RETURNING) { + if (!reportedDeadCode) { + reportTypeWarning(link.head, MessageKind.UNREACHABLE_CODE); + reportedDeadCode = true; + } + } else if (type == StatementType.MAYBE_RETURNING){ + if (nextType == StatementType.RETURNING) { + type = nextType; + } + } else { + type = nextType; + } + } + return type; + } + + DartType visitRethrow(Rethrow node) { + return StatementType.RETURNING; + } + + /** Dart Programming Language Specification: 11.10 Return */ + DartType visitReturn(Return node) { + if (identical(node.getBeginToken().stringValue, 'native')) { + return StatementType.RETURNING; + } + if (node.isRedirectingFactoryBody) { + // TODO(lrn): Typecheck the body. It must refer to the constructor + // of a subtype. + return StatementType.RETURNING; + } + + final expression = node.expression; + final isVoidFunction = (identical(expectedReturnType, types.voidType)); + + // Executing a return statement return e; [...] It is a static type warning + // if the type of e may not be assigned to the declared return type of the + // immediately enclosing function. + if (expression != null) { + final expressionType = analyze(expression); + Element element = elements.currentElement; + if (element != null && element.isGenerativeConstructor()) { + // The resolver already emitted an error for this expression. + } else if (isVoidFunction + && !types.isAssignable(expressionType, types.voidType)) { + reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID); + } else { + checkAssignable(expression, expressionType, expectedReturnType); + } + + // Let f be the function immediately enclosing a return statement of the + // form 'return;' It is a static warning if both of the following conditions + // hold: + // - f is not a generative constructor. + // - The return type of f may not be assigned to void. + } else if (!types.isAssignable(expectedReturnType, types.voidType)) { + reportTypeWarning(node, MessageKind.RETURN_NOTHING, + {'returnType': expectedReturnType}); + } + return StatementType.RETURNING; + } + + DartType visitThrow(Throw node) { + // TODO(johnniwinther): Handle reachability. + analyze(node.expression); + return types.dynamicType; + } + + DartType visitTypeAnnotation(TypeAnnotation node) { + return elements.getType(node); + } + + DartType visitVariableDefinitions(VariableDefinitions node) { + DartType type = analyzeWithDefault(node.type, types.dynamicType); + if (type == types.voidType) { + reportTypeWarning(node.type, MessageKind.VOID_VARIABLE); + type = types.dynamicType; + } + for (Link link = node.definitions.nodes; !link.isEmpty; + link = link.tail) { + Node definition = link.head; + invariant(definition, definition is Identifier || definition is SendSet, + message: 'expected identifier or initialization'); + if (definition is SendSet) { + SendSet initialization = definition; + DartType initializer = analyzeNonVoid(initialization.arguments.head); + checkAssignable(initialization.assignmentOperator, initializer, type); + } + } + return StatementType.NOT_RETURNING; + } + + DartType visitWhile(While node) { + checkCondition(node.condition); + StatementType bodyType = analyze(node.body); + Expression cond = node.condition.asParenthesizedExpression().expression; + if (cond.asLiteralBool() != null && cond.asLiteralBool().value == true) { + // If the condition is a constant boolean expression denoting true, + // control-flow always enters the loop body. + // TODO(karlklose): this should be StatementType.RETURNING unless there + // is a break in the loop body that has the loop or a label outside the + // loop as a target. + return bodyType; + } else { + return bodyType.join(StatementType.NOT_RETURNING); + } + } + + DartType visitParenthesizedExpression(ParenthesizedExpression node) { + Expression expression = node.expression; + DartType type = analyze(expression); + for (TypePromotion typePromotion in getShownTypePromotionsFor(expression)) { + showTypePromotion(node, typePromotion); + } + return type; + } + + DartType visitConditional(Conditional node) { + Expression condition = node.condition; + Expression thenExpression = node.thenExpression; + + checkCondition(condition); + + DartType thenType = analyzeInPromotedContext(condition, thenExpression); + + DartType elseType = analyzeNonVoid(node.elseExpression); + return compiler.types.computeLeastUpperBound(thenType, elseType); + } + + visitStringInterpolation(StringInterpolation node) { + node.visitChildren(this); + return stringType; + } + + visitStringInterpolationPart(StringInterpolationPart node) { + node.visitChildren(this); + return stringType; + } + + visitEmptyStatement(EmptyStatement node) { + return StatementType.NOT_RETURNING; + } + + visitBreakStatement(BreakStatement node) { + return StatementType.NOT_RETURNING; + } + + visitContinueStatement(ContinueStatement node) { + return StatementType.NOT_RETURNING; + } + + visitForIn(ForIn node) { + analyze(node.expression); + StatementType bodyType = analyze(node.body); + return bodyType.join(StatementType.NOT_RETURNING); + } + + visitLabeledStatement(LabeledStatement node) { + return analyze(node.statement); + } + + visitLiteralMap(LiteralMap node) { + InterfaceType mapType = elements.getType(node); + DartType mapKeyType = firstType(mapType.typeArguments); + DartType mapValueType = secondType(mapType.typeArguments); + bool isConst = node.isConst(); + for (Link link = node.entries.nodes; + !link.isEmpty; + link = link.tail) { + LiteralMapEntry entry = link.head; + DartType keyType = analyze(entry.key); + checkAssignable(entry.key, keyType, mapKeyType, isConst: isConst); + DartType valueType = analyze(entry.value); + checkAssignable(entry.value, valueType, mapValueType, isConst: isConst); + } + return mapType; + } + + visitNamedArgument(NamedArgument node) { + // Named arguments are visited as part of analyzing invocations of + // unresolved methods. For instance [: foo(a: 42); :] where 'foo' is neither + // found in the enclosing scope nor through lookup on 'this' or + // [: x.foo(b: 42); :] where 'foo' cannot be not found through lookup on + // the static type of 'x'. + return analyze(node.expression); + } + + visitSwitchStatement(SwitchStatement node) { + // TODO(johnniwinther): Handle reachability based on reachability of + // switch cases. + + DartType expressionType = analyze(node.expression); + + // Check that all the case expressions are assignable to the expression. + for (SwitchCase switchCase in node.cases) { + for (Node labelOrCase in switchCase.labelsAndCases) { + CaseMatch caseMatch = labelOrCase.asCaseMatch(); + if (caseMatch == null) continue; + + DartType caseType = analyze(caseMatch.expression); + checkAssignable(caseMatch, expressionType, caseType); + } + + analyze(switchCase); + } + + return StatementType.NOT_RETURNING; + } + + visitSwitchCase(SwitchCase node) { + return analyze(node.statements); + } + + visitTryStatement(TryStatement node) { + // TODO(johnniwinther): Use reachability information of try-block, + // catch-blocks and finally-block to compute the whether the try statement + // is returning. + analyze(node.tryBlock); + for (CatchBlock catchBlock in node.catchBlocks) { + analyze(catchBlock); + } + analyzeWithDefault(node.finallyBlock, null); + return StatementType.NOT_RETURNING; + } + + visitCatchBlock(CatchBlock node) { + return analyze(node.block); + } + + visitTypedef(Typedef node) { + // Do not typecheck [Typedef] nodes. + } + + visitNode(Node node) { + compiler.internalError(node, + 'Unexpected node ${node.getObjectDescription()} in the type checker.'); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/container_type_mask.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/container_type_mask.dart new file mode 100644 index 0000000..b1a09fb --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/container_type_mask.dart @@ -0,0 +1,100 @@ +// Copyright (c) 2013, 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. + +part of types; + +/// A [ContainerTypeMask] is a [TypeMask] for a specific allocation +/// site of a container (currently only List) that will get specialized +/// once the [TypeGraphInferrer] phase finds an element type for it. +class ContainerTypeMask extends ForwardingTypeMask { + final TypeMask forwardTo; + + // The [Node] where this type mask was created. + final Node allocationNode; + + // The [Element] where this type mask was created. + final Element allocationElement; + + // The element type of this container. + final TypeMask elementType; + + // The length of the container. + final int length; + + ContainerTypeMask(this.forwardTo, + this.allocationNode, + this.allocationElement, + this.elementType, + this.length); + + TypeMask nullable() { + return isNullable + ? this + : new ContainerTypeMask(forwardTo.nullable(), + allocationNode, + allocationElement, + elementType, + length); + } + + TypeMask nonNullable() { + return isNullable + ? new ContainerTypeMask(forwardTo.nonNullable(), + allocationNode, + allocationElement, + elementType, + length) + : this; + } + + bool get isContainer => true; + bool get isExact => true; + + bool equalsDisregardNull(other) { + if (other is! ContainerTypeMask) return false; + return allocationNode == other.allocationNode + && elementType == other.elementType + && length == other.length; + } + + TypeMask intersection(TypeMask other, Compiler compiler) { + TypeMask forwardIntersection = forwardTo.intersection(other, compiler); + if (forwardIntersection.isEmpty) return forwardIntersection; + return forwardIntersection.isNullable + ? nullable() + : nonNullable(); + } + + TypeMask union(other, Compiler compiler) { + if (this == other) { + return this; + } else if (equalsDisregardNull(other)) { + return other.isNullable ? other : this; + } else if (other.isEmpty) { + return other.isNullable ? this.nullable() : this; + } else if (other.isContainer + && elementType != null + && other.elementType != null) { + TypeMask newElementType = + elementType.union(other.elementType, compiler); + int newLength = (length == other.length) ? length : null; + TypeMask newForwardTo = forwardTo.union(other.forwardTo, compiler); + return new ContainerTypeMask( + newForwardTo, null, null, newElementType, newLength); + } else { + return forwardTo.union(other, compiler); + } + } + + bool operator==(other) => super == other; + + int get hashCode { + return computeHashCode( + allocationNode, isNullable, elementType, length, forwardTo); + } + + String toString() { + return 'Container mask: $elementType length: $length type: $forwardTo'; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/dictionary_type_mask.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/dictionary_type_mask.dart new file mode 100644 index 0000000..3bd0f6f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/dictionary_type_mask.dart @@ -0,0 +1,118 @@ +// Copyright (c) 2014, 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. + +part of types; + +/** + * A [DictionaryTypeMask] is a [TypeMask] for a specific allocation + * site of a map (currently only internal Map class) that is used as + * a dictionary, i.e. a mapping from a set of statically known strings + * to values. These typemasks only come into existence after the + * [TypeGraphInferrer] has successfully identified such a usage. Otherwise, + * the more general [MapTypeMask] is used. + */ +class DictionaryTypeMask extends MapTypeMask { + // The underlying key/value map of this dictionary. + final Map typeMap; + + DictionaryTypeMask(forwardTo, + allocationNode, + allocationElement, + keyType, valueType, + this.typeMap) : + super(forwardTo, allocationNode, allocationElement, keyType, valueType); + + TypeMask nullable() { + return isNullable + ? this + : new DictionaryTypeMask(forwardTo.nullable(), + allocationNode, + allocationElement, + keyType, valueType, + typeMap); + } + + TypeMask nonNullable() { + return isNullable + ? new DictionaryTypeMask(forwardTo.nonNullable(), + allocationNode, + allocationElement, + keyType, valueType, + typeMap) + : this; + } + + bool get isDictionary => true; + bool get isExact => true; + + bool equalsDisregardNull(other) { + if (other is! DictionaryTypeMask) return false; + return allocationNode == other.allocationNode && + keyType == other.keyType && + valueType == other.valueType && + typeMap.keys.every((k) => other.typeMap.containsKey(k)) && + other.typeMap.keys.every((k) => typeMap.containsKey(k) && + typeMap[k] == other.typeMap[k]); + + } + + TypeMask intersection(TypeMask other, Compiler compiler) { + TypeMask forwardIntersection = forwardTo.intersection(other, compiler); + if (forwardIntersection.isEmpty) return forwardIntersection; + return forwardIntersection.isNullable + ? nullable() + : nonNullable(); + } + + TypeMask union(other, Compiler compiler) { + if (this == other) { + return this; + } else if (equalsDisregardNull(other)) { + return other.isNullable ? other : this; + } else if (other.isEmpty) { + return other.isNullable ? this.nullable() : this; + } else if (other.isDictionary) { + TypeMask newForwardTo = forwardTo.union(other.forwardTo, compiler); + TypeMask newKeyType = keyType.union(other.keyType, compiler); + TypeMask newValueType = valueType.union(other.valueType, compiler); + Map mappings = {}; + typeMap.forEach((k,v) { + if (!other.typeMap.containsKey(k)) { + mappings[k] = v.nullable(); + } + }); + other.typeMap.forEach((k,v) { + if (typeMap.containsKey(k)) { + mappings[k] = v.union(typeMap[k], compiler); + } else { + mappings[k] = v.nullable(); + } + }); + return new DictionaryTypeMask(newForwardTo, null, null, + newKeyType, newValueType, mappings); + } else if (other.isMap && + (other.keyType != null) && + (other.valueType != null)) { + TypeMask newForwardTo = forwardTo.union(other.forwardTo, compiler); + TypeMask newKeyType = keyType.union(other.keyType, compiler); + TypeMask newValueType = valueType.union(other.valueType, compiler); + return new MapTypeMask(newForwardTo, null, null, + newKeyType, newValueType); + } else { + return forwardTo.union(other, compiler); + } + } + + bool operator==(other) => super == other; + + int get hashCode { + return computeHashCode( + allocationNode, isNullable, typeMap, forwardTo); + } + + String toString() { + return + 'Dictionary mask: [$keyType/$valueType with $typeMap] type: $forwardTo'; + } +} \ No newline at end of file diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/flat_type_mask.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/flat_type_mask.dart new file mode 100644 index 0000000..4a7a509 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/flat_type_mask.dart @@ -0,0 +1,642 @@ +// Copyright (c) 2013, 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. + +part of types; + +/** + * A flat type mask is a type mask that has been flatten to contain a + * base type. + */ +class FlatTypeMask implements TypeMask { + + static const int EMPTY = 0; + static const int EXACT = 1; + static const int SUBCLASS = 2; + static const int SUBTYPE = 3; + + final ClassElement base; + final int flags; + + FlatTypeMask(ClassElement base, int kind, bool isNullable) + : this.internal(base, (kind << 1) | (isNullable ? 1 : 0)); + + FlatTypeMask.exact(ClassElement base) + : this.internal(base, (EXACT << 1) | 1); + FlatTypeMask.subclass(ClassElement base) + : this.internal(base, (SUBCLASS << 1) | 1); + FlatTypeMask.subtype(ClassElement base) + : this.internal(base, (SUBTYPE << 1) | 1); + + const FlatTypeMask.nonNullEmpty(): base = null, flags = 0; + const FlatTypeMask.empty() : base = null, flags = 1; + + FlatTypeMask.nonNullExact(ClassElement base) + : this.internal(base, EXACT << 1); + FlatTypeMask.nonNullSubclass(ClassElement base) + : this.internal(base, SUBCLASS << 1); + FlatTypeMask.nonNullSubtype(ClassElement base) + : this.internal(base, SUBTYPE << 1); + + FlatTypeMask.internal(this.base, this.flags) { + assert(base == null || base.isDeclaration); + } + + bool get isEmpty => (flags >> 1) == EMPTY; + bool get isExact => (flags >> 1) == EXACT; + bool get isNullable => (flags & 1) != 0; + + bool get isUnion => false; + bool get isContainer => false; + bool get isMap => false; + bool get isDictionary => false; + bool get isForwarding => false; + bool get isValue => false; + + // TODO(kasperl): Get rid of these. They should not be a visible + // part of the implementation because they make it hard to add + // proper union types if we ever want to. + bool get isSubclass => (flags >> 1) == SUBCLASS; + bool get isSubtype => (flags >> 1) == SUBTYPE; + + TypeMask nullable() { + return isNullable ? this : new FlatTypeMask.internal(base, flags | 1); + } + + TypeMask nonNullable() { + return isNullable ? new FlatTypeMask.internal(base, flags & ~1) : this; + } + + bool contains(ClassElement type, Compiler compiler) { + assert(type.isDeclaration); + if (isEmpty) { + return false; + } else if (identical(base, type)) { + return true; + } else if (isExact) { + return false; + } else if (isSubclass) { + return isSubclassOf(type, base, compiler); + } else { + assert(isSubtype); + return isSubtypeOf(type, base, compiler); + } + } + + bool isSingleImplementationOf(ClassElement cls, Compiler compiler) { + // Special case basic types so that, for example, JSString is the + // single implementation of String. + // The general optimization is to realize there is only one class that + // implements [base] and [base] is not instantiated. We however do + // not track correctly the list of truly instantiated classes. + Backend backend = compiler.backend; + if (containsOnlyString(compiler)) { + return cls == compiler.stringClass || cls == backend.stringImplementation; + } + if (containsOnlyBool(compiler)) { + return cls == compiler.boolClass || cls == backend.boolImplementation; + } + if (containsOnlyInt(compiler)) { + return cls == compiler.intClass + || cls == backend.intImplementation + || cls == backend.positiveIntImplementation + || cls == backend.uint32Implementation + || cls == backend.uint31Implementation; + } + if (containsOnlyDouble(compiler)) { + return cls == compiler.doubleClass + || cls == compiler.backend.doubleImplementation; + } + return false; + } + + bool isInMask(TypeMask other, Compiler compiler) { + if (isEmpty) { + return isNullable ? other.isNullable : true; + } + if (other.isEmpty) return false; + if (isNullable && !other.isNullable) return false; + if (other is! FlatTypeMask) return other.containsMask(this, compiler); + FlatTypeMask flatOther = other; + ClassElement otherBase = flatOther.base; + if (flatOther.isExact) { + return (isExact && base == otherBase) + || isSingleImplementationOf(otherBase, compiler); + } + if (flatOther.isSubclass) { + if (isSubtype) return false; + return base == otherBase || isSubclassOf(base, otherBase, compiler); + } + assert(flatOther.isSubtype); + return satisfies(otherBase, compiler); + } + + bool containsMask(TypeMask other, Compiler compiler) { + return other.isInMask(this, compiler); + } + + bool containsOnlyInt(Compiler compiler) { + return base == compiler.intClass + || base == compiler.backend.intImplementation + || base == compiler.backend.positiveIntImplementation + || base == compiler.backend.uint31Implementation + || base == compiler.backend.uint32Implementation; + } + + bool containsOnlyDouble(Compiler compiler) { + return base == compiler.doubleClass + || base == compiler.backend.doubleImplementation; + } + + bool containsOnlyNum(Compiler compiler) { + return containsOnlyInt(compiler) + || containsOnlyDouble(compiler) + || base == compiler.numClass + || base == compiler.backend.numImplementation; + } + + bool containsOnlyBool(Compiler compiler) { + return base == compiler.boolClass + || base == compiler.backend.boolImplementation; + } + + bool containsOnlyString(Compiler compiler) { + return base == compiler.stringClass + || base == compiler.backend.stringImplementation; + } + + bool containsOnly(ClassElement cls) { + assert(cls.isDeclaration); + return base == cls; + } + + bool satisfies(ClassElement cls, Compiler compiler) { + assert(cls.isDeclaration); + if (isEmpty) return false; + if (base == cls) return true; + if (isSubtypeOf(base, cls, compiler)) return true; + return false; + } + + /** + * Returns the [ClassElement] if this type represents a single class, + * otherwise returns `null`. This method is conservative. + */ + ClassElement singleClass(Compiler compiler) { + if (isEmpty) return null; + if (isNullable) return null; // It is Null and some other class. + if (isExact) { + return base; + } else if (isSubclass) { + return compiler.world.hasAnySubclass(base) ? null : base; + } else { + assert(isSubtype); + return null; + } + } + + /** + * Returns whether or not this type mask contains all types. + */ + bool containsAll(Compiler compiler) { + if (isEmpty || isExact) return false; + return identical(base, compiler.objectClass) + || identical(base, compiler.dynamicClass); + } + + TypeMask union(TypeMask other, Compiler compiler) { + assert(other != null); + if (other is! FlatTypeMask) return other.union(this, compiler); + FlatTypeMask flatOther = other; + if (isEmpty) { + return isNullable ? flatOther.nullable() : flatOther; + } else if (flatOther.isEmpty) { + return flatOther.isNullable ? nullable() : this; + } else if (base == flatOther.base) { + return unionSame(flatOther, compiler); + } else if (isSubclassOf(flatOther.base, base, compiler)) { + return unionSubclass(flatOther, compiler); + } else if (isSubclassOf(base, flatOther.base, compiler)) { + return flatOther.unionSubclass(this, compiler); + } else if (isSubtypeOf(flatOther.base, base, compiler)) { + return unionSubtype(flatOther, compiler); + } else if (isSubtypeOf(base, flatOther.base, compiler)) { + return flatOther.unionSubtype(this, compiler); + } else { + return new UnionTypeMask._([this, flatOther]); + } + } + + TypeMask unionSame(FlatTypeMask other, Compiler compiler) { + assert(base == other.base); + // The two masks share the base type, so we must chose the least + // constraining kind (the highest) of the two. If either one of + // the masks are nullable the result should be nullable too. + int combined = (flags > other.flags) + ? flags | (other.flags & 1) + : other.flags | (flags & 1); + if (flags == combined) { + return this; + } else if (other.flags == combined) { + return other; + } else { + return new FlatTypeMask.internal(base, combined); + } + } + + TypeMask unionSubclass(FlatTypeMask other, Compiler compiler) { + assert(isSubclassOf(other.base, base, compiler)); + int combined; + if ((isExact && other.isExact) || base == compiler.objectClass) { + // Since the other mask is a subclass of this mask, we need the + // resulting union to be a subclass too. If either one of the + // masks are nullable the result should be nullable too. + combined = (SUBCLASS << 1) | ((flags | other.flags) & 1); + } else { + // Both masks are at least subclass masks, so we pick the least + // constraining kind (the highest) of the two. If either one of + // the masks are nullable the result should be nullable too. + combined = (flags > other.flags) + ? flags | (other.flags & 1) + : other.flags | (flags & 1); + } + return (flags != combined) + ? new FlatTypeMask.internal(base, combined) + : this; + } + + TypeMask unionSubtype(FlatTypeMask other, Compiler compiler) { + assert(isSubtypeOf(other.base, base, compiler)); + // Since the other mask is a subtype of this mask, we need the + // resulting union to be a subtype too. If either one of the masks + // are nullable the result should be nullable too. + int combined = (SUBTYPE << 1) | ((flags | other.flags) & 1); + return (flags != combined) + ? new FlatTypeMask.internal(base, combined) + : this; + } + + TypeMask intersection(TypeMask other, Compiler compiler) { + assert(other != null); + if (other is! FlatTypeMask) return other.intersection(this, compiler); + FlatTypeMask flatOther = other; + if (isEmpty) { + return flatOther.isNullable ? this : nonNullable(); + } else if (flatOther.isEmpty) { + return isNullable ? flatOther : other.nonNullable(); + } else if (base == flatOther.base) { + return intersectionSame(flatOther, compiler); + } else if (isSubclassOf(flatOther.base, base, compiler)) { + return intersectionSubclass(flatOther, compiler); + } else if (isSubclassOf(base, flatOther.base, compiler)) { + return flatOther.intersectionSubclass(this, compiler); + } else if (isSubtypeOf(flatOther.base, base, compiler)) { + return intersectionSubtype(flatOther, compiler); + } else if (isSubtypeOf(base, flatOther.base, compiler)) { + return flatOther.intersectionSubtype(this, compiler); + } else { + return intersectionDisjoint(flatOther, compiler); + } + } + + TypeMask intersectionSame(FlatTypeMask other, Compiler compiler) { + assert(base == other.base); + // The two masks share the base type, so we must chose the most + // constraining kind (the lowest) of the two. Only if both masks + // are nullable, will the result be nullable too. + int combined = (flags < other.flags) + ? flags & ((other.flags & 1) | ~1) + : other.flags & ((flags & 1) | ~1); + if (flags == combined) { + return this; + } else if (other.flags == combined) { + return other; + } else { + return new FlatTypeMask.internal(base, combined); + } + } + + TypeMask intersectionSubclass(FlatTypeMask other, Compiler compiler) { + assert(isSubclassOf(other.base, base, compiler)); + // If this mask isn't at least a subclass mask, then the + // intersection with the other mask is empty. + if (isExact) return intersectionEmpty(other); + // Only the other mask puts constraints on the intersection mask, + // so base the combined flags on the other mask. Only if both + // masks are nullable, will the result be nullable too. + int combined = other.flags & ((flags & 1) | ~1); + if (other.flags == combined) { + return other; + } else { + return new FlatTypeMask.internal(other.base, combined); + } + } + + TypeMask intersectionSubtype(FlatTypeMask other, Compiler compiler) { + assert(isSubtypeOf(other.base, base, compiler)); + if (!isSubtype) return intersectionHelper(other, compiler); + // Only the other mask puts constraints on the intersection mask, + // so base the combined flags on the other mask. Only if both + // masks are nullable, will the result be nullable too. + int combined = other.flags & ((flags & 1) | ~1); + if (other.flags == combined) { + return other; + } else { + return new FlatTypeMask.internal(other.base, combined); + } + } + + TypeMask intersectionDisjoint(FlatTypeMask other, Compiler compiler) { + assert(base != other.base); + assert(!isSubtypeOf(base, other.base, compiler)); + assert(!isSubtypeOf(other.base, base, compiler)); + return intersectionHelper(other, compiler); + } + + TypeMask intersectionHelper(FlatTypeMask other, Compiler compiler) { + assert(base != other.base); + assert(!isSubclassOf(base, other.base, compiler)); + assert(!isSubclassOf(other.base, base, compiler)); + // If one of the masks are exact or if both of them are subclass + // masks, then the intersection is empty. + if (isExact || other.isExact) return intersectionEmpty(other); + if (isSubclass && other.isSubclass) return intersectionEmpty(other); + assert(isSubtype || other.isSubtype); + int kind = (isSubclass || other.isSubclass) ? SUBCLASS : SUBTYPE; + // Compute the set of classes that are contained in both type masks. + Set common = commonContainedClasses(this, other, compiler); + if (common == null || common.isEmpty) return intersectionEmpty(other); + // Narrow down the candidates by only looking at common classes + // that do not have a superclass or supertype that will be a + // better candidate. + Iterable candidates = common.where((ClassElement each) { + bool containsSuperclass = common.contains(each.supertype.element); + // If the superclass is also a candidate, then we don't want to + // deal with this class. If we're only looking for a subclass we + // know we don't have to look at the list of interfaces because + // they can never be in the common set. + if (containsSuperclass || kind == SUBCLASS) return !containsSuperclass; + // Run through the direct supertypes of the class. If the common + // set contains the direct supertype of the class, we ignore the + // the class because the supertype is a better candidate. + for (Link link = each.interfaces; !link.isEmpty; link = link.tail) { + if (common.contains(link.head.element)) return false; + } + return true; + }); + // Run through the list of candidates and compute the union. The + // result will only be nullable if both masks are nullable. + int combined = (kind << 1) | (flags & other.flags & 1); + TypeMask result; + for (ClassElement each in candidates) { + TypeMask mask = new FlatTypeMask.internal(each, combined); + result = (result == null) ? mask : result.union(mask, compiler); + } + return result; + } + + TypeMask intersectionEmpty(FlatTypeMask other) { + return new TypeMask(null, EMPTY, isNullable && other.isNullable); + } + + /** + * Returns whether [element] will be the one used at runtime when being + * invoked on an instance of [cls]. [selector] is used to ensure library + * privacy is taken into account. + */ + static bool hasElementIn(ClassElement cls, + Selector selector, + Element element, + Compiler compiler) { + // Use [:implementation:] of [element] + // because our function set only stores declarations. + Element result = findMatchIn(cls, selector, compiler); + return result == null + ? false + : result.implementation == element.implementation; + } + + static Element findMatchIn(ClassElement cls, + Selector selector, + Compiler compiler) { + // Use the [:implementation] of [cls] in case the found [element] + // is in the patch class. + return cls.implementation.lookupSelector(selector, compiler); + } + + /** + * Returns whether [element] is a potential target when being + * invoked on this type mask. [selector] is used to ensure library + * privacy is taken into account. + */ + bool canHit(Element element, Selector selector, Compiler compiler) { + assert(element.name == selector.name); + if (isEmpty) { + if (!isNullable) return false; + return hasElementIn( + compiler.backend.nullImplementation, selector, element, compiler); + } + + // TODO(kasperl): Can't we just avoid creating typed selectors + // based of function types? + Element self = base; + if (self.isTypedef()) { + // A typedef is a function type that doesn't have any + // user-defined members. + return false; + } + + ClassElement other = element.getEnclosingClass(); + if (compiler.backend.isNullImplementation(other)) { + return isNullable; + } else if (isExact) { + return hasElementIn(self, selector, element, compiler); + } else if (isSubclass) { + return hasElementIn(self, selector, element, compiler) + || other.isSubclassOf(self) + || compiler.world.hasAnySubclassThatMixes(self, other); + } else { + assert(isSubtype); + bool result = hasElementIn(self, selector, element, compiler) + || other.implementsInterface(self) + || compiler.world.hasAnySubclassThatImplements(other, base) + || compiler.world.hasAnySubclassOfMixinUseThatImplements(other, base); + if (result) return true; + // If the class is used as a mixin, we have to check if the element + // can be hit from any of the mixin applications. + Iterable mixinUses = compiler.world.mixinUses[self]; + if (mixinUses == null) return false; + return mixinUses.any((mixinApplication) => + hasElementIn(mixinApplication, selector, element, compiler) + || other.isSubclassOf(mixinApplication) + || compiler.world.hasAnySubclassThatMixes(mixinApplication, other)); + } + } + + /** + * Returns whether a [selector] call on an instance of [cls] + * will hit a method at runtime, and not go through [noSuchMethod]. + */ + static bool hasConcreteMatch(ClassElement cls, + Selector selector, + Compiler compiler) { + Element element = findMatchIn(cls, selector, compiler); + if (element == null) return false; + + if (element.isAbstract) { + ClassElement enclosingClass = element.getEnclosingClass(); + return hasConcreteMatch(enclosingClass.superclass, selector, compiler); + } + return selector.appliesUntyped(element, compiler); + } + + bool needsNoSuchMethodHandling(Selector selector, Compiler compiler) { + // A call on an empty type mask is either dead code, or a call on + // `null`. + if (isEmpty) return false; + // A call on an exact mask for an abstract class is dead code. + if (isExact && base.isAbstract) return false; + // If the receiver is guaranteed to have a member that + // matches what we're looking for, there's no need to + // introduce a noSuchMethod handler. It will never be called. + // + // As an example, consider this class hierarchy: + // + // A <-- noSuchMethod + // / \ + // C B <-- foo + // + // If we know we're calling foo on an object of type B we + // don't have to worry about the noSuchMethod method in A + // because objects of type B implement foo. On the other hand, + // if we end up calling foo on something of type C we have to + // add a handler for it. + + // If the holders of all user-defined noSuchMethod + // implementations that might be applicable to the receiver + // type have a matching member for the current name and + // selector, we avoid introducing a noSuchMethod handler. + // + // As an example, consider this class hierarchy: + // + // A <-- foo + // / \ + // noSuchMethod --> B C <-- bar + // | | + // C D <-- noSuchMethod + // + // When calling foo on an object of type A, we know that the + // implementations of noSuchMethod are in the classes B and D + // that also (indirectly) implement foo, so we do not need a + // handler for it. + // + // If we're calling bar on an object of type D, we don't need + // the handler either because all objects of type D implement + // bar through inheritance. + // + // If we're calling bar on an object of type A we do need the + // handler because we may have to call B.noSuchMethod since B + // does not implement bar. + + bool hasMatch = hasConcreteMatch(base, selector, compiler); + if (isExact) return !hasMatch; + if (!base.isAbstract && !hasMatch) return true; + + Set subtypesToCheck; + if (isSubtype) { + subtypesToCheck = compiler.world.subtypesOf(base); + } else { + assert(isSubclass); + subtypesToCheck = compiler.world.subclassesOf(base); + } + + return subtypesToCheck != null + && subtypesToCheck.any((ClassElement cls) { + // We can just skip abstract classes because we know no + // instance of them will be created at runtime, and + // therefore there is no instance that will require + // [noSuchMethod] handling. + return !cls.isAbstract + && !hasConcreteMatch(cls, selector, compiler); + }); + } + + Element locateSingleElement(Selector selector, Compiler compiler) { + if (isEmpty) return null; + Iterable targets = compiler.world.allFunctions.filter(selector); + if (targets.length != 1) return null; + Element result = targets.first; + ClassElement enclosing = result.getEnclosingClass(); + // We only return the found element if it is guaranteed to be + // implemented on the exact receiver type. It could be found in a + // subclass or in an inheritance-wise unrelated class in case of + // subtype selectors. + return (base.isSubclassOf(enclosing)) ? result : null; + } + + bool operator ==(var other) { + if (other is !FlatTypeMask) return false; + FlatTypeMask otherMask = other; + return (flags == otherMask.flags) && (base == otherMask.base); + } + + int get hashCode { + return (base == null ? 0 : base.hashCode) + 31 * flags.hashCode; + } + + String toString() { + if (isEmpty) return isNullable ? '[null]' : '[empty]'; + StringBuffer buffer = new StringBuffer(); + if (isNullable) buffer.write('null|'); + if (isExact) buffer.write('exact='); + if (isSubclass) buffer.write('subclass='); + if (isSubtype) buffer.write('subtype='); + buffer.write(base.name); + return "[$buffer]"; + } + + static bool isSubclassOf(ClassElement x, ClassElement y, Compiler compiler) { + assert(x.isDeclaration && y.isDeclaration); + Set subclasses = compiler.world.subclassesOf(y); + return (subclasses != null) ? subclasses.contains(x) : false; + } + + static bool isSubtypeOf(ClassElement x, ClassElement y, Compiler compiler) { + assert(x.isDeclaration && y.isDeclaration); + Set subtypes = compiler.world.subtypesOf(y); + if (subtypes != null && subtypes.contains(x)) return true; + if (y != compiler.functionClass) return false; + return x.callType != null; + } + + static Set commonContainedClasses(FlatTypeMask x, + FlatTypeMask y, + Compiler compiler) { + Set xSubset = containedSubset(x, compiler); + if (xSubset == null) return null; + Set ySubset = containedSubset(y, compiler); + if (ySubset == null) return null; + Set smallSet, largeSet; + if (xSubset.length <= ySubset.length) { + smallSet = xSubset; + largeSet = ySubset; + } else { + smallSet = ySubset; + largeSet = xSubset; + } + var result = smallSet.where((ClassElement each) => largeSet.contains(each)); + return result.toSet(); + } + + static Set containedSubset(FlatTypeMask x, Compiler compiler) { + ClassElement element = x.base; + if (x.isExact) { + return null; + } else if (x.isSubclass) { + return compiler.world.subclassesOf(element); + } else { + assert(x.isSubtype); + return compiler.world.subtypesOf(element); + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/forwarding_type_mask.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/forwarding_type_mask.dart new file mode 100644 index 0000000..2d71c7d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/forwarding_type_mask.dart @@ -0,0 +1,112 @@ +// Copyright (c) 2013, 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. + +part of types; + +/** + * A type mask that wraps an other one, and delegate all its + * implementation methods to it. + */ +abstract class ForwardingTypeMask implements TypeMask { + + TypeMask get forwardTo; + + ForwardingTypeMask(); + + bool get isEmpty => forwardTo.isEmpty; + bool get isNullable => forwardTo.isNullable; + bool get isExact => forwardTo.isExact; + + bool get isUnion => false; + bool get isContainer => false; + bool get isMap => false; + bool get isDictionary => false; + bool get isValue => false; + bool get isForwarding => true; + + bool isInMask(TypeMask other, Compiler compiler) { + return forwardTo.isInMask(other, compiler); + } + + bool containsMask(TypeMask other, Compiler compiler) { + return forwardTo.containsMask(other, compiler); + } + + bool containsOnlyInt(Compiler compiler) { + return forwardTo.containsOnlyInt(compiler); + } + + bool containsOnlyDouble(Compiler compiler) { + return forwardTo.containsOnlyDouble(compiler); + } + + bool containsOnlyNum(Compiler compiler) { + return forwardTo.containsOnlyNum(compiler); + } + + bool containsOnlyBool(Compiler compiler) { + return forwardTo.containsOnlyBool(compiler); + } + + bool containsOnlyString(Compiler compiler) { + return forwardTo.containsOnlyString(compiler); + } + + bool containsOnly(ClassElement element) { + return forwardTo.containsOnly(element); + } + + bool satisfies(ClassElement cls, Compiler compiler) { + return forwardTo.satisfies(cls, compiler); + } + + bool contains(ClassElement type, Compiler compiler) { + return forwardTo.contains(type, compiler); + } + + bool containsAll(Compiler compiler) { + return forwardTo.containsAll(compiler); + } + + ClassElement singleClass(Compiler compiler) { + return forwardTo.singleClass(compiler); + } + + TypeMask union(other, Compiler compiler) { + if (this == other) { + return this; + } else if (equalsDisregardNull(other)) { + return other.isNullable ? other : this; + } else if (other.isEmpty) { + return other.isNullable ? this.nullable() : this; + } + return forwardTo.union(other, compiler); + } + + TypeMask intersection(TypeMask other, Compiler compiler) { + return forwardTo.intersection(other, compiler); + } + + bool needsNoSuchMethodHandling(Selector selector, Compiler compiler) { + return forwardTo.needsNoSuchMethodHandling(selector, compiler); + } + + bool canHit(Element element, Selector selector, Compiler compiler) { + return forwardTo.canHit(element, selector, compiler); + } + + Element locateSingleElement(Selector selector, Compiler compiler) { + return forwardTo.locateSingleElement(selector, compiler); + } + + bool equalsDisregardNull(other); + + bool operator==(other) { + return equalsDisregardNull(other) && + isNullable == other.isNullable && + forwardTo == other.forwardTo; + } + + int get hashCode => throw "Subclass should implement hashCode getter"; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/map_type_mask.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/map_type_mask.dart new file mode 100644 index 0000000..8673d59 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/map_type_mask.dart @@ -0,0 +1,115 @@ +// Copyright (c) 2014, 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. + +part of types; + +/** + * A [MapTypeMask] is a [TypeMask] for a specific allocation + * site of a map (currently only internal Map class) that will get specialized + * once the [TypeGraphInferrer] phase finds a key and/or value type for it. + */ +class MapTypeMask extends ForwardingTypeMask { + final TypeMask forwardTo; + + // The [Node] where this type mask was created. + final Node allocationNode; + + // The [Element] where this type mask was created. + final Element allocationElement; + + // The value type of this map. + final TypeMask valueType; + + // The key type of this map. + final TypeMask keyType; + + MapTypeMask(this.forwardTo, + this.allocationNode, + this.allocationElement, + this.keyType, + this.valueType); + + TypeMask nullable() { + return isNullable + ? this + : new MapTypeMask(forwardTo.nullable(), + allocationNode, + allocationElement, + keyType, + valueType); + } + + TypeMask nonNullable() { + return isNullable + ? new MapTypeMask(forwardTo.nonNullable(), + allocationNode, + allocationElement, + keyType, + valueType) + : this; + } + + bool get isContainer => false; + bool get isMap => true; + bool get isExact => true; + + bool equalsDisregardNull(other) { + if (other is! MapTypeMask) return false; + return allocationNode == other.allocationNode && + keyType == other.keyType && + valueType == other.valueType; + } + + TypeMask intersection(TypeMask other, Compiler compiler) { + TypeMask forwardIntersection = forwardTo.intersection(other, compiler); + if (forwardIntersection.isEmpty) return forwardIntersection; + return forwardIntersection.isNullable + ? nullable() + : nonNullable(); + } + + TypeMask union(other, Compiler compiler) { + if (this == other) { + return this; + } else if (equalsDisregardNull(other)) { + return other.isNullable ? other : this; + } else if (other.isEmpty) { + return other.isNullable ? this.nullable() : this; + } else if (other.isMap && + keyType != null && + other.keyType != null && + valueType != null && + other.valueType != null) { + TypeMask newKeyType = + keyType.union(other.keyType, compiler); + TypeMask newValueType = + valueType.union(other.valueType, compiler); + TypeMask newForwardTo = forwardTo.union(other.forwardTo, compiler); + return new MapTypeMask( + newForwardTo, null, null, newKeyType, newValueType); + } else if (other.isDictionary) { + TypeMask newKeyType = + keyType.union(compiler.typesTask.stringType, compiler); + TypeMask newValueType = + other.typeMap.values.fold(keyType, (p,n) => p.union(n, compiler)); + TypeMask newForwardTo = forwardTo.union(other.forwardTo, compiler); + MapTypeMask newMapTypeMask = new MapTypeMask( + newForwardTo, null, null, newKeyType, newValueType); + return newMapTypeMask; + } else { + return forwardTo.union(other, compiler); + } + } + + bool operator==(other) => super == other; + + int get hashCode { + return computeHashCode( + allocationNode, isNullable, keyType, valueType, forwardTo); + } + + String toString() { + return 'Map mask: [$keyType/$valueType] type: $forwardTo'; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/type_mask.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/type_mask.dart new file mode 100644 index 0000000..520f084 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/type_mask.dart @@ -0,0 +1,124 @@ +// Copyright (c) 2013, 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. + +part of types; + +/** + * A type mask represents a set of contained classes, but the + * operations on it are not guaranteed to be precise and they may + * yield conservative answers that contain too many classes. + */ +abstract class TypeMask { + factory TypeMask(ClassElement base, int kind, bool isNullable) + => new FlatTypeMask(base, kind, isNullable); + + const factory TypeMask.empty() = FlatTypeMask.empty; + + factory TypeMask.exact(ClassElement base) + => new FlatTypeMask.exact(base); + factory TypeMask.subclass(ClassElement base) + => new FlatTypeMask.subclass(base); + factory TypeMask.subtype(ClassElement base) + => new FlatTypeMask.subtype(base); + + const factory TypeMask.nonNullEmpty() = FlatTypeMask.nonNullEmpty; + factory TypeMask.nonNullExact(ClassElement base) + => new FlatTypeMask.nonNullExact(base); + factory TypeMask.nonNullSubclass(ClassElement base) + => new FlatTypeMask.nonNullSubclass(base); + factory TypeMask.nonNullSubtype(ClassElement base) + => new FlatTypeMask.nonNullSubtype(base); + + factory TypeMask.unionOf(Iterable masks, Compiler compiler) { + return UnionTypeMask.unionOf(masks, compiler); + } + + /** + * Returns a nullable variant of [this] type mask. + */ + TypeMask nullable(); + + /** + * Returns a non-nullable variant of [this] type mask. + */ + TypeMask nonNullable(); + + bool get isEmpty; + bool get isNullable; + bool get isExact; + + bool get isUnion; + bool get isContainer; + bool get isMap; + bool get isDictionary; + bool get isForwarding; + bool get isValue; + + bool containsOnlyInt(Compiler compiler); + bool containsOnlyDouble(Compiler compiler); + bool containsOnlyNum(Compiler compiler); + bool containsOnlyBool(Compiler compiler); + bool containsOnlyString(Compiler compiler); + bool containsOnly(ClassElement element); + + /** + * Returns whether this type mask is a subtype of [other]. + */ + bool isInMask(TypeMask other, Compiler compiler); + + /** + * Returns whether [other] is a subtype of this type mask. + */ + bool containsMask(TypeMask other, Compiler compiler); + + /** + * Returns whether this type mask is an instance of [cls]. + */ + bool satisfies(ClassElement cls, Compiler compiler); + + /** + * Returns whether or not this type mask contains the given type. + */ + bool contains(ClassElement type, Compiler compiler); + + /** + * Returns whether or not this type mask contains all types. + */ + bool containsAll(Compiler compiler); + + /** + * Returns the [ClassElement] if this type represents a single class, + * otherwise returns `null`. This method is conservative. + */ + ClassElement singleClass(Compiler compiler); + + /** + * Returns a type mask representing the union of [this] and [other]. + */ + TypeMask union(TypeMask other, Compiler compiler); + + /** + * Returns a type mask representing the intersection of [this] and [other]. + */ + TypeMask intersection(TypeMask other, Compiler compiler); + + /** + * Returns whether this [TypeMask] applied to [selector] can hit a + * [noSuchMethod]. + */ + bool needsNoSuchMethodHandling(Selector selector, Compiler compiler); + + /** + * Returns whether [element] is a potential target when being + * invoked on this type mask. [selector] is used to ensure library + * privacy is taken into account. + */ + bool canHit(Element element, Selector selector, Compiler compiler); + + /** + * Returns the [element] that is known to always be hit at runtime + * on this mask. Returns null if there is none. + */ + Element locateSingleElement(Selector selector, Compiler compiler); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/types.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/types.dart new file mode 100644 index 0000000..3185550 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/types.dart @@ -0,0 +1,331 @@ +// Copyright (c) 2012, 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. + +library types; + +import '../dart2jslib.dart' hide Selector, TypedSelector; +import '../elements/elements.dart'; +import '../inferrer/type_graph_inferrer.dart' show TypeGraphInferrer; +import '../tree/tree.dart'; +import '../util/util.dart'; +import '../universe/universe.dart'; +import '../inferrer/concrete_types_inferrer.dart' show ConcreteTypesInferrer; + +part 'container_type_mask.dart'; +part 'dictionary_type_mask.dart'; +part 'flat_type_mask.dart'; +part 'forwarding_type_mask.dart'; +part 'map_type_mask.dart'; +part 'type_mask.dart'; +part 'union_type_mask.dart'; +part 'value_type_mask.dart'; + +/** + * Common super class for our type inferrers. + */ +abstract class TypesInferrer { + void analyzeMain(Element element); + TypeMask getReturnTypeOfElement(Element element); + TypeMask getTypeOfElement(Element element); + TypeMask getTypeOfNode(Element owner, Node node); + TypeMask getTypeOfSelector(Selector selector); + void clear(); + bool isCalledOnce(Element element); + bool isFixedArrayCheckedForGrowable(Node node); +} + +/** + * The types task infers guaranteed types globally. + */ +class TypesTask extends CompilerTask { + static final bool DUMP_BAD_CPA_RESULTS = false; + static final bool DUMP_GOOD_CPA_RESULTS = false; + + final String name = 'Type inference'; + TypesInferrer typesInferrer; + ConcreteTypesInferrer concreteTypesInferrer; + + TypesTask(Compiler compiler) : super(compiler) { + typesInferrer = new TypeGraphInferrer(compiler); + if (compiler.enableConcreteTypeInference) { + concreteTypesInferrer = new ConcreteTypesInferrer(compiler); + } + } + + TypeMask dynamicTypeCache; + TypeMask nonNullTypeCache; + TypeMask nullTypeCache; + TypeMask intTypeCache; + TypeMask uint32TypeCache; + TypeMask uint31TypeCache; + TypeMask positiveIntTypeCache; + TypeMask doubleTypeCache; + TypeMask numTypeCache; + TypeMask boolTypeCache; + TypeMask functionTypeCache; + TypeMask listTypeCache; + TypeMask constListTypeCache; + TypeMask fixedListTypeCache; + TypeMask growableListTypeCache; + TypeMask mapTypeCache; + TypeMask constMapTypeCache; + TypeMask stringTypeCache; + TypeMask typeTypeCache; + + TypeMask get dynamicType { + if (dynamicTypeCache == null) { + dynamicTypeCache = new TypeMask.subclass(compiler.objectClass); + } + return dynamicTypeCache; + } + + TypeMask get nonNullType { + if (nonNullTypeCache == null) { + nonNullTypeCache = new TypeMask.nonNullSubclass(compiler.objectClass); + } + return nonNullTypeCache; + } + + TypeMask get intType { + if (intTypeCache == null) { + intTypeCache = new TypeMask.nonNullSubclass( + compiler.backend.intImplementation); + } + return intTypeCache; + } + + TypeMask get uint32Type { + if (uint32TypeCache == null) { + uint32TypeCache = new TypeMask.nonNullSubclass( + compiler.backend.uint32Implementation); + } + return uint32TypeCache; + } + + TypeMask get uint31Type { + if (uint31TypeCache == null) { + uint31TypeCache = new TypeMask.nonNullExact( + compiler.backend.uint31Implementation); + } + return uint31TypeCache; + } + + TypeMask get positiveIntType { + if (positiveIntTypeCache == null) { + positiveIntTypeCache = new TypeMask.nonNullSubclass( + compiler.backend.positiveIntImplementation); + } + return positiveIntTypeCache; + } + + TypeMask get doubleType { + if (doubleTypeCache == null) { + doubleTypeCache = new TypeMask.nonNullExact( + compiler.backend.doubleImplementation); + } + return doubleTypeCache; + } + + TypeMask get numType { + if (numTypeCache == null) { + numTypeCache = new TypeMask.nonNullSubclass( + compiler.backend.numImplementation); + } + return numTypeCache; + } + + TypeMask get boolType { + if (boolTypeCache == null) { + boolTypeCache = new TypeMask.nonNullExact( + compiler.backend.boolImplementation); + } + return boolTypeCache; + } + + TypeMask get functionType { + if (functionTypeCache == null) { + functionTypeCache = new TypeMask.nonNullSubtype( + compiler.backend.functionImplementation); + } + return functionTypeCache; + } + + TypeMask get listType { + if (listTypeCache == null) { + listTypeCache = new TypeMask.nonNullExact( + compiler.backend.listImplementation); + } + return listTypeCache; + } + + TypeMask get constListType { + if (constListTypeCache == null) { + constListTypeCache = new TypeMask.nonNullExact( + compiler.backend.constListImplementation); + } + return constListTypeCache; + } + + TypeMask get fixedListType { + if (fixedListTypeCache == null) { + fixedListTypeCache = new TypeMask.nonNullExact( + compiler.backend.fixedListImplementation); + } + return fixedListTypeCache; + } + + TypeMask get growableListType { + if (growableListTypeCache == null) { + growableListTypeCache = new TypeMask.nonNullExact( + compiler.backend.growableListImplementation); + } + return growableListTypeCache; + } + + TypeMask get mapType { + if (mapTypeCache == null) { + mapTypeCache = new TypeMask.nonNullSubtype( + compiler.backend.mapImplementation); + } + return mapTypeCache; + } + + TypeMask get constMapType { + if (constMapTypeCache == null) { + constMapTypeCache = new TypeMask.nonNullSubtype( + compiler.backend.constMapImplementation); + } + return constMapTypeCache; + } + + TypeMask get stringType { + if (stringTypeCache == null) { + stringTypeCache = new TypeMask.nonNullExact( + compiler.backend.stringImplementation); + } + return stringTypeCache; + } + + TypeMask get typeType { + if (typeTypeCache == null) { + typeTypeCache = new TypeMask.nonNullExact( + compiler.backend.typeImplementation); + } + return typeTypeCache; + } + + TypeMask get nullType { + if (nullTypeCache == null) { + // TODO(johnniwinther): Assert that the null type has been resolved. + nullTypeCache = const TypeMask.empty(); + } + return nullTypeCache; + } + + /** Helper method for [intersection]. */ + TypeMask _intersection(TypeMask type1, TypeMask type2) { + if (type1 == null) return type2; + if (type2 == null) return type1; + return type1.intersection(type2, compiler); + } + + /** Computes the intersection of [type1] and [type2] */ + TypeMask intersection(TypeMask type1, TypeMask type2, element) { + TypeMask result = _intersection(type1, type2); + if (DUMP_BAD_CPA_RESULTS && better(type1, type2)) { + print("CPA is worse for $element: $type1 /\\ $type2 = $result"); + } + if (DUMP_GOOD_CPA_RESULTS && better(type2, type1)) { + print("CPA is better for $element: $type1 /\\ $type2 = $result"); + } + return result; + } + + /** Returns true if [type1] is strictly bettern than [type2]. */ + bool better(TypeMask type1, TypeMask type2) { + if (type1 == null) return false; + if (type2 == null) { + return (type1 != null) && + (type1 != new TypeMask.subclass(compiler.objectClass)); + } + return (type1 != type2) && + type2.containsMask(type1, compiler) && + !type1.containsMask(type2, compiler); + } + + /** + * Called when resolution is complete. + */ + void onResolutionComplete(Element mainElement) { + measure(() { + typesInferrer.analyzeMain(mainElement); + if (concreteTypesInferrer != null) { + bool success = concreteTypesInferrer.analyzeMain(mainElement); + if (!success) { + // If the concrete type inference bailed out, we pretend it didn't + // happen. In the future we might want to record that it failed but + // use the partial results as hints. + concreteTypesInferrer = null; + } + } + }); + typesInferrer.clear(); + } + + /** + * Return the (inferred) guaranteed type of [element] or null. + */ + TypeMask getGuaranteedTypeOfElement(Element element) { + return measure(() { + TypeMask guaranteedType = typesInferrer.getTypeOfElement(element); + return (concreteTypesInferrer == null) + ? guaranteedType + : intersection(guaranteedType, + concreteTypesInferrer.getTypeOfElement(element), + element); + }); + } + + TypeMask getGuaranteedReturnTypeOfElement(Element element) { + return measure(() { + TypeMask guaranteedType = + typesInferrer.getReturnTypeOfElement(element); + return (concreteTypesInferrer == null) + ? guaranteedType + : intersection(guaranteedType, + concreteTypesInferrer.getReturnTypeOfElement(element), + element); + }); + } + + /** + * Return the (inferred) guaranteed type of [node] or null. + * [node] must be an AST node of [owner]. + */ + TypeMask getGuaranteedTypeOfNode(owner, node) { + return measure(() { + TypeMask guaranteedType = typesInferrer.getTypeOfNode(owner, node); + return (concreteTypesInferrer == null) + ? guaranteedType + : intersection(guaranteedType, + concreteTypesInferrer.getTypeOfNode(owner, node), + node); + }); + } + + /** + * Return the (inferred) guaranteed type of [selector] or null. + */ + TypeMask getGuaranteedTypeOfSelector(Selector selector) { + return measure(() { + TypeMask guaranteedType = + typesInferrer.getTypeOfSelector(selector); + return (concreteTypesInferrer == null) + ? guaranteedType + : intersection(guaranteedType, + concreteTypesInferrer.getTypeOfSelector(selector), + selector); + }); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/union_type_mask.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/union_type_mask.dart new file mode 100644 index 0000000..e782f10 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/union_type_mask.dart @@ -0,0 +1,300 @@ +// Copyright (c) 2013, 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. + +part of types; + +class UnionTypeMask implements TypeMask { + final Iterable disjointMasks; + + static const int MAX_UNION_LENGTH = 4; + + UnionTypeMask._(this.disjointMasks); + + static TypeMask unionOf(Iterable masks, Compiler compiler) { + List disjoint = []; + unionOfHelper(masks, disjoint, compiler); + if (disjoint.isEmpty) return new TypeMask.nonNullEmpty(); + if (disjoint.length > MAX_UNION_LENGTH) return flatten(disjoint, compiler); + if (disjoint.length == 1) return disjoint[0]; + return new UnionTypeMask._(disjoint); + } + + static TypeMask nonForwardingMask(mask) { + while (mask.isForwarding) mask = mask.forwardTo; + return mask; + } + + static void unionOfHelper(Iterable masks, + List disjoint, + Compiler compiler) { + for (TypeMask mask in masks) { + mask = nonForwardingMask(mask); + if (mask.isUnion) { + UnionTypeMask union = mask; + unionOfHelper(union.disjointMasks, disjoint, compiler); + } else if (mask.isEmpty && !mask.isNullable) { + continue; + } else { + FlatTypeMask flatMask = mask; + assert(flatMask.base == null || flatMask.base != compiler.dynamicClass); + int inListIndex = -1; + bool covered = false; + + // Iterate over [disjoint] to find out if one of the mask + // already covers [mask]. + for (int i = 0; i < disjoint.length; i++) { + FlatTypeMask current = disjoint[i]; + if (current == null) continue; + TypeMask newMask = mask.union(current, compiler); + // If we have found a disjoint union, continue iterating. + if (newMask.isUnion) continue; + covered = true; + // We found a mask that is either equal to [mask] or is a + // supertype of [mask]. + if (current == newMask) break; + + // [mask] is a supertype of [current], replace the [disjoint] + // list with [newMask] instead of [current]. Note that + // [newMask] may contain different information than [mask], + // like nullability. + disjoint[i] = newMask; + mask = newMask; + + if (inListIndex != -1) { + // If the mask was already covered, we remove the previous + // place where it was inserted. This new mask subsumes the + // previously covered one. + disjoint.removeAt(inListIndex); + i--; + } + // Record where the mask was inserted. + inListIndex = i; + } + // If none of the masks in [disjoint] covers [mask], we just + // add [mask] to the list. + if (!covered) disjoint.add(mask); + } + } + } + + static TypeMask flatten(List masks, Compiler compiler) { + assert(masks.length > 1); + // If either type mask is a subtype type mask, we cannot use a + // subclass type mask to represent their union. + bool useSubclass = masks.every((e) => !e.isSubtype); + bool isNullable = masks.any((e) => e.isNullable); + + // Compute the common supertypes of the two types. + ClassElement firstElement = masks[0].base; + ClassElement secondElement = masks[1].base; + Iterable candidates = + compiler.world.commonSupertypesOf(firstElement, secondElement); + bool unseenType = false; + for (int i = 2; i < masks.length; i++) { + ClassElement element = masks[i].base; + Set supertypes = compiler.world.supertypesOf(element); + if (supertypes == null) { + unseenType = true; + break; + } + candidates = candidates.where((e) => supertypes.contains(e)); + } + + if (candidates.isEmpty || unseenType) { + // TODO(kasperl): Get rid of this check. It can only happen when + // at least one of the two base types is 'unseen'. + return new TypeMask(compiler.objectClass, + FlatTypeMask.SUBCLASS, + isNullable); + } + // Compute the best candidate and its kind. + ClassElement bestElement; + int bestKind; + int bestSize; + for (ClassElement candidate in candidates) { + Set subclasses = useSubclass + ? compiler.world.subclassesOf(candidate) + : null; + int size; + int kind; + if (subclasses != null + && masks.every((t) => subclasses.contains(t.base))) { + // If both [this] and [other] are subclasses of the supertype, + // then we prefer to construct a subclass type mask because it + // will always be at least as small as the corresponding + // subtype type mask. + kind = FlatTypeMask.SUBCLASS; + size = subclasses.length; + assert(size <= compiler.world.subtypesOf(candidate).length); + } else { + kind = FlatTypeMask.SUBTYPE; + size = compiler.world.subtypesOf(candidate).length; + } + // Update the best candidate if the new one is better. + if (bestElement == null || size < bestSize) { + bestElement = candidate; + bestSize = size; + bestKind = kind; + } + } + if (bestElement == compiler.objectClass) bestKind = FlatTypeMask.SUBCLASS; + return new TypeMask(bestElement, bestKind, isNullable); + } + + TypeMask union(var other, Compiler compiler) { + other = nonForwardingMask(other); + if (!other.isUnion && disjointMasks.contains(other)) return this; + + List newList = + new List.from(disjointMasks); + if (!other.isUnion) { + newList.add(other); + } else { + assert(other is UnionTypeMask); + newList.addAll(other.disjointMasks); + } + return new TypeMask.unionOf(newList, compiler); + } + + TypeMask intersection(var other, Compiler compiler) { + other = nonForwardingMask(other); + if (!other.isUnion && disjointMasks.contains(other)) return other; + + List intersections = []; + for (TypeMask current in disjointMasks) { + if (other.isUnion) { + for (FlatTypeMask flatOther in other.disjointMasks) { + intersections.add(current.intersection(flatOther, compiler)); + } + } else { + intersections.add(current.intersection(other, compiler)); + } + } + return new TypeMask.unionOf(intersections, compiler); + } + + TypeMask nullable() { + if (isNullable) return this; + List newList = new List.from(disjointMasks); + newList[0] = newList[0].nullable(); + return new UnionTypeMask._(newList); + } + + TypeMask nonNullable() { + if (!isNullable) return this; + Iterable newIterable = + disjointMasks.map((e) => e.nonNullable()); + return new UnionTypeMask._(newIterable); + } + + bool get isEmpty => false; + bool get isNullable => disjointMasks.any((e) => e.isNullable); + bool get isExact => false; + bool get isUnion => true; + bool get isContainer => false; + bool get isMap => false; + bool get isDictionary => false; + bool get isForwarding => false; + bool get isValue => false; + + bool isInMask(TypeMask other, Compiler compiler) { + return disjointMasks.every((mask) => mask.isInMask(other, compiler)); + } + + bool containsMask(TypeMask other, Compiler compiler) { + return disjointMasks.any((mask) => mask.containsMask(other, compiler)); + } + + bool containsOnlyInt(Compiler compiler) { + return disjointMasks.every((mask) => mask.containsOnlyInt(compiler)); + } + + bool containsOnlyDouble(Compiler compiler) { + return disjointMasks.every((mask) => mask.containsOnlyDouble(compiler)); + } + + bool containsOnlyNum(Compiler compiler) { + return disjointMasks.every((mask) { + return mask.containsOnlyNum(compiler); + }); + } + + bool containsOnlyBool(Compiler compiler) { + return disjointMasks.every((mask) => mask.containsOnlyBool(compiler)); + } + + bool containsOnlyString(Compiler compiler) { + return disjointMasks.every((mask) => mask.containsOnlyString(compiler)); + } + + bool containsOnly(ClassElement element) { + return disjointMasks.every((mask) => mask.containsOnly(element)); + } + + bool satisfies(ClassElement cls, Compiler compiler) { + return disjointMasks.every((mask) => mask.satisfies(cls, compiler)); + } + + bool contains(ClassElement type, Compiler compiler) { + return disjointMasks.any((e) => e.contains(type, compiler)); + } + + bool containsAll(Compiler compiler) { + return disjointMasks.any((mask) => mask.containsAll(compiler)); + } + + ClassElement singleClass(Compiler compiler) => null; + + bool needsNoSuchMethodHandling(Selector selector, Compiler compiler) { + return disjointMasks.any( + (e) => e.needsNoSuchMethodHandling(selector, compiler)); + } + + bool canHit(Element element, Selector selector, Compiler compiler) { + return disjointMasks.any((e) => e.canHit(element, selector, compiler)); + } + + Element locateSingleElement(Selector selector, Compiler compiler) { + Element candidate; + for (FlatTypeMask mask in disjointMasks) { + Element current = mask.locateSingleElement(selector, compiler); + if (current == null) { + return null; + } else if (candidate == null) { + candidate = current; + } else if (candidate != current) { + return null; + } + } + return candidate; + } + + String toString() => 'Union of $disjointMasks'; + + bool operator==(other) { + if (identical(this, other)) return true; + + bool containsAll() { + return other.disjointMasks.every((e) { + var map = disjointMasks.map((e) => e.nonNullable()); + return map.contains(e.nonNullable()); + }); + } + + return other is UnionTypeMask + && other.isNullable == isNullable + && other.disjointMasks.length == disjointMasks.length + && containsAll(); + } + + int get hashCode { + int hashCode = isNullable ? 86 : 43; + // The order of the masks in [disjointMasks] must not affect the + // hashCode. + for (var mask in disjointMasks) { + hashCode = (hashCode ^ mask.nonNullable().hashCode) & 0x3fffffff; + } + return hashCode; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/value_type_mask.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/value_type_mask.dart new file mode 100644 index 0000000..b12b1b4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/types/value_type_mask.dart @@ -0,0 +1,49 @@ +// Copyright (c) 2014, 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. + +part of types; + +class ValueTypeMask extends ForwardingTypeMask { + final TypeMask forwardTo; + final value; + + ValueTypeMask(this.forwardTo, this.value); + + TypeMask nullable() { + return isNullable + ? this + : new ValueTypeMask(forwardTo.nullable(), value); + } + + TypeMask nonNullable() { + return isNullable + ? new ValueTypeMask(forwardTo.nonNullable(), value) + : this; + } + + bool get isValue => true; + + bool equalsDisregardNull(other) { + if (other is! ValueTypeMask) return false; + return value == other.value; + } + + TypeMask intersection(TypeMask other, Compiler compiler) { + TypeMask forwardIntersection = forwardTo.intersection(other, compiler); + if (forwardIntersection.isEmpty) return forwardIntersection; + return forwardIntersection.isNullable + ? nullable() + : nonNullable(); + } + + bool operator==(other) => super == other; + + int get hashCode { + return computeHashCode(value, isNullable, forwardTo); + } + + String toString() { + return 'Value mask: [$value] type: $forwardTo'; + } +} \ No newline at end of file diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/universe/function_set.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/universe/function_set.dart new file mode 100644 index 0000000..793362b --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/universe/function_set.dart @@ -0,0 +1,235 @@ +// Copyright (c) 2012, 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. + +part of universe; + +// TODO(kasperl): This actually holds getters and setters just fine +// too and stricly they aren't functions. Maybe this needs a better +// name -- something like ElementSet seems a bit too generic. +class FunctionSet { + final Compiler compiler; + final Map nodes = + new Map(); + FunctionSet(this.compiler); + + FunctionSetNode newNode(String name) + => new FunctionSetNode(name); + + void add(Element element) { + assert(element.isInstanceMember()); + assert(!element.isAbstract); + String name = element.name; + FunctionSetNode node = nodes.putIfAbsent(name, () => newNode(name)); + node.add(element); + } + + void remove(Element element) { + assert(element.isInstanceMember()); + assert(!element.isAbstract); + String name = element.name; + FunctionSetNode node = nodes[name]; + if (node != null) { + node.remove(element); + } + } + + bool contains(Element element) { + assert(element.isInstanceMember()); + assert(!element.isAbstract); + String name = element.name; + FunctionSetNode node = nodes[name]; + return (node != null) + ? node.contains(element) + : false; + } + + /** + * Returns an object that allows iterating over all the functions + * that may be invoked with the given [selector]. + */ + Iterable filter(Selector selector) { + return query(selector).functions; + } + + TypeMask receiverType(Selector selector) { + return query(selector).computeMask(compiler); + } + + FunctionSetQuery query(Selector selector) { + String name = selector.name; + FunctionSetNode node = nodes[name]; + FunctionSetNode noSuchMethods = nodes[Compiler.NO_SUCH_METHOD]; + if (node != null) { + return node.query(selector, compiler, noSuchMethods); + } + // If there is no method that matches [selector] we know we can + // only hit [:noSuchMethod:]. + if (noSuchMethods == null) return const FunctionSetQuery(const []); + selector = (selector.mask == null) + ? compiler.noSuchMethodSelector + : new TypedSelector(selector.mask, compiler.noSuchMethodSelector); + + return noSuchMethods.query(selector, compiler, null); + } + + void forEach(Function action) { + nodes.forEach((String name, FunctionSetNode node) { + node.forEach(action); + }); + } +} + + +class FunctionSetNode { + final String name; + final Map cache = + new Map(); + + // Initially, we keep the elements in a list because it is more + // compact than a hash set. Once we get enough elements, we change + // the representation to be a set to get faster contains checks. + static const int MAX_ELEMENTS_IN_LIST = 8; + var elements = []; + bool isList = true; + + FunctionSetNode(this.name); + + void add(Element element) { + assert(element.name == name); + // We try to avoid clearing the cache unless we have to. For that + // reason we keep the explicit contains check even though the add + // method ends up doing the work again (for sets). + if (!elements.contains(element)) { + if (isList && elements.length >= MAX_ELEMENTS_IN_LIST) { + elements = elements.toSet(); + isList = false; + } + elements.add(element); + if (!cache.isEmpty) cache.clear(); + } + } + + void remove(Element element) { + assert(element.name == name); + if (isList) { + List list = elements; + int index = list.indexOf(element); + if (index < 0) return; + Element last = list.removeLast(); + if (index != list.length) { + list[index] = last; + } + if (!cache.isEmpty) cache.clear(); + } else { + Set set = elements; + if (set.remove(element)) { + // To avoid wobbling between the two representations, we do + // not transition back to the list representation even if we + // end up with few enough elements at this point. + if (!cache.isEmpty) cache.clear(); + } + } + } + + bool contains(Element element) { + assert(element.name == name); + return elements.contains(element); + } + + void forEach(Function action) { + elements.forEach(action); + } + + TypeMask getNonNullTypeMaskOfSelector(Selector selector, Compiler compiler) { + // TODO(ngeoffray): We should probably change untyped selector + // to always be a subclass of Object. + return selector.mask != null + ? selector.mask + : new TypeMask.subclass(compiler.objectClass); + } + + FunctionSetQuery query(Selector selector, + Compiler compiler, + FunctionSetNode noSuchMethods) { + assert(selector.name == name); + FunctionSetQuery result = cache[selector]; + if (result != null) return result; + Setlet functions; + for (Element element in elements) { + if (selector.appliesUnnamed(element, compiler)) { + if (functions == null) { + // Defer the allocation of the functions set until we are + // sure we need it. This allows us to return immutable empty + // lists when the filtering produced no results. + functions = new Setlet(); + } + functions.add(element); + } + } + + TypeMask mask = getNonNullTypeMaskOfSelector(selector, compiler); + // If we cannot ensure a method will be found at runtime, we also + // add [noSuchMethod] implementations that apply to [mask] as + // potential targets. + if (noSuchMethods != null + && mask.needsNoSuchMethodHandling(selector, compiler)) { + FunctionSetQuery noSuchMethodQuery = noSuchMethods.query( + new TypedSelector(mask, compiler.noSuchMethodSelector), + compiler, + null); + if (!noSuchMethodQuery.functions.isEmpty) { + if (functions == null) { + functions = new Setlet.from(noSuchMethodQuery.functions); + } else { + functions.addAll(noSuchMethodQuery.functions); + } + } + } + cache[selector] = result = (functions != null) + ? newQuery(functions, selector, compiler) + : const FunctionSetQuery(const []); + return result; + } + + FunctionSetQuery newQuery(Iterable functions, + Selector selector, + Compiler compiler) { + return new FullFunctionSetQuery(functions); + } +} + +class FunctionSetQuery { + final Iterable functions; + TypeMask computeMask(Compiler compiler) => const TypeMask.nonNullEmpty(); + const FunctionSetQuery(this.functions); +} + +class FullFunctionSetQuery extends FunctionSetQuery { + TypeMask _mask; + + /** + * Compute the type of all potential receivers of this function set. + */ + TypeMask computeMask(Compiler compiler) { + if (_mask != null) return _mask; + return _mask = new TypeMask.unionOf(functions + .expand((element) { + ClassElement cls = element.getEnclosingClass(); + return compiler.world.isUsedAsMixin(cls) + ? ([cls]..addAll(compiler.world.mixinUses[cls])) + : [cls]; + }) + .map((cls) { + if (compiler.backend.isNullImplementation(cls)) { + return const TypeMask.empty(); + } + return compiler.world.hasSubclasses(cls) + ? new TypeMask.nonNullSubclass(cls.declaration) + : new TypeMask.nonNullExact(cls.declaration); + }), + compiler); + } + + FullFunctionSetQuery(functions) : super(functions); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/universe/side_effects.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/universe/side_effects.dart new file mode 100644 index 0000000..e4a1fa4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/universe/side_effects.dart @@ -0,0 +1,107 @@ +// Copyright (c) 2013, 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. + +part of universe; + +class SideEffects { + // Changes flags. + static const int FLAG_CHANGES_INDEX = 0; + static const int FLAG_CHANGES_INSTANCE_PROPERTY = FLAG_CHANGES_INDEX + 1; + static const int FLAG_CHANGES_STATIC_PROPERTY + = FLAG_CHANGES_INSTANCE_PROPERTY + 1; + static const int FLAG_CHANGES_COUNT = FLAG_CHANGES_STATIC_PROPERTY + 1; + + // Depends flags (one for each changes flag). + static const int FLAG_DEPENDS_ON_INDEX_STORE = FLAG_CHANGES_COUNT; + static const int FLAG_DEPENDS_ON_INSTANCE_PROPERTY_STORE = + FLAG_DEPENDS_ON_INDEX_STORE + 1; + static const int FLAG_DEPENDS_ON_STATIC_PROPERTY_STORE = + FLAG_DEPENDS_ON_INSTANCE_PROPERTY_STORE + 1; + static const int FLAG_DEPENDS_ON_COUNT = + FLAG_DEPENDS_ON_STATIC_PROPERTY_STORE + 1; + + int flags = 0; + + SideEffects() { + setAllSideEffects(); + setDependsOnSomething(); + } + + SideEffects.empty(); + + bool operator==(other) => flags == other.flags; + + int get hashCode => throw new UnsupportedError('SideEffects.hashCode'); + + bool getFlag(int position) => (flags & (1 << position)) != 0; + void setFlag(int position) { flags |= (1 << position); } + + int getChangesFlags() => flags & ((1 << FLAG_CHANGES_COUNT) - 1); + int getDependsOnFlags() { + return (flags & ((1 << FLAG_DEPENDS_ON_COUNT) - 1)) >> FLAG_CHANGES_COUNT; + } + + bool hasSideEffects() => getChangesFlags() != 0; + bool dependsOnSomething() => getDependsOnFlags() != 0; + + void setAllSideEffects() { flags |= ((1 << FLAG_CHANGES_COUNT) - 1); } + + void clearAllSideEffects() { flags &= ~((1 << FLAG_CHANGES_COUNT) - 1); } + + void setDependsOnSomething() { + int count = FLAG_DEPENDS_ON_COUNT - FLAG_CHANGES_COUNT; + flags |= (((1 << count) - 1) << FLAG_CHANGES_COUNT); + } + void clearAllDependencies() { + int count = FLAG_DEPENDS_ON_COUNT - FLAG_CHANGES_COUNT; + flags &= ~(((1 << count) - 1) << FLAG_CHANGES_COUNT); + } + + bool dependsOnStaticPropertyStore() { + return getFlag(FLAG_DEPENDS_ON_STATIC_PROPERTY_STORE); + } + void setDependsOnStaticPropertyStore() { + setFlag(FLAG_DEPENDS_ON_STATIC_PROPERTY_STORE); + } + void setChangesStaticProperty() { setFlag(FLAG_CHANGES_STATIC_PROPERTY); } + bool changesStaticProperty() => getFlag(FLAG_CHANGES_STATIC_PROPERTY); + + bool dependsOnIndexStore() => getFlag(FLAG_DEPENDS_ON_INDEX_STORE); + void setDependsOnIndexStore() { setFlag(FLAG_DEPENDS_ON_INDEX_STORE); } + void setChangesIndex() { setFlag(FLAG_CHANGES_INDEX); } + bool changesIndex() => getFlag(FLAG_CHANGES_INDEX); + + bool dependsOnInstancePropertyStore() { + return getFlag(FLAG_DEPENDS_ON_INSTANCE_PROPERTY_STORE); + } + void setDependsOnInstancePropertyStore() { + setFlag(FLAG_DEPENDS_ON_INSTANCE_PROPERTY_STORE); + } + void setChangesInstanceProperty() { setFlag(FLAG_CHANGES_INSTANCE_PROPERTY); } + bool changesInstanceProperty() => getFlag(FLAG_CHANGES_INSTANCE_PROPERTY); + + static int computeDependsOnFlags(int flags) => flags << FLAG_CHANGES_COUNT; + + bool dependsOn(int dependsFlags) => (flags & dependsFlags) != 0; + + void add(SideEffects other) { + flags |= other.flags; + } + + String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.write('Depends on'); + if (dependsOnIndexStore()) buffer.write(' []'); + if (dependsOnInstancePropertyStore()) buffer.write(' field store'); + if (dependsOnStaticPropertyStore()) buffer.write(' static store'); + if (!dependsOnSomething()) buffer.write(' nothing'); + buffer.write(', Changes'); + if (changesIndex()) buffer.write(' []'); + if (changesInstanceProperty()) buffer.write(' field'); + if (changesStaticProperty()) buffer.write(' static'); + if (!hasSideEffects()) buffer.write(' nothing'); + buffer.write('.'); + return buffer.toString(); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/universe/universe.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/universe/universe.dart new file mode 100644 index 0000000..27da00d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/universe/universe.dart @@ -0,0 +1,685 @@ +// Copyright (c) 2012, 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. + +library universe; + +import '../elements/elements.dart'; +import '../dart2jslib.dart'; +import '../dart_types.dart'; +import '../types/types.dart'; +import '../tree/tree.dart'; +import '../util/util.dart'; + +part 'function_set.dart'; +part 'side_effects.dart'; + +class Universe { + /** + * Documentation wanted -- johnniwinther + * + * Invariant: Elements are declaration elements. + */ + // TODO(karlklose): these sets should be merged. + final Set instantiatedClasses = new Set(); + final Set instantiatedTypes = new Set(); + + /** + * Documentation wanted -- johnniwinther + * + * Invariant: Elements are declaration elements. + */ + final Set staticFunctionsNeedingGetter = + new Set(); + final Set methodsNeedingSuperGetter = + new Set(); + final Map> invokedNames = + new Map>(); + final Map> invokedGetters = + new Map>(); + final Map> invokedSetters = + new Map>(); + + /** + * Fields accessed. Currently only the codegen knows this + * information. The resolver is too conservative when seeing a + * getter and only registers an invoked getter. + */ + final Set fieldGetters = new Set(); + + /** + * Fields set. See comment in [fieldGetters]. + */ + final Set fieldSetters = new Set(); + final Set isChecks = new Set(); + + /** + * Set of [:call:] methods in instantiated classes that use type variables + * in their signature. + */ + final Set genericCallMethods = new Set(); + + /** + * Set of closures that use type variables in their signature. + */ + final Set genericClosures = new Set(); + + /** + * Set of methods in instantiated classes that are potentially + * closurized. + */ + final Set closurizedMembers = new Set(); + + bool usingFactoryWithTypeArguments = false; + + bool hasMatchingSelector(Set selectors, + Element member, + Compiler compiler) { + if (selectors == null) return false; + for (Selector selector in selectors) { + if (selector.appliesUnnamed(member, compiler)) return true; + } + return false; + } + + bool hasInvocation(Element member, Compiler compiler) { + return hasMatchingSelector(invokedNames[member.name], member, compiler); + } + + bool hasInvokedGetter(Element member, Compiler compiler) { + return hasMatchingSelector(invokedGetters[member.name], member, compiler); + } + + bool hasInvokedSetter(Element member, Compiler compiler) { + return hasMatchingSelector(invokedSetters[member.name], member, compiler); + } + + DartType registerIsCheck(DartType type, Compiler compiler) { + type = type.unalias(compiler); + // Even in checked mode, type annotations for return type and argument + // types do not imply type checks, so there should never be a check + // against the type variable of a typedef. + isChecks.add(type); + return type; + } +} + +class SelectorKind { + final String name; + final int hashCode; + const SelectorKind(this.name, this.hashCode); + + static const SelectorKind GETTER = const SelectorKind('getter', 0); + static const SelectorKind SETTER = const SelectorKind('setter', 1); + static const SelectorKind CALL = const SelectorKind('call', 2); + static const SelectorKind OPERATOR = const SelectorKind('operator', 3); + static const SelectorKind INDEX = const SelectorKind('index', 4); + + String toString() => name; +} + +class Selector { + final SelectorKind kind; + final String name; + final LibraryElement library; // Library is null for non-private selectors. + + // The numbers of arguments of the selector. Includes named arguments. + final int argumentCount; + final List namedArguments; + final List _orderedNamedArguments; + final int hashCode; + + static const String INDEX_NAME ="[]"; + static const String INDEX_SET_NAME = "[]="; + static const String CALL_NAME = Compiler.CALL_OPERATOR_NAME; + + Selector.internal(this.kind, + this.name, + this.library, + this.argumentCount, + this.namedArguments, + this._orderedNamedArguments, + this.hashCode) { + assert(kind == SelectorKind.INDEX + || (name != INDEX_NAME && name != INDEX_SET_NAME)); + assert(kind == SelectorKind.OPERATOR + || kind == SelectorKind.INDEX + || Elements.operatorNameToIdentifier(name) == name); + assert(kind == SelectorKind.CALL + || kind == SelectorKind.GETTER + || kind == SelectorKind.SETTER + || Elements.operatorNameToIdentifier(name) != name); + assert(!isPrivateName(name) || library != null); + } + + static Map> canonicalizedValues = + new Map>(); + + factory Selector(SelectorKind kind, + String name, + LibraryElement library, + int argumentCount, + [List namedArguments]) { + if (!isPrivateName(name)) library = null; + if (namedArguments == null) namedArguments = const []; + int hashCode = computeHashCode( + kind, name, library, argumentCount, namedArguments); + List list = canonicalizedValues.putIfAbsent(hashCode, + () => []); + for (int i = 0; i < list.length; i++) { + Selector existing = list[i]; + if (existing.match(kind, name, library, argumentCount, namedArguments)) { + assert(existing.hashCode == hashCode); + assert(existing.mask == null); + return existing; + } + } + List orderedNamedArguments = namedArguments.isEmpty + ? const [] + : []; + Selector result = new Selector.internal( + kind, name, library, argumentCount, + namedArguments, orderedNamedArguments, + hashCode); + list.add(result); + return result; + } + + factory Selector.fromElement(Element element, Compiler compiler) { + String name = element.name; + if (element.isFunction()) { + if (name == '[]') { + return new Selector.index(); + } else if (name == '[]=') { + return new Selector.indexSet(); + } + FunctionSignature signature = + element.asFunctionElement().functionSignature; + int arity = signature.parameterCount; + List namedArguments = null; + if (signature.optionalParametersAreNamed) { + namedArguments = + signature.orderedOptionalParameters.map((e) => e.name).toList(); + } + if (Elements.operatorNameToIdentifier(name) != name) { + // Operators cannot have named arguments, however, that doesn't prevent + // a user from declaring such an operator. + return new Selector( + SelectorKind.OPERATOR, name, null, arity, namedArguments); + } else { + return new Selector.call( + name, element.getLibrary(), arity, namedArguments); + } + } else if (element.isSetter()) { + return new Selector.setter(name, element.getLibrary()); + } else if (element.isGetter()) { + return new Selector.getter(name, element.getLibrary()); + } else if (element.isField()) { + return new Selector.getter(name, element.getLibrary()); + } else { + throw new SpannableAssertionFailure( + element, "Can't get selector from $element"); + } + } + + factory Selector.getter(String name, LibraryElement library) + => new Selector(SelectorKind.GETTER, name, library, 0); + + factory Selector.getterFrom(Selector selector) + => new Selector(SelectorKind.GETTER, selector.name, selector.library, 0); + + factory Selector.setter(String name, LibraryElement library) + => new Selector(SelectorKind.SETTER, name, library, 1); + + factory Selector.unaryOperator(String name) + => new Selector(SelectorKind.OPERATOR, + Elements.constructOperatorName(name, true), + null, 0); + + factory Selector.binaryOperator(String name) + => new Selector(SelectorKind.OPERATOR, + Elements.constructOperatorName(name, false), + null, 1); + + factory Selector.index() + => new Selector(SelectorKind.INDEX, + Elements.constructOperatorName(INDEX_NAME, false), + null, 1); + + factory Selector.indexSet() + => new Selector(SelectorKind.INDEX, + Elements.constructOperatorName(INDEX_SET_NAME, false), + null, 2); + + factory Selector.call(String name, + LibraryElement library, + int arity, + [List namedArguments]) + => new Selector(SelectorKind.CALL, name, library, arity, namedArguments); + + factory Selector.callClosure(int arity, [List namedArguments]) + => new Selector(SelectorKind.CALL, CALL_NAME, null, + arity, namedArguments); + + factory Selector.callClosureFrom(Selector selector) + => new Selector(SelectorKind.CALL, CALL_NAME, null, + selector.argumentCount, selector.namedArguments); + + factory Selector.callConstructor(String name, LibraryElement library, + [int arity = 0, + List namedArguments]) + => new Selector(SelectorKind.CALL, name, library, + arity, namedArguments); + + factory Selector.callDefaultConstructor(LibraryElement library) + => new Selector(SelectorKind.CALL, "", library, 0); + + bool isGetter() => identical(kind, SelectorKind.GETTER); + bool isSetter() => identical(kind, SelectorKind.SETTER); + bool isCall() => identical(kind, SelectorKind.CALL); + bool isClosureCall() { + String callName = Compiler.CALL_OPERATOR_NAME; + return isCall() && name == callName; + } + + bool isIndex() => identical(kind, SelectorKind.INDEX) && argumentCount == 1; + bool isIndexSet() => identical(kind, SelectorKind.INDEX) && argumentCount == 2; + + bool isOperator() => identical(kind, SelectorKind.OPERATOR); + bool isUnaryOperator() => isOperator() && argumentCount == 0; + + /** Check whether this is a call to 'assert'. */ + bool isAssert() => isCall() && identical(name, "assert"); + + int get namedArgumentCount => namedArguments.length; + int get positionalArgumentCount => argumentCount - namedArgumentCount; + + bool get hasExactMask => false; + TypeMask get mask => null; + Selector get asUntyped => this; + + /** + * The member name for invocation mirrors created from this selector. + */ + String get invocationMirrorMemberName => + isSetter() ? '$name=' : name; + + int get invocationMirrorKind { + const int METHOD = 0; + const int GETTER = 1; + const int SETTER = 2; + int kind = METHOD; + if (isGetter()) { + kind = GETTER; + } else if (isSetter()) { + kind = SETTER; + } + return kind; + } + + bool appliesUnnamed(Element element, Compiler compiler) { + assert(sameNameHack(element, compiler)); + return appliesUntyped(element, compiler); + } + + bool appliesUntyped(Element element, Compiler compiler) { + assert(sameNameHack(element, compiler)); + if (Elements.isUnresolved(element)) return false; + if (isPrivateName(name) && library != element.getLibrary()) return false; + if (element.isForeign(compiler)) return true; + if (element.isSetter()) return isSetter(); + if (element.isGetter()) return isGetter() || isCall(); + if (element.isField()) { + return isSetter() + ? !element.modifiers.isFinalOrConst() + : isGetter() || isCall(); + } + if (isGetter()) return true; + if (isSetter()) return false; + return signatureApplies(element, compiler); + } + + bool signatureApplies(FunctionElement function, Compiler compiler) { + FunctionSignature parameters = function.computeSignature(compiler); + if (argumentCount > parameters.parameterCount) return false; + int requiredParameterCount = parameters.requiredParameterCount; + int optionalParameterCount = parameters.optionalParameterCount; + if (positionalArgumentCount < requiredParameterCount) return false; + + if (!parameters.optionalParametersAreNamed) { + // We have already checked that the number of arguments are + // not greater than the number of parameters. Therefore the + // number of positional arguments are not greater than the + // number of parameters. + assert(positionalArgumentCount <= parameters.parameterCount); + return namedArguments.isEmpty; + } else { + if (positionalArgumentCount > requiredParameterCount) return false; + assert(positionalArgumentCount == requiredParameterCount); + if (namedArgumentCount > optionalParameterCount) return false; + Set nameSet = new Set(); + parameters.optionalParameters.forEach((Element element) { + nameSet.add(element.name); + }); + for (String name in namedArguments) { + if (!nameSet.contains(name)) return false; + // TODO(5213): By removing from the set we are checking + // that we are not passing the name twice. We should have this + // check in the resolver also. + nameSet.remove(name); + } + return true; + } + } + + bool sameNameHack(Element element, Compiler compiler) { + // TODO(ngeoffray): Remove workaround checks. + return element == compiler.assertMethod + || element.isConstructor() + || name == element.name; + } + + bool applies(Element element, Compiler compiler) { + if (!sameNameHack(element, compiler)) return false; + return appliesUnnamed(element, compiler); + } + + /** + * Fills [list] with the arguments in a defined order. + * + * [compileArgument] is a function that returns a compiled version + * of an argument located in [arguments]. + * + * [compileConstant] is a function that returns a compiled constant + * of an optional argument that is not in [arguments]. + * + * Returns [:true:] if the selector and the [element] match; [:false:] + * otherwise. + * + * Invariant: [element] must be the implementation element. + */ + bool addArgumentsToList(Link arguments, + List list, + FunctionElement element, + compileArgument(Node argument), + compileConstant(Element element), + Compiler compiler) { + assert(invariant(element, element.isImplementation)); + if (!this.applies(element, compiler)) return false; + + FunctionSignature parameters = element.functionSignature; + parameters.forEachRequiredParameter((element) { + list.add(compileArgument(arguments.head)); + arguments = arguments.tail; + }); + + if (!parameters.optionalParametersAreNamed) { + parameters.forEachOptionalParameter((element) { + if (!arguments.isEmpty) { + list.add(compileArgument(arguments.head)); + arguments = arguments.tail; + } else { + list.add(compileConstant(element)); + } + }); + } else { + // Visit named arguments and add them into a temporary list. + List compiledNamedArguments = []; + for (; !arguments.isEmpty; arguments = arguments.tail) { + NamedArgument namedArgument = arguments.head; + compiledNamedArguments.add(compileArgument(namedArgument.expression)); + } + // Iterate over the optional parameters of the signature, and try to + // find them in [compiledNamedArguments]. If found, we use the + // value in the temporary list, otherwise the default value. + parameters.orderedOptionalParameters.forEach((element) { + int foundIndex = namedArguments.indexOf(element.name); + if (foundIndex != -1) { + list.add(compiledNamedArguments[foundIndex]); + } else { + list.add(compileConstant(element)); + } + }); + } + return true; + } + + /** + * Fills [list] with the arguments in the order expected by + * [callee], and where [caller] is a synthesized element + * + * [compileArgument] is a function that returns a compiled version + * of a parameter of [callee]. + * + * [compileConstant] is a function that returns a compiled constant + * of an optional argument that is not in the parameters of [callee]. + * + * Returns [:true:] if the signature of the [caller] matches the + * signature of the [callee], [:false:] otherwise. + */ + static bool addForwardingElementArgumentsToList( + FunctionElement caller, + List list, + FunctionElement callee, + compileArgument(Element element), + compileConstant(Element element), + Compiler compiler) { + + FunctionSignature signature = caller.functionSignature; + Map mapping = new Map(); + + // TODO(ngeoffray): This is a hack that fakes up AST nodes, so + // that we can call [addArgumentsToList]. + Link computeCallNodesFromParameters() { + LinkBuilder builder = new LinkBuilder(); + signature.forEachRequiredParameter((Element element) { + Node node = element.parseNode(compiler); + mapping[node] = element; + builder.addLast(node); + }); + if (signature.optionalParametersAreNamed) { + signature.forEachOptionalParameter((ParameterElement element) { + mapping[element.initializer] = element; + builder.addLast(new NamedArgument(null, null, element.initializer)); + }); + } else { + signature.forEachOptionalParameter((Element element) { + Node node = element.parseNode(compiler); + mapping[node] = element; + builder.addLast(node); + }); + } + return builder.toLink(); + } + + internalCompileArgument(Node node) { + return compileArgument(mapping[node]); + } + + Link nodes = computeCallNodesFromParameters(); + + // Synthesize a selector for the call. + // TODO(ngeoffray): Should the resolver do it instead? + List namedParameters; + if (signature.optionalParametersAreNamed) { + namedParameters = + signature.optionalParameters.toList().map((e) => e.name).toList(); + } + Selector selector = new Selector.call(callee.name, + caller.getLibrary(), + signature.parameterCount, + namedParameters); + + return selector.addArgumentsToList(nodes, + list, + callee, + internalCompileArgument, + compileConstant, + compiler); + } + + static bool sameNames(List first, List second) { + for (int i = 0; i < first.length; i++) { + if (first[i] != second[i]) return false; + } + return true; + } + + bool match(SelectorKind kind, + String name, + LibraryElement library, + int argumentCount, + List namedArguments) { + return this.kind == kind + && this.name == name + && identical(this.library, library) + && this.argumentCount == argumentCount + && this.namedArguments.length == namedArguments.length + && sameNames(this.namedArguments, namedArguments); + } + + static int computeHashCode(SelectorKind kind, + String name, + LibraryElement library, + int argumentCount, + List namedArguments) { + // Add bits from name and kind. + int hash = mixHashCodeBits(name.hashCode, kind.hashCode); + // Add bits from the library. + if (library != null) hash = mixHashCodeBits(hash, library.hashCode); + // Add bits from the unnamed arguments. + hash = mixHashCodeBits(hash, argumentCount); + // Add bits from the named arguments. + int named = namedArguments.length; + hash = mixHashCodeBits(hash, named); + for (int i = 0; i < named; i++) { + hash = mixHashCodeBits(hash, namedArguments[i].hashCode); + } + return hash; + } + + // TODO(kasperl): Move this out so it becomes useful in other places too? + static int mixHashCodeBits(int existing, int value) { + // Spread the bits of value. Try to stay in the 30-bit range to + // avoid overflowing into a more expensive integer representation. + int h = value & 0x1fffffff; + h += ((h & 0x3fff) << 15) ^ 0x1fffcd7d; + h ^= (h >> 10); + h += ((h & 0x3ffffff) << 3); + h ^= (h >> 6); + h += ((h & 0x7ffffff) << 2) + ((h & 0x7fff) << 14); + h ^= (h >> 16); + // Combine the two hash values. + int high = existing >> 15; + int low = existing & 0x7fff; + return (high * 13) ^ (low * 997) ^ h; + } + + List getOrderedNamedArguments() { + if (namedArguments.isEmpty) return namedArguments; + if (!_orderedNamedArguments.isEmpty) return _orderedNamedArguments; + + _orderedNamedArguments.addAll(namedArguments); + _orderedNamedArguments.sort((String first, String second) { + return first.compareTo(second); + }); + return _orderedNamedArguments; + } + + String namedArgumentsToString() { + if (namedArgumentCount > 0) { + StringBuffer result = new StringBuffer(); + for (int i = 0; i < namedArgumentCount; i++) { + if (i != 0) result.write(', '); + result.write(namedArguments[i]); + } + return "[$result]"; + } + return ''; + } + + String toString() { + String named = ''; + String type = ''; + if (namedArgumentCount > 0) named = ', named=${namedArgumentsToString()}'; + if (mask != null) type = ', mask=$mask'; + return 'Selector($kind, $name, ' + 'arity=$argumentCount$named$type)'; + } + + Selector extendIfReachesAll(Compiler compiler) { + return new TypedSelector(compiler.typesTask.dynamicType, this); + } + + Selector toCallSelector() => new Selector.callClosureFrom(this); +} + +class TypedSelector extends Selector { + final Selector asUntyped; + final TypeMask mask; + + TypedSelector.internal(this.mask, Selector selector, int hashCode) + : asUntyped = selector, + super.internal(selector.kind, + selector.name, + selector.library, + selector.argumentCount, + selector.namedArguments, + selector._orderedNamedArguments, + hashCode) { + assert(mask != null); + assert(asUntyped.mask == null); + } + + static Map> canonicalizedValues = + new Map>(); + + factory TypedSelector(TypeMask mask, Selector selector) { + if (selector.mask == mask) return selector; + Selector untyped = selector.asUntyped; + Map map = canonicalizedValues.putIfAbsent(untyped, + () => new Map()); + TypedSelector result = map[mask]; + if (result == null) { + int hashCode = Selector.mixHashCodeBits(untyped.hashCode, mask.hashCode); + result = map[mask] = new TypedSelector.internal(mask, untyped, hashCode); + } + return result; + } + + factory TypedSelector.exact(ClassElement base, Selector selector) + => new TypedSelector(new TypeMask.exact(base), selector); + + factory TypedSelector.subclass(ClassElement base, Selector selector) + => new TypedSelector(new TypeMask.subclass(base), selector); + + factory TypedSelector.subtype(ClassElement base, Selector selector) + => new TypedSelector(new TypeMask.subtype(base), selector); + + bool appliesUnnamed(Element element, Compiler compiler) { + assert(sameNameHack(element, compiler)); + // [TypedSelector] are only used after resolution. + assert(compiler.phase > Compiler.PHASE_RESOLVING); + if (!element.isMember()) return false; + + // A closure can be called through any typed selector: + // class A { + // get foo => () => 42; + // bar() => foo(); // The call to 'foo' is a typed selector. + // } + if (element.getEnclosingClass().isClosure()) { + return appliesUntyped(element, compiler); + } + + if (!mask.canHit(element, this, compiler)) return false; + return appliesUntyped(element, compiler); + } + + Selector extendIfReachesAll(Compiler compiler) { + bool canReachAll = compiler.enabledInvokeOn + && mask.needsNoSuchMethodHandling(this, compiler); + return canReachAll + ? new TypedSelector(compiler.typesTask.dynamicType, this) + : this; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/use_unused_api.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/use_unused_api.dart new file mode 100644 index 0000000..abbcb91 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/use_unused_api.dart @@ -0,0 +1,199 @@ +// Copyright (c) 2012, 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. + +/// This file use methods that aren't used by dart2js.dart, but that we wish to +/// keep anyway. This might be general API that isn't currently in use, +/// debugging aids, or API only used for testing (see TODO below). + +library dart2js.use_unused_api; + +import 'dart2js.dart' as dart2js; + +import 'dart2jslib.dart' as dart2jslib; + +import 'tree/tree.dart' as tree; + +import 'util/util.dart' as util; + +import 'elements/elements.dart' as elements; + +import 'elements/visitor.dart' as elements_visitor; + +import 'js/js.dart' as js; + +import 'inferrer/concrete_types_inferrer.dart' as concrete_types_inferrer; + +import 'colors.dart' as colors; + +import 'filenames.dart' as filenames; + +import 'dart_types.dart' as dart_types; + +import 'universe/universe.dart' as universe; + +import 'inferrer/type_graph_inferrer.dart' as type_graph_inferrer; + +import 'source_file_provider.dart' as source_file_provider; + +import 'ssa/ssa.dart' as ssa; + +class ElementVisitor extends elements_visitor.ElementVisitor { + visitElement(e) {} +} + +void main(List arguments) { + dart2js.main(arguments); + useConstant(null, null); + useNode(null); + useUtil(null); + useElementVisitor(new ElementVisitor()); + useJs(new js.Program(null)); + useJs(new js.Blob(null)); + useJs(new js.NamedFunction(null, null)); + useConcreteTypesInferrer(null); + useColor(); + useFilenames(); + useSsa(null); + useCodeBuffer(null); + usedByTests(); + useElements(null, null); +} + +void useConstant(dart2jslib.Constant constant, dart2jslib.ConstantSystem cs) { + constant.isObject; + cs.isBool(constant); +} + +void useNode(tree.Node node) { + node + ..asBreakStatement() + ..asCascade() + ..asCatchBlock() + ..asClassNode() + ..asCombinator() + ..asConditional() + ..asContinueStatement() + ..asErrorExpression() + ..asExport() + ..asFor() + ..asFunctionDeclaration() + ..asIf() + ..asLabeledStatement() + ..asLibraryDependency() + ..asLibraryName() + ..asLiteralDouble() + ..asLiteralList() + ..asLiteralMap() + ..asLiteralMapEntry() + ..asLiteralNull() + ..asLiteralSymbol() + ..asMetadata() + ..asModifiers() + ..asPart() + ..asPartOf() + ..asRethrow() + ..asStatement() + ..asStringInterpolation() + ..asStringInterpolationPart() + ..asStringJuxtaposition() + ..asStringNode() + ..asSwitchCase() + ..asSwitchStatement() + ..asTryStatement() + ..asTypeAnnotation() + ..asTypeVariable() + ..asTypedef() + ..asWhile(); +} + +void useUtil(util.Link link) { + link.reversePrependAll(link); + util.trace(""); +} + +void useElementVisitor(ElementVisitor visitor) { + visitor + ..visit(null) + ..visitAbstractFieldElement(null) + ..visitAmbiguousElement(null) + ..visitBoxElement(null) + ..visitBoxFieldElement(null) + ..visitClassElement(null) + ..visitClosureClassElement(null) + ..visitClosureFieldElement(null) + ..visitCompilationUnitElement(null) + ..visitConstructorBodyElement(null) + ..visitElement(null) + ..visitErroneousElement(null) + ..visitFieldParameterElement(null) + ..visitFunctionElement(null) + ..visitInterceptedElement(null) + ..visitLabelElement(null) + ..visitLibraryElement(null) + ..visitMixinApplicationElement(null) + ..visitPrefixElement(null) + ..visitScopeContainerElement(null) + ..visitTargetElement(null) + ..visitThisElement(null) + ..visitTypeDeclarationElement(null) + ..visitTypeVariableElement(null) + ..visitTypedefElement(null) + ..visitVariableElement(null) + ..visitVoidElement(null) + ..visitWarnOnUseElement(null); +} + +useJs(js.Node node) { + node.asVariableUse(); +} + +useConcreteTypesInferrer(concrete_types_inferrer.ConcreteTypesInferrer c) { + c.debug(); +} + +useColor() { + colors.white(null); + colors.blue(null); + colors.yellow(null); + colors.black(null); +} + +useFilenames() { + filenames.appendSlash(null); +} + +useSsa(ssa.HInstruction instruction) { + instruction.isConstantNumber(); + new ssa.HAndOrBlockInformation(null, null, null); + new ssa.HStatementSequenceInformation(null); +} + +useCodeBuffer(dart2jslib.CodeBuffer buffer) { + buffer.writeln(); +} + +usedByTests() { + // TODO(ahe): We should try to avoid including API used only for tests. In + // most cases, such API can be moved to a test library. + dart2jslib.World world = null; + dart2jslib.Compiler compiler = null; + compiler.currentlyInUserCode(); + compiler.inUserCode(null); + type_graph_inferrer.TypeGraphInferrer typeGraphInferrer = null; + source_file_provider.SourceFileProvider sourceFileProvider = null; + world.hasAnyUserDefinedGetter(null); + compiler.importHelperLibrary(null); + typeGraphInferrer.getCallersOf(null); + dart_types.Types.sorted(null); + new universe.TypedSelector.subclass(null, null); + new universe.TypedSelector.subtype(null, null); + new universe.TypedSelector.exact(null, null); + sourceFileProvider.readStringFromUri(null); +} + +useElements(elements.ClassElement e, elements.Name n) { + e.lookupClassMember(null); + e.lookupInterfaceMember(null); + n.isAccessibleFrom(null); +} \ No newline at end of file diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/characters.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/characters.dart new file mode 100644 index 0000000..5961d07 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/characters.dart @@ -0,0 +1,143 @@ +// Copyright (c) 2011, 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. + +library characters; + +const int $EOF = 0; +const int $STX = 2; +const int $BS = 8; +const int $TAB = 9; +const int $LF = 10; +const int $VTAB = 11; +const int $FF = 12; +const int $CR = 13; +const int $SPACE = 32; +const int $BANG = 33; +const int $DQ = 34; +const int $HASH = 35; +const int $$ = 36; +const int $PERCENT = 37; +const int $AMPERSAND = 38; +const int $SQ = 39; +const int $OPEN_PAREN = 40; +const int $CLOSE_PAREN = 41; +const int $STAR = 42; +const int $PLUS = 43; +const int $COMMA = 44; +const int $MINUS = 45; +const int $PERIOD = 46; +const int $SLASH = 47; +const int $0 = 48; +const int $1 = 49; +const int $2 = 50; +const int $3 = 51; +const int $4 = 52; +const int $5 = 53; +const int $6 = 54; +const int $7 = 55; +const int $8 = 56; +const int $9 = 57; +const int $COLON = 58; +const int $SEMICOLON = 59; +const int $LT = 60; +const int $EQ = 61; +const int $GT = 62; +const int $QUESTION = 63; +const int $AT = 64; +const int $A = 65; +const int $B = 66; +const int $C = 67; +const int $D = 68; +const int $E = 69; +const int $F = 70; +const int $G = 71; +const int $H = 72; +const int $I = 73; +const int $J = 74; +const int $K = 75; +const int $L = 76; +const int $M = 77; +const int $N = 78; +const int $O = 79; +const int $P = 80; +const int $Q = 81; +const int $R = 82; +const int $S = 83; +const int $T = 84; +const int $U = 85; +const int $V = 86; +const int $W = 87; +const int $X = 88; +const int $Y = 89; +const int $Z = 90; +const int $OPEN_SQUARE_BRACKET = 91; +const int $BACKSLASH = 92; +const int $CLOSE_SQUARE_BRACKET = 93; +const int $CARET = 94; +const int $_ = 95; +const int $BACKPING = 96; +const int $a = 97; +const int $b = 98; +const int $c = 99; +const int $d = 100; +const int $e = 101; +const int $f = 102; +const int $g = 103; +const int $h = 104; +const int $i = 105; +const int $j = 106; +const int $k = 107; +const int $l = 108; +const int $m = 109; +const int $n = 110; +const int $o = 111; +const int $p = 112; +const int $q = 113; +const int $r = 114; +const int $s = 115; +const int $t = 116; +const int $u = 117; +const int $v = 118; +const int $w = 119; +const int $x = 120; +const int $y = 121; +const int $z = 122; +const int $OPEN_CURLY_BRACKET = 123; +const int $BAR = 124; +const int $CLOSE_CURLY_BRACKET = 125; +const int $TILDE = 126; +const int $DEL = 127; +const int $NBSP = 160; +const int $LS = 0x2028; +const int $PS = 0x2029; + +const int $FIRST_SURROGATE = 0xd800; +const int $LAST_SURROGATE = 0xdfff; +const int $LAST_CODE_POINT = 0x10ffff; + +bool isHexDigit(int characterCode) { + if (characterCode <= $9) return $0 <= characterCode; + characterCode |= $a ^ $A; + return ($a <= characterCode && characterCode <= $f); +} + +int hexDigitValue(int hexDigit) { + assert(isHexDigit(hexDigit)); + // hexDigit is one of '0'..'9', 'A'..'F' and 'a'..'f'. + if (hexDigit <= $9) return hexDigit - $0; + return (hexDigit | ($a ^ $A)) - ($a - 10); +} + +bool isUnicodeScalarValue(int value) { + return value < $FIRST_SURROGATE || + (value > $LAST_SURROGATE && value <= $LAST_CODE_POINT); +} + +bool isUtf16LeadSurrogate(int value) { + return value >= 0xd800 && value <= 0xdbff; +} + +bool isUtf16TrailSurrogate(int value) { + return value >= 0xdc00 && value <= 0xdfff; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/expensive_map.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/expensive_map.dart new file mode 100644 index 0000000..d9c7659 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/expensive_map.dart @@ -0,0 +1,73 @@ +// Copyright (c) 2013, 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. + +part of dart2js.util; + +/** + * The expensive map is a data structure useful for tracking down + * excessive memory usage due to large maps. It acts as an ordinary + * hash map, but it uses 10 times more memory (by default). + */ +class ExpensiveMap implements Map { + + final List _maps; + + ExpensiveMap([int copies = 10]) : _maps = new List(copies) { + assert(copies > 0); + for (int i = 0; i < _maps.length; i++) { + _maps[i] = new Map(); + } + } + + int get length => _maps[0].length; + bool get isEmpty => _maps[0].isEmpty; + bool get isNotEmpty => _maps[0].isNotEmpty; + + Iterable get keys => _maps[0].keys; + Iterable get values => _maps[0].values; + + bool containsKey(K key) => _maps[0].containsKey(key); + bool containsValue(V value) => _maps[0].containsValue(value); + + V operator[](K key) => _maps[0][key]; + + void forEach(void action(K key, V value)) { + _maps[0].forEach(action); + } + + void operator[]=(K key, V value) { + for (int i = 0; i < _maps.length; i++) { + _maps[i][key] = value; + } + } + + V putIfAbsent(K key, V ifAbsent()) { + if (containsKey(key)) return this[key]; + V value = ifAbsent(); + this[key] = value; + return value; + } + + void addAll(Map other) { + for (int i = 0; i < _maps.length; i++) { + _maps[i].addAll(other); + } + } + + V remove(Object key) { + V result = _maps[0].remove(key); + for (int i = 1; i < _maps.length; i++) { + _maps[i].remove(key); + } + return result; + } + + void clear() { + for (int i = 0; i < _maps.length; i++) { + _maps[i].clear(); + } + } + + String toString() => "expensive(${_maps[0]}x${_maps.length})"; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/expensive_set.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/expensive_set.dart new file mode 100644 index 0000000..2daa9c9 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/expensive_set.dart @@ -0,0 +1,124 @@ +// Copyright (c) 2013, 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. + +part of dart2js.util; + +/** + * The expensive set is a data structure useful for tracking down + * excessive memory usage due to large sets. It acts as an ordinary + * hash set, but it uses 10 times more memory (by default). + */ +class ExpensiveSet extends IterableBase implements Set { + + final List _sets; + + ExpensiveSet([int copies = 10]) : _sets = new List(copies) { + assert(copies > 0); + for (int i = 0; i < _sets.length; i++) { + _sets[i] = new Set(); + } + } + + int get length => _sets[0].length; + bool get isEmpty => _sets[0].isEmpty; + bool get isNotEmpty => _sets[0].isNotEmpty; + + Iterator get iterator => _sets[0].iterator; + + bool contains(Object object) => _sets[0].contains(object); + E lookup(Object object) => _sets[0].lookup(object); + + void forEach(void action(E element)) { + _sets[0].forEach(action); + } + + bool add(E element) { + bool result = _sets[0].add(element); + for (int i = 1; i < _sets.length; i++) { + _sets[i].add(element); + } + return result; + } + + void addAll(Iterable objects) { + for (E each in objects) { + add(each); + } + } + + bool remove(Object object) { + bool result = _sets[0].remove(object); + for (int i = 1; i < _sets.length; i++) { + _sets[i].remove(object); + } + return result; + } + + void clear() { + for (int i = 0; i < _sets.length; i++) { + _sets[i].clear(); + } + } + + void removeAll(Iterable objectsToRemove) { + for (var each in objectsToRemove) { + remove(each); + } + } + + void removeWhere(bool test(E element)) { + removeAll(this.toList().where((e) => test(e))); + } + + void retainWhere(bool test(E element)) { + removeAll(toList().where((e) => !test(e))); + } + + bool containsAll(Iterable other) { + for (Object object in other) { + if (!this.contains(object)) return false; + } + return true; + } + + Set _newSet() => new ExpensiveSet(_sets.length); + + Set intersection(Set other) { + Set result = _newSet(); + if (other.length < this.length) { + for (var element in other) { + if (this.contains(element)) result.add(element); + } + } else { + for (E element in this) { + if (other.contains(element)) result.add(element); + } + } + return result; + } + + Set union(Set other) { + return _newSet()..addAll(this)..addAll(other); + } + + Set difference(Set other) { + Set result = _newSet(); + for (E element in this) { + if (!other.contains(element)) result.add(element); + } + return result; + } + + void retainAll(Iterable objectsToRetain) { + Set retainSet; + if (objectsToRetain is Set) { + retainSet = objectsToRetain; + } else { + retainSet = objectsToRetain.toSet(); + } + retainWhere(retainSet.contains); + } + + String toString() => "expensive(${_sets[0]}x${_sets.length})"; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/link.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/link.dart new file mode 100644 index 0000000..44b7f29 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/link.dart @@ -0,0 +1,132 @@ +// Copyright (c) 2011, 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. + +part of dart2js.util; + +class Link { + T get head => null; + Link get tail => null; + + factory Link.fromList(List list) { + switch (list.length) { + case 0: + return new Link(); + case 1: + return new LinkEntry(list[0]); + case 2: + return new LinkEntry(list[0], new LinkEntry(list[1])); + case 3: + return new LinkEntry( + list[0], new LinkEntry(list[1], new LinkEntry(list[2]))); + } + Link link = new Link(); + for (int i = list.length ; i > 0; i--) { + link = link.prepend(list[i - 1]); + } + return link; + } + + const Link(); + + Link prepend(T element) { + return new LinkEntry(element, this); + } + + Iterator get iterator => new LinkIterator(this); + + void printOn(StringBuffer buffer, [separatedBy]) { + } + + List toList({ bool growable: true }) { + List result; + if (!growable) { + result = new List(slowLength()); + } else { + result = new List(); + result.length = slowLength(); + } + int i = 0; + for (Link link = this; !link.isEmpty; link = link.tail) { + result[i++] = link.head; + } + return result; + } + + bool get isEmpty => true; + + Link reverse() => this; + + Link reversePrependAll(Link from) { + if (from.isEmpty) return this; + return this.prepend(from.head).reversePrependAll(from.tail); + } + + Link skip(int n) { + if (n == 0) return this; + throw new RangeError('Index $n out of range'); + } + + void forEach(void f(T element)) {} + + bool operator ==(other) { + if (other is !Link) return false; + return other.isEmpty; + } + + int get hashCode => throw new UnsupportedError('Link.hashCode'); + + String toString() => "[]"; + + get length { + throw new UnsupportedError('get:length'); + } + + int slowLength() => 0; + + // TODO(ahe): Remove this method? + bool contains(T element) { + for (Link link = this; !link.isEmpty; link = link.tail) { + if (link.head == element) return true; + } + return false; + } + + // TODO(ahe): Remove this method? + T get single { + if (isEmpty) throw new StateError('No elements'); + if (!tail.isEmpty) throw new StateError('More than one element'); + return head; + } + + // TODO(ahe): Remove this method? + T get first { + if (isEmpty) throw new StateError('No elements'); + return head; + } + + /// Returns true if f returns true for all elements of this list. + /// + /// Returns true for the empty list. + bool every(bool f(T)) { + for (Link link = this; !link.isEmpty; link = link.tail){ + if (!f(link.head)) return false; + } + return true; + } +} + +abstract class LinkBuilder { + factory LinkBuilder() = LinkBuilderImplementation; + + /** + * Prepends all elements added to the builder to [tail]. The resulting list is + * returned and the builder is cleared. + */ + Link toLink([Link tail = const Link()]); + + void addLast(T t); + + final int length; + final bool isEmpty; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/link_implementation.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/link_implementation.dart new file mode 100644 index 0000000..1f85236 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/link_implementation.dart @@ -0,0 +1,136 @@ +// Copyright (c) 2011, 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. + +part of util_implementation; + +class LinkIterator implements Iterator { + T _current; + Link _link; + + LinkIterator(Link this._link); + + T get current => _current; + + bool moveNext() { + if (_link.isEmpty) { + _current = null; + return false; + } + _current = _link.head; + _link = _link.tail; + return true; + } +} + +class LinkEntry extends Link { + final T head; + Link tail; + + LinkEntry(T this.head, [Link tail]) + : this.tail = ((tail == null) ? new Link() : tail); + + Link prepend(T element) { + // TODO(ahe): Use new Link, but this cost 8% performance on VM. + return new LinkEntry(element, this); + } + + void printOn(StringBuffer buffer, [separatedBy]) { + buffer.write(head); + if (separatedBy == null) separatedBy = ''; + for (Link link = tail; !link.isEmpty; link = link.tail) { + buffer.write(separatedBy); + buffer.write(link.head); + } + } + + String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.write('[ '); + printOn(buffer, ', '); + buffer.write(' ]'); + return buffer.toString(); + } + + Link reverse() { + Link result = const Link(); + for (Link link = this; !link.isEmpty; link = link.tail) { + result = result.prepend(link.head); + } + return result; + } + + Link reversePrependAll(Link from) { + Link result; + for (result = this; !from.isEmpty; from = from.tail) { + result = result.prepend(from.head); + } + return result; + } + + Link skip(int n) { + Link link = this; + for (int i = 0 ; i < n ; i++) { + if (link.isEmpty) { + throw new RangeError('Index $n out of range'); + } + link = link.tail; + } + return link; + } + + bool get isEmpty => false; + + void forEach(void f(T element)) { + for (Link link = this; !link.isEmpty; link = link.tail) { + f(link.head); + } + } + + bool operator ==(other) { + if (other is !Link) return false; + Link myElements = this; + while (!myElements.isEmpty && !other.isEmpty) { + if (myElements.head != other.head) { + return false; + } + myElements = myElements.tail; + other = other.tail; + } + return myElements.isEmpty && other.isEmpty; + } + + int get hashCode => throw new UnsupportedError('LinkEntry.hashCode'); + + int slowLength() => 1 + tail.slowLength(); +} + +class LinkBuilderImplementation implements LinkBuilder { + LinkEntry head = null; + LinkEntry lastLink = null; + int length = 0; + + LinkBuilderImplementation(); + + Link toLink([Link tail = const Link()]) { + if (head == null) return tail; + lastLink.tail = tail; + Link link = head; + lastLink = null; + head = null; + return link; + } + + void addLast(T t) { + length++; + LinkEntry entry = new LinkEntry(t, null); + if (head == null) { + head = entry; + } else { + lastLink.tail = entry; + } + lastLink = entry; + } + + bool get isEmpty => length == 0; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/setlet.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/setlet.dart new file mode 100644 index 0000000..7b9d0bc --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/setlet.dart @@ -0,0 +1,237 @@ +// Copyright (c) 2013, 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. + +library dart2js.util.setlet; + +import 'dart:collection' show IterableBase; + +class Setlet extends IterableBase { + static const _MARKER = const _SetletMarker(); + static const CAPACITY = 8; + + // The setlet can be in one of four states: + // + // * Empty (extra: null, contents: marker) + // * Single element (extra: null, contents: element) + // * List-backed (extra: length, contents: list) + // * Set-backed (extra: marker, contents: set) + // + // When the setlet is list-backed, the list in the contents field + // may have empty slots filled with the marker value. + var _contents = _MARKER; + var _extra; + + Setlet(); + Setlet.from(Iterable elements) { + addAll(elements); + } + + Iterator get iterator { + if (_extra == null) { + return new _SetletSingleIterator(_contents); + } else if (_MARKER == _extra) { + return _contents.iterator; + } else { + return new _SetletListIterator(_contents, _extra); + } + } + + int get length { + if (_extra == null) { + return (_MARKER == _contents) ? 0 : 1; + } else if (_MARKER == _extra) { + return _contents.length; + } else { + return _extra; + } + } + + bool get isEmpty { + if (_extra == null) { + return _MARKER == _contents; + } else if (_MARKER == _extra) { + return _contents.isEmpty; + } else { + return _extra == 0; + } + } + + bool contains(E element) { + if (_extra == null) { + return _contents == element; + } else if (_MARKER == _extra) { + return _contents.contains(element); + } else { + for (int remaining = _extra, i = 0; remaining > 0 && i < CAPACITY; i++) { + var candidate = _contents[i]; + if (_MARKER == candidate) continue; + if (candidate == element) return true; + remaining--; + } + return false; + } + } + + bool remove(E element) { + if (_extra == null) { + if (_contents == element) { + _contents = _MARKER; + return true; + } else { + return false; + } + } else if (_MARKER == _extra) { + return _contents.remove(element); + } else { + for (int remaining = _extra, i = 0; remaining > 0 && i < CAPACITY; i++) { + var candidate = _contents[i]; + if (_MARKER == candidate) continue; + if (candidate == element) { + _contents[i] = _MARKER; + _extra--; + return true; + } + remaining--; + } + return false; + } + } + + void add(E element) { + if (_extra == null) { + if (_MARKER == _contents) { + _contents = element; + } else if (_contents == element) { + // Do nothing. + } else { + List list = new List(CAPACITY); + list[0] = _contents; + list[1] = element; + _contents = list; + _extra = 2; // Two elements. + } + } else if (_MARKER == _extra) { + _contents.add(element); + } else { + int remaining = _extra; + int index = 0; + int copyTo, copyFrom; + while (remaining > 0 && index < CAPACITY) { + var candidate = _contents[index++]; + if (_MARKER == candidate) { + // Keep track of the last range of empty slots in the + // list. When we're done we'll move all the elements + // after those empty slots down, so that adding an element + // after that will preserve the insertion order. + if (copyFrom == index - 1) { + copyFrom++; + } else { + copyTo = index - 1; + copyFrom = index; + } + continue; + } else if (candidate == element) { + return; + } + remaining--; + } + if (index < CAPACITY) { + _contents[index] = element; + _extra++; + } else if (_extra < CAPACITY) { + // Move the last elements down into the last empty slots + // so that we have empty slots after the last element. + while (copyFrom < CAPACITY) { + _contents[copyTo++] = _contents[copyFrom++]; + } + // Insert the new element as the last element. + _contents[copyTo++] = element; + _extra++; + // Clear all elements after the new last elements to + // make sure we don't keep extra stuff alive. + while (copyTo < CAPACITY) _contents[copyTo++] = null; + } else { + _contents = new Set()..addAll(_contents)..add(element); + _extra = _MARKER; + } + } + } + + void addAll(Iterable elements) { + elements.forEach((each) => add(each)); + } + + void forEach(void action(E element)) { + if (_extra == null) { + if (_MARKER != _contents) action(_contents); + } else if (_MARKER == _extra) { + _contents.forEach(action); + } else { + for (int remaining = _extra, i = 0; remaining > 0 && i < CAPACITY; i++) { + var element = _contents[i]; + if (_MARKER == element) continue; + action(element); + remaining--; + } + } + } + + bool containsAll(Iterable other) { + for (E e in other) { + if (!this.contains(e)) return false; + }; + return true; + } + + clear() { + _contents = _MARKER; + _extra = null; + } +} + +class _SetletMarker { + const _SetletMarker(); + toString() => "-"; +} + +class _SetletSingleIterator implements Iterator { + var _element; + E _current; + _SetletSingleIterator(this._element); + + E get current => _current; + + bool moveNext() { + if (Setlet._MARKER == _element) { + _current = null; + return false; + } + _current = _element; + _element = Setlet._MARKER; + return true; + } +} + +class _SetletListIterator implements Iterator { + final List _list; + int _remaining; + int _index = 0; + E _current; + _SetletListIterator(this._list, this._remaining); + + E get current => _current; + + bool moveNext() { + while (_remaining > 0) { + var candidate = _list[_index++]; + if (Setlet._MARKER != candidate) { + _current = candidate; + _remaining--; + return true; + } + } + _current = null; + return false; + } +} \ No newline at end of file diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/uri_extras.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/uri_extras.dart new file mode 100644 index 0000000..65a4ed2 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/uri_extras.dart @@ -0,0 +1,68 @@ +// Copyright (c) 2012, 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. + +library uri_extras; + +import 'dart:math'; + +String relativize(Uri base, Uri uri, bool isWindows) { + if (!base.path.startsWith('/')) { + // Also throw an exception if [base] or base.path is null. + throw new ArgumentError('Expected absolute path: ${base.path}'); + } + if (!uri.path.startsWith('/')) { + // Also throw an exception if [uri] or uri.path is null. + throw new ArgumentError('Expected absolute path: ${uri.path}'); + } + bool equalsNCS(String a, String b) { + return a.toLowerCase() == b.toLowerCase(); + } + + String normalize(String path) { + if (isWindows) { + return path.toLowerCase(); + } else { + return path; + } + } + + if (equalsNCS(base.scheme, 'file') && + equalsNCS(base.scheme, uri.scheme) && + base.userInfo == uri.userInfo && + equalsNCS(base.host, uri.host) && + base.port == uri.port && + uri.query == "" && uri.fragment == "") { + if (normalize(uri.path).startsWith(normalize(base.path))) { + return uri.path.substring(base.path.lastIndexOf('/') + 1); + } + + List uriParts = uri.path.split('/'); + List baseParts = base.path.split('/'); + int common = 0; + int length = min(uriParts.length, baseParts.length); + while (common < length && + normalize(uriParts[common]) == normalize(baseParts[common])) { + common++; + } + if (common == 1 || (isWindows && common == 2)) { + // The first part will always be an empty string because the + // paths are absolute. On Windows, we must also consider drive + // letters or hostnames. + if (baseParts.length > common + 1) { + // Avoid using '..' to go to the root, unless we are already there. + return uri.path; + } + } + StringBuffer sb = new StringBuffer(); + for (int i = common + 1; i < baseParts.length; i++) { + sb.write('../'); + } + for (int i = common; i < uriParts.length - 1; i++) { + sb.write('${uriParts[i]}/'); + } + sb.write('${uriParts.last}'); + return sb.toString(); + } + return uri.toString(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/util.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/util.dart new file mode 100644 index 0000000..b24cc1f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/util.dart @@ -0,0 +1,308 @@ +// Copyright (c) 2011, 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. + +library dart2js.util; + +import "dart:collection"; +import 'util_implementation.dart'; +import 'characters.dart'; + +export 'setlet.dart'; + +part 'link.dart'; +part 'expensive_map.dart'; +part 'expensive_set.dart'; + +/** + * Tagging interface for classes from which source spans can be generated. + */ +// TODO(johnniwinther): Find a better name. +// TODO(ahe): How about "Bolt"? +abstract class Spannable {} + +class _SpannableSentinel implements Spannable { + final String name; + + const _SpannableSentinel(this.name); + + String toString() => name; +} + +/// Sentinel spannable used to mark that diagnostics should point to the +/// current element. Note that the diagnostic reporting will fail if the current +/// element is `null`. +const Spannable CURRENT_ELEMENT_SPANNABLE = + const _SpannableSentinel("Current element"); + +/// Sentinel spannable used to mark that there might be no location for the +/// diagnostic. Use this only when it is not an error not to have a current +/// element. +const Spannable NO_LOCATION_SPANNABLE = + const _SpannableSentinel("No location"); + +class SpannableAssertionFailure { + final Spannable node; + final String message; + SpannableAssertionFailure(this.node, this.message); + + String toString() => 'Assertion failure' + '${message != null ? ': $message' : ''}'; +} + +/** + * Helper method for printing stack traces for debugging. + * + * [message] is printed as the header of the stack trace. + * + * If [condition] is provided, the stack trace is only printed if [condition] + * returns [:true:] on the stack trace text. This can be used to filter the + * printed stack traces based on their content. For instance only print stack + * traces that contain specific paths. + */ +void trace(String message, [bool condition(String stackTrace)]) { + try { + throw ''; + } catch (e, s) { + String stackTrace = prettifyStackTrace( + s, rangeStart: 1, filePrefix: stackTraceFilePrefix); + if (condition != null) { + if (!condition(stackTrace)) return; + } + print('$message\n$stackTrace'); + } +} + +/** + * File name prefix used to shorten the file name in stack traces printed by + * [trace]. + */ +String stackTraceFilePrefix = null; + +/// Helper class for the processing of stack traces in [prettifyStackTrace]. +class _StackTraceLine { + final int index; + final String file; + final String lineNo; + final String columnNo; + final String method; + + _StackTraceLine(this.index, this.file, this.lineNo, + this.columnNo, this.method); + + String toString() { + return 'index=$index, file=$file, ' + 'lineNo=$lineNo, columnNo=$columnNo, method=$method'; + } +} + +// TODO(johnniwinther): Use this format for --throw-on-error. +/** + * Converts the normal VM stack trace into a more compact and readable format. + * + * The output format is [: . . . : :] where + * [: :] is file name, [: :] is the line number, + * [: :] is the column number, and [: :] is the method name. + * + * If [rangeStart] and/or [rangeEnd] are provided, only the lines within the + * range are included. + * If [showColumnNo] is [:false:], the [: : :] part is omitted. + * If [showDots] is [:true:], the space between [: :] and [: :] + * is padded with dots on every other line. + * If [filePrefix] is provided, then for every file name thats starts with + * [filePrefix] only the remainder is printed. + * If [lambda] is non-null, anonymous closures are printed as [lambda]. + */ +String prettifyStackTrace(StackTrace s, + {int rangeStart, + int rangeEnd, + bool showColumnNo: false, + bool showDots: true, + String filePrefix, + String lambda: r'?'}) { + int index = -1; + int maxFileLength = 0; + int maxLineNoLength = 0; + int maxColumnNoLength = 0; + + String stackTrace = '$s'; + List<_StackTraceLine> lines = <_StackTraceLine>[]; + for (String line in stackTrace.split('\n')) { + try { + index++; + if (rangeStart != null && index < rangeStart) continue; + if (rangeEnd != null && index > rangeEnd) continue; + if (line.isEmpty) continue; + + // Strip index. + line = line.replaceFirst(new RegExp(r'#\d+\s*'), ''); + + int leftParenPos = line.indexOf('('); + int rightParenPos = line.indexOf(')', leftParenPos); + int lastColon = line.lastIndexOf(':', rightParenPos); + int nextToLastColon = line.lastIndexOf(':', lastColon-1); + + String lineNo; + String columnNo; + if (nextToLastColon != -1) { + lineNo = line.substring(nextToLastColon+1, lastColon); + columnNo = line.substring(lastColon+1, rightParenPos); + try { + int.parse(lineNo); + } on FormatException catch (e) { + lineNo = columnNo; + columnNo = ''; + nextToLastColon = lastColon; + } + } else { + lineNo = line.substring(lastColon+1, rightParenPos); + columnNo = ''; + nextToLastColon = lastColon; + } + + if (lineNo.length > maxLineNoLength) { + maxLineNoLength = lineNo.length; + } + if (columnNo.length > maxColumnNoLength) { + maxColumnNoLength = columnNo.length; + } + + String file = line.substring(leftParenPos+1, nextToLastColon); + if (filePrefix != null && file.startsWith(filePrefix)) { + file = file.substring(filePrefix.length); + } + if (file.length > maxFileLength) { + maxFileLength = file.length; + } + String method = line.substring(0, leftParenPos-1); + if (lambda != null) { + method = method.replaceAll('', lambda); + } + lines.add(new _StackTraceLine(index, file, lineNo, columnNo, method)); + } catch (e) { + print('Error prettifying "$line": $e'); + return stackTrace; + } + } + + StringBuffer sb = new StringBuffer(); + bool dots = true; + for (_StackTraceLine line in lines) { + String file = pad('${line.file} ', maxFileLength, + dots: showDots && dots ? ' .' : ' '); + String lineNo = pad(line.lineNo, maxLineNoLength, padLeft: true); + String columnNo = + showColumnNo ? ':${pad(line.columnNo, maxColumnNoLength)}' : ''; + String method = line.method; + sb.write(' $file $lineNo$columnNo $method\n'); + dots = !dots; + } + return sb.toString(); +} + +/** + * Pads (or truncates) [text] to the [intendedLength]. + * + * If [padLeft] is [:true:] the text is padding inserted to the left of [text]. + * A repetition of the [dots] text is used for padding. + */ +String pad(String text, int intendedLength, + {bool padLeft: false, String dots: ' '}) { + if (text.length == intendedLength) return text; + if (text.length > intendedLength) return text.substring(0, intendedLength); + if (dots == null || dots.isEmpty) dots = ' '; + int dotsLength = dots.length; + StringBuffer sb = new StringBuffer(); + if (!padLeft) { + sb.write(text); + } + for (int index = text.length ; index < intendedLength ; index ++) { + int dotsIndex = index % dotsLength; + sb.write(dots.substring(dotsIndex, dotsIndex + 1)); + } + if (padLeft) { + sb.write(text); + } + return sb.toString(); +} + +/// Writes the characters of [string] on [buffer]. The characters +/// are escaped as suitable for JavaScript and JSON. [buffer] is +/// anything which supports [:write:] and [:writeCharCode:], for example, +/// [StringBuffer]. Note that JS supports \xnn and \unnnn whereas JSON only +/// supports the \unnnn notation. Therefore we use the \unnnn notation. +void writeJsonEscapedCharsOn(String string, buffer) { + void addCodeUnitEscaped(var buffer, int code) { + assert(code < 0x10000); + buffer.write(r'\u'); + if (code < 0x1000) { + buffer.write('0'); + if (code < 0x100) { + buffer.write('0'); + if (code < 0x10) { + buffer.write('0'); + } + } + } + buffer.write(code.toRadixString(16)); + } + + void writeEscapedOn(String string, var buffer) { + for (int i = 0; i < string.length; i++) { + int code = string.codeUnitAt(i); + if (code == $DQ) { + buffer.write(r'\"'); + } else if (code == $TAB) { + buffer.write(r'\t'); + } else if (code == $LF) { + buffer.write(r'\n'); + } else if (code == $CR) { + buffer.write(r'\r'); + } else if (code == $DEL) { + addCodeUnitEscaped(buffer, $DEL); + } else if (code == $LS) { + // This Unicode line terminator and $PS are invalid in JS string + // literals. + addCodeUnitEscaped(buffer, $LS); // 0x2028. + } else if (code == $PS) { + addCodeUnitEscaped(buffer, $PS); // 0x2029. + } else if (code == $BACKSLASH) { + buffer.write(r'\\'); + } else { + if (code < 0x20) { + addCodeUnitEscaped(buffer, code); + // We emit DEL (ASCII 0x7f) as an escape because it would be confusing + // to have it unescaped in a string literal. We also escape + // everything above 0x7f because that means we don't have to worry + // about whether the web server serves it up as Latin1 or UTF-8. + } else if (code < 0x7f) { + buffer.writeCharCode(code); + } else { + // This will output surrogate pairs in the form \udxxx\udyyy, rather + // than the more logical \u{zzzzzz}. This should work in JavaScript + // (especially old UCS-2 based implementations) and is the only + // format that is allowed in JSON. + addCodeUnitEscaped(buffer, code); + } + } + } + } + + for (int i = 0; i < string.length; i++) { + int code = string.codeUnitAt(i); + if (code < 0x20 || code == $DEL || code == $DQ || code == $LS || + code == $PS || code == $BACKSLASH || code >= 0x80) { + writeEscapedOn(string, buffer); + return; + } + } + buffer.write(string); +} + +int computeHashCode(part1, [part2, part3, part4, part5]) { + return (part1.hashCode + ^ part2.hashCode + ^ part3.hashCode + ^ part4.hashCode + ^ part5.hashCode) & 0x3fffffff; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/util_implementation.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/util_implementation.dart new file mode 100644 index 0000000..bbb852a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/util/util_implementation.dart @@ -0,0 +1,9 @@ +// Copyright (c) 2011, 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. + +library util_implementation; + +import 'util.dart'; + +part 'link_implementation.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/warnings.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/warnings.dart new file mode 100644 index 0000000..f50505a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/warnings.dart @@ -0,0 +1,1932 @@ +// Copyright (c) 2011, 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. + +part of dart2js; + +const DONT_KNOW_HOW_TO_FIX = ""; + +/** + * The messages in this file should meet the following guide lines: + * + * 1. The message should be a complete sentence starting with an uppercase + * letter, and ending with a period. + * + * 2. Reserved words and embedded identifiers should be in single quotes, so + * prefer double quotes for the complete message. For example, "The + * class '#{className}' can't use 'super'." Notice that the word 'class' in the + * preceding message is not quoted as it refers to the concept 'class', not the + * reserved word. On the other hand, 'super' refers to the reserved word. Do + * not quote 'null' and numeric literals. + * + * 3. Do not try to compose messages, as it can make translating them hard. + * + * 4. Try to keep the error messages short, but informative. + * + * 5. Use simple words and terminology, assume the reader of the message + * doesn't have an advanced degree in math, and that English is not the + * reader's native language. Do not assume any formal computer science + * training. For example, do not use Latin abbreviations (prefer "that is" over + * "i.e.", and "for example" over "e.g."). Also avoid phrases such as "if and + * only if" and "iff", that level of precision is unnecessary. + * + * 6. Prefer contractions when they are in common use, for example, prefer + * "can't" over "cannot". Using "cannot", "must not", "shall not", etc. is + * off-putting to people new to programming. + * + * 7. Use common terminology, preferably from the Dart Language + * Specification. This increases the user's chance of finding a good + * explanation on the web. + * + * 8. Do not try to be cute or funny. It is extremely frustrating to work on a + * product that crashes with a "tongue-in-cheek" message, especially if you did + * not want to use this product to begin with with. + * + * 9. Do not lie, that is, do not write error messages containing phrases like + * "can't happen". If the user ever saw this message, it would be a + * lie. Prefer messages like: "Internal error: This function should not be + * called when 'x' is null.". + * + * 10. Prefer to not use imperative tone. That is, the message should not sound + * accusing or like it is ordering the user around. The computer should + * describe the problem, not criticize for violating the specification. + * + * Other things to keep in mind: + * + * An INFO message should always be preceded by a non-INFO message, and the + * INFO messages are additional details about the preceding non-INFO + * message. For example, consider duplicated elements. First report a WARNING + * or ERROR about the duplicated element, and then report an INFO about the + * location of the existing element. + * + * Generally, we want to provide messages that consists of three sentences: + * 1. what is wrong, 2. why is it wrong, 3. how do I fix it. However, we + * combine the first two in [template] and the last in [howToFix]. + */ +// TODO(johnnniwinther): For Infos, consider adding a reference to the +// error/warning/hint that they belong to. +class MessageKind { + /// Should describe what is wrong and why. + final String template; + + /// Should describe how to fix the problem. Elided when using --terse option. + final String howToFix; + + /** + * Examples will be checked by + * tests/compiler/dart2js/message_kind_test.dart. + * + * An example is either a String containing the example source code or a Map + * from filenames to source code. In the latter case, the filename for the + * main library code must be 'main.dart'. + */ + final List examples; + + const MessageKind(this.template, {this.howToFix, this.examples}); + + /// Do not use this. It is here for legacy and debugging. It violates item 4 + /// above. + static const MessageKind GENERIC = const MessageKind('#{text}'); + + static const MessageKind NOT_ASSIGNABLE = const MessageKind( + "'#{fromType}' is not assignable to '#{toType}'."); + + static const MessageKind VOID_EXPRESSION = const MessageKind( + "Expression does not yield a value."); + + static const MessageKind VOID_VARIABLE = const MessageKind( + "Variable cannot be of type void."); + + static const MessageKind RETURN_VALUE_IN_VOID = const MessageKind( + "Cannot return value from void function."); + + static const MessageKind RETURN_NOTHING = const MessageKind( + "Value of type '#{returnType}' expected."); + + static const MessageKind MISSING_ARGUMENT = const MessageKind( + "Missing argument of type '#{argumentType}'."); + + static const MessageKind ADDITIONAL_ARGUMENT = const MessageKind( + "Additional argument."); + + static const MessageKind NAMED_ARGUMENT_NOT_FOUND = const MessageKind( + "No named argument '#{argumentName}' found on method."); + + static const MessageKind MEMBER_NOT_FOUND = const MessageKind( + "No member named '#{memberName}' in class '#{className}'."); + + static const MessageKind METHOD_NOT_FOUND = const MessageKind( + "No method named '#{memberName}' in class '#{className}'."); + + static const MessageKind OPERATOR_NOT_FOUND = const MessageKind( + "No operator '#{memberName}' in class '#{className}'."); + + static const MessageKind SETTER_NOT_FOUND = const MessageKind( + "No setter named '#{memberName}' in class '#{className}'."); + + static const MessageKind GETTER_NOT_FOUND = const MessageKind( + "No getter named '#{memberName}' in class '#{className}'."); + + static const MessageKind NOT_CALLABLE = const MessageKind( + "'#{elementName}' is not callable."); + + static const MessageKind MEMBER_NOT_STATIC = const MessageKind( + "'#{className}.#{memberName}' is not static."); + + static const MessageKind NO_INSTANCE_AVAILABLE = const MessageKind( + "'#{name}' is only available in instance methods."); + + static const MessageKind PRIVATE_ACCESS = const MessageKind( + "'#{name}' is declared private within library " + "'#{libraryName}'."); + + static const MessageKind THIS_IS_THE_DECLARATION = const MessageKind( + "This is the declaration of '#{name}'."); + + static const MessageKind THIS_IS_THE_METHOD = const MessageKind( + "This is the method declaration."); + + static const MessageKind UNREACHABLE_CODE = const MessageKind( + "Unreachable code."); + + static const MessageKind MISSING_RETURN = const MessageKind( + "Missing return."); + + static const MessageKind MAYBE_MISSING_RETURN = const MessageKind( + "Not all paths lead to a return or throw statement."); + + static const MessageKind CANNOT_RESOLVE = const MessageKind( + "Cannot resolve '#{name}'."); + + static const MessageKind CANNOT_RESOLVE_CONSTRUCTOR = const MessageKind( + "Cannot resolve constructor '#{constructorName}'."); + + static const MessageKind CANNOT_RESOLVE_CONSTRUCTOR_FOR_IMPLICIT = + const MessageKind("cannot resolve constructor '#{constructorName}'" + " for implicit super call.", + howToFix: "Try explicitly invoking a constructor of the super class", + examples: const [""" +class A { + A.foo() {} +} +class B extends A { + B(); +} +main() => new B(); +"""]); + + static const MessageKind INVALID_UNNAMED_CONSTRUCTOR_NAME = const MessageKind( + "Unnamed constructor name must be '#{name}'."); + + static const MessageKind INVALID_CONSTRUCTOR_NAME = const MessageKind( + "Constructor name must start with '#{name}'."); + + static const MessageKind CANNOT_RESOLVE_TYPE = const MessageKind( + "Cannot resolve type '#{typeName}'."); + + static const MessageKind DUPLICATE_DEFINITION = const MessageKind( + "Duplicate definition of '#{name}'."); + + static const MessageKind EXISTING_DEFINITION = const MessageKind( + "Existing definition of '#{name}'."); + + static const MessageKind DUPLICATE_IMPORT = const MessageKind( + "Duplicate import of '#{name}'."); + + static const MessageKind HIDDEN_IMPORT = const MessageKind( + "'#{name}' from library '#{hiddenUri}' is hidden by '#{name}' " + "from library '#{hidingUri}'.", + howToFix: "Try adding 'hide #{name}' to the import of '#{hiddenUri}'.", + examples: const [ + const { +'main.dart': +""" +import 'dart:async'; // This imports a class Future. +import 'future.dart'; + +void main() => new Future();""", + +'future.dart': +""" +library future; + +class Future {}"""}, + + const { +'main.dart': +""" +import 'future.dart'; +import 'dart:async'; // This imports a class Future. + +void main() => new Future();""", + +'future.dart': +""" +library future; + +class Future {}"""}, + + const { +'main.dart': +""" +import 'export.dart'; +import 'dart:async'; // This imports a class Future. + +void main() => new Future();""", + +'future.dart': +""" +library future; + +class Future {}""", + +'export.dart': +""" +library export; + +export 'future.dart';"""}, + + const { +'main.dart': +""" +import 'future.dart' as prefix; +import 'dart:async' as prefix; // This imports a class Future. + +void main() => new prefix.Future();""", + +'future.dart': +""" +library future; + +class Future {}"""}]); + + + static const MessageKind HIDDEN_IMPLICIT_IMPORT = const MessageKind( + "'#{name}' from library '#{hiddenUri}' is hidden by '#{name}' " + "from library '#{hidingUri}'.", + howToFix: "Try adding an explicit " + "'import \"#{hiddenUri}\" hide #{name}'.", + examples: const [ + const { +'main.dart': +""" +// This hides the implicit import of class Type from dart:core. +import 'type.dart'; + +void main() => new Type();""", + +'type.dart': +""" +library type; + +class Type {}"""}, + const { +'conflictsWithDart.dart': +""" +library conflictsWithDart; + +class Duration { + static var x = 100; +} +""", + +'conflictsWithDartAsWell.dart': +""" +library conflictsWithDartAsWell; + +class Duration { + static var x = 100; +} +""", + +'main.dart': +r""" +library testDartConflicts; + +import 'conflictsWithDart.dart'; +import 'conflictsWithDartAsWell.dart'; + +main() { + print("Hail Caesar ${Duration.x}"); +} +"""}]); + + static const MessageKind DUPLICATE_EXPORT = const MessageKind( + "Duplicate export of '#{name}'.", + howToFix: "Trying adding 'hide #{name}' to one of the exports.", + examples: const [const { +'main.dart': """ +export 'decl1.dart'; +export 'decl2.dart'; + +main() {}""", +'decl1.dart': "class Class {}", +'decl2.dart': "class Class {}"}]); + + static const MessageKind DUPLICATE_EXPORT_CONT = const MessageKind( + "This is another export of '#{name}'."); + + static const MessageKind DUPLICATE_EXPORT_DECL = const MessageKind( + "The exported '#{name}' from export #{uriString} is defined here."); + + static const MessageKind NOT_A_TYPE = const MessageKind( + "'#{node}' is not a type."); + + static const MessageKind NOT_A_PREFIX = const MessageKind( + "'#{node}' is not a prefix."); + + static const MessageKind CANNOT_FIND_CONSTRUCTOR = const MessageKind( + "Cannot find constructor '#{constructorName}'."); + + static const MessageKind CYCLIC_CLASS_HIERARCHY = const MessageKind( + "'#{className}' creates a cycle in the class hierarchy."); + + static const MessageKind CYCLIC_REDIRECTING_FACTORY = const MessageKind( + 'Redirecting factory leads to a cyclic redirection.'); + + static const MessageKind INVALID_RECEIVER_IN_INITIALIZER = const MessageKind( + "Field initializer expected."); + + static const MessageKind NO_SUPER_IN_STATIC = const MessageKind( + "'super' is only available in instance methods."); + + static const MessageKind DUPLICATE_INITIALIZER = const MessageKind( + "Field '#{fieldName}' is initialized more than once."); + + static const MessageKind ALREADY_INITIALIZED = const MessageKind( + "'#{fieldName}' was already initialized here."); + + static const MessageKind INIT_STATIC_FIELD = const MessageKind( + "Cannot initialize static field '#{fieldName}'."); + + static const MessageKind NOT_A_FIELD = const MessageKind( + "'#{fieldName}' is not a field."); + + static const MessageKind CONSTRUCTOR_CALL_EXPECTED = const MessageKind( + "only call to 'this' or 'super' constructor allowed."); + + static const MessageKind INVALID_FOR_IN = const MessageKind( + "Invalid for-in variable declaration."); + + static const MessageKind INVALID_INITIALIZER = const MessageKind( + "Invalid initializer."); + + static const MessageKind FUNCTION_WITH_INITIALIZER = const MessageKind( + "Only constructors can have initializers."); + + static const MessageKind REDIRECTING_CONSTRUCTOR_CYCLE = const MessageKind( + "Cyclic constructor redirection."); + + static const MessageKind REDIRECTING_CONSTRUCTOR_HAS_BODY = const MessageKind( + "Redirecting constructor can't have a body."); + + static const MessageKind CONST_CONSTRUCTOR_HAS_BODY = const MessageKind( + "Const constructor or factory can't have a body.", + howToFix: "Remove the 'const' keyword or the body", + examples: const [""" +class C { + const C() {} +} + +main() => new C();"""]); + + static const MessageKind REDIRECTING_CONSTRUCTOR_HAS_INITIALIZER = + const MessageKind( + "Redirecting constructor cannot have other initializers."); + + static const MessageKind SUPER_INITIALIZER_IN_OBJECT = const MessageKind( + "'Object' cannot have a super initializer."); + + static const MessageKind DUPLICATE_SUPER_INITIALIZER = const MessageKind( + "Cannot have more than one super initializer."); + + static const MessageKind INVALID_ARGUMENTS = const MessageKind( + "Arguments do not match the expected parameters of '#{methodName}'."); + + static const MessageKind NO_MATCHING_CONSTRUCTOR = const MessageKind( + "'super' call arguments and constructor parameters do not match."); + + static const MessageKind NO_MATCHING_CONSTRUCTOR_FOR_IMPLICIT = + const MessageKind( + "Implicit 'super' call arguments and constructor parameters " + "do not match."); + + static const MessageKind CONST_CALLS_NON_CONST = const MessageKind( + "'const' constructor cannot call a non-const constructor."); + + static const MessageKind CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS = + const MessageKind( + "Can't declare constructor 'const' on class #{className} " + "because the class contains non-final instance fields.", + howToFix: "Try making all fields final.", + examples: const [""" +class C { + // 'a' must be declared final to allow for the const constructor. + var a; + const C(this.a); +} + +main() => new C(0);"""]); + + static const MessageKind CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS_FIELD = + const MessageKind("This non-final field prevents using const " + "constructors."); + + static const MessageKind CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS_CONSTRUCTOR = + const MessageKind("This const constructor is not allowed due to " + "non-final fields."); + + + static const MessageKind INITIALIZING_FORMAL_NOT_ALLOWED = const MessageKind( + "Initializing formal parameter only allowed in generative " + "constructor."); + + static const MessageKind INVALID_PARAMETER = const MessageKind( + "Cannot resolve parameter."); + + static const MessageKind NOT_INSTANCE_FIELD = const MessageKind( + "'#{fieldName}' is not an instance field."); + + static const MessageKind NO_CATCH_NOR_FINALLY = const MessageKind( + "Expected 'catch' or 'finally'."); + + static const MessageKind EMPTY_CATCH_DECLARATION = const MessageKind( + "Expected an identifier in catch declaration."); + + static const MessageKind EXTRA_CATCH_DECLARATION = const MessageKind( + "Extra parameter in catch declaration."); + + static const MessageKind PARAMETER_WITH_TYPE_IN_CATCH = const MessageKind( + "Cannot use type annotations in catch."); + + static const MessageKind PARAMETER_WITH_MODIFIER_IN_CATCH = const MessageKind( + "Cannot use modifiers in catch."); + + static const MessageKind OPTIONAL_PARAMETER_IN_CATCH = const MessageKind( + "Cannot use optional parameters in catch."); + + static const MessageKind THROW_WITHOUT_EXPRESSION = const MessageKind( + "Cannot use re-throw outside of catch block " + "(expression expected after 'throw')."); + + static const MessageKind UNBOUND_LABEL = const MessageKind( + "Cannot resolve label '#{labelName}'."); + + static const MessageKind NO_BREAK_TARGET = const MessageKind( + "'break' statement not inside switch or loop."); + + static const MessageKind NO_CONTINUE_TARGET = const MessageKind( + "'continue' statement not inside loop."); + + static const MessageKind EXISTING_LABEL = const MessageKind( + "Original declaration of duplicate label '#{labelName}'."); + + static const MessageKind DUPLICATE_LABEL = const MessageKind( + "Duplicate declaration of label '#{labelName}'."); + + static const MessageKind UNUSED_LABEL = const MessageKind( + "Unused label '#{labelName}'."); + + static const MessageKind INVALID_CONTINUE = const MessageKind( + "Target of continue is not a loop or switch case."); + + static const MessageKind INVALID_BREAK = const MessageKind( + "Target of break is not a statement."); + + static const MessageKind DUPLICATE_TYPE_VARIABLE_NAME = const MessageKind( + "Type variable '#{typeVariableName}' already declared."); + + static const MessageKind TYPE_VARIABLE_WITHIN_STATIC_MEMBER = + const MessageKind( + "Cannot refer to type variable '#{typeVariableName}' " + "within a static member."); + + static const MessageKind TYPE_VARIABLE_IN_CONSTANT = const MessageKind( + "Constant expressions can't refer to type variables.", + howToFix: "Try removing the type variable or replacing it with a " + "concrete type.", + examples: const [""" +class C { + const C(); + + m(T t) => const C(); +} + +void main() => new C().m(null); +""" +]); + + + static const MessageKind INVALID_TYPE_VARIABLE_BOUND = const MessageKind( + "'#{typeArgument}' is not a subtype of bound '#{bound}' for " + "type variable '#{typeVariable}' of type '#{thisType}'.", + howToFix: "Try to change or remove the type argument.", + examples: const [""" +class C {} + +// 'String' is not a valid instantiation of T with bound num.'. +main() => new C(); +"""]); + + static const MessageKind INVALID_USE_OF_SUPER = const MessageKind( + "'super' not allowed here."); + + static const MessageKind INVALID_CASE_DEFAULT = const MessageKind( + "'default' only allowed on last case of a switch."); + + static const MessageKind SWITCH_CASE_TYPES_NOT_EQUAL = const MessageKind( + "'case' expressions do not all have type '#{type}'."); + + static const MessageKind SWITCH_CASE_TYPES_NOT_EQUAL_CASE = const MessageKind( + "'case' expression of type '#{type}'."); + + static const MessageKind SWITCH_CASE_FORBIDDEN = const MessageKind( + "'case' expression may not be of type '#{type}'."); + + static const MessageKind SWITCH_CASE_VALUE_OVERRIDES_EQUALS = + const MessageKind( + "'case' expression type '#{type}' overrides 'operator =='."); + + static const MessageKind INVALID_ARGUMENT_AFTER_NAMED = const MessageKind( + "Unnamed argument after named argument."); + + static const MessageKind NOT_A_COMPILE_TIME_CONSTANT = const MessageKind( + "Not a compile-time constant."); + + static const MessageKind DEFERRED_COMPILE_TIME_CONSTANT = const MessageKind( + "A Deferred value cannot be used as a compile-time constant."); + + static const MessageKind DEFERRED_COMPILE_TIME_CONSTANT_CONSTRUCTION = + const MessageKind("A deferred class cannot be used to create a" + "compile-time constant."); + + static const MessageKind CYCLIC_COMPILE_TIME_CONSTANTS = const MessageKind( + "Cycle in the compile-time constant computation."); + + static const MessageKind CONSTRUCTOR_IS_NOT_CONST = const MessageKind( + "Constructor is not a 'const' constructor."); + + static const MessageKind CONST_MAP_KEY_OVERRIDES_EQUALS = + const MessageKind( + "Const-map key type '#{type}' overrides 'operator =='."); + + static const MessageKind NO_SUCH_LIBRARY_MEMBER = const MessageKind( + "'#{libraryName}' has no member named '#{memberName}'."); + + static const MessageKind CANNOT_INSTANTIATE_TYPEDEF = const MessageKind( + "Cannot instantiate typedef '#{typedefName}'."); + + static const MessageKind REQUIRED_PARAMETER_WITH_DEFAULT = const MessageKind( + "Non-optional parameters can't have a default value.", + howToFix: + "Try removing the default value or making the parameter optional.", + examples: const [""" +main() { + foo(a: 1) => print(a); + foo(2); +}""", """ +main() { + foo(a = 1) => print(a); + foo(2); +}"""]); + + static const MessageKind NAMED_PARAMETER_WITH_EQUALS = const MessageKind( + "Named optional parameters can't use '=' to specify a default " + "value.", + howToFix: "Try replacing '=' with ':'.", + examples: const [""" +main() { + foo({a = 1}) => print(a); + foo(a: 2); +}"""]); + + static const MessageKind POSITIONAL_PARAMETER_WITH_EQUALS = const MessageKind( + "Positional optional parameters can't use ':' to specify a " + "default value.", + howToFix: "Try replacing ':' with '='.", + examples: const [""" +main() { + foo([a: 1]) => print(a); + foo(2); +}"""]); + + static const MessageKind TYPEDEF_FORMAL_WITH_DEFAULT = const MessageKind( + "A parameter of a typedef can't specify a default value.", + howToFix: + "Try removing the default value.", + examples: const [""" +typedef void F([int arg = 0]); + +main() { + F f; +}""", """ +typedef void F({int arg: 0}); + +main() { + F f; +}"""]); + + static const MessageKind FUNCTION_TYPE_FORMAL_WITH_DEFAULT = const MessageKind( + "A function type parameter can't specify a default value.", + howToFix: + "Try removing the default value.", + examples: const [""" +foo(f(int i, [a = 1])) {} + +main() { + foo(1, 2); +}""", """ +foo(f(int i, {a: 1})) {} + +main() { + foo(1, a: 2); +}"""]); + + static const MessageKind REDIRECTING_FACTORY_WITH_DEFAULT = const MessageKind( + "A parameter of a redirecting factory constructor can't specify a " + "default value.", + howToFix: + "Try removing the default value.", + examples: const [""" +class A { + A([a]); + factory A.foo([a = 1]) = A; +} + +main() { + new A.foo(1); +}""", """ +class A { + A({a}); + factory A.foo({a: 1}) = A; +} + +main() { + new A.foo(a: 1); +}"""]); + + static const MessageKind FORMAL_DECLARED_CONST = const MessageKind( + "A formal parameter can't be declared const.", + howToFix: "Try removing 'const'.", + examples: const [""" +foo(const x) {} +main() => foo(42); +""", """ +foo({const x}) {} +main() => foo(42); +""", """ +foo([const x]) {} +main() => foo(42); +"""]); + + static const MessageKind FORMAL_DECLARED_STATIC = const MessageKind( + "A formal parameter can't be declared static.", + howToFix: "Try removing 'static'.", + examples: const [""" +foo(static x) {} +main() => foo(42); +""", """ +foo({static x}) {} +main() => foo(42); +""", """ +foo([static x]) {} +main() => foo(42); +"""]); + + static const MessageKind FINAL_FUNCTION_TYPE_PARAMETER = const MessageKind( + "A function type parameter can't be declared final.", + howToFix: "Try removing 'final'.", + examples: const [""" +foo(final int x(int a)) {} +main() => foo((y) => 42); +""", """ +foo({final int x(int a)}) {} +main() => foo((y) => 42); +""", """ +foo([final int x(int a)]) {} +main() => foo((y) => 42); +"""]); + + static const MessageKind VAR_FUNCTION_TYPE_PARAMETER = const MessageKind( + "A function type parameter can't be declared with 'var'.", + howToFix: "Try removing 'var'.", + examples: const [""" +foo(var int x(int a)) {} +main() => foo((y) => 42); +""", """ +foo({var int x(int a)}) {} +main() => foo((y) => 42); +""", """ +foo([var int x(int a)]) {} +main() => foo((y) => 42); +"""]); + + static const MessageKind CANNOT_INSTANTIATE_TYPE_VARIABLE = const MessageKind( + "Cannot instantiate type variable '#{typeVariableName}'."); + + static const MessageKind CYCLIC_TYPE_VARIABLE = const MessageKind( + "Type variable '#{typeVariableName}' is a supertype of itself."); + + static const CYCLIC_TYPEDEF = const MessageKind( + "A typedef can't refer to itself.", + howToFix: "Try removing all references to '#{typedefName}' " + "in the definition of '#{typedefName}'.", + examples: const [""" +typedef F F(); // The return type 'F' is a self-reference. +main() { F f = null; }"""]); + + static const CYCLIC_TYPEDEF_ONE = const MessageKind( + "A typedef can't refer to itself through another typedef.", + howToFix: "Try removing all references to " + "'#{otherTypedefName}' in the definition of '#{typedefName}'.", + examples: const [""" +typedef G F(); // The return type 'G' is a self-reference through typedef 'G'. +typedef F G(); // The return type 'F' is a self-reference through typedef 'F'. +main() { F f = null; }""", +""" +typedef G F(); // The return type 'G' creates a self-reference. +typedef H G(); // The return type 'H' creates a self-reference. +typedef H(F f); // The argument type 'F' creates a self-reference. +main() { F f = null; }"""]); + + static const MessageKind CLASS_NAME_EXPECTED = const MessageKind( + "Class name expected."); + + static const MessageKind CANNOT_EXTEND = const MessageKind( + "'#{type}' cannot be extended."); + + static const MessageKind CANNOT_IMPLEMENT = const MessageKind( + "'#{type}' cannot be implemented."); + + static const MessageKind CANNOT_EXTEND_MALFORMED = const MessageKind( + "A class can't extend a malformed type.", + howToFix: "Try correcting the malformed type annotation or removing the " + "'extends' clause.", + examples: const [""" +class A extends Malformed {} +main() => new A();"""]); + + static const MessageKind CANNOT_IMPLEMENT_MALFORMED = const MessageKind( + "A class can't implement a malformed type.", + howToFix: "Try correcting the malformed type annotation or removing the " + "type from the 'implements' clause.", + examples: const [""" +class A implements Malformed {} +main() => new A();"""]); + + static const MessageKind CANNOT_MIXIN_MALFORMED = const MessageKind( + "A class can't mixin a malformed type.", + howToFix: "Try correcting the malformed type annotation or removing the " + "type from the 'with' clause.", + examples: const [""" +class A extends Object with Malformed {} +main() => new A();"""]); + + static const MessageKind CANNOT_MIXIN = const MessageKind( + "The type '#{type}' can't be mixed in.", + howToFix: "Try removing '#{type}' from the 'with' clause.", + examples: const [""" +class C extends Object with String {} + +main() => new C(); +""", """ +typedef C = Object with String; + +main() => new C(); +"""]); + + static const MessageKind DUPLICATE_EXTENDS_IMPLEMENTS = const MessageKind( + "'#{type}' can not be both extended and implemented."); + + static const MessageKind DUPLICATE_IMPLEMENTS = const MessageKind( + "'#{type}' must not occur more than once " + "in the implements clause."); + + static const MessageKind MULTI_INHERITANCE = const MessageKind( + "Dart2js does not currently support inheritance of the same class with " + "different type arguments: Both #{firstType} and #{secondType} are " + "supertypes of #{thisType}."); + + static const MessageKind ILLEGAL_SUPER_SEND = const MessageKind( + "'#{name}' cannot be called on super."); + + static const MessageKind NO_SUCH_SUPER_MEMBER = const MessageKind( + "Cannot resolve '#{memberName}' in a superclass of '#{className}'."); + + static const MessageKind ADDITIONAL_TYPE_ARGUMENT = const MessageKind( + "Additional type argument."); + + static const MessageKind MISSING_TYPE_ARGUMENT = const MessageKind( + "Missing type argument."); + + // TODO(johnniwinther): Use ADDITIONAL_TYPE_ARGUMENT or MISSING_TYPE_ARGUMENT + // instead. + static const MessageKind TYPE_ARGUMENT_COUNT_MISMATCH = const MessageKind( + "Incorrect number of type arguments on '#{type}'."); + + static const MessageKind GETTER_MISMATCH = const MessageKind( + "Setter disagrees on: '#{modifiers}'."); + + static const MessageKind SETTER_MISMATCH = const MessageKind( + "Getter disagrees on: '#{modifiers}'."); + + static const MessageKind ILLEGAL_SETTER_FORMALS = const MessageKind( + "A setter must have exactly one argument."); + + static const MessageKind NO_STATIC_OVERRIDE = const MessageKind( + "Static member cannot override instance member '#{memberName}' of " + "'#{className}'."); + + static const MessageKind NO_STATIC_OVERRIDE_CONT = const MessageKind( + "This is the instance member that cannot be overridden " + "by a static member."); + + static const MessageKind INSTANCE_STATIC_SAME_NAME = const MessageKind( + "Instance member '#{memberName}' and static member of " + "superclass '#{className}' have the same name."); + + static const MessageKind INSTANCE_STATIC_SAME_NAME_CONT = const MessageKind( + "This is the static member with the same name."); + + static const MessageKind INVALID_OVERRIDE_METHOD = const MessageKind( + "The type '#{declaredType}' of method '#{name}' declared in " + "'#{class}' is not a subtype of the overridden method type " + "'#{inheritedType}' inherited from '#{inheritedClass}'."); + + static const MessageKind INVALID_OVERRIDDEN_METHOD = const MessageKind( + "This is the overridden method '#{name}' declared in class " + "'#{class}'."); + + static const MessageKind INVALID_OVERRIDE_GETTER = const MessageKind( + "The type '#{declaredType}' of getter '#{name}' declared in " + "'#{class}' is not assignable to the type '#{inheritedType}' of the " + "overridden getter inherited from '#{inheritedClass}'."); + + static const MessageKind INVALID_OVERRIDDEN_GETTER = const MessageKind( + "This is the overridden getter '#{name}' declared in class " + "'#{class}'."); + + static const MessageKind INVALID_OVERRIDE_GETTER_WITH_FIELD = + const MessageKind( + "The type '#{declaredType}' of field '#{name}' declared in " + "'#{class}' is not assignable to the type '#{inheritedType}' of the " + "overridden getter inherited from '#{inheritedClass}'."); + + static const MessageKind INVALID_OVERRIDE_FIELD_WITH_GETTER = + const MessageKind( + "The type '#{declaredType}' of getter '#{name}' declared in " + "'#{class}' is not assignable to the type '#{inheritedType}' of the " + "overridden field inherited from '#{inheritedClass}'."); + + static const MessageKind INVALID_OVERRIDE_SETTER = const MessageKind( + "The type '#{declaredType}' of setter '#{name}' declared in " + "'#{class}' is not assignable to the type '#{inheritedType}' of the " + "overridden setter inherited from '#{inheritedClass}'."); + + static const MessageKind INVALID_OVERRIDDEN_SETTER = const MessageKind( + "This is the overridden setter '#{name}' declared in class " + "'#{class}'."); + + static const MessageKind INVALID_OVERRIDE_SETTER_WITH_FIELD = + const MessageKind( + "The type '#{declaredType}' of field '#{name}' declared in " + "'#{class}' is not assignable to the type '#{inheritedType}' of the " + "overridden setter inherited from '#{inheritedClass}'."); + + static const MessageKind INVALID_OVERRIDE_FIELD_WITH_SETTER = + const MessageKind( + "The type '#{declaredType}' of setter '#{name}' declared in " + "'#{class}' is not assignable to the type '#{inheritedType}' of the " + "overridden field inherited from '#{inheritedClass}'."); + + static const MessageKind INVALID_OVERRIDE_FIELD = const MessageKind( + "The type '#{declaredType}' of field '#{name}' declared in " + "'#{class}' is not assignable to the type '#{inheritedType}' of the " + "overridden field inherited from '#{inheritedClass}'."); + + static const MessageKind INVALID_OVERRIDDEN_FIELD = const MessageKind( + "This is the overridden field '#{name}' declared in class " + "'#{class}'."); + + static const MessageKind CANNOT_OVERRIDE_FIELD_WITH_METHOD = + const MessageKind( + "Method '#{name}' in '#{class}' can't override field from " + "'#{inheritedClass}'."); + + static const MessageKind CANNOT_OVERRIDE_FIELD_WITH_METHOD_CONT = + const MessageKind( + "This is the field that cannot be overridden by a method."); + + static const MessageKind CANNOT_OVERRIDE_METHOD_WITH_FIELD = + const MessageKind( + "Field '#{name}' in '#{class}' can't override method from " + "'#{inheritedClass}'."); + + static const MessageKind CANNOT_OVERRIDE_METHOD_WITH_FIELD_CONT = + const MessageKind( + "This is the method that cannot be overridden by a field."); + + static const MessageKind CANNOT_OVERRIDE_GETTER_WITH_METHOD = + const MessageKind( + "Method '#{name}' in '#{class}' can't override getter from " + "'#{inheritedClass}'."); + + static const MessageKind CANNOT_OVERRIDE_GETTER_WITH_METHOD_CONT = + const MessageKind( + "This is the getter that cannot be overridden by a method."); + + static const MessageKind CANNOT_OVERRIDE_METHOD_WITH_GETTER = + const MessageKind( + "Getter '#{name}' in '#{class}' can't override method from " + "'#{inheritedClass}'."); + + static const MessageKind CANNOT_OVERRIDE_METHOD_WITH_GETTER_CONT = + const MessageKind( + "This is the method that cannot be overridden by a getter."); + + static const MessageKind MISSING_FORMALS = const MessageKind( + "Formal parameters are missing."); + + static const MessageKind EXTRA_FORMALS = const MessageKind( + "Formal parameters are not allowed here."); + + static const MessageKind UNARY_OPERATOR_BAD_ARITY = const MessageKind( + "Operator '#{operatorName}' must have no parameters."); + + static const MessageKind MINUS_OPERATOR_BAD_ARITY = const MessageKind( + "Operator '-' must have 0 or 1 parameters."); + + static const MessageKind BINARY_OPERATOR_BAD_ARITY = const MessageKind( + "Operator '#{operatorName}' must have exactly 1 parameter."); + + static const MessageKind TERNARY_OPERATOR_BAD_ARITY = const MessageKind( + "Operator '#{operatorName}' must have exactly 2 parameters."); + + static const MessageKind OPERATOR_OPTIONAL_PARAMETERS = const MessageKind( + "Operator '#{operatorName}' cannot have optional parameters."); + + static const MessageKind OPERATOR_NAMED_PARAMETERS = const MessageKind( + "Operator '#{operatorName}' cannot have named parameters."); + + static const MessageKind CONSTRUCTOR_WITH_RETURN_TYPE = const MessageKind( + "Cannot have return type for constructor."); + + static const MessageKind CANNOT_RETURN_FROM_CONSTRUCTOR = const MessageKind( + "Constructors can't return values.", + howToFix: "Remove the return statement or use a factory constructor.", + examples: const [""" +class C { + C() { + return 1; + } +} + +main() => new C();"""]); + + static const MessageKind ILLEGAL_FINAL_METHOD_MODIFIER = const MessageKind( + "Cannot have final modifier on method."); + + static const MessageKind ILLEGAL_CONSTRUCTOR_MODIFIERS = const MessageKind( + "Illegal constructor modifiers: '#{modifiers}'."); + + static const MessageKind ILLEGAL_MIXIN_APPLICATION_MODIFIERS = + const MessageKind( + "Illegal mixin application modifiers: '#{modifiers}'."); + + static const MessageKind ILLEGAL_MIXIN_SUPERCLASS = const MessageKind( + "Class used as mixin must have Object as superclass."); + + static const MessageKind ILLEGAL_MIXIN_OBJECT = const MessageKind( + "Cannot use Object as mixin."); + + static const MessageKind ILLEGAL_MIXIN_CONSTRUCTOR = const MessageKind( + "Class used as mixin cannot have non-factory constructor."); + + static const MessageKind ILLEGAL_MIXIN_CYCLE = const MessageKind( + "Class used as mixin introduces mixin cycle: " + "'#{mixinName1}' <-> '#{mixinName2}'."); + + static const MessageKind ILLEGAL_MIXIN_WITH_SUPER = const MessageKind( + "Cannot use class '#{className}' as a mixin because it uses " + "'super'."); + + static const MessageKind ILLEGAL_MIXIN_SUPER_USE = const MessageKind( + "Use of 'super' in class used as mixin."); + + static const MessageKind PARAMETER_NAME_EXPECTED = const MessageKind( + "parameter name expected."); + + static const MessageKind CANNOT_RESOLVE_GETTER = const MessageKind( + "Cannot resolve getter."); + + static const MessageKind CANNOT_RESOLVE_SETTER = const MessageKind( + "Cannot resolve setter."); + + static const MessageKind ASSIGNING_METHOD = const MessageKind( + "Cannot assign a value to a method."); + + static const MessageKind ASSIGNING_TYPE = const MessageKind( + "Cannot assign a value to a type."); + + static const MessageKind VOID_NOT_ALLOWED = const MessageKind( + "Type 'void' can't be used here because it isn't a return type.", + howToFix: "Try removing 'void' keyword or replace it with 'var', 'final'," + " or a type.", + examples: const [ + "void x; main() {}", + "foo(void x) {} main() { foo(null); }", + ]); + + static const MessageKind NULL_NOT_ALLOWED = const MessageKind( + "`null` can't be used here."); + + static const MessageKind BEFORE_TOP_LEVEL = const MessageKind( + "Part header must come before top-level definitions."); + + static const MessageKind LIBRARY_NAME_MISMATCH = const MessageKind( + "Expected part of library name '#{libraryName}'.", + howToFix: "Trying changing the directive to 'part of #{libraryName};'.", + examples: const [const { +'main.dart': """ +library lib.foo; + +part 'part.dart'; + +main() {} +""", + +'part.dart': """ +part of lib.bar; +"""}]); + + static const MessageKind MISSING_LIBRARY_NAME = const MessageKind( + "Library has no name. Part directive expected library name " + "to be '#{libraryName}'.", + howToFix: "Trying adding 'library #{libraryName};' to the library.", + examples: const [const { +'main.dart': """ +part 'part.dart'; + +main() {} +""", + +'part.dart': """ +part of lib.foo; +"""}]); + + static const MessageKind THIS_IS_THE_PART_OF_TAG = const MessageKind( + "This is the part of directive."); + + static const MessageKind MISSING_PART_OF_TAG = const MessageKind( + "This file has no part-of tag, but it is being used as a part."); + + static const MessageKind DUPLICATED_PART_OF = const MessageKind( + "Duplicated part-of directive."); + + static const MessageKind ILLEGAL_DIRECTIVE = const MessageKind( + "Directive not allowed here."); + + static const MessageKind DUPLICATED_LIBRARY_NAME = const MessageKind( + "Duplicated library name '#{libraryName}'."); + + static const MessageKind DUPLICATED_RESOURCE = const MessageKind( + "The resource '#{resourceUri}' is loaded through both " + "'#{canonicalUri1}' and '#{canonicalUri2}'."); + + static const MessageKind DUPLICATED_LIBRARY_RESOURCE = + const MessageKind( + "The library '#{libraryName}' in '#{resourceUri}' is loaded through " + "both '#{canonicalUri1}' and '#{canonicalUri2}'."); + + // This is used as an exception. + static const MessageKind INVALID_SOURCE_FILE_LOCATION = const MessageKind(''' +Invalid offset (#{offset}) in source map. +File: #{fileName} +Length: #{length}'''); + + static const MessageKind TOP_LEVEL_VARIABLE_DECLARED_STATIC = + const MessageKind( + "Top-level variable cannot be declared static."); + + static const MessageKind REFERENCE_IN_INITIALIZATION = const MessageKind( + "Variable '#{variableName}' is referenced during its " + "initialization.", + howToFix: "If you are trying to reference a shadowed variable, rename" + " one of the variables.", + examples: const [""" +foo(t) { + var t = t; + return t; +} + +main() => foo(1); +"""]); + + static const MessageKind CONST_WITHOUT_INITIALIZER = const MessageKind( + "A constant variable must be initialized.", + howToFix: "Try adding an initializer or " + "removing the 'const' modifier.", + examples: const [""" +void main() { + const c; // This constant variable must be initialized. +}"""]); + + static const MessageKind FINAL_WITHOUT_INITIALIZER = const MessageKind( + "A final variable must be initialized.", + howToFix: "Try adding an initializer or " + "removing the 'final' modifier.", + examples: const [ + "class C { static final field; } main() => C.field;"]); + + static const MessageKind MEMBER_USES_CLASS_NAME = const MessageKind( + "Member variable can't have the same name as the class it is " + "declared in.", + howToFix: "Try renaming the variable.", + examples: const [""" +class A { var A; } +main() { + var a = new A(); + a.A = 1; +} +""", """ +class A { static var A; } +main() => A.A = 1; +"""]); + + static const MessageKind WRONG_NUMBER_OF_ARGUMENTS_FOR_ASSERT = + const MessageKind( + "Wrong number of arguments to assert. Should be 1, but given " + "#{argumentCount}."); + + static const MessageKind ASSERT_IS_GIVEN_NAMED_ARGUMENTS = const MessageKind( + "'assert' takes no named arguments, but given #{argumentCount}."); + + static const MessageKind FACTORY_REDIRECTION_IN_NON_FACTORY = + const MessageKind( + "Factory redirection only allowed in factories."); + + static const MessageKind MISSING_FACTORY_KEYWORD = const MessageKind( + "Did you forget a factory keyword here?"); + + static const MessageKind DEFERRED_LIBRARY_DART_2_DART = + const MessageKind( + "Deferred loading is not supported by the dart backend yet. " + "Will not split the output."); + + static const MessageKind DEFERRED_LIBRARY_WITHOUT_PREFIX = + const MessageKind( + "This import is deferred but there is no prefix keyword.", + howToFix: + "Try adding a prefix to the import."); + + static const MessageKind DEFERRED_LIBRARY_DUPLICATE_PREFIX = + const MessageKind( + "The prefix of this deferred import is not unique.", + howToFix: + "Try changing the import prefix."); + + static const MessageKind DEFERRED_TYPE_ANNOTATION = + const MessageKind( + "The type #{node} is deferred. " + "Deferred types are not valid as type annotations.", + howToFix: + "Try using a non-deferred abstract class as an interface."); + + static const MessageKind ILLEGAL_STATIC = const MessageKind( + "Modifier static is only allowed on functions declared in " + "a class."); + + static const MessageKind STATIC_FUNCTION_BLOAT = const MessageKind( + "Using '#{class}.#{name}' may lead to unnecessarily large " + "generated code.", + howToFix: + "Try adding '@MirrorsUsed(...)' as described at " + "https://goo.gl/Akrrog."); + + static const MessageKind NON_CONST_BLOAT = const MessageKind( + "Using 'new #{name}' may lead to unnecessarily large generated " + "code.", + howToFix: + "Try using 'const #{name}' or adding '@MirrorsUsed(...)' as " + "described at https://goo.gl/Akrrog."); + + static const MessageKind STRING_EXPECTED = const MessageKind( + "Expected a 'String', but got an instance of '#{type}'."); + + static const MessageKind PRIVATE_IDENTIFIER = const MessageKind( + "'#{value}' is not a valid Symbol name because it starts with " + "'_'."); + + static const MessageKind PRIVATE_NAMED_PARAMETER = const MessageKind( + "Named optional parameter can't have a library private name.", + howToFix: "Try removing the '_' or making the parameter positional or " + "required.", + examples: const ["""foo({int _p}) {} main() => foo();"""] + ); + + static const MessageKind UNSUPPORTED_LITERAL_SYMBOL = const MessageKind( + "Symbol literal '##{value}' is currently unsupported by dart2js."); + + static const MessageKind INVALID_SYMBOL = const MessageKind(''' +'#{value}' is not a valid Symbol name because is not: + * an empty String, + * a user defined operator, + * a qualified non-private identifier optionally followed by '=', or + * a qualified non-private identifier followed by '.' and a user-defined ''' +"operator."); + + static const MessageKind AMBIGUOUS_REEXPORT = const MessageKind( + "'#{name}' is (re)exported by multiple libraries."); + + static const MessageKind AMBIGUOUS_LOCATION = const MessageKind( + "'#{name}' is defined here."); + + static const MessageKind IMPORTED_HERE = const MessageKind( + "'#{name}' is imported here."); + + static const MessageKind OVERRIDE_EQUALS_NOT_HASH_CODE = const MessageKind( + "The class '#{class}' overrides 'operator==', " + "but not 'get hashCode'."); + + static const MessageKind PACKAGE_ROOT_NOT_SET = const MessageKind( + "Cannot resolve '#{uri}'. Package root has not been set."); + + static const MessageKind INTERNAL_LIBRARY_FROM = const MessageKind( + "Internal library '#{resolvedUri}' is not accessible from " + "'#{importingUri}'."); + + static const MessageKind INTERNAL_LIBRARY = const MessageKind( + "Internal library '#{resolvedUri}' is not accessible."); + + static const MessageKind LIBRARY_NOT_FOUND = const MessageKind( + "Library not found '#{resolvedUri}'."); + + static const MessageKind UNSUPPORTED_EQ_EQ_EQ = const MessageKind( + "'===' is not an operator. " + "Did you mean '#{lhs} == #{rhs}' or 'identical(#{lhs}, #{rhs})'?"); + + static const MessageKind UNSUPPORTED_BANG_EQ_EQ = const MessageKind( + "'!==' is not an operator. " + "Did you mean '#{lhs} != #{rhs}' or '!identical(#{lhs}, #{rhs})'?"); + + static const MessageKind UNSUPPORTED_PREFIX_PLUS = const MessageKind( + "'+' is not a prefix operator. ", + howToFix: "Try removing '+'.", + examples: const [ + "main() => +2; // No longer a valid way to write '2'" + ]); + + static const MessageKind UNSUPPORTED_THROW_WITHOUT_EXP = const MessageKind( + "No expression after 'throw'. " + "Did you mean 'rethrow'?"); + + static const MessageKind DEPRECATED_TYPEDEF_MIXIN_SYNTAX = const MessageKind( + "'typedef' not allowed here. ", + howToFix: "Try replacing 'typedef' with 'class'.", + examples: const [ + """ +class B { } +class M1 { } +typedef C = B with M1; // Need to replace 'typedef' with 'class'. +main() { new C(); } +"""] +); + + static const MessageKind MIRRORS_EXPECTED_STRING = const MessageKind( + "Can't use '#{name}' here because it's an instance of '#{type}' " + "and a 'String' value is expected.", + howToFix: "Did you forget to add quotes?", + examples: const [ + """ +// 'Foo' is a type literal, not a string. +@MirrorsUsed(symbols: const [Foo]) +import 'dart:mirrors'; + +class Foo {} + +main() {} +"""]); + + static const MessageKind MIRRORS_EXPECTED_STRING_OR_TYPE = const MessageKind( + "Can't use '#{name}' here because it's an instance of '#{type}' " + "and a 'String' or 'Type' value is expected.", + howToFix: "Did you forget to add quotes?", + examples: const [ + """ +// 'main' is a method, not a class. +@MirrorsUsed(targets: const [main]) +import 'dart:mirrors'; + +main() {} +"""]); + + static const MessageKind MIRRORS_EXPECTED_STRING_OR_LIST = const MessageKind( + "Can't use '#{name}' here because it's an instance of '#{type}' " + "and a 'String' or 'List' value is expected.", + howToFix: "Did you forget to add quotes?", + examples: const [ + """ +// 'Foo' is not a string. +@MirrorsUsed(symbols: Foo) +import 'dart:mirrors'; + +class Foo {} + +main() {} +"""]); + + static const MessageKind MIRRORS_EXPECTED_STRING_TYPE_OR_LIST = + const MessageKind( + "Can't use '#{name}' here because it's an instance of '#{type}' " + "but a 'String', 'Type', or 'List' value is expected.", + howToFix: "Did you forget to add quotes?", + examples: const [ + """ +// '1' is not a string. +@MirrorsUsed(targets: 1) +import 'dart:mirrors'; + +main() {} +"""]); + + static const MessageKind MIRRORS_CANNOT_RESOLVE_IN_CURRENT_LIBRARY = + const MessageKind( + "Can't find '#{name}' in the current library.", + // TODO(ahe): The closest identifiers in edit distance would be nice. + howToFix: "Did you forget to add an import?", + examples: const [ + """ +// 'window' is not in scope because dart:html isn't imported. +@MirrorsUsed(targets: 'window') +import 'dart:mirrors'; + +main() {} +"""]); + + static const MessageKind MIRRORS_CANNOT_RESOLVE_IN_LIBRARY = + const MessageKind( + "Can't find '#{name}' in the library '#{library}'.", + // TODO(ahe): The closest identifiers in edit distance would be nice. + howToFix: "Is '#{name}' spelled right?", + examples: const [ + """ +// 'List' is misspelled. +@MirrorsUsed(targets: 'dart.core.Lsit') +import 'dart:mirrors'; + +main() {} +"""]); + + static const MessageKind MIRRORS_CANNOT_FIND_IN_ELEMENT = + const MessageKind( + "Can't find '#{name}' in '#{element}'.", + // TODO(ahe): The closest identifiers in edit distance would be nice. + howToFix: "Is '#{name}' spelled right?", + examples: const [ + """ +// 'addAll' is misspelled. +@MirrorsUsed(targets: 'dart.core.List.addAl') +import 'dart:mirrors'; + +main() {} +"""]); + + static const MessageKind READ_SCRIPT_ERROR = const MessageKind( + "Can't read '#{uri}' (#{exception}).", + // Don't know how to fix since the underlying error is unknown. + howToFix: DONT_KNOW_HOW_TO_FIX, + examples: const [ + """ +// 'foo.dart' does not exist. +import 'foo.dart'; + +main() {} +"""]); + + static const MessageKind EXTRANEOUS_MODIFIER = const MessageKind( + "Can't have modifier '#{modifier}' here.", + howToFix: "Try removing '#{modifier}'.", + examples: const [ + "var String foo; main(){}", + // "var get foo; main(){}", + "var set foo; main(){}", + "var final foo; main(){}", + "var var foo; main(){}", + "var const foo; main(){}", + "var abstract foo; main(){}", + "var static foo; main(){}", + "var external foo; main(){}", + "get var foo; main(){}", + "set var foo; main(){}", + "final var foo; main(){}", + "var var foo; main(){}", + "const var foo; main(){}", + "abstract var foo; main(){}", + "static var foo; main(){}", + "external var foo; main(){}"]); + + static const MessageKind EXTRANEOUS_MODIFIER_REPLACE = const MessageKind( + "Can't have modifier '#{modifier}' here.", + howToFix: "Try replacing modifier '#{modifier}' with 'var', 'final'," + " or a type.", + examples: const [ + // "get foo; main(){}", + "set foo; main(){}", + "abstract foo; main(){}", + "static foo; main(){}", + "external foo; main(){}"]); + + static const MessageKind ABSTRACT_CLASS_INSTANTIATION = const MessageKind( + "Can't instantiate abstract class.", + howToFix: DONT_KNOW_HOW_TO_FIX, + examples: const ["abstract class A {} main() { new A(); }"]); + + static const MessageKind BODY_EXPECTED = const MessageKind( + "Expected a function body or '=>'.", + // TODO(ahe): In some scenarios, we can suggest removing the 'static' + // keyword. + howToFix: "Try adding {}.", + examples: const [ + "main();"]); + + static const MessageKind MIRROR_BLOAT = const MessageKind( + "#{count} methods retained for use by dart:mirrors out of #{total}" + " total methods (#{percentage}%)."); + + static const MessageKind MIRROR_IMPORT = const MessageKind( + "Import of 'dart:mirrors'."); + + static const MessageKind MIRROR_IMPORT_NO_USAGE = const MessageKind( + "This import is not annotated with @MirrorsUsed, which may lead to " + "unnecessarily large generated code.", + howToFix: + "Try adding '@MirrorsUsed(...)' as described at " + "https://goo.gl/Akrrog."); + + static const MessageKind WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT = + const MessageKind( + "Argument for 'JS_INTERCEPTOR_CONSTANT' must be a type constant."); + + static const MessageKind EXPECTED_IDENTIFIER_NOT_RESERVED_WORD = + const MessageKind( + "'#{keyword}' is a reserved word and can't be used here.", + howToFix: "Try using a different name.", + examples: const ["do() {} main() {}"]); + + static const MessageKind UNUSED_METHOD = const MessageKind( + "The method '#{method_name}' is never called.", + howToFix: "Consider deleting it.", + examples: const ["deadCode() {} main() {}"]); + + static const MessageKind ABSTRACT_METHOD = const MessageKind( + "The method '#{name}' has no implementation in " + "class '#{class}'.", + howToFix: "Try adding a body to '#{name}' or declaring " + "'#{class}' to be 'abstract'.", + examples: const [""" +class Class { + method(); +} +main() => new Class(); +"""]); + + static const MessageKind ABSTRACT_GETTER = const MessageKind( + "The getter '#{name}' has no implementation in " + "class '#{class}'.", + howToFix: "Try adding a body to '#{name}' or declaring " + "'#{class}' to be 'abstract'.", + examples: const [""" +class Class { + get getter; +} +main() => new Class(); +"""]); + + static const MessageKind ABSTRACT_SETTER = const MessageKind( + "The setter '#{name}' has no implementation in " + "class '#{class}'.", + howToFix: "Try adding a body to '#{name}' or declaring " + "'#{class}' to be 'abstract'.", + examples: const [""" +class Class { + set setter(_); +} +main() => new Class(); +"""]); + + static const MessageKind INHERIT_GETTER_AND_METHOD = const MessageKind( + "The class '#{class}' can't inherit both getters and methods " + "by the named '#{name}'.", + howToFix: DONT_KNOW_HOW_TO_FIX, + examples: const [""" +class A { + get member => null; +} +class B { + member() {} +} +class Class implements A, B { +} +main() => new Class(); +"""]); + + static const MessageKind INHERITED_METHOD = const MessageKind( + "The inherited method '#{name}' is declared here in class " + "'#{class}'."); + + static const MessageKind INHERITED_EXPLICIT_GETTER = const MessageKind( + "The inherited getter '#{name}' is declared here in class " + "'#{class}'."); + + static const MessageKind INHERITED_IMPLICIT_GETTER = const MessageKind( + "The inherited getter '#{name}' is implicitly declared by this " + "field in class '#{class}'."); + + static const MessageKind UNIMPLEMENTED_METHOD_ONE = const MessageKind( + "'#{class}' doesn't implement '#{method}' " + "declared in '#{declarer}'.", + howToFix: "Try adding an implementation of '#{name}' or declaring " + "'#{class}' to be 'abstract'.", + examples: const [""" +abstract class I { + m(); +} +class C implements I {} +main() => new C(); +""", """ +abstract class I { + m(); +} +class C extends I {} +main() => new C(); +"""]); + + static const MessageKind UNIMPLEMENTED_METHOD = const MessageKind( + "'#{class}' doesn't implement '#{method}'.", + howToFix: "Try adding an implementation of '#{name}' or declaring " + "'#{class}' to be 'abstract'.", + examples: const [""" +abstract class I { + m(); +} + +abstract class J { + m(); +} + +class C implements I, J {} + +main() { + new C(); +} +""", """ +abstract class I { + m(); +} + +abstract class J { + m(); +} + +class C extends I implements J {} + +main() { + new C(); +} +"""]); + + static const MessageKind UNIMPLEMENTED_METHOD_CONT = const MessageKind( + "The method '#{name}' is declared here in class '#{class}'."); + + static const MessageKind UNIMPLEMENTED_SETTER_ONE = const MessageKind( + "'#{class}' doesn't implement the setter '#{name}' " + "declared in '#{declarer}'.", + howToFix: "Try adding an implementation of '#{name}' or declaring " + "'#{class}' to be 'abstract'.", + examples: const [""" +abstract class I { + set m(_); +} +class C implements I {} +class D implements I { + set m(_) {} +} +main() { + new D().m = 0; + new C(); +} +"""]); + + static const MessageKind UNIMPLEMENTED_SETTER = const MessageKind( + "'#{class}' doesn't implement the setter '#{name}'.", + howToFix: "Try adding an implementation of '#{name}' or declaring " + "'#{class}' to be 'abstract'.", + examples: const [""" +abstract class I { + set m(_); +} +abstract class J { + set m(_); +} +class C implements I, J {} +main() => new C(); +""", """ +abstract class I { + set m(_); +} +abstract class J { + set m(_); +} +class C extends I implements J {} +main() => new C(); +"""]); + + static const MessageKind UNIMPLEMENTED_EXPLICIT_SETTER = const MessageKind( + "The setter '#{name}' is declared here in class '#{class}'."); + + static const MessageKind UNIMPLEMENTED_IMPLICIT_SETTER = const MessageKind( + "The setter '#{name}' is implicitly declared by this field " + "in class '#{class}'."); + + static const MessageKind UNIMPLEMENTED_GETTER_ONE = const MessageKind( + "'#{class}' doesn't implement the getter '#{name}' " + "declared in '#{declarer}'.", + howToFix: "Try adding an implementation of '#{name}' or declaring " + "'#{class}' to be 'abstract'.", + examples: const [""" +abstract class I { + get m; +} +class C implements I {} +main() => new C(); +""", """ +abstract class I { + get m; +} +class C extends I {} +main() => new C(); +"""]); + + static const MessageKind UNIMPLEMENTED_GETTER = const MessageKind( + "'#{class}' doesn't implement the getter '#{name}'.", + howToFix: "Try adding an implementation of '#{name}' or declaring " + "'#{class}' to be 'abstract'.", + examples: const [""" +abstract class I { + get m; +} +abstract class J { + get m; +} +class C implements I, J {} +main() => new C(); +""", """ +abstract class I { + get m; +} +abstract class J { + get m; +} +class C extends I implements J {} +main() => new C(); +"""]); + + static const MessageKind UNIMPLEMENTED_EXPLICIT_GETTER = const MessageKind( + "The getter '#{name}' is declared here in class '#{class}'."); + + static const MessageKind UNIMPLEMENTED_IMPLICIT_GETTER = const MessageKind( + "The getter '#{name}' is implicitly declared by this field " + "in class '#{class}'."); + + static const MessageKind EQUAL_MAP_ENTRY_KEY = const MessageKind( + "An entry with the same key already exists in the map.", + howToFix: "Try removing the previous entry or changing the key in one " + "of the entries.", + examples: const [""" +main() { + var m = const {'foo': 1, 'foo': 2}; +}"""]); + + static const MessageKind COMPILER_CRASHED = const MessageKind( + "The compiler crashed when compiling this element."); + + static const MessageKind PLEASE_REPORT_THE_CRASH = const MessageKind(''' +The compiler is broken. + +When compiling the above element, the compiler crashed. It is not +possible to tell if this is caused by a problem in your program or +not. Regardless, the compiler should not crash. + +The Dart team would greatly appreciate if you would take a moment to +report this problem at http://dartbug.com/new. + +Please include the following information: + +* the name and version of your operating system, + +* the Dart SDK build number (#{buildId}), and + +* the entire message you see here (including the full stack trace + below as well as the source location above). +'''); + + static const MessageKind POTENTIAL_MUTATION = const MessageKind( + "Variable '#{variableName}' is not known to be of type " + "'#{shownType}' because it is potentially mutated in the scope for " + "promotion."); + + static const MessageKind POTENTIAL_MUTATION_HERE = const MessageKind( + "Variable '#{variableName}' is potentially mutated here."); + + static const MessageKind POTENTIAL_MUTATION_IN_CLOSURE = const MessageKind( + "Variable '#{variableName}' is not known to be of type " + "'#{shownType}' because it is potentially mutated within a closure."); + + static const MessageKind POTENTIAL_MUTATION_IN_CLOSURE_HERE = + const MessageKind( + "Variable '#{variableName}' is potentially mutated in a " + "closure here."); + + static const MessageKind ACCESSED_IN_CLOSURE = const MessageKind( + "Variable '#{variableName}' is not known to be of type " + "'#{shownType}' because it is accessed by a closure in the scope for " + "promotion and potentially mutated in the scope of '#{variableName}'."); + + static const MessageKind ACCESSED_IN_CLOSURE_HERE = const MessageKind( + "Variable '#{variableName}' is accessed in a closure here."); + + static const MessageKind NOT_MORE_SPECIFIC = const MessageKind( + "Variable '#{variableName}' is not shown to have type " + "'#{shownType}' because '#{shownType}' is not more specific than the " + "known type '#{knownType}' of '#{variableName}'."); + + static const MessageKind NOT_MORE_SPECIFIC_SUBTYPE = const MessageKind( + "Variable '#{variableName}' is not shown to have type " + "'#{shownType}' because '#{shownType}' is not a subtype of the " + "known type '#{knownType}' of '#{variableName}'."); + + static const MessageKind NOT_MORE_SPECIFIC_SUGGESTION = const MessageKind( + "Variable '#{variableName}' is not shown to have type " + "'#{shownType}' because '#{shownType}' is not more specific than the " + "known type '#{knownType}' of '#{variableName}'.", + howToFix: "Try replacing '#{shownType}' with '#{shownTypeSuggestion}'."); + + static const MessageKind HIDDEN_WARNINGS_HINTS = const MessageKind( + "#{warnings} warning(s) and #{hints} hint(s) suppressed in #{uri}."); + + static const MessageKind HIDDEN_WARNINGS = const MessageKind( + "#{warnings} warning(s) suppressed in #{uri}."); + + static const MessageKind HIDDEN_HINTS = const MessageKind( + "#{hints} hint(s) suppressed in #{uri}."); + + ////////////////////////////////////////////////////////////////////////////// + // Patch errors start. + ////////////////////////////////////////////////////////////////////////////// + + static const MessageKind PATCH_RETURN_TYPE_MISMATCH = const MessageKind( + "Patch return type '#{patchReturnType}' does not match " + "'#{originReturnType}' on origin method '#{methodName}'."); + + static const MessageKind PATCH_REQUIRED_PARAMETER_COUNT_MISMATCH = + const MessageKind( + "Required parameter count of patch method " + "(#{patchParameterCount}) does not match parameter count on origin " + "method '#{methodName}' (#{originParameterCount})."); + + static const MessageKind PATCH_OPTIONAL_PARAMETER_COUNT_MISMATCH = + const MessageKind( + "Optional parameter count of patch method " + "(#{patchParameterCount}) does not match parameter count on origin " + "method '#{methodName}' (#{originParameterCount})."); + + static const MessageKind PATCH_OPTIONAL_PARAMETER_NAMED_MISMATCH = + const MessageKind( + "Optional parameters of origin and patch method " + "'#{methodName}' must both be either named or positional."); + + static const MessageKind PATCH_PARAMETER_MISMATCH = const MessageKind( + "Patch method parameter '#{patchParameter}' does not match " + "'#{originParameter}' on origin method '#{methodName}'."); + + static const MessageKind PATCH_PARAMETER_TYPE_MISMATCH = const MessageKind( + "Patch method parameter '#{parameterName}' type " + "'#{patchParameterType}' does not match '#{originParameterType}' on " + "origin method '#{methodName}'."); + + static const MessageKind PATCH_EXTERNAL_WITHOUT_IMPLEMENTATION = + const MessageKind("External method without an implementation."); + + static const MessageKind PATCH_POINT_TO_FUNCTION = const MessageKind( + "This is the function patch '#{functionName}'."); + + static const MessageKind PATCH_POINT_TO_CLASS = const MessageKind( + "This is the class patch '#{className}'."); + + static const MessageKind PATCH_POINT_TO_GETTER = const MessageKind( + "This is the getter patch '#{getterName}'."); + + static const MessageKind PATCH_POINT_TO_SETTER = const MessageKind( + "This is the setter patch '#{setterName}'."); + + static const MessageKind PATCH_POINT_TO_CONSTRUCTOR = const MessageKind( + "This is the constructor patch '#{constructorName}'."); + + static const MessageKind PATCH_POINT_TO_PARAMETER = const MessageKind( + "This is the patch parameter '#{parameterName}'."); + + static const MessageKind PATCH_NON_EXISTING = const MessageKind( + "Origin does not exist for patch '#{name}'."); + + // TODO(ahe): Eventually, this error should be removed as it will be handled + // by the regular parser. + static const MessageKind PATCH_NONPATCHABLE = const MessageKind( + "Only classes and functions can be patched."); + + static const MessageKind PATCH_NON_EXTERNAL = const MessageKind( + "Only external functions can be patched."); + + static const MessageKind PATCH_NON_CLASS = const MessageKind( + "Patching non-class with class patch '#{className}'."); + + static const MessageKind PATCH_NON_GETTER = const MessageKind( + "Cannot patch non-getter '#{name}' with getter patch."); + + static const MessageKind PATCH_NO_GETTER = const MessageKind( + "No getter found for getter patch '#{getterName}'."); + + static const MessageKind PATCH_NON_SETTER = const MessageKind( + "Cannot patch non-setter '#{name}' with setter patch."); + + static const MessageKind PATCH_NO_SETTER = const MessageKind( + "No setter found for setter patch '#{setterName}'."); + + static const MessageKind PATCH_NON_CONSTRUCTOR = const MessageKind( + "Cannot patch non-constructor with constructor patch " + "'#{constructorName}'."); + + static const MessageKind PATCH_NON_FUNCTION = const MessageKind( + "Cannot patch non-function with function patch " + "'#{functionName}'."); + + ////////////////////////////////////////////////////////////////////////////// + // Patch errors end. + ////////////////////////////////////////////////////////////////////////////// + + static const MessageKind CALL_NOT_SUPPORTED_ON_NATIVE_CLASS = + const MessageKind( + "Non-supported 'call' member on a native class, or a " + "subclass of a native class."); + + toString() => template; + + Message message([Map arguments = const {}, bool terse = false]) { + return new Message(this, arguments, terse); + } + + bool get hasHowToFix => howToFix != null && howToFix != DONT_KNOW_HOW_TO_FIX; +} + +class Message { + final MessageKind kind; + final Map arguments; + final bool terse; + String message; + + Message(this.kind, this.arguments, this.terse) { + assert(() { computeMessage(); return true; }); + } + + String computeMessage() { + if (message == null) { + message = kind.template; + arguments.forEach((key, value) { + message = message.replaceAll('#{${key}}', value.toString()); + }); + assert(invariant( + CURRENT_ELEMENT_SPANNABLE, + kind == MessageKind.GENERIC || + !message.contains(new RegExp(r'#\{.+\}')), + message: 'Missing arguments in error message: "$message"')); + if (!terse && kind.hasHowToFix) { + String howToFix = kind.howToFix; + arguments.forEach((key, value) { + howToFix = howToFix.replaceAll('#{${key}}', value.toString()); + }); + message = '$message\n$howToFix'; + } + } + return message; + } + + String toString() { + return computeMessage(); + } + + bool operator==(other) { + if (other is !Message) return false; + return (kind == other.kind) && (toString() == other.toString()); + } + + int get hashCode => throw new UnsupportedError('Message.hashCode'); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/world.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/world.dart new file mode 100644 index 0000000..91a95d2 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/implementation/world.dart @@ -0,0 +1,276 @@ +// Copyright (c) 2012, 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. + +part of dart2js; + +class World { + final Compiler compiler; + final FunctionSet allFunctions; + final Set functionsCalledInLoop = new Set(); + final Map sideEffects = new Map(); + + final Map> mixinUses = + new Map>(); + + final Map> _typesImplementedBySubclasses = + new Map>(); + + // We keep track of subtype and subclass relationships in four + // distinct sets to make class hierarchy analysis faster. + final Map> _subclasses = + new Map>(); + final Map> _subtypes = + new Map>(); + final Map> _supertypes = + new Map>(); + + final Set sideEffectsFreeElements = new Set(); + + final Set elementsThatCannotThrow = new Set(); + + Set subclassesOf(ClassElement cls) { + return _subclasses[cls.declaration]; + } + + Set subtypesOf(ClassElement cls) { + return _subtypes[cls.declaration]; + } + + Set supertypesOf(ClassElement cls) { + return _supertypes[cls.declaration]; + } + + Set typesImplementedBySubclassesOf(ClassElement cls) { + return _typesImplementedBySubclasses[cls.declaration]; + } + + bool hasSubclasses(ClassElement cls) { + Set subclasses = compiler.world.subclassesOf(cls); + return subclasses != null && !subclasses.isEmpty; + } + + World(Compiler compiler) + : allFunctions = new FunctionSet(compiler), + this.compiler = compiler; + + void populate() { + void addSubtypes(ClassElement cls) { + assert(cls.isDeclaration); + if (cls.resolutionState != STATE_DONE) { + compiler.internalError(cls, 'Class "${cls.name}" is not resolved.'); + } + + for (DartType type in cls.allSupertypes) { + Set supertypesOfClass = + _supertypes.putIfAbsent(cls, () => new Set()); + Set subtypesOfSupertype = + _subtypes.putIfAbsent(type.element, () => new Set()); + supertypesOfClass.add(type.element); + subtypesOfSupertype.add(cls); + } + + // Walk through the superclasses, and record the types + // implemented by that type on the superclasses. + DartType type = cls.supertype; + while (type != null) { + Set subclassesOfSuperclass = + _subclasses.putIfAbsent(type.element, () => new Set()); + subclassesOfSuperclass.add(cls); + + Set typesImplementedBySubclassesOfCls = + _typesImplementedBySubclasses.putIfAbsent( + type.element, () => new Set()); + for (DartType current in cls.allSupertypes) { + typesImplementedBySubclassesOfCls.add(current.element); + } + ClassElement classElement = type.element; + type = classElement.supertype; + } + } + + // Use the [:seenClasses:] set to include non-instantiated + // classes: if the superclass of these classes require RTI, then + // they also need RTI, so that a constructor passes the type + // variables to the super constructor. + compiler.enqueuer.resolution.seenClasses.forEach(addSubtypes); + } + + Iterable commonSupertypesOf(ClassElement x, ClassElement y) { + Set xSet = supertypesOf(x); + if (xSet == null) return const []; + Set ySet = supertypesOf(y); + if (ySet == null) return const []; + Set smallSet, largeSet; + if (xSet.length <= ySet.length) { + smallSet = xSet; + largeSet = ySet; + } else { + smallSet = ySet; + largeSet = xSet; + } + return smallSet.where((ClassElement each) => largeSet.contains(each)); + } + + void registerMixinUse(MixinApplicationElement mixinApplication, + ClassElement mixin) { + // We don't support patch classes as mixin. + assert(mixin.isDeclaration); + Set users = + mixinUses.putIfAbsent(mixin, () => + new Set()); + users.add(mixinApplication); + } + + bool isUsedAsMixin(ClassElement cls) { + Set uses = mixinUses[cls]; + return uses != null && !uses.isEmpty; + } + + bool hasAnySubclass(ClassElement cls) { + Set classes = subclassesOf(cls); + return classes != null && !classes.isEmpty; + } + + bool hasAnySubtype(ClassElement cls) { + Set classes = subtypesOf(cls); + return classes != null && !classes.isEmpty; + } + + bool hasAnyUserDefinedGetter(Selector selector) { + return allFunctions.filter(selector).any((each) => each.isGetter()); + } + + // Returns whether a subclass of [superclass] implements [type]. + bool hasAnySubclassThatImplements(ClassElement superclass, + ClassElement type) { + Set subclasses = typesImplementedBySubclassesOf(superclass); + if (subclasses == null) return false; + return subclasses.contains(type); + } + + // Returns whether a subclass of any mixin application of [cls] implements + // [type]. + bool hasAnySubclassOfMixinUseThatImplements(ClassElement cls, + ClassElement type) { + Set uses = mixinUses[cls]; + if (uses == null || uses.isEmpty) return false; + return uses.any((use) => hasAnySubclassThatImplements(use, type)); + } + + // Returns whether a subclass of [superclass] mixes in [other]. + bool hasAnySubclassThatMixes(ClassElement superclass, ClassElement other) { + Set uses = mixinUses[other]; + return (uses != null) + ? uses.any((each) => each.isSubclassOf(superclass)) + : false; + } + + bool isSubtype(ClassElement supertype, ClassElement test) { + Set subtypes = subtypesOf(supertype); + return subtypes != null && subtypes.contains(test.declaration); + } + + bool isSubclass(ClassElement superclass, ClassElement test) { + Set subclasses = subclassesOf(superclass); + return subclasses != null && subclasses.contains(test.declaration); + } + + void registerUsedElement(Element element) { + if (element.isInstanceMember() && !element.isAbstract) { + allFunctions.add(element); + } + } + + VariableElement locateSingleField(Selector selector) { + Element result = locateSingleElement(selector); + return (result != null && result.isField()) ? result : null; + } + + Element locateSingleElement(Selector selector) { + ti.TypeMask mask = selector.mask == null + ? new ti.TypeMask.subclass(compiler.objectClass) + : selector.mask; + return mask.locateSingleElement(selector, compiler); + } + + void addFunctionCalledInLoop(Element element) { + functionsCalledInLoop.add(element.declaration); + } + + bool isCalledInLoop(Element element) { + return functionsCalledInLoop.contains(element.declaration); + } + + bool fieldNeverChanges(Element element) { + if (!element.isField()) return false; + if (element.isNative()) { + // Some native fields are views of data that may be changed by operations. + // E.g. node.firstChild depends on parentNode.removeBefore(n1, n2). + // TODO(sra): Refine the effect classification so that native effects are + // distinct from ordinary Dart effects. + return false; + } + + return element.modifiers.isFinal() + || element.modifiers.isConst() + || (element.isInstanceMember() + && !compiler.resolverWorld.hasInvokedSetter(element, compiler)); + } + + SideEffects getSideEffectsOfElement(Element element) { + // The type inferrer (where the side effects are being computed), + // does not see generative constructor bodies because they are + // created by the backend. Also, it does not make any distinction + // between a constructor and its body for side effects. This + // implies that currently, the side effects of a constructor body + // contain the side effects of the initializers. + assert(!element.isGenerativeConstructorBody()); + assert(!element.isField()); + return sideEffects.putIfAbsent(element.declaration, () { + return new SideEffects(); + }); + } + + void registerSideEffects(Element element, SideEffects effects) { + if (sideEffectsFreeElements.contains(element)) return; + sideEffects[element.declaration] = effects; + } + + void registerSideEffectsFree(Element element) { + sideEffects[element.declaration] = new SideEffects.empty(); + sideEffectsFreeElements.add(element); + } + + SideEffects getSideEffectsOfSelector(Selector selector) { + // We're not tracking side effects of closures. + if (selector.isClosureCall()) return new SideEffects(); + SideEffects sideEffects = new SideEffects.empty(); + for (Element e in allFunctions.filter(selector)) { + if (e.isField()) { + if (selector.isGetter()) { + if (!fieldNeverChanges(e)) { + sideEffects.setDependsOnInstancePropertyStore(); + } + } else if (selector.isSetter()) { + sideEffects.setChangesInstanceProperty(); + } else { + assert(selector.isCall()); + sideEffects.setAllSideEffects(); + sideEffects.setDependsOnSomething(); + } + } else { + sideEffects.add(getSideEffectsOfElement(e)); + } + } + return sideEffects; + } + + void registerCannotThrow(Element element) { + elementsThatCannotThrow.add(element); + } + + bool getCannotThrow(Element element) { + return elementsThatCannotThrow.contains(element); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/compile_loop/compile_loop.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/compile_loop/compile_loop.dart new file mode 100644 index 0000000..f4920ae --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/compile_loop/compile_loop.dart @@ -0,0 +1,76 @@ +// Copyright (c) 2013, 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. + +// This sample demonstrates how to run the compiler in a loop reading +// all sources from memory, instead of using dart:io. +library sample.compile_loop; + +import 'dart:async'; + +import '../../compiler.dart' as compiler; + +// If this file is missing, generate it using ../jsonify/jsonify.dart. +import 'sdk.dart'; + +Future compile(source) { + Future inputProvider(Uri uri) { + if (uri.scheme == 'sdk') { + var value = SDK_SOURCES['$uri']; + if (value == null) { + // TODO(ahe): Use new Future.error. + throw new Exception('Error: Cannot read: $uri'); + } + return new Future.value(value); + } else if ('$uri' == 'memory:/main.dart') { + return new Future.value(source); + } + // TODO(ahe): Use new Future.error. + throw new Exception('Error: Cannot read: $uri'); + } + void handler(Uri uri, int begin, int end, + String message, compiler.Diagnostic kind) { + // TODO(ahe): Remove dart:io import from + // ../../implementation/source_file_provider.dart and use + // FormattingDiagnosticHandler instead. + print({ 'uri': '$uri', + 'begin': begin, + 'end': end, + 'message': message, + 'kind': kind.name }); + if (kind == compiler.Diagnostic.ERROR) { + throw new Exception('Unexpected error occurred.'); + } + } + return compiler.compile( + Uri.parse('memory:/main.dart'), + Uri.parse('sdk:/sdk/'), + null, + inputProvider, + handler, + []); +} + +int iterations = 10; + +main() { + compile(EXAMPLE_HELLO_HTML).then((jsResult) { + if (jsResult == null) throw 'Compilation failed'; + if (--iterations > 0) main(); + }); +} + +const String EXAMPLE_HELLO_HTML = r''' +// Go ahead and modify this example. + +import "dart:html"; + +var greeting = "Hello, World!"; + +// Displays a greeting. +void main() { + // This example uses HTML to display the greeting and it will appear + // in a nested HTML frame (an iframe). + document.body.append(new HeadingElement.h1()..appendText(greeting)); +} +'''; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/darttags/darttags.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/darttags/darttags.dart new file mode 100644 index 0000000..3a00fc6 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/darttags/darttags.dart @@ -0,0 +1,134 @@ +// Copyright (c) 2013, 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. + +// Usage: Add the following to your .gclient file (found in the parent +// of the "dart" in a gclient checkout of the Dart repositor). +// +// hooks = [ +// { +// "pattern": ".", +// "action": [ +// "dart/sdk/bin/dart", +// "dart/sdk/lib/_internal/compiler/samples/darttags/darttags.dart", +// "dart/TAGS" +// ], +// }, +// ] +// +// Modify .emacs to contain: +// +// (setq tags-table-list +// '("DART_LOCATION/dart")) +// +// Where DART_LOCATION is the gclient directory where you found .gclient. + +import 'dart:io'; + +import 'dart:mirrors'; + +import '../../../libraries.dart' + show LIBRARIES, LibraryInfo; + +import '../../implementation/mirrors/analyze.dart' + show analyze; +import '../../implementation/mirrors/dart2js_mirrors.dart' + show BackDoor; +import '../../implementation/mirrors/mirrors_util.dart' show nameOf; + +import '../../implementation/filenames.dart'; +import '../../implementation/source_file.dart'; +import '../../implementation/source_file_provider.dart'; +import '../../implementation/util/uri_extras.dart'; + +const DART2JS = '../../implementation/dart2js.dart'; +const DART2JS_MIRROR = '../../implementation/mirrors/dart2js_mirrors.dart'; +const SDK_ROOT = '../../../../../'; + +bool isPublicDart2jsLibrary(String name) { + return !name.startsWith('_') && LIBRARIES[name].isDart2jsLibrary; +} + +var handler; +RandomAccessFile output; +Uri outputUri; + +main(List arguments) { + handler = new FormattingDiagnosticHandler() + ..throwOnError = true; + + outputUri = + handler.provider.cwd.resolve(nativeToUriPath(arguments.first)); + output = new File(arguments.first).openSync(mode: FileMode.WRITE); + + Uri myLocation = + handler.provider.cwd.resolveUri(Platform.script); + + // Get the names of public dart2js libraries. + Iterable names = LIBRARIES.keys.where(isPublicDart2jsLibrary); + + // Prepend "dart:" to the names. + List uris = names.map((String name) => Uri.parse('dart:$name')).toList(); + + // Append dart2js itself. + uris.add(myLocation.resolve(DART2JS)); + uris.add(myLocation.resolve(DART2JS_MIRROR)); + + analyze(uris, myLocation.resolve(SDK_ROOT), null, handler.provider, handler) + .then(processMirrors); +} + +processMirrors(MirrorSystem mirrors) { + mirrors.libraries.forEach((_, LibraryMirror library) { + BackDoor.compilationUnitsOf(library).forEach(emitTagsForCompilationUnit); + }); + + output.closeSync(); +} + +/** + * From http://en.wikipedia.org/wiki/Ctags#Etags_2 + * + * A section starts with a two line header, one line containing a + * single <\x0c> character, followed by a line which consists of: + * + * {src_file},{size_of_tag_definition_data_in_bytes} + * + * The header is followed by tag definitions, one definition per line, + * with the format: + * + * {tag_definition_text}<\x7f>{tagname}<\x01>{line_number},{byte_offset} + */ +emitTagsForCompilationUnit(compilationUnit) { + // Certain variables in this method do not follow Dart naming + // conventions. This is because the format as written on Wikipedia + // looks very similar to Dart string interpolation that the author + // felt it would make sense to keep the names. + Uri uri = compilationUnit.uri; + var buffer = new StringBuffer(); + SourceFile file = handler.provider.sourceFiles['$uri']; + + compilationUnit.declarations.forEach((_, DeclarationMirror mirror) { + var tagname = nameOf(mirror); + var byte_offset = mirror.location.offset; + var line_number = file.getLine(byte_offset) + 1; + + var lineStart = file.lineStarts[line_number - 1]; + // TODO(ahe): Most often an empty string. Try to see if we can + // get the position of the name token instead. + var tag_definition_text = file.slowText().substring(lineStart, byte_offset); + + // One definition. + buffer.write('${tag_definition_text}\x7f${tagname}' + '\x01${line_number},${byte_offset}\n'); + }); + + var tag_definition_data = '$buffer'; + var src_file = relativize(outputUri, uri, false); + var size_of_tag_definition_data_in_bytes = tag_definition_data.length; + + // The header. + output.writeStringSync( + '\x0c\n${src_file},${size_of_tag_definition_data_in_bytes}\n'); + output.writeStringSync(tag_definition_data); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/jsonify/jsonify.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/jsonify/jsonify.dart new file mode 100644 index 0000000..9306368 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/jsonify/jsonify.dart @@ -0,0 +1,101 @@ +// Copyright (c) 2013, 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. + +import 'dart:io'; +import 'dart:convert'; + +import 'dart:mirrors'; + +import '../../../libraries.dart' + show LIBRARIES, LibraryInfo; + +import '../../implementation/mirrors/analyze.dart' + show analyze; +import '../../implementation/mirrors/dart2js_mirrors.dart' + show BackDoor; + +import '../../implementation/filenames.dart'; +import '../../implementation/source_file.dart'; +import '../../implementation/source_file_provider.dart'; +import '../../implementation/util/uri_extras.dart'; + +const DART2JS = '../../implementation/dart2js.dart'; +const DART2JS_MIRROR = '../../implementation/mirrors/dart2js_mirror.dart'; +const SDK_ROOT = '../../../../../'; + +bool isPublicDart2jsLibrary(String name) { + return !name.startsWith('_') && LIBRARIES[name].isDart2jsLibrary; +} + +var handler; +RandomAccessFile output; +Uri outputUri; +Uri sdkRoot; +const bool outputJson = + const bool.fromEnvironment('outputJson', defaultValue: false); + +main(List arguments) { + handler = new FormattingDiagnosticHandler() + ..throwOnError = true; + + outputUri = + handler.provider.cwd.resolve(nativeToUriPath(arguments.first)); + output = new File(arguments.first).openSync(mode: FileMode.WRITE); + + Uri myLocation = + handler.provider.cwd.resolveUri(Platform.script); + + sdkRoot = myLocation.resolve(SDK_ROOT).resolve('../'); + + // Get the names of public dart2js libraries. + Iterable names = LIBRARIES.keys.where(isPublicDart2jsLibrary); + + // Turn the names into uris by prepending dart: to them. + List uris = names.map((String name) => Uri.parse('dart:$name')).toList(); + + analyze(uris, myLocation.resolve(SDK_ROOT), null, handler.provider, handler) + .then(jsonify); +} + +jsonify(MirrorSystem mirrors) { + var map = {}; + + mirrors.libraries.forEach((_, LibraryMirror library) { + BackDoor.compilationUnitsOf(library).forEach((compilationUnit) { + Uri uri = compilationUnit.uri; + String filename = relativize(sdkRoot, uri, false); + SourceFile file = handler.provider.sourceFiles['$uri']; + map['sdk:/$filename'] = file.slowText(); + }); + }); + + LIBRARIES.forEach((name, info) { + var patch = info.dart2jsPatchPath; + if (patch != null) { + Uri uri = sdkRoot.resolve('sdk/lib/$patch'); + String filename = relativize(sdkRoot, uri, false); + SourceFile file = handler.provider.sourceFiles['$uri']; + map['sdk:/$filename'] = file.slowText(); + } + }); + + if (outputJson) { + output.writeStringSync(JSON.encode(map)); + } else { + output.writeStringSync(''' +// Copyright (c) 2013, 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. + +// DO NOT EDIT. +// This file is generated by jsonify.dart. + +library dart.sdk_sources; + +const Map SDK_SOURCES = const '''); + output.writeStringSync(JSON.encode(map).replaceAll(r'$', r'\$')); + output.writeStringSync(';\n'); + } + output.closeSync(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/leap_server/leap_server.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/leap_server/leap_server.dart new file mode 100644 index 0000000..2f741af --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/compiler/samples/leap_server/leap_server.dart @@ -0,0 +1,222 @@ +// Copyright (c) 2012, 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. + +library leap_server; + +import 'dart:io'; + +import 'dart:convert' show JSON, HtmlEscape; + +/// Represents a "project" command. These commands are accessed from the URL +/// "/project?name". +class ProjectCommand { + final String name; + + /// For each query parameter, this map describes rules for validating them. + final Map rules; + + final Function handle; + + const ProjectCommand(this.name, this.rules, this.handle); +} + +class Conversation { + HttpRequest request; + HttpResponse response; + + static const String PROJECT_PATH = '/project'; + + static const String PACKAGES_PATH = '/packages'; + + static const String CONTENT_TYPE = HttpHeaders.CONTENT_TYPE; + + static Uri documentRoot = Uri.base; + + static Uri projectRoot = Uri.base.resolve('site/try/src/'); + + static Uri packageRoot = Uri.base.resolve('sdk/lib/_internal/'); + + static const List COMMANDS = const [ + const ProjectCommand('list', const {'list': null}, handleProjectList), + ]; + + Conversation(this.request, this.response); + + onClosed(_) { + if (response.statusCode == HttpStatus.OK) return; + print('Request for ${request.uri} ${response.statusCode}'); + } + + notFound(path) { + response.statusCode = HttpStatus.NOT_FOUND; + response.write(htmlInfo('Not Found', + 'The file "$path" could not be found.')); + response.close(); + } + + redirect(String location) { + response.statusCode = HttpStatus.FOUND; + response.headers.add(HttpHeaders.LOCATION, location); + response.close(); + } + + badRequest(String problem) { + response.statusCode = HttpStatus.BAD_REQUEST; + response.write(htmlInfo("Bad request", + "Bad request '${request.uri}': $problem")); + response.close(); + } + + bool validate(Map parameters, Map rules) { + Iterable problems = rules.keys + .where((name) => !parameters.containsKey(name)) + .map((name) => "Missing parameter: '$name'."); + if (!problems.isEmpty) { + badRequest(problems.first); + return false; + } + Set extra = new Set.from(parameters.keys)..removeAll(rules.keys); + if (extra.isEmpty) return true; + String extraString = (extra.toList()..sort()).join("', '"); + badRequest("Extra parameters: '$extraString'."); + return false; + } + + static handleProjectList(Conversation self) { + String nativeDir = projectRoot.toFilePath(); + Directory dir = new Directory(nativeDir); + var future = dir.list(recursive: true, followLinks: false).toList(); + future.then((List entries) { + List files = entries + .map((e) => e.path) + .where((p) => !p.endsWith('~') && p.startsWith(nativeDir)) + .map((p) => p.substring(nativeDir.length)) + .map((p) => new Uri.file(p).path).toList(); + self.response + ..write(JSON.encode(files)) + ..close(); + }); + } + + handleProjectRequest() { + Map parameters = request.uri.queryParameters; + for (ProjectCommand command in COMMANDS) { + if (parameters.containsKey(command.name)) { + if (validate(parameters, command.rules)) { + (command.handle)(this); + } + return; + } + } + String commands = COMMANDS.map((c) => c.name).join("', '"); + badRequest("Valid commands are: '$commands'"); + } + + handle() { + response.done + .then(onClosed) + .catchError(onError); + + Uri uri = request.uri; + if (uri.path == PROJECT_PATH) { + return handleProjectRequest(); + } + if (uri.path.endsWith('/')) { + uri = uri.resolve('index.html'); + } + if (uri.path == '/css/fonts/fontawesome-webfont.woff') { + uri = uri.resolve('/fontawesome-webfont.woff'); + } + if (uri.path.contains('..') || uri.path.contains('%')) { + return notFound(uri.path); + } + String path = uri.path; + Uri root = documentRoot; + String dartType = 'application/dart'; + if (path.startsWith('/project/packages/')) { + root = packageRoot; + path = path.substring('/project/packages'.length); + } else if (path.startsWith('${PROJECT_PATH}/')) { + root = projectRoot; + path = path.substring(PROJECT_PATH.length); + dartType = 'text/plain'; + } else if (path.startsWith('${PACKAGES_PATH}/')) { + root = packageRoot; + path = path.substring(PACKAGES_PATH.length); + } + var f = new File(root.resolve('.$path').toFilePath()); + f.exists().then((bool exists) { + if (!exists) return notFound(path); + if (path.endsWith('.html')) { + response.headers.set(CONTENT_TYPE, 'text/html'); + } else if (path.endsWith('.dart')) { + response.headers.set(CONTENT_TYPE, dartType); + } else if (path.endsWith('.js')) { + response.headers.set(CONTENT_TYPE, 'application/javascript'); + } else if (path.endsWith('.ico')) { + response.headers.set(CONTENT_TYPE, 'image/x-icon'); + } else if (path.endsWith('.appcache')) { + response.headers.set(CONTENT_TYPE, 'text/cache-manifest'); + } + f.openRead().pipe(response).catchError(onError); + }); + } + + static onRequest(HttpRequest request) { + new Conversation(request, request.response).handle(); + } + + static onError(error) { + if (error is HttpException) { + print('Error: ${error.message}'); + } else { + print('Error: ${error}'); + } + } + + String htmlInfo(String title, String text) { + // No script injection, please. + title = const HtmlEscape().convert(title); + text = const HtmlEscape().convert(text); + return """ + + + +$title + + +

$title

+

$text

+ + +"""; + } +} + +main(List arguments) { + if (arguments.length > 0) { + Conversation.documentRoot = Uri.base.resolve(arguments[0]); + } + var host = '127.0.0.1'; + if (arguments.length > 1) { + host = arguments[1]; + } + int port = 0; + if (arguments.length > 2) { + port = int.parse(arguments[2]); + } + if (arguments.length > 3) { + Conversation.projectRoot = Uri.base.resolve(arguments[3]); + } + if (arguments.length > 4) { + Conversation.packageRoot = Uri.base.resolve(arguments[4]); + } + HttpServer.bind(host, port).then((HttpServer server) { + print('HTTP server started on http://$host:${server.port}/'); + server.listen(Conversation.onRequest, onError: Conversation.onError); + }).catchError((e) { + print("HttpServer.bind error: $e"); + exit(1); + }); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/annotations.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/annotations.dart new file mode 100644 index 0000000..23f1234 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/annotations.dart @@ -0,0 +1,30 @@ +// Copyright (c) 2013, 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. + +part of _js_helper; + +/// Tells the optimizing compiler that the annotated method has no +/// side-effects. +/// Requires @NoInline() to function correctly. +class NoSideEffects { + const NoSideEffects(); +} + +/// Tells the optimizing compiler that the annotated method cannot throw. +/// Requires @NoInline() to function correctly. +class NoThrows { + const NoThrows(); +} + +/// Tells the optimizing compiler to not inline the annotated method. +class NoInline { + const NoInline(); +} + +// Ensures that the annotated method is represented internally using +// IR nodes ([:value == true:]) or AST nodes ([:value == false:]). +class IrRepresentation { + final bool value; + const IrRepresentation(this.value); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/async_patch.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/async_patch.dart new file mode 100644 index 0000000..06bd2a5 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/async_patch.dart @@ -0,0 +1,41 @@ +// Copyright (c) 2012, 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. + +// Patch file for the dart:async library. + +import 'dart:_js_helper' show + Primitives, + convertDartClosureToJS, + loadDeferredLibrary; +import 'dart:_isolate_helper' show TimerImpl; + +import 'dart:_foreign_helper' show JS; + +patch Timer _createTimer(Duration duration, void callback()) { + int milliseconds = duration.inMilliseconds; + if (milliseconds < 0) milliseconds = 0; + return new TimerImpl(milliseconds, callback); +} + +patch Timer _createPeriodicTimer(Duration duration, + void callback(Timer timer)) { + int milliseconds = duration.inMilliseconds; + if (milliseconds < 0) milliseconds = 0; + return new TimerImpl.periodic(milliseconds, callback); +} + +patch class _AsyncRun { + patch static void _scheduleImmediate(void callback()) { + // TODO(9002): don't use the Timer to enqueue the immediate callback. + _createTimer(Duration.ZERO, callback); + } +} + +patch class DeferredLibrary { + patch Future load() { + return loadDeferredLibrary(libraryName, uri); + } +} + +bool get _hasDocument => JS('String', 'typeof document') == 'object'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/collection_patch.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/collection_patch.dart new file mode 100644 index 0000000..192dfd3 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/collection_patch.dart @@ -0,0 +1,1793 @@ +// Copyright (c) 2013, 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. + +// Patch file for dart:collection classes. +import 'dart:_foreign_helper' show JS; + +patch class HashMap { + patch factory HashMap({ bool equals(K key1, K key2), + int hashCode(K key), + bool isValidKey(potentialKey) }) { + if (isValidKey == null) { + if (hashCode == null) { + if (equals == null) { + return new _HashMap(); + } + hashCode = _defaultHashCode; + } else { + if (identical(identityHashCode, hashCode) && + identical(identical, equals)) { + return new _IdentityHashMap(); + } + if (equals == null) { + equals = _defaultEquals; + } + } + } else { + if (hashCode == null) { + hashCode = _defaultHashCode; + } + if (equals == null) { + equals = _defaultEquals; + } + } + return new _CustomHashMap(equals, hashCode, isValidKey); + } + + patch factory HashMap.identity() = _IdentityHashMap; +} + +class _HashMap implements HashMap { + int _length = 0; + + // The hash map contents are divided into three parts: one part for + // string keys, one for numeric keys, and one for the rest. String + // and numeric keys map directly to their values, but the rest of + // the entries are stored in bucket lists of the form: + // + // [key-0, value-0, key-1, value-1, ...] + // + // where all keys in the same bucket share the same hash code. + var _strings; + var _nums; + var _rest; + + // When iterating over the hash map, it is very convenient to have a + // list of all the keys. We cache that on the instance and clear the + // the cache whenever the key set changes. This is also used to + // guard against concurrent modifications. + List _keys; + + _HashMap(); + + + int get length => _length; + bool get isEmpty => _length == 0; + bool get isNotEmpty => !isEmpty; + + Iterable get keys { + return new HashMapKeyIterable(this); + } + + Iterable get values { + return new MappedIterable(keys, (each) => this[each]); + } + + bool containsKey(Object key) { + if (_isStringKey(key)) { + var strings = _strings; + return (strings == null) ? false : _hasTableEntry(strings, key); + } else if (_isNumericKey(key)) { + var nums = _nums; + return (nums == null) ? false : _hasTableEntry(nums, key); + } else { + var rest = _rest; + if (rest == null) return false; + var bucket = _getBucket(rest, key); + return _findBucketIndex(bucket, key) >= 0; + } + } + + bool containsValue(Object value) { + return _computeKeys().any((each) => this[each] == value); + } + + void addAll(Map other) { + other.forEach((K key, V value) { + this[key] = value; + }); + } + + V operator[](Object key) { + if (_isStringKey(key)) { + var strings = _strings; + return (strings == null) ? null : _getTableEntry(strings, key); + } else if (_isNumericKey(key)) { + var nums = _nums; + return (nums == null) ? null : _getTableEntry(nums, key); + } else { + var rest = _rest; + if (rest == null) return null; + var bucket = _getBucket(rest, key); + int index = _findBucketIndex(bucket, key); + return (index < 0) ? null : JS('var', '#[#]', bucket, index + 1); + } + } + + void operator[]=(K key, V value) { + if (_isStringKey(key)) { + var strings = _strings; + if (strings == null) _strings = strings = _newHashTable(); + _addHashTableEntry(strings, key, value); + } else if (_isNumericKey(key)) { + var nums = _nums; + if (nums == null) _nums = nums = _newHashTable(); + _addHashTableEntry(nums, key, value); + } else { + var rest = _rest; + if (rest == null) _rest = rest = _newHashTable(); + var hash = _computeHashCode(key); + var bucket = JS('var', '#[#]', rest, hash); + if (bucket == null) { + _setTableEntry(rest, hash, JS('var', '[#, #]', key, value)); + _length++; + _keys = null; + } else { + int index = _findBucketIndex(bucket, key); + if (index >= 0) { + JS('void', '#[#] = #', bucket, index + 1, value); + } else { + JS('void', '#.push(#, #)', bucket, key, value); + _length++; + _keys = null; + } + } + } + } + + V putIfAbsent(K key, V ifAbsent()) { + if (containsKey(key)) return this[key]; + V value = ifAbsent(); + this[key] = value; + return value; + } + + V remove(Object key) { + if (_isStringKey(key)) { + return _removeHashTableEntry(_strings, key); + } else if (_isNumericKey(key)) { + return _removeHashTableEntry(_nums, key); + } else { + var rest = _rest; + if (rest == null) return null; + var bucket = _getBucket(rest, key); + int index = _findBucketIndex(bucket, key); + if (index < 0) return null; + // TODO(kasperl): Consider getting rid of the bucket list when + // the length reaches zero. + _length--; + _keys = null; + // Use splice to remove the two [key, value] elements at the + // index and return the value. + return JS('var', '#.splice(#, 2)[1]', bucket, index); + } + } + + void clear() { + if (_length > 0) { + _strings = _nums = _rest = _keys = null; + _length = 0; + } + } + + void forEach(void action(K key, V value)) { + List keys = _computeKeys(); + for (int i = 0, length = keys.length; i < length; i++) { + var key = JS('var', '#[#]', keys, i); + action(key, this[key]); + if (JS('bool', '# !== #', keys, _keys)) { + throw new ConcurrentModificationError(this); + } + } + } + + List _computeKeys() { + if (_keys != null) return _keys; + List result = new List(_length); + int index = 0; + + // Add all string keys to the list. + var strings = _strings; + if (strings != null) { + var names = JS('var', 'Object.getOwnPropertyNames(#)', strings); + int entries = JS('int', '#.length', names); + for (int i = 0; i < entries; i++) { + String key = JS('String', '#[#]', names, i); + JS('void', '#[#] = #', result, index, key); + index++; + } + } + + // Add all numeric keys to the list. + var nums = _nums; + if (nums != null) { + var names = JS('var', 'Object.getOwnPropertyNames(#)', nums); + int entries = JS('int', '#.length', names); + for (int i = 0; i < entries; i++) { + // Object.getOwnPropertyNames returns a list of strings, so we + // have to convert the keys back to numbers (+). + num key = JS('num', '+#[#]', names, i); + JS('void', '#[#] = #', result, index, key); + index++; + } + } + + // Add all the remaining keys to the list. + var rest = _rest; + if (rest != null) { + var names = JS('var', 'Object.getOwnPropertyNames(#)', rest); + int entries = JS('int', '#.length', names); + for (int i = 0; i < entries; i++) { + var key = JS('String', '#[#]', names, i); + var bucket = JS('var', '#[#]', rest, key); + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i += 2) { + var key = JS('var', '#[#]', bucket, i); + JS('void', '#[#] = #', result, index, key); + index++; + } + } + } + assert(index == _length); + return _keys = result; + } + + void _addHashTableEntry(var table, K key, V value) { + if (!_hasTableEntry(table, key)) { + _length++; + _keys = null; + } + _setTableEntry(table, key, value); + } + + V _removeHashTableEntry(var table, Object key) { + if (table != null && _hasTableEntry(table, key)) { + V value = _getTableEntry(table, key); + _deleteTableEntry(table, key); + _length--; + _keys = null; + return value; + } else { + return null; + } + } + + static bool _isStringKey(var key) { + return key is String && key != '__proto__'; + } + + static bool _isNumericKey(var key) { + // Only treat unsigned 30-bit integers as numeric keys. This way, + // we avoid converting them to strings when we use them as keys in + // the JavaScript hash table object. + return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key); + } + + int _computeHashCode(var key) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic keys like '__proto__'. Another option + // would be to throw an exception if the hash code isn't a number. + return JS('int', '# & 0x3ffffff', key.hashCode); + } + + static bool _hasTableEntry(var table, var key) { + var entry = JS('var', '#[#]', table, key); + // We take care to only store non-null entries in the table, so we + // can check if the table has an entry for the given key with a + // simple null check. + return entry != null; + } + + static _getTableEntry(var table, var key) { + var entry = JS('var', '#[#]', table, key); + // We store the table itself as the entry to signal that it really + // is a null value, so we have to map back to null here. + return JS('bool', '# === #', entry, table) ? null : entry; + } + + static void _setTableEntry(var table, var key, var value) { + // We only store non-null entries in the table, so we have to + // change null values to refer to the table itself. Such values + // will be recognized and mapped back to null on access. + if (value == null) { + // Do not update [value] with [table], otherwise our + // optimizations could be confused by this opaque object being + // now used for more things than storing and fetching from it. + JS('void', '#[#] = #', table, key, table); + } else { + JS('void', '#[#] = #', table, key, value); + } + } + + static void _deleteTableEntry(var table, var key) { + JS('void', 'delete #[#]', table, key); + } + + List _getBucket(var table, var key) { + var hash = _computeHashCode(key); + return JS('var', '#[#]', table, hash); + } + + int _findBucketIndex(var bucket, var key) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i += 2) { + if (JS('var', '#[#]', bucket, i) == key) return i; + } + return -1; + } + + static _newHashTable() { + // Create a new JavaScript object to be used as a hash table. Use + // Object.create to avoid the properties on Object.prototype + // showing up as entries. + var table = JS('var', 'Object.create(null)'); + // Attempt to force the hash table into 'dictionary' mode by + // adding a property to it and deleting it again. + var temporaryKey = ''; + _setTableEntry(table, temporaryKey, table); + _deleteTableEntry(table, temporaryKey); + return table; + } +} + +class _IdentityHashMap extends _HashMap { + int _computeHashCode(var key) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic keys like '__proto__'. Another option + // would be to throw an exception if the hash code isn't a number. + return JS('int', '# & 0x3ffffff', identityHashCode(key)); + } + + int _findBucketIndex(var bucket, var key) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i += 2) { + if (identical(JS('var', '#[#]', bucket, i), key)) return i; + } + return -1; + } +} + +class _CustomHashMap extends _HashMap { + final _Equality _equals; + final _Hasher _hashCode; + final _Predicate _validKey; + _CustomHashMap(this._equals, this._hashCode, bool validKey(potentialKey)) + : _validKey = (validKey != null) ? validKey : ((v) => v is K); + + V operator[](Object key) { + if (!_validKey(key)) return null; + return super[key]; + } + + bool containsKey(Object key) { + if (!_validKey(key)) return false; + return super.containsKey(key); + } + + V remove(Object key) { + if (!_validKey(key)) return null; + return super.remove(key); + } + + int _computeHashCode(var key) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic keys like '__proto__'. Another option + // would be to throw an exception if the hash code isn't a number. + return JS('int', '# & 0x3ffffff', _hashCode(key)); + } + + int _findBucketIndex(var bucket, var key) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i += 2) { + if (_equals(JS('var', '#[#]', bucket, i), key)) return i; + } + return -1; + } + + String toString() => Maps.mapToString(this); +} + +class HashMapKeyIterable extends IterableBase + implements EfficientLength { + final _map; + HashMapKeyIterable(this._map); + + int get length => _map._length; + bool get isEmpty => _map._length == 0; + + Iterator get iterator { + return new HashMapKeyIterator(_map, _map._computeKeys()); + } + + bool contains(Object element) { + return _map.containsKey(element); + } + + void forEach(void f(E element)) { + List keys = _map._computeKeys(); + for (int i = 0, length = JS('int', '#.length', keys); i < length; i++) { + f(JS('var', '#[#]', keys, i)); + if (JS('bool', '# !== #', keys, _map._keys)) { + throw new ConcurrentModificationError(_map); + } + } + } +} + +class HashMapKeyIterator implements Iterator { + final _map; + final List _keys; + int _offset = 0; + E _current; + + HashMapKeyIterator(this._map, this._keys); + + E get current => _current; + + bool moveNext() { + var keys = _keys; + int offset = _offset; + if (JS('bool', '# !== #', keys, _map._keys)) { + throw new ConcurrentModificationError(_map); + } else if (offset >= JS('int', '#.length', keys)) { + _current = null; + return false; + } else { + _current = JS('var', '#[#]', keys, offset); + // TODO(kasperl): For now, we have to tell the type inferrer to + // treat the result of doing offset + 1 as an int. Otherwise, we + // get unnecessary bailout code. + _offset = JS('int', '#', offset + 1); + return true; + } + } +} + +patch class LinkedHashMap { + patch factory LinkedHashMap({ bool equals(K key1, K key2), + int hashCode(K key), + bool isValidKey(potentialKey) }) { + if (isValidKey == null) { + if (hashCode == null) { + if (equals == null) { + return new _LinkedHashMap(); + } + hashCode = _defaultHashCode; + } else { + if (identical(identityHashCode, hashCode) && + identical(identical, equals)) { + return new _LinkedIdentityHashMap(); + } + if (equals == null) { + equals = _defaultEquals; + } + } + } else { + if (hashCode == null) { + hashCode = _defaultHashCode; + } + if (equals == null) { + equals = _defaultEquals; + } + } + return new _LinkedCustomHashMap(equals, hashCode, isValidKey); + } + + patch factory LinkedHashMap.identity() = _LinkedIdentityHashMap; +} + +class _LinkedHashMap implements LinkedHashMap { + int _length = 0; + + // The hash map contents are divided into three parts: one part for + // string keys, one for numeric keys, and one for the rest. String + // and numeric keys map directly to their linked cells, but the rest + // of the entries are stored in bucket lists of the form: + // + // [cell-0, cell-1, ...] + // + // where all keys in the same bucket share the same hash code. + var _strings; + var _nums; + var _rest; + + // The keys and values are stored in cells that are linked together + // to form a double linked list. + LinkedHashMapCell _first; + LinkedHashMapCell _last; + + // We track the number of modifications done to the key set of the + // hash map to be able to throw when the map is modified while being + // iterated over. + int _modifications = 0; + + _LinkedHashMap(); + + + int get length => _length; + bool get isEmpty => _length == 0; + bool get isNotEmpty => !isEmpty; + + Iterable get keys { + return new LinkedHashMapKeyIterable(this); + } + + Iterable get values { + return new MappedIterable(keys, (each) => this[each]); + } + + bool containsKey(Object key) { + if (_isStringKey(key)) { + var strings = _strings; + if (strings == null) return false; + LinkedHashMapCell cell = _getTableEntry(strings, key); + return cell != null; + } else if (_isNumericKey(key)) { + var nums = _nums; + if (nums == null) return false; + LinkedHashMapCell cell = _getTableEntry(nums, key); + return cell != null; + } else { + var rest = _rest; + if (rest == null) return false; + var bucket = _getBucket(rest, key); + return _findBucketIndex(bucket, key) >= 0; + } + } + + bool containsValue(Object value) { + return keys.any((each) => this[each] == value); + } + + void addAll(Map other) { + other.forEach((K key, V value) { + this[key] = value; + }); + } + + V operator[](Object key) { + if (_isStringKey(key)) { + var strings = _strings; + if (strings == null) return null; + LinkedHashMapCell cell = _getTableEntry(strings, key); + return (cell == null) ? null : cell._value; + } else if (_isNumericKey(key)) { + var nums = _nums; + if (nums == null) return null; + LinkedHashMapCell cell = _getTableEntry(nums, key); + return (cell == null) ? null : cell._value; + } else { + var rest = _rest; + if (rest == null) return null; + var bucket = _getBucket(rest, key); + int index = _findBucketIndex(bucket, key); + if (index < 0) return null; + LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); + return cell._value; + } + } + + void operator[]=(K key, V value) { + if (_isStringKey(key)) { + var strings = _strings; + if (strings == null) _strings = strings = _newHashTable(); + _addHashTableEntry(strings, key, value); + } else if (_isNumericKey(key)) { + var nums = _nums; + if (nums == null) _nums = nums = _newHashTable(); + _addHashTableEntry(nums, key, value); + } else { + var rest = _rest; + if (rest == null) _rest = rest = _newHashTable(); + var hash = _computeHashCode(key); + var bucket = JS('var', '#[#]', rest, hash); + if (bucket == null) { + LinkedHashMapCell cell = _newLinkedCell(key, value); + _setTableEntry(rest, hash, JS('var', '[#]', cell)); + } else { + int index = _findBucketIndex(bucket, key); + if (index >= 0) { + LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); + cell._value = value; + } else { + LinkedHashMapCell cell = _newLinkedCell(key, value); + JS('void', '#.push(#)', bucket, cell); + } + } + } + } + + V putIfAbsent(K key, V ifAbsent()) { + if (containsKey(key)) return this[key]; + V value = ifAbsent(); + this[key] = value; + return value; + } + + V remove(Object key) { + if (_isStringKey(key)) { + return _removeHashTableEntry(_strings, key); + } else if (_isNumericKey(key)) { + return _removeHashTableEntry(_nums, key); + } else { + var rest = _rest; + if (rest == null) return null; + var bucket = _getBucket(rest, key); + int index = _findBucketIndex(bucket, key); + if (index < 0) return null; + // Use splice to remove the [cell] element at the index and + // unlink the cell before returning its value. + LinkedHashMapCell cell = JS('var', '#.splice(#, 1)[0]', bucket, index); + _unlinkCell(cell); + // TODO(kasperl): Consider getting rid of the bucket list when + // the length reaches zero. + return cell._value; + } + } + + void clear() { + if (_length > 0) { + _strings = _nums = _rest = _first = _last = null; + _length = 0; + _modified(); + } + } + + void forEach(void action(K key, V value)) { + LinkedHashMapCell cell = _first; + int modifications = _modifications; + while (cell != null) { + action(cell._key, cell._value); + if (modifications != _modifications) { + throw new ConcurrentModificationError(this); + } + cell = cell._next; + } + } + + void _addHashTableEntry(var table, K key, V value) { + LinkedHashMapCell cell = _getTableEntry(table, key); + if (cell == null) { + _setTableEntry(table, key, _newLinkedCell(key, value)); + } else { + cell._value = value; + } + } + + V _removeHashTableEntry(var table, Object key) { + if (table == null) return null; + LinkedHashMapCell cell = _getTableEntry(table, key); + if (cell == null) return null; + _unlinkCell(cell); + _deleteTableEntry(table, key); + return cell._value; + } + + void _modified() { + // Value cycles after 2^30 modifications. If you keep hold of an + // iterator for that long, you might miss a modification + // detection, and iteration can go sour. Don't do that. + _modifications = (_modifications + 1) & 0x3ffffff; + } + + // Create a new cell and link it in as the last one in the list. + LinkedHashMapCell _newLinkedCell(K key, V value) { + LinkedHashMapCell cell = new LinkedHashMapCell(key, value); + if (_first == null) { + _first = _last = cell; + } else { + LinkedHashMapCell last = _last; + cell._previous = last; + _last = last._next = cell; + } + _length++; + _modified(); + return cell; + } + + // Unlink the given cell from the linked list of cells. + void _unlinkCell(LinkedHashMapCell cell) { + LinkedHashMapCell previous = cell._previous; + LinkedHashMapCell next = cell._next; + if (previous == null) { + assert(cell == _first); + _first = next; + } else { + previous._next = next; + } + if (next == null) { + assert(cell == _last); + _last = previous; + } else { + next._previous = previous; + } + _length--; + _modified(); + } + + static bool _isStringKey(var key) { + return key is String && key != '__proto__'; + } + + static bool _isNumericKey(var key) { + // Only treat unsigned 30-bit integers as numeric keys. This way, + // we avoid converting them to strings when we use them as keys in + // the JavaScript hash table object. + return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key); + } + + int _computeHashCode(var key) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic keys like '__proto__'. Another option + // would be to throw an exception if the hash code isn't a number. + return JS('int', '# & 0x3ffffff', key.hashCode); + } + + static _getTableEntry(var table, var key) { + return JS('var', '#[#]', table, key); + } + + static void _setTableEntry(var table, var key, var value) { + assert(value != null); + JS('void', '#[#] = #', table, key, value); + } + + static void _deleteTableEntry(var table, var key) { + JS('void', 'delete #[#]', table, key); + } + + List _getBucket(var table, var key) { + var hash = _computeHashCode(key); + return JS('var', '#[#]', table, hash); + } + + int _findBucketIndex(var bucket, var key) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i++) { + LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); + if (cell._key == key) return i; + } + return -1; + } + + static _newHashTable() { + // Create a new JavaScript object to be used as a hash table. Use + // Object.create to avoid the properties on Object.prototype + // showing up as entries. + var table = JS('var', 'Object.create(null)'); + // Attempt to force the hash table into 'dictionary' mode by + // adding a property to it and deleting it again. + var temporaryKey = ''; + _setTableEntry(table, temporaryKey, table); + _deleteTableEntry(table, temporaryKey); + return table; + } + + String toString() => Maps.mapToString(this); +} + +class _LinkedIdentityHashMap extends _LinkedHashMap { + int _computeHashCode(var key) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic keys like '__proto__'. Another option + // would be to throw an exception if the hash code isn't a number. + return JS('int', '# & 0x3ffffff', identityHashCode(key)); + } + + int _findBucketIndex(var bucket, var key) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i++) { + LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); + if (identical(cell._key, key)) return i; + } + return -1; + } +} + +class _LinkedCustomHashMap extends _LinkedHashMap { + final _Equality _equals; + final _Hasher _hashCode; + final _Predicate _validKey; + _LinkedCustomHashMap(this._equals, this._hashCode, + bool validKey(potentialKey)) + : _validKey = (validKey != null) ? validKey : ((v) => v is K); + + V operator[](Object key) { + if (!_validKey(key)) return null; + return super[key]; + } + + bool containsKey(Object key) { + if (!_validKey(key)) return false; + return super.containsKey(key); + } + + V remove(Object key) { + if (!_validKey(key)) return null; + return super.remove(key); + } + + int _computeHashCode(var key) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic keys like '__proto__'. Another option + // would be to throw an exception if the hash code isn't a number. + return JS('int', '# & 0x3ffffff', _hashCode(key)); + } + + int _findBucketIndex(var bucket, var key) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i++) { + LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); + if (_equals(cell._key, key)) return i; + } + return -1; + } +} + +class LinkedHashMapCell { + final _key; + var _value; + + LinkedHashMapCell _next; + LinkedHashMapCell _previous; + + LinkedHashMapCell(this._key, this._value); +} + +class LinkedHashMapKeyIterable extends IterableBase + implements EfficientLength { + final _map; + LinkedHashMapKeyIterable(this._map); + + int get length => _map._length; + bool get isEmpty => _map._length == 0; + + Iterator get iterator { + return new LinkedHashMapKeyIterator(_map, _map._modifications); + } + + bool contains(Object element) { + return _map.containsKey(element); + } + + void forEach(void f(E element)) { + LinkedHashMapCell cell = _map._first; + int modifications = _map._modifications; + while (cell != null) { + f(cell._key); + if (modifications != _map._modifications) { + throw new ConcurrentModificationError(_map); + } + cell = cell._next; + } + } +} + +class LinkedHashMapKeyIterator implements Iterator { + final _map; + final int _modifications; + LinkedHashMapCell _cell; + E _current; + + LinkedHashMapKeyIterator(this._map, this._modifications) { + _cell = _map._first; + } + + E get current => _current; + + bool moveNext() { + if (_modifications != _map._modifications) { + throw new ConcurrentModificationError(_map); + } else if (_cell == null) { + _current = null; + return false; + } else { + _current = _cell._key; + _cell = _cell._next; + return true; + } + } +} + +patch class HashSet { + patch factory HashSet({ bool equals(E e1, E e2), + int hashCode(E e), + bool isValidKey(potentialKey) }) { + if (isValidKey == null) { + if (hashCode == null) { + if (equals == null) { + return new _HashSet(); + } + hashCode = _defaultHashCode; + } else { + if (identical(identityHashCode, hashCode) && + identical(identical, equals)) { + return new _IdentityHashSet(); + } + if (equals == null) { + equals = _defaultEquals; + } + } + } else { + if (hashCode == null) { + hashCode = _defaultHashCode; + } + if (equals == null) { + equals = _defaultEquals; + } + } + return new _CustomHashSet(equals, hashCode, isValidKey); + } + + patch factory HashSet.identity() = _IdentityHashSet; +} + +class _HashSet extends _HashSetBase implements HashSet { + int _length = 0; + + // The hash set contents are divided into three parts: one part for + // string elements, one for numeric elements, and one for the + // rest. String and numeric elements map directly to a sentinel + // value, but the rest of the entries are stored in bucket lists of + // the form: + // + // [element-0, element-1, element-2, ...] + // + // where all elements in the same bucket share the same hash code. + var _strings; + var _nums; + var _rest; + + // When iterating over the hash set, it is very convenient to have a + // list of all the elements. We cache that on the instance and clear + // the the cache whenever the set changes. This is also used to + // guard against concurrent modifications. + List _elements; + + _HashSet(); + + Set _newSet() => new _HashSet(); + + // Iterable. + Iterator get iterator { + return new HashSetIterator(this, _computeElements()); + } + + int get length => _length; + bool get isEmpty => _length == 0; + bool get isNotEmpty => !isEmpty; + + bool contains(Object object) { + if (_isStringElement(object)) { + var strings = _strings; + return (strings == null) ? false : _hasTableEntry(strings, object); + } else if (_isNumericElement(object)) { + var nums = _nums; + return (nums == null) ? false : _hasTableEntry(nums, object); + } else { + var rest = _rest; + if (rest == null) return false; + var bucket = _getBucket(rest, object); + return _findBucketIndex(bucket, object) >= 0; + } + } + + E lookup(Object object) { + if (_isStringElement(object) || _isNumericElement(object)) { + return this.contains(object) ? object : null; + } + var rest = _rest; + if (rest == null) return null; + var bucket = _getBucket(rest, object); + var index = _findBucketIndex(bucket, object); + if (index < 0) return null; + return bucket[index]; + } + + // Collection. + bool add(E element) { + if (_isStringElement(element)) { + var strings = _strings; + if (strings == null) _strings = strings = _newHashTable(); + return _addHashTableEntry(strings, element); + } else if (_isNumericElement(element)) { + var nums = _nums; + if (nums == null) _nums = nums = _newHashTable(); + return _addHashTableEntry(nums, element); + } else { + var rest = _rest; + if (rest == null) _rest = rest = _newHashTable(); + var hash = _computeHashCode(element); + var bucket = JS('var', '#[#]', rest, hash); + if (bucket == null) { + _setTableEntry(rest, hash, JS('var', '[#]', element)); + } else { + int index = _findBucketIndex(bucket, element); + if (index >= 0) return false; + JS('void', '#.push(#)', bucket, element); + } + _length++; + _elements = null; + return true; + } + } + + void addAll(Iterable objects) { + for (E each in objects) { + add(each); + } + } + + bool remove(Object object) { + if (_isStringElement(object)) { + return _removeHashTableEntry(_strings, object); + } else if (_isNumericElement(object)) { + return _removeHashTableEntry(_nums, object); + } else { + var rest = _rest; + if (rest == null) return false; + var bucket = _getBucket(rest, object); + int index = _findBucketIndex(bucket, object); + if (index < 0) return false; + // TODO(kasperl): Consider getting rid of the bucket list when + // the length reaches zero. + _length--; + _elements = null; + // TODO(kasperl): It would probably be faster to move the + // element to the end and reduce the length of the bucket list. + JS('void', '#.splice(#, 1)', bucket, index); + return true; + } + } + + void removeAll(Iterable objectsToRemove) { + for (var each in objectsToRemove) { + remove(each); + } + } + + void retainAll(Iterable elements) { + super._retainAll(elements, (o) => o is E); + } + + void removeWhere(bool test(E element)) { + removeAll(_computeElements().where(test)); + } + + void retainWhere(bool test(E element)) { + removeAll(_computeElements().where((E element) => !test(element))); + } + + void clear() { + if (_length > 0) { + _strings = _nums = _rest = _elements = null; + _length = 0; + } + } + + List _computeElements() { + if (_elements != null) return _elements; + List result = new List(_length); + int index = 0; + + // Add all string elements to the list. + var strings = _strings; + if (strings != null) { + var names = JS('var', 'Object.getOwnPropertyNames(#)', strings); + int entries = JS('int', '#.length', names); + for (int i = 0; i < entries; i++) { + String element = JS('String', '#[#]', names, i); + JS('void', '#[#] = #', result, index, element); + index++; + } + } + + // Add all numeric elements to the list. + var nums = _nums; + if (nums != null) { + var names = JS('var', 'Object.getOwnPropertyNames(#)', nums); + int entries = JS('int', '#.length', names); + for (int i = 0; i < entries; i++) { + // Object.getOwnPropertyNames returns a list of strings, so we + // have to convert the elements back to numbers (+). + num element = JS('num', '+#[#]', names, i); + JS('void', '#[#] = #', result, index, element); + index++; + } + } + + // Add all the remaining elements to the list. + var rest = _rest; + if (rest != null) { + var names = JS('var', 'Object.getOwnPropertyNames(#)', rest); + int entries = JS('int', '#.length', names); + for (int i = 0; i < entries; i++) { + var entry = JS('String', '#[#]', names, i); + var bucket = JS('var', '#[#]', rest, entry); + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i++) { + JS('void', '#[#] = #[#]', result, index, bucket, i); + index++; + } + } + } + assert(index == _length); + return _elements = result; + } + + bool _addHashTableEntry(var table, E element) { + if (_hasTableEntry(table, element)) return false; + _setTableEntry(table, element, 0); + _length++; + _elements = null; + return true; + } + + bool _removeHashTableEntry(var table, Object element) { + if (table != null && _hasTableEntry(table, element)) { + _deleteTableEntry(table, element); + _length--; + _elements = null; + return true; + } else { + return false; + } + } + + static bool _isStringElement(var element) { + return element is String && element != '__proto__'; + } + + static bool _isNumericElement(var element) { + // Only treat unsigned 30-bit integers as numeric elements. This + // way, we avoid converting them to strings when we use them as + // keys in the JavaScript hash table object. + return element is num && + JS('bool', '(# & 0x3ffffff) === #', element, element); + } + + int _computeHashCode(var element) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic elements like '__proto__'. Another + // option would be to throw an exception if the hash code isn't a + // number. + return JS('int', '# & 0x3ffffff', element.hashCode); + } + + static bool _hasTableEntry(var table, var key) { + var entry = JS('var', '#[#]', table, key); + // We take care to only store non-null entries in the table, so we + // can check if the table has an entry for the given key with a + // simple null check. + return entry != null; + } + + static void _setTableEntry(var table, var key, var value) { + assert(value != null); + JS('void', '#[#] = #', table, key, value); + } + + static void _deleteTableEntry(var table, var key) { + JS('void', 'delete #[#]', table, key); + } + + List _getBucket(var table, var element) { + var hash = _computeHashCode(element); + return JS('var', '#[#]', table, hash); + } + + int _findBucketIndex(var bucket, var element) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i++) { + if (JS('var', '#[#]', bucket, i) == element) return i; + } + return -1; + } + + static _newHashTable() { + // Create a new JavaScript object to be used as a hash table. Use + // Object.create to avoid the properties on Object.prototype + // showing up as entries. + var table = JS('var', 'Object.create(null)'); + // Attempt to force the hash table into 'dictionary' mode by + // adding a property to it and deleting it again. + var temporaryKey = ''; + _setTableEntry(table, temporaryKey, table); + _deleteTableEntry(table, temporaryKey); + return table; + } +} + +class _IdentityHashSet extends _HashSet { + Set _newSet() => new _IdentityHashSet(); + + int _computeHashCode(var key) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic keys like '__proto__'. Another option + // would be to throw an exception if the hash code isn't a number. + return JS('int', '# & 0x3ffffff', identityHashCode(key)); + } + + int _findBucketIndex(var bucket, var element) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i++) { + if (identical(JS('var', '#[#]', bucket, i), element)) return i; + } + return -1; + } +} + +class _CustomHashSet extends _HashSet { + _Equality _equality; + _Hasher _hasher; + _Predicate _validKey; + _CustomHashSet(this._equality, this._hasher, bool validKey(potentialKey)) + : _validKey = (validKey != null) ? validKey : ((x) => x is E); + + Set _newSet() => new _CustomHashSet(_equality, _hasher, _validKey); + + int _findBucketIndex(var bucket, var element) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i++) { + if (_equality(JS('var', '#[#]', bucket, i), element)) return i; + } + return -1; + } + + int _computeHashCode(var element) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic elements like '__proto__'. Another + // option would be to throw an exception if the hash code isn't a + // number. + return JS('int', '# & 0x3ffffff', _hasher(element)); + } + + bool contains(Object object) { + if (!_validKey(object)) return false; + return super.contains(object); + } + + E lookup(Object object) { + if (!_validKey(object)) return null; + return super.lookup(object); + } + + bool remove(Object object) { + if (!_validKey(object)) return false; + return super.remove(object); + } + + bool containsAll(Iterable elements) { + for (Object element in elements) { + if (!_validKey(element) || !this.contains(element)) return false; + } + return true; + } + + void removeAll(Iterable elements) { + for (Object element in elements) { + if (_validKey(element)) { + super.remove(element); + } + } + } + + void retainAll(Iterable elements) { + super._retainAll(elements, _validKey); + } +} + +// TODO(kasperl): Share this code with HashMapKeyIterator? +class HashSetIterator implements Iterator { + final _set; + final List _elements; + int _offset = 0; + E _current; + + HashSetIterator(this._set, this._elements); + + E get current => _current; + + bool moveNext() { + var elements = _elements; + int offset = _offset; + if (JS('bool', '# !== #', elements, _set._elements)) { + throw new ConcurrentModificationError(_set); + } else if (offset >= JS('int', '#.length', elements)) { + _current = null; + return false; + } else { + _current = JS('var', '#[#]', elements, offset); + // TODO(kasperl): For now, we have to tell the type inferrer to + // treat the result of doing offset + 1 as an int. Otherwise, we + // get unnecessary bailout code. + _offset = JS('int', '#', offset + 1); + return true; + } + } +} + +patch class LinkedHashSet { + patch factory LinkedHashSet({ bool equals(E e1, E e2), + int hashCode(E e), + bool isValidKey(potentialKey) }) { + if (isValidKey == null) { + if (hashCode == null) { + if (equals == null) { + return new _LinkedHashSet(); + } + hashCode = _defaultHashCode; + } else { + if (identical(identityHashCode, hashCode) && + identical(identical, equals)) { + return new _LinkedIdentityHashSet(); + } + if (equals == null) { + equals = _defaultEquals; + } + } + } else { + if (hashCode == null) { + hashCode = _defaultHashCode; + } + if (equals == null) { + equals = _defaultEquals; + } + } + return new _LinkedCustomHashSet(equals, hashCode, isValidKey); + } + + patch factory LinkedHashSet.identity() = _LinkedIdentityHashSet; +} + +class _LinkedHashSet extends _HashSetBase implements LinkedHashSet { + int _length = 0; + + // The hash set contents are divided into three parts: one part for + // string elements, one for numeric elements, and one for the + // rest. String and numeric elements map directly to their linked + // cells, but the rest of the entries are stored in bucket lists of + // the form: + // + // [cell-0, cell-1, ...] + // + // where all elements in the same bucket share the same hash code. + var _strings; + var _nums; + var _rest; + + // The elements are stored in cells that are linked together + // to form a double linked list. + LinkedHashSetCell _first; + LinkedHashSetCell _last; + + // We track the number of modifications done to the element set to + // be able to throw when the set is modified while being iterated + // over. + int _modifications = 0; + + _LinkedHashSet(); + + Set _newSet() => new _LinkedHashSet(); + + void _unsupported(String operation) { + throw 'LinkedHashSet: unsupported $operation'; + } + + // Iterable. + Iterator get iterator { + return new LinkedHashSetIterator(this, _modifications); + } + + int get length => _length; + bool get isEmpty => _length == 0; + bool get isNotEmpty => !isEmpty; + + bool contains(Object object) { + if (_isStringElement(object)) { + var strings = _strings; + if (strings == null) return false; + LinkedHashSetCell cell = _getTableEntry(strings, object); + return cell != null; + } else if (_isNumericElement(object)) { + var nums = _nums; + if (nums == null) return false; + LinkedHashSetCell cell = _getTableEntry(nums, object); + return cell != null; + } else { + var rest = _rest; + if (rest == null) return false; + var bucket = _getBucket(rest, object); + return _findBucketIndex(bucket, object) >= 0; + } + } + + E lookup(Object object) { + if (_isStringElement(object) || _isNumericElement(object)) { + return this.contains(object) ? object : null; + } else { + var rest = _rest; + if (rest == null) return null; + var bucket = _getBucket(rest, object); + var index = _findBucketIndex(bucket, object); + if (index < 0) return null; + return bucket[index]._element; + } + } + + void forEach(void action(E element)) { + LinkedHashSetCell cell = _first; + int modifications = _modifications; + while (cell != null) { + action(cell._element); + if (modifications != _modifications) { + throw new ConcurrentModificationError(this); + } + cell = cell._next; + } + } + + E get first { + if (_first == null) throw new StateError("No elements"); + return _first._element; + } + + E get last { + if (_last == null) throw new StateError("No elements"); + return _last._element; + } + + // Collection. + bool add(E element) { + if (_isStringElement(element)) { + var strings = _strings; + if (strings == null) _strings = strings = _newHashTable(); + return _addHashTableEntry(strings, element); + } else if (_isNumericElement(element)) { + var nums = _nums; + if (nums == null) _nums = nums = _newHashTable(); + return _addHashTableEntry(nums, element); + } else { + var rest = _rest; + if (rest == null) _rest = rest = _newHashTable(); + var hash = _computeHashCode(element); + var bucket = JS('var', '#[#]', rest, hash); + if (bucket == null) { + LinkedHashSetCell cell = _newLinkedCell(element); + _setTableEntry(rest, hash, JS('var', '[#]', cell)); + } else { + int index = _findBucketIndex(bucket, element); + if (index >= 0) return false; + LinkedHashSetCell cell = _newLinkedCell(element); + JS('void', '#.push(#)', bucket, cell); + } + return true; + } + } + + void addAll(Iterable objects) { + for (E object in objects) { + add(object); + } + } + + bool remove(Object object) { + if (_isStringElement(object)) { + return _removeHashTableEntry(_strings, object); + } else if (_isNumericElement(object)) { + return _removeHashTableEntry(_nums, object); + } else { + var rest = _rest; + if (rest == null) return false; + var bucket = _getBucket(rest, object); + int index = _findBucketIndex(bucket, object); + if (index < 0) return false; + // Use splice to remove the [cell] element at the index and + // unlink it. + LinkedHashSetCell cell = JS('var', '#.splice(#, 1)[0]', bucket, index); + _unlinkCell(cell); + return true; + } + } + + void removeAll(Iterable objectsToRemove) { + for (var each in objectsToRemove) { + remove(each); + } + } + + void retainAll(Iterable elements) { + super._retainAll(elements, (o) => o is E); + } + + void removeWhere(bool test(E element)) { + _filterWhere(test, true); + } + + void retainWhere(bool test(E element)) { + _filterWhere(test, false); + } + + void _filterWhere(bool test(E element), bool removeMatching) { + LinkedHashSetCell cell = _first; + while (cell != null) { + E element = cell._element; + LinkedHashSetCell next = cell._next; + int modifications = _modifications; + bool shouldRemove = (removeMatching == test(element)); + if (modifications != _modifications) { + throw new ConcurrentModificationError(this); + } + if (shouldRemove) remove(element); + cell = next; + } + } + + void clear() { + if (_length > 0) { + _strings = _nums = _rest = _first = _last = null; + _length = 0; + _modified(); + } + } + + bool _addHashTableEntry(var table, E element) { + LinkedHashSetCell cell = _getTableEntry(table, element); + if (cell != null) return false; + _setTableEntry(table, element, _newLinkedCell(element)); + return true; + } + + bool _removeHashTableEntry(var table, Object element) { + if (table == null) return false; + LinkedHashSetCell cell = _getTableEntry(table, element); + if (cell == null) return false; + _unlinkCell(cell); + _deleteTableEntry(table, element); + return true; + } + + void _modified() { + // Value cycles after 2^30 modifications. If you keep hold of an + // iterator for that long, you might miss a modification + // detection, and iteration can go sour. Don't do that. + _modifications = (_modifications + 1) & 0x3ffffff; + } + + // Create a new cell and link it in as the last one in the list. + LinkedHashSetCell _newLinkedCell(E element) { + LinkedHashSetCell cell = new LinkedHashSetCell(element); + if (_first == null) { + _first = _last = cell; + } else { + LinkedHashSetCell last = _last; + cell._previous = last; + _last = last._next = cell; + } + _length++; + _modified(); + return cell; + } + + // Unlink the given cell from the linked list of cells. + void _unlinkCell(LinkedHashSetCell cell) { + LinkedHashSetCell previous = cell._previous; + LinkedHashSetCell next = cell._next; + if (previous == null) { + assert(cell == _first); + _first = next; + } else { + previous._next = next; + } + if (next == null) { + assert(cell == _last); + _last = previous; + } else { + next._previous = previous; + } + _length--; + _modified(); + } + + static bool _isStringElement(var element) { + return element is String && element != '__proto__'; + } + + static bool _isNumericElement(var element) { + // Only treat unsigned 30-bit integers as numeric elements. This + // way, we avoid converting them to strings when we use them as + // keys in the JavaScript hash table object. + return element is num && + JS('bool', '(# & 0x3ffffff) === #', element, element); + } + + int _computeHashCode(var element) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic elements like '__proto__'. Another + // option would be to throw an exception if the hash code isn't a + // number. + return JS('int', '# & 0x3ffffff', element.hashCode); + } + + static _getTableEntry(var table, var key) { + return JS('var', '#[#]', table, key); + } + + static void _setTableEntry(var table, var key, var value) { + assert(value != null); + JS('void', '#[#] = #', table, key, value); + } + + static void _deleteTableEntry(var table, var key) { + JS('void', 'delete #[#]', table, key); + } + + List _getBucket(var table, var element) { + var hash = _computeHashCode(element); + return JS('var', '#[#]', table, hash); + } + + int _findBucketIndex(var bucket, var element) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i++) { + LinkedHashSetCell cell = JS('var', '#[#]', bucket, i); + if (cell._element == element) return i; + } + return -1; + } + + static _newHashTable() { + // Create a new JavaScript object to be used as a hash table. Use + // Object.create to avoid the properties on Object.prototype + // showing up as entries. + var table = JS('var', 'Object.create(null)'); + // Attempt to force the hash table into 'dictionary' mode by + // adding a property to it and deleting it again. + var temporaryKey = ''; + _setTableEntry(table, temporaryKey, table); + _deleteTableEntry(table, temporaryKey); + return table; + } +} + +class _LinkedIdentityHashSet extends _LinkedHashSet { + Set _newSet() => new _LinkedIdentityHashSet(); + + int _computeHashCode(var key) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic keys like '__proto__'. Another option + // would be to throw an exception if the hash code isn't a number. + return JS('int', '# & 0x3ffffff', identityHashCode(key)); + } + + int _findBucketIndex(var bucket, var element) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i++) { + LinkedHashSetCell cell = JS('var', '#[#]', bucket, i); + if (identical(cell._element, element)) return i; + } + return -1; + } +} + +class _LinkedCustomHashSet extends _LinkedHashSet { + _Equality _equality; + _Hasher _hasher; + _Predicate _validKey; + _LinkedCustomHashSet(this._equality, this._hasher, + bool validKey(potentialKey)) + : _validKey = (validKey != null) ? validKey : ((x) => x is E); + + Set _newSet() => + new _LinkedCustomHashSet(_equality, _hasher, _validKey); + + int _findBucketIndex(var bucket, var element) { + if (bucket == null) return -1; + int length = JS('int', '#.length', bucket); + for (int i = 0; i < length; i++) { + LinkedHashSetCell cell = JS('var', '#[#]', bucket, i); + if (_equality(cell._element, element)) return i; + } + return -1; + } + + int _computeHashCode(var element) { + // We force the hash codes to be unsigned 30-bit integers to avoid + // issues with problematic elements like '__proto__'. Another + // option would be to throw an exception if the hash code isn't a + // number. + return JS('int', '# & 0x3ffffff', _hasher(element)); + } + + bool contains(Object object) { + if (!_validKey(object)) return false; + return super.contains(object); + } + + E lookup(Object object) { + if (!_validKey(object)) return null; + return super.lookup(object); + } + + bool remove(Object object) { + if (!_validKey(object)) return false; + return super.remove(object); + } + + bool containsAll(Iterable elements) { + for (Object element in elements) { + if (!_validKey(element) || !this.contains(element)) return false; + } + return true; + } + + void removeAll(Iterable elements) { + for (Object element in elements) { + if (_validKey(element)) { + super.remove(element); + } + } + } + + void retainAll(Iterable elements) { + super._retainAll(elements, _validKey); + } +} + +class LinkedHashSetCell { + final _element; + + LinkedHashSetCell _next; + LinkedHashSetCell _previous; + + LinkedHashSetCell(this._element); +} + +// TODO(kasperl): Share this code with LinkedHashMapKeyIterator? +class LinkedHashSetIterator implements Iterator { + final _set; + final int _modifications; + LinkedHashSetCell _cell; + E _current; + + LinkedHashSetIterator(this._set, this._modifications) { + _cell = _set._first; + } + + E get current => _current; + + bool moveNext() { + if (_modifications != _set._modifications) { + throw new ConcurrentModificationError(_set); + } else if (_cell == null) { + _current = null; + return false; + } else { + _current = _cell._element; + _cell = _cell._next; + return true; + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/constant_map.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/constant_map.dart new file mode 100644 index 0000000..22b513c --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/constant_map.dart @@ -0,0 +1,145 @@ +// Copyright (c) 2012, 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. + +part of _js_helper; + +abstract class ConstantMap implements Map { + const ConstantMap._(); + + bool get isEmpty => length == 0; + + bool get isNotEmpty => !isEmpty; + + String toString() => Maps.mapToString(this); + + _throwUnmodifiable() { + throw new UnsupportedError("Cannot modify unmodifiable Map"); + } + void operator []=(K key, V val) => _throwUnmodifiable(); + V putIfAbsent(K key, V ifAbsent()) => _throwUnmodifiable(); + V remove(K key) => _throwUnmodifiable(); + void clear() => _throwUnmodifiable(); + void addAll(Map other) => _throwUnmodifiable(); +} + +class ConstantStringMap extends ConstantMap + implements _symbol_dev.EfficientLength { + + // This constructor is not used. The instantiation is shortcut by the + // compiler. It is here to make the uninitialized final fields legal. + const ConstantStringMap._(this.length, this._jsObject, this._keys) + : super._(); + + final int length; + // A constant map is backed by a JavaScript object. + final _jsObject; + final List _keys; + + bool containsValue(V needle) { + return values.any((V value) => value == needle); + } + + bool containsKey(Object key) { + if (key is! String) return false; + if ('__proto__' == key) return false; + return jsHasOwnProperty(_jsObject, key); + } + + V operator [](Object key) { + if (!containsKey(key)) return null; + return _fetch(key); + } + + // [_fetch] is the indexer for keys for which `containsKey(key)` is true. + _fetch(key) => jsPropertyAccess(_jsObject, key); + + void forEach(void f(K key, V value)) { + // Use a JS 'cast' to get efficient loop. Type inferrence doesn't get this + // since constant map representation is chosen after type inferrence and the + // instantiation is shortcut by the compiler. + var keys = JS('JSArray', '#', _keys); + for (int i = 0; i < keys.length; i++) { + var key = keys[i]; + f(key, _fetch(key)); + } + } + + Iterable get keys { + return new _ConstantMapKeyIterable(this); + } + + Iterable get values { + return new MappedIterable(_keys, (key) => _fetch(key)); + } +} + +class ConstantProtoMap extends ConstantStringMap { + // This constructor is not used. The instantiation is shortcut by the + // compiler. It is here to make the uninitialized final fields legal. + ConstantProtoMap._(length, jsObject, keys, this._protoValue) + : super._(length, jsObject, keys); + + final V _protoValue; + + bool containsKey(Object key) { + if (key is! String) return false; + if ('__proto__' == key) return true; + return jsHasOwnProperty(_jsObject, key); + } + + _fetch(key) => + '__proto__' == key ? _protoValue : jsPropertyAccess(_jsObject, key); +} + +class _ConstantMapKeyIterable extends IterableBase { + ConstantStringMap _map; + _ConstantMapKeyIterable(this._map); + + Iterator get iterator => _map._keys.iterator; +} + +class GeneralConstantMap extends ConstantMap { + // This constructor is not used. The instantiation is shortcut by the + // compiler. It is here to make the uninitialized final fields legal. + GeneralConstantMap(this._jsData) : super._(); + + // [_jsData] holds a key-value pair list. + final _jsData; + + // We cannot create the backing map on creation since hashCode interceptors + // have not been defined when constants are created. + Map _getMap() { + if (JS('bool', r'!this.$map')) { + Map backingMap = new LinkedHashMap(); + JS('', r'this.$map = #', fillLiteralMap(_jsData, backingMap)); + } + return JS('Map', r'this.$map'); + } + + bool containsValue(V needle) { + return _getMap().containsValue(needle); + } + + bool containsKey(Object key) { + return _getMap().containsKey(key); + } + + V operator [](Object key) { + return _getMap()[key]; + } + + void forEach(void f(K key, V value)) { + _getMap().forEach(f); + } + + Iterable get keys { + return _getMap().keys; + } + + Iterable get values { + return _getMap().values; + } + + int get length => _getMap().length; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/convert_patch.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/convert_patch.dart new file mode 100644 index 0000000..997db63 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/convert_patch.dart @@ -0,0 +1,99 @@ +// Copyright (c) 2013, 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. + +// Patch file for dart:convert library. + +import 'dart:_foreign_helper' show JS; +import 'dart:_interceptors' show JSExtendableArray; + +/** + * Parses [json] and builds the corresponding parsed JSON value. + * + * Parsed JSON values Nare of the types [num], [String], [bool], [Null], + * [List]s of parsed JSON values or [Map]s from [String] to parsed + * JSON values. + * + * The optional [reviver] function, if provided, is called once for each object + * or list property parsed. The arguments are the property name ([String]) or + * list index ([int]), and the value is the parsed value. The return value of + * the reviver will be used as the value of that property instead of the parsed + * value. The top level value is passed to the reviver with the empty string as + * a key. + * + * Throws [FormatException] if the input is not valid JSON text. + */ +patch _parseJson(String source, reviver(key, value)) { + if (source is! String) throw new ArgumentError(source); + + var parsed; + try { + parsed = JS('=Object|JSExtendableArray|Null|bool|num|String', + 'JSON.parse(#)', + source); + } catch (e) { + throw new FormatException(JS('String', 'String(#)', e)); + } + + return _convertJsonToDart(parsed, reviver); +} + +/** + * Walks the raw JavaScript value [json], replacing JavaScript Objects with + * Maps. [json] is expected to be freshly allocated so elements can be replaced + * in-place. + */ +_convertJsonToDart(json, reviver(key, value)) { + + var revive = reviver == null ? (key, value) => value : reviver; + + walk(e) { + // JavaScript null, string, number, bool are in the correct representation. + if (JS('bool', '# == null', e) || JS('bool', 'typeof # != "object"', e)) { + return e; + } + + // This test is needed to avoid identifing '{"__proto__":[]}' as an Array. + // TODO(sra): Replace this test with cheaper '#.constructor === Array' when + // bug 621 below is fixed. + if (JS('bool', 'Object.getPrototypeOf(#) === Array.prototype', e)) { + var list = JS('JSExtendableArray', '#', e); // Teach compiler the type is known. + // In-place update of the elements since JS Array is a Dart List. + for (int i = 0; i < list.length; i++) { + // Use JS indexing to avoid range checks. We know this is the only + // reference to the list, but the compiler will likely never be able to + // tell that this instance of the list cannot have its length changed by + // the reviver even though it later will be passed to the reviver at the + // outer level. + var item = JS('', '#[#]', list, i); + JS('', '#[#]=#', list, i, revive(i, walk(item))); + } + return list; + } + + // Otherwise it is a plain Object, so copy to a Map. + var keys = JS('JSExtendableArray', 'Object.keys(#)', e); + Map map = {}; + for (int i = 0; i < keys.length; i++) { + String key = keys[i]; + map[key] = revive(key, walk(JS('', '#[#]', e, key))); + } + // V8 has a bug with properties named "__proto__" + // https://code.google.com/p/v8/issues/detail?id=621 + var proto = JS('', '#.__proto__', e); + // __proto__ can be undefined on IE9. + if (JS('bool', + 'typeof # !== "undefined" && # !== Object.prototype', + proto, proto)) { + map['__proto__'] = revive('__proto__', walk(proto)); + } + return map; + } + + return revive(null, walk(json)); +} + +patch class _Utf8Encoder { + // Use Uint8List when supported on all platforms. + patch static List _createBuffer(int size) => new List(size); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/core_patch.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/core_patch.dart new file mode 100644 index 0000000..30aa926 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/core_patch.dart @@ -0,0 +1,332 @@ +// Copyright (c) 2012, 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. + +// Patch file for dart:core classes. +import "dart:_internal" as _symbol_dev; +import 'dart:_interceptors'; +import 'dart:_js_helper' show checkNull, + getRuntimeType, + JSSyntaxRegExp, + Primitives, + stringJoinUnchecked, + objectHashCode; + +String _symbolToString(Symbol symbol) => _symbol_dev.Symbol.getName(symbol); + +_symbolMapToStringMap(Map map) { + if (map == null) return null; + var result = new Map(); + map.forEach((Symbol key, value) { + result[_symbolToString(key)] = value; + }); + return result; +} + +patch int identityHashCode(Object object) => objectHashCode(object); + +// Patch for Object implementation. +patch class Object { + patch int get hashCode => Primitives.objectHashCode(this); + + + patch String toString() => Primitives.objectToString(this); + + patch dynamic noSuchMethod(Invocation invocation) { + throw new NoSuchMethodError( + this, + invocation.memberName, + invocation.positionalArguments, + invocation.namedArguments); + } + + patch Type get runtimeType => getRuntimeType(this); +} + +// Patch for Function implementation. +patch class Function { + patch static apply(Function function, + List positionalArguments, + [Map namedArguments]) { + return Primitives.applyFunction( + function, positionalArguments, _toMangledNames(namedArguments)); + } + + static Map _toMangledNames( + Map namedArguments) { + if (namedArguments == null) return null; + Map result = {}; + namedArguments.forEach((symbol, value) { + result[_symbolToString(symbol)] = value; + }); + return result; + } +} + +// Patch for Expando implementation. +patch class Expando { + patch Expando([String name]) : this.name = name; + + patch T operator[](Object object) { + var values = Primitives.getProperty(object, _EXPANDO_PROPERTY_NAME); + return (values == null) ? null : Primitives.getProperty(values, _getKey()); + } + + patch void operator[]=(Object object, T value) { + var values = Primitives.getProperty(object, _EXPANDO_PROPERTY_NAME); + if (values == null) { + values = new Object(); + Primitives.setProperty(object, _EXPANDO_PROPERTY_NAME, values); + } + Primitives.setProperty(values, _getKey(), value); + } + + String _getKey() { + String key = Primitives.getProperty(this, _KEY_PROPERTY_NAME); + if (key == null) { + key = "expando\$key\$${_keyCount++}"; + Primitives.setProperty(this, _KEY_PROPERTY_NAME, key); + } + return key; + } + + static const String _KEY_PROPERTY_NAME = 'expando\$key'; + static const String _EXPANDO_PROPERTY_NAME = 'expando\$values'; + static int _keyCount = 0; +} + +patch class int { + patch static int parse(String source, + { int radix, + int onError(String source) }) { + return Primitives.parseInt(source, radix, onError); + } + + patch factory int.fromEnvironment(String name, {int defaultValue}) { + throw new UnsupportedError( + 'int.fromEnvironment can only be used as a const constructor'); + } +} + +patch class double { + patch static double parse(String source, + [double onError(String source)]) { + return Primitives.parseDouble(source, onError); + } +} + +patch class Error { + patch static String _objectToString(Object object) { + return Primitives.objectToString(object); + } + + patch StackTrace get stackTrace => Primitives.extractStackTrace(this); +} + +// Patch for DateTime implementation. +patch class DateTime { + patch DateTime._internal(int year, + int month, + int day, + int hour, + int minute, + int second, + int millisecond, + bool isUtc) + : this.isUtc = checkNull(isUtc), + millisecondsSinceEpoch = Primitives.valueFromDecomposedDate( + year, month, day, hour, minute, second, millisecond, isUtc) { + Primitives.lazyAsJsDate(this); + } + + patch DateTime._now() + : isUtc = false, + millisecondsSinceEpoch = Primitives.dateNow() { + Primitives.lazyAsJsDate(this); + } + + patch static int _brokenDownDateToMillisecondsSinceEpoch( + int year, int month, int day, int hour, int minute, int second, + int millisecond, bool isUtc) { + return Primitives.valueFromDecomposedDate( + year, month, day, hour, minute, second, millisecond, isUtc); + } + + patch String get timeZoneName { + if (isUtc) return "UTC"; + return Primitives.getTimeZoneName(this); + } + + patch Duration get timeZoneOffset { + if (isUtc) return new Duration(); + return new Duration(minutes: Primitives.getTimeZoneOffsetInMinutes(this)); + } + + patch int get year => Primitives.getYear(this); + + patch int get month => Primitives.getMonth(this); + + patch int get day => Primitives.getDay(this); + + patch int get hour => Primitives.getHours(this); + + patch int get minute => Primitives.getMinutes(this); + + patch int get second => Primitives.getSeconds(this); + + patch int get millisecond => Primitives.getMilliseconds(this); + + patch int get weekday => Primitives.getWeekday(this); +} + + +// Patch for Stopwatch implementation. +patch class Stopwatch { + patch static int _frequency() => 1000000; + patch static int _now() => Primitives.numMicroseconds(); +} + +class _ListConstructorSentinel extends JSInt { + const _ListConstructorSentinel(); +} + +// Patch for List implementation. +patch class List { + patch factory List([int length = const _ListConstructorSentinel()]) { + if (length == const _ListConstructorSentinel()) { + return new JSArray.emptyGrowable(); + } + return new JSArray.fixed(length); + } + + patch factory List.filled(int length, E fill) { + List result = new JSArray.fixed(length); + if (length != 0 && fill != null) { + for (int i = 0; i < result.length; i++) { + result[i] = fill; + } + } + return result; + } +} + + +patch class String { + patch factory String.fromCharCodes(Iterable charCodes) { + if (charCodes is! JSArray) { + charCodes = new List.from(charCodes); + } + return Primitives.stringFromCharCodes(charCodes); + } + + patch factory String.fromEnvironment(String name, {String defaultValue}) { + throw new UnsupportedError( + 'String.fromEnvironment can only be used as a const constructor'); + } +} + +patch class bool { + patch factory bool.fromEnvironment(String name, {bool defaultValue: false}) { + throw new UnsupportedError( + 'bool.fromEnvironment can only be used as a const constructor'); + } +} + +patch class RegExp { + patch factory RegExp(String source, + {bool multiLine: false, + bool caseSensitive: true}) + => new JSSyntaxRegExp(source, + multiLine: multiLine, + caseSensitive: caseSensitive); +} + +// Patch for 'identical' function. +patch bool identical(Object a, Object b) { + return Primitives.identicalImplementation(a, b); +} + +patch class StringBuffer { + String _contents = ""; + + patch StringBuffer([Object content = ""]) { + if (content is String) { + _contents = content; + } else { + write(content); + } + } + + patch int get length => _contents.length; + + patch void write(Object obj) { + String str = obj is String ? obj : "$obj"; + _contents = Primitives.stringConcatUnchecked(_contents, str); + } + + patch void writeCharCode(int charCode) { + write(new String.fromCharCode(charCode)); + } + + patch void clear() { + _contents = ""; + } + + patch String toString() => _contents; +} + +patch class NoSuchMethodError { + patch String toString() { + StringBuffer sb = new StringBuffer(); + int i = 0; + if (_arguments != null) { + for (; i < _arguments.length; i++) { + if (i > 0) { + sb.write(", "); + } + sb.write(Error.safeToString(_arguments[i])); + } + } + if (_namedArguments != null) { + _namedArguments.forEach((Symbol key, var value) { + if (i > 0) { + sb.write(", "); + } + sb.write(_symbolToString(key)); + sb.write(": "); + sb.write(Error.safeToString(value)); + i++; + }); + } + if (_existingArgumentNames == null) { + return "NoSuchMethodError : method not found: '$_memberName'\n" + "Receiver: ${Error.safeToString(_receiver)}\n" + "Arguments: [$sb]"; + } else { + String actualParameters = sb.toString(); + sb = new StringBuffer(); + for (int i = 0; i < _existingArgumentNames.length; i++) { + if (i > 0) { + sb.write(", "); + } + sb.write(_existingArgumentNames[i]); + } + String formalParameters = sb.toString(); + return "NoSuchMethodError: incorrect number of arguments passed to " + "method named '$_memberName'\n" + "Receiver: ${Error.safeToString(_receiver)}\n" + "Tried calling: $_memberName($actualParameters)\n" + "Found: $_memberName($formalParameters)"; + } + } +} + +patch class Uri { + patch static bool get _isWindows => false; + + patch static Uri get base { + String uri = Primitives.currentUri(); + if (uri != null) return Uri.parse(uri); + throw new UnsupportedError("'Uri.base' is not supported"); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/foreign_helper.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/foreign_helper.dart new file mode 100644 index 0000000..cfaa701 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/foreign_helper.dart @@ -0,0 +1,281 @@ +// Copyright (c) 2012, 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. + +library _foreign_helper; + +/** + * Emits a JavaScript code fragment parameterized by arguments. + * + * Hash characters `#` in the [codeTemplate] are replaced in left-to-right order + * with expressions that contain the values of, or evaluate to, the arguments. + * The number of hash marks must match the number or arguments. Although + * declared with arguments [arg0] through [arg2], the form actually has no limit + * on the number of arguments. + * + * The [typeDescription] argument is interpreted as a description of the + * behavior of the JavaScript code. Currently it describes the types that may + * be returned by the expression, with the additional behavior that the returned + * values may be fresh instances of the types. The type information must be + * correct as it is trusted by the compiler in optimizations, and it must be + * precise as possible since it is used for native live type analysis to + * tree-shake large parts of the DOM libraries. If poorly written, the + * [typeDescription] will cause unnecessarily bloated programs. (You can check + * for this by compiling with `--verbose`; there is an info message describing + * the number of native (DOM) types that can be removed, which usually should be + * greater than zero.) + * + * The [typeDescription] is a [String] which contains a union of types separated + * by vertical bar `|` symbols, e.g. `"num|String"` describes the union of + * numbers and Strings. There is no type in Dart that is this precise. The + * Dart alternative would be `Object` or `dynamic`, but these types imply that + * the JS-code might also be creating instances of all the DOM types. If `null` + * is possible, it must be specified explicitly, e.g. `"String|Null"`. + * [typeDescription] has several extensions to help describe the behavior more + * accurately. In addition to the union type already described: + * + * + `=Object` is a plain JavaScript object. Some DOM methods return instances + * that have no corresponing Dart type (e.g. cross-frame documents), + * `=Object` can be used to describe these untyped' values. + * + * + `var` (or empty string). If the entire [typeDescription] is `var` (or + * empty string) then the type is `dynamic` but the code is known to not + * create any instances. + * + * Examples: + * + * // Parent window might be an opaque cross-frame window. + * var thing = JS('=Object|Window', '#.parent', myWindow); + * + * Guidelines: + * + * + Do not use any parameter, local, method or field names in the + * [codeTemplate]. These names are all subject to arbitrary renaming by the + * compiler. Pass the values in via `#` substition, and test with the + * `--minify` dart2js command-line option. + * + * + The substituted expressions are values, not locations. + * + * JS('void', '# += "x"', this.field); + * + * `this.field` might not be a substituted as a reference to the field. The + * generated code might accidentally work as intended, but it also might be + * + * var t1 = this.field; + * t1 += "x"; + * + * or + * + * this.get$field() += "x"; + * + * The remedy in this case is to expand the `+=` operator, leaving all + * references to the Dart field as Dart code: + * + * this.field = JS('String', '# + "x"', this.field); + * + * + Never use `#` in function bodies. + * + * This is a variation on the previous guideline. Since `#` is replaced with + * an *expression* and the expression is only valid in the immediate context, + * `#` should never appear in a function body. Doing so might defer the + * evaluation of the expression, and its side effects, until the function is + * called. + * + * For example, + * + * var value = foo(); + * var f = JS('', 'function(){return #}', value) + * + * might result in no immediate call to `foo` and a call to `foo` on every + * call to the JavaScript function bound to `f`. This is better: + * + * var f = JS('', + * '(function(val) { return function(){return val}; })(#)', value); + * + * Since `#` occurs in the immediately evaluated expression, the expression + * is immediately evaluated and bound to `val` in the immediate call. + * + * + * Additional notes. + * + * In the future we may extend [typeDescription] to include other aspects of the + * behavior, for example, separating the returned types from the instantiated + * types, or including effects to allow the compiler to perform more + * optimizations around the code. This might be an extension of [JS] or a new + * function similar to [JS] with additional arguments for the new information. + */ +// Add additional optional arguments if needed. The method is treated internally +// as a variable argument method. +JS(String typeDescription, String codeTemplate, + [arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11]) +{} + +/** + * Returns the isolate in which this code is running. + */ +IsolateContext JS_CURRENT_ISOLATE_CONTEXT() {} + +abstract class IsolateContext { + /// Holds a (native) JavaScript instance of Isolate, see + /// finishIsolateConstructorFunction in emitter.dart. + get isolateStatics; +} + +/** + * Invokes [function] in the context of [isolate]. + */ +JS_CALL_IN_ISOLATE(isolate, Function function) {} + +/** + * Converts the Dart closure [function] into a JavaScript closure. + * + * Warning: This is no different from [RAW_DART_FUNCTION_REF] which means care + * must be taken to store the current isolate. + */ +DART_CLOSURE_TO_JS(Function function) {} + +/** + * Returns a raw reference to the JavaScript function which implements + * [function]. + * + * Warning: this is dangerous, you should probably use + * [DART_CLOSURE_TO_JS] instead. The returned object is not a valid + * Dart closure, does not store the isolate context or arity. + * + * A valid example of where this can be used is as the second argument + * to V8's Error.captureStackTrace. See + * https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi. + */ +RAW_DART_FUNCTION_REF(Function function) {} + +/** + * Sets the current isolate to [isolate]. + */ +void JS_SET_CURRENT_ISOLATE(isolate) {} + +/** + * Creates an isolate and returns it. + */ +JS_CREATE_ISOLATE() {} + +/** + * Returns the JavaScript constructor function for Dart's Object class. + * This can be used for type tests, as in + * + * if (JS('bool', '# instanceof #', obj, JS_DART_OBJECT_CONSTRUCTOR())) + * ... + */ +JS_DART_OBJECT_CONSTRUCTOR() {} + +/** + * Returns the interceptor for class [type]. The interceptor is the type's + * constructor's `prototype` property. [type] will typically be the class, not + * an interface, e.g. `JS_INTERCEPTOR_CONSTANT(JSInt)`, not + * `JS_INTERCEPTOR_CONSTANT(int)`. + */ +JS_INTERCEPTOR_CONSTANT(Type type) {} + +/** + * Returns the prefix used for generated is checks on classes. + */ +String JS_OPERATOR_IS_PREFIX() {} + +/** + * Returns the prefix used for generated type argument substitutions on classes. + */ +String JS_OPERATOR_AS_PREFIX() {} + +/// Returns the name of the class `Object` in the generated code. +String JS_OBJECT_CLASS_NAME() {} + +/// Returns the name of the class `Null` in the generated code. +String JS_NULL_CLASS_NAME() {} + +/// Returns the name of the class `Function` in the generated code. +String JS_FUNCTION_CLASS_NAME() {} + +/** + * Returns the field name used for determining if an object or its + * interceptor has JavaScript indexing behavior. + */ +String JS_IS_INDEXABLE_FIELD_NAME() {} + +/** + * Returns the object corresponding to Namer.CURRENT_ISOLATE. + */ +JS_CURRENT_ISOLATE() {} + +/// Returns the name used for generated function types on classes and methods. +String JS_SIGNATURE_NAME() {} + +/// Returns the name used to tag function type representations in JavaScript. +String JS_FUNCTION_TYPE_TAG() {} + +/** + * Returns the name used to tag void return in function type representations + * in JavaScript. + */ +String JS_FUNCTION_TYPE_VOID_RETURN_TAG() {} + +/** + * Returns the name used to tag return types in function type representations + * in JavaScript. + */ +String JS_FUNCTION_TYPE_RETURN_TYPE_TAG() {} + +/** + * Returns the name used to tag required parameters in function type + * representations in JavaScript. + */ +String JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG() {} + +/** + * Returns the name used to tag optional parameters in function type + * representations in JavaScript. + */ +String JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG() {} + +/** + * Returns the name used to tag named parameters in function type + * representations in JavaScript. + */ +String JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG() {} + +/** + * Obtain [name] from Namer. + */ +String JS_GET_NAME(String name) {} + +/// Returns the state of a flag that is determined by the state of the compiler +/// when the program has been analyzed. +bool JS_GET_FLAG(String name) {} + +/** + * Pretend [code] is executed. Generates no executable code. This is used to + * model effects at some other point in external code. For example, the + * following models an assignment to foo with an unknown value. + * + * var foo; + * + * main() { + * JS_EFFECT((_){ foo = _; }) + * } + * + * TODO(sra): Replace this hack with something to mark the volatile or + * externally initialized elements. + */ +void JS_EFFECT(Function code) { code(null); } + +/** + * Use this class for creating constants that hold JavaScript code. + * For example: + * + * const constant = JS_CONST('typeof window != "undefined"); + * + * This code will generate: + * $.JS_CONST_1 = typeof window != "undefined"; + */ +class JS_CONST { + final String code; + const JS_CONST(this.code); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/interceptors.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/interceptors.dart new file mode 100644 index 0000000..2823455 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/interceptors.dart @@ -0,0 +1,392 @@ +// Copyright (c) 2012, 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. + +library _interceptors; + +import 'dart:collection'; +import 'dart:_internal' hide Symbol; +import "dart:_internal" as _symbol_dev show Symbol; +import 'dart:_js_helper' show allMatchesInStringUnchecked, + Null, + JSSyntaxRegExp, + Primitives, + checkInt, + checkNull, + checkNum, + checkString, + defineProperty, + getRuntimeType, + initNativeDispatch, + initNativeDispatchFlag, + regExpGetNative, + stringContainsUnchecked, + stringLastIndexOfUnchecked, + stringReplaceAllFuncUnchecked, + stringReplaceAllUnchecked, + stringReplaceFirstUnchecked, + lookupAndCacheInterceptor, + lookupDispatchRecord, + StringMatch, + firstMatchAfter, + NoInline; +import 'dart:_foreign_helper' show JS, JS_EFFECT, JS_INTERCEPTOR_CONSTANT; +import 'dart:math' show Random; + +part 'js_array.dart'; +part 'js_number.dart'; +part 'js_string.dart'; + +String _symbolToString(Symbol symbol) => _symbol_dev.Symbol.getName(symbol); + +_symbolMapToStringMap(Map map) { + if (map == null) return null; + var result = new Map(); + map.forEach((Symbol key, value) { + result[_symbolToString(key)] = value; + }); + return result; +} + +/** + * Get the interceptor for [object]. Called by the compiler when it needs + * to emit a call to an intercepted method, that is a method that is + * defined in an interceptor class. + */ +getInterceptor(object) { + // This is a magic method: the compiler does specialization of it + // depending on the uses of intercepted methods and instantiated + // primitive types. + + // The [JS] call prevents the type analyzer from making assumptions about the + // return type. + return JS('', 'void 0'); +} + +getDispatchProperty(object) { + return JS('', '#[#]', object, JS('String', 'init.dispatchPropertyName')); +} + +setDispatchProperty(object, value) { + defineProperty(object, JS('String', 'init.dispatchPropertyName'), value); +} + +makeDispatchRecord(interceptor, proto, extension, indexability) { + // Dispatch records are stored in the prototype chain, and in some cases, on + // instances. + // + // The record layout and field usage is designed to minimize the number of + // operations on the common paths. + // + // [interceptor] is the interceptor - a holder of methods for the object, + // i.e. the prototype of the interceptor class. + // + // [proto] is usually the prototype, used to check that the dispatch record + // matches the object and is not the dispatch record of a superclass. Other + // values: + // - `false` for leaf classes that need no check. + // - `true` for Dart classes where the object is its own interceptor (unused) + // - a function used to continue matching. + // + // [extension] is used for irregular cases. + // + // [indexability] is used to cache whether or not the object + // implements JavaScriptIndexingBehavior. + // + // proto interceptor extension action + // ----- ----------- --------- ------ + // false I use interceptor I + // true - use object + // P I if object's prototype is P, use I + // F - P if object's prototype is P, call F + + // BUG(10903): Remove this hack. It is needed to avoid inlining this + // method because inlining gives us multiple allocation points for + // records which is bad because it leads to polymorphic access. + if (false) return null; + return JS('', '{i: #, p: #, e: #, x: #}', + interceptor, proto, extension, indexability); +} + +dispatchRecordInterceptor(record) => JS('', '#.i', record); +dispatchRecordProto(record) => JS('', '#.p', record); +dispatchRecordExtension(record) => JS('', '#.e', record); +dispatchRecordIndexability(record) => JS('bool|Null', '#.x', record); + +/** + * Returns the interceptor for a native class instance. Used by + * [getInterceptor]. + */ +getNativeInterceptor(object) { + var record = getDispatchProperty(object); + + if (record == null) { + if (initNativeDispatchFlag == null) { + initNativeDispatch(); + record = getDispatchProperty(object); + } + } + + if (record != null) { + var proto = dispatchRecordProto(record); + if (false == proto) return dispatchRecordInterceptor(record); + if (true == proto) return object; + var objectProto = JS('', 'Object.getPrototypeOf(#)', object); + if (JS('bool', '# === #', proto, objectProto)) { + return dispatchRecordInterceptor(record); + } + + var extension = dispatchRecordExtension(record); + if (JS('bool', '# === #', extension, objectProto)) { + // TODO(sra): The discriminator returns a tag. The tag is an uncached or + // instance-cached tag, defaulting to instance-cached if caching + // unspecified. + var discriminatedTag = JS('', '(#)(#, #)', proto, object, record); + throw new UnimplementedError('Return interceptor for $discriminatedTag'); + } + } + + var interceptor = lookupAndCacheInterceptor(object); + if (interceptor == null) { + // JavaScript Objects created via object literals and `Object.create(null)` + // are 'plain' Objects. This test could be simplified and the dispatch path + // be faster if Object.prototype was pre-patched with a non-leaf dispatch + // record. + var proto = JS('', 'Object.getPrototypeOf(#)', object); + if (JS('bool', '# == null || # === Object.prototype', proto, proto)) { + return JS_INTERCEPTOR_CONSTANT(PlainJavaScriptObject); + } else { + return JS_INTERCEPTOR_CONSTANT(UnknownJavaScriptObject); + } + } + + return interceptor; +} + +/** + * If [JSInvocationMirror._invokeOn] is being used, this variable + * contains a JavaScript array with the names of methods that are + * intercepted. + */ +var interceptedNames; + + +/** + * Data structure used to map a [Type] to the [Interceptor] and constructors for + * that type. It is JavaScript array of 3N entries of adjacent slots containing + * a [Type], followed by an [Interceptor] class for the type, followed by a + * JavaScript object map for the constructors. + * + * The value of this variable is set by the compiler and contains only types + * that are user extensions of native classes where the type occurs as a + * constant in the program. + * + * The compiler, in CustomElementsAnalysis, assumes that [mapTypeToInterceptor] + * is accessed only by code that also calls [findIndexForWebComponentType]. If + * this assumption is invalidated, the compiler will have to be updated. + */ +// TODO(sra): Mark this as initialized to a constant with unknown value. +var mapTypeToInterceptor; + +int findIndexForNativeSubclassType(Type type) { + if (JS('bool', '# == null', mapTypeToInterceptor)) return null; + List map = JS('JSFixedArray', '#', mapTypeToInterceptor); + for (int i = 0; i + 1 < map.length; i += 3) { + if (type == map[i]) { + return i; + } + } + return null; +} + +findInterceptorConstructorForType(Type type) { + var index = findIndexForNativeSubclassType(type); + if (index == null) return null; + List map = JS('JSFixedArray', '#', mapTypeToInterceptor); + return map[index + 1]; +} + +/** + * Returns a JavaScript function that runs the constructor on its argument, or + * `null` if there is no such constructor. + * + * The returned function takes one argument, the web component object. + */ +findConstructorForNativeSubclassType(Type type, String name) { + var index = findIndexForNativeSubclassType(type); + if (index == null) return null; + List map = JS('JSFixedArray', '#', mapTypeToInterceptor); + var constructorMap = map[index + 2]; + var constructorFn = JS('', '#[#]', constructorMap, name); + return constructorFn; +} + +findInterceptorForType(Type type) { + var constructor = findInterceptorConstructorForType(type); + if (constructor == null) return null; + return JS('', '#.prototype', constructor); +} + +/** + * The base interceptor class. + * + * The code `r.foo(a)` is compiled to `getInterceptor(r).foo$1(r, a)`. The + * value returned by [getInterceptor] holds the methods separately from the + * state of the instance. The compiler converts the methods on an interceptor + * to take the Dart `this` argument as an explicit `receiver` argument. The + * JavaScript `this` parameter is bound to the interceptor. + * + * In order to have uniform call sites, if a method is defined on an + * interceptor, methods of that name on plain unintercepted classes also use the + * interceptor calling convention. The plain classes are _self-interceptors_, + * and for them, `getInterceptor(r)` returns `r`. Methods on plain + * unintercepted classes have a redundant `receiver` argument and should ignore + * it in favour of `this`. + * + * In the case of mixins, a method may be placed on both an intercepted class + * and an unintercepted class. In this case, the method must use the `receiver` + * parameter. + * + * + * There are various optimizations of the general call pattern. + * + * When the interceptor can be statically determined, it can be used directly: + * + * CONSTANT_INTERCEPTOR.foo$1(r, a) + * + * If there are only a few classes, [getInterceptor] can be specialized with a + * more efficient dispatch: + * + * getInterceptor$specialized(r).foo$1(r, a) + * + * If it can be determined that the receiver is an unintercepted class, it can + * be called directly: + * + * r.foo$1(r, a) + * + * If, further, it is known that the call site cannot call a foo that is + * mixed-in to a native class, then it is known that the explicit receiver is + * ignored, and space-saving dummy value can be passed instead: + * + * r.foo$1(0, a) + * + * This class defines implementations of *all* methods on [Object] so no + * interceptor inherits an implementation from [Object]. This enables the + * implementations on Object to ignore the explicit receiver argument, which + * allows dummy receiver optimization. + */ +abstract class Interceptor { + const Interceptor(); + + bool operator ==(other) => identical(this, other); + + int get hashCode => Primitives.objectHashCode(this); + + String toString() => Primitives.objectToString(this); + + dynamic noSuchMethod(Invocation invocation) { + throw new NoSuchMethodError( + this, + invocation.memberName, + invocation.positionalArguments, + invocation.namedArguments); + } + + Type get runtimeType => getRuntimeType(this); +} + +/** + * The interceptor class for [bool]. + */ +class JSBool extends Interceptor implements bool { + const JSBool(); + + // Note: if you change this, also change the function [S]. + String toString() => JS('String', r'String(#)', this); + + // The values here are SMIs, co-prime and differ about half of the bit + // positions, including the low bit, so they are different mod 2^k. + int get hashCode => this ? (2 * 3 * 23 * 3761) : (269 * 811); + + Type get runtimeType => bool; +} + +/** + * The interceptor class for [Null]. + * + * This class defines implementations for *all* methods on [Object] since + * the methods on Object assume the receiver is non-null. This means that + * JSNull will always be in the interceptor set for methods defined on Object. + */ +class JSNull extends Interceptor implements Null { + const JSNull(); + + bool operator ==(other) => identical(null, other); + + // Note: if you change this, also change the function [S]. + String toString() => 'null'; + + int get hashCode => 0; + + Type get runtimeType => Null; +} + +/** + * The supertype for JSString and JSArray. Used by the backend as to + * have a type mask that contains the objects that we can use the + * native JS [] operator and length on. + */ +abstract class JSIndexable { + int get length; + operator[](int index); +} + +/** + * The supertype for JSMutableArray and + * JavaScriptIndexingBehavior. Used by the backend to have a type mask + * that contains the objects we can use the JS []= operator on. + */ +abstract class JSMutableIndexable extends JSIndexable { + operator[]=(int index, var value); +} + +/** + * The interface implemented by JavaScript objects. These are methods in + * addition to the regular Dart Object methods like [Object.hashCode]. + * + * This is the type that should be exported by a JavaScript interop library. + */ +abstract class JSObject { +} + + +/** + * Interceptor base class for JavaScript objects not recognized as some more + * specific native type. + */ +abstract class JavaScriptObject extends Interceptor implements JSObject { + const JavaScriptObject(); + + // It would be impolite to stash a property on the object. + int get hashCode => 0; + + Type get runtimeType => JSObject; +} + + +/** + * Interceptor for plain JavaScript objects created as JavaScript object + * literals or `new Object()`. + */ +class PlainJavaScriptObject extends JavaScriptObject { + const PlainJavaScriptObject(); +} + + +/** + * Interceptor for unclassified JavaScript objects, typically objects with a + * non-trivial prototype chain. + */ +class UnknownJavaScriptObject extends JavaScriptObject { + const UnknownJavaScriptObject(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/internal_patch.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/internal_patch.dart new file mode 100644 index 0000000..485c895 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/internal_patch.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2013, 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. + +import 'dart:_js_primitives' show printString; +import 'dart:_js_helper' show JS; +import 'dart:_interceptors' show JSArray; + +patch class Symbol implements core.Symbol { + patch const Symbol(String name) + : this._name = name; +} + +patch void printToConsole(String line) { + printString('$line'); +} + +patch List makeListFixedLength(List growableList) { + JSArray.markFixedList(growableList); + return growableList; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/io_patch.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/io_patch.dart new file mode 100644 index 0000000..6e0821a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/io_patch.dart @@ -0,0 +1,402 @@ +// Copyright (c) 2013, 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. + +patch class _Directory { + patch static _current() { + throw new UnsupportedError("Directory._current"); + } + patch static _setCurrent(path) { + throw new UnsupportedError("Directory_SetCurrent"); + } + patch static _createTemp(String path) { + throw new UnsupportedError("Directory._createTemp"); + } + patch static String _systemTemp() { + throw new UnsupportedError("Directory._systemTemp"); + } + patch static _exists(String path) { + throw new UnsupportedError("Directory._exists"); + } + patch static _create(String path) { + throw new UnsupportedError("Directory._create"); + } + patch static _deleteNative(String path, bool recursive) { + throw new UnsupportedError("Directory._deleteNative"); + } + patch static _rename(String path, String newPath) { + throw new UnsupportedError("Directory._rename"); + } + patch static List _list(String path, bool recursive, bool followLinks) { + throw new UnsupportedError("Directory._list"); + } +} + +patch class _EventHandler { + patch static void _sendData(Object sender, + RawReceivePort receivePort, + int data) { + throw new UnsupportedError("EventHandler._sendData"); + } +} + +patch class FileStat { + patch static _statSync(String path) { + throw new UnsupportedError("FileStat.stat"); + } +} + +patch class FileSystemEntity { + patch static _getType(String path, bool followLinks) { + throw new UnsupportedError("FileSystemEntity._getType"); + } + patch static _identical(String path1, String path2) { + throw new UnsupportedError("FileSystemEntity._identical"); + } + patch static _resolveSymbolicLinks(String path) { + throw new UnsupportedError("FileSystemEntity._resolveSymbolicLinks"); + } +} + +patch class _File { + patch static _exists(String path) { + throw new UnsupportedError("File._exists"); + } + patch static _create(String path) { + throw new UnsupportedError("File._create"); + } + patch static _createLink(String path, String target) { + throw new UnsupportedError("File._createLink"); + } + patch static _linkTarget(String path) { + throw new UnsupportedError("File._linkTarget"); + } + patch static _deleteNative(String path) { + throw new UnsupportedError("File._deleteNative"); + } + patch static _deleteLinkNative(String path) { + throw new UnsupportedError("File._deleteLinkNative"); + } + patch static _rename(String oldPath, String newPath) { + throw new UnsupportedError("File._rename"); + } + patch static _renameLink(String oldPath, String newPath) { + throw new UnsupportedError("File._renameLink"); + } + patch static _copy(String oldPath, String newPath) { + throw new UnsupportedError("File._copy"); + } + patch static _lengthFromPath(String path) { + throw new UnsupportedError("File._lengthFromPath"); + } + patch static _lastModified(String path) { + throw new UnsupportedError("File._lastModified"); + } + patch static _open(String path, int mode) { + throw new UnsupportedError("File._open"); + } + patch static int _openStdio(int fd) { + throw new UnsupportedError("File._openStdio"); + } +} + +patch class _RandomAccessFile { + patch static int _close(int id) { + throw new UnsupportedError("RandomAccessFile._close"); + } + patch static _readByte(int id) { + throw new UnsupportedError("RandomAccessFile._readByte"); + } + patch static _read(int id, int bytes) { + throw new UnsupportedError("RandomAccessFile._read"); + } + patch static _readInto(int id, List buffer, int start, int end) { + throw new UnsupportedError("RandomAccessFile._readInto"); + } + patch static _writeByte(int id, int value) { + throw new UnsupportedError("RandomAccessFile._writeByte"); + } + patch static _writeFrom(int id, List buffer, int start, int end) { + throw new UnsupportedError("RandomAccessFile._writeFrom"); + } + patch static _position(int id) { + throw new UnsupportedError("RandomAccessFile._position"); + } + patch static _setPosition(int id, int position) { + throw new UnsupportedError("RandomAccessFile._setPosition"); + } + patch static _truncate(int id, int length) { + throw new UnsupportedError("RandomAccessFile._truncate"); + } + patch static _length(int id) { + throw new UnsupportedError("RandomAccessFile._length"); + } + patch static _flush(int id) { + throw new UnsupportedError("RandomAccessFile._flush"); + } +} + +patch class _IOCrypto { + patch static Uint8List getRandomBytes(int count) { + throw new UnsupportedError("_IOCrypto.getRandomBytes"); + } +} + +patch class _Platform { + patch static int _numberOfProcessors() { + throw new UnsupportedError("Platform._numberOfProcessors"); + } + patch static String _pathSeparator() { + throw new UnsupportedError("Platform._pathSeparator"); + } + patch static String _operatingSystem() { + throw new UnsupportedError("Platform._operatingSystem"); + } + patch static _localHostname() { + throw new UnsupportedError("Platform._localHostname"); + } + patch static _executable() { + throw new UnsupportedError("Platform._executable"); + } + patch static List _executableArguments() { + throw new UnsupportedError("Platform._executableArguments"); + } + patch static String _packageRoot() { + throw new UnsupportedError("Platform._packageRoot"); + } + patch static _environment() { + throw new UnsupportedError("Platform._environment"); + } + patch static String _version() { + throw new UnsupportedError("Platform._version"); + } +} + +patch class _ProcessUtils { + patch static void _exit(int status) { + throw new UnsupportedError("ProcessUtils._exit"); + } + patch static void _setExitCode(int status) { + throw new UnsupportedError("ProcessUtils._setExitCode"); + } + patch static int _getExitCode() { + throw new UnsupportedError("ProcessUtils._getExitCode"); + } + patch static void _sleep(int millis) { + throw new UnsupportedError("ProcessUtils._sleep"); + } + patch static int _pid(Process process) { + throw new UnsupportedError("ProcessUtils._pid"); + } + patch static Stream _watchSignal(ProcessSignal signal) { + throw new UnsupportedError("ProcessUtils._watchSignal"); + } +} + +patch class Process { + patch static Future start( + String executable, + List arguments, + {String workingDirectory, + Map environment, + bool includeParentEnvironment: true, + bool runInShell: false}) { + throw new UnsupportedError("Process.start"); + } + + patch static Future run( + String executable, + List arguments, + {String workingDirectory, + Map environment, + bool includeParentEnvironment: true, + bool runInShell: false, + Encoding stdoutEncoding: SYSTEM_ENCODING, + Encoding stderrEncoding: SYSTEM_ENCODING}) { + throw new UnsupportedError("Process.run"); + } + + patch static ProcessResult runSync( + String executable, + List arguments, + {String workingDirectory, + Map environment, + bool includeParentEnvironment: true, + bool runInShell: false, + Encoding stdoutEncoding: SYSTEM_ENCODING, + Encoding stderrEncoding: SYSTEM_ENCODING}) { + throw new UnsupportedError("Process.runSync"); + } +} + +patch class InternetAddress { + patch static InternetAddress get LOOPBACK_IP_V4 { + throw new UnsupportedError("InternetAddress.LOOPBACK_IP_V4"); + } + patch static InternetAddress get LOOPBACK_IP_V6 { + throw new UnsupportedError("InternetAddress.LOOPBACK_IP_V6"); + } + patch static InternetAddress get ANY_IP_V4 { + throw new UnsupportedError("InternetAddress.ANY_IP_V4"); + } + patch static InternetAddress get ANY_IP_V6 { + throw new UnsupportedError("InternetAddress.ANY_IP_V6"); + } + patch factory InternetAddress(String address) { + throw new UnsupportedError("InternetAddress"); + } + patch static Future> lookup( + String host, {InternetAddressType type: InternetAddressType.ANY}) { + throw new UnsupportedError("InternetAddress.lookup"); + } +} + +patch class NetworkInterface { + patch static Future> list({ + bool includeLoopback: false, + bool includeLinkLocal: false, + InternetAddressType type: InternetAddressType.ANY}) { + throw new UnsupportedError("NetworkInterface.list"); + } +} + +patch class RawServerSocket { + patch static Future bind(address, + int port, + {int backlog: 0, + bool v6Only: false}) { + throw new UnsupportedError("RawServerSocket.bind"); + } +} + +patch class ServerSocket { + patch static Future bind(address, + int port, + {int backlog: 0, + bool v6Only: false}) { + throw new UnsupportedError("ServerSocket.bind"); + } +} + +patch class RawSocket { + patch static Future connect(host, int port) { + throw new UnsupportedError("RawSocket constructor"); + } +} + +patch class Socket { + patch static Future connect(host, int port) { + throw new UnsupportedError("Socket constructor"); + } +} + +patch class SecureSocket { + patch factory SecureSocket._(RawSecureSocket rawSocket) { + throw new UnsupportedError("SecureSocket constructor"); + } + + patch static void initialize({String database, + String password, + bool useBuiltinRoots: true}) { + throw new UnsupportedError("SecureSocket.initialize"); + } +} + +patch class RawDatagramSocket { + patch static Future bind( + host, int port, {bool reuseAddress: true}) { + throw new UnsupportedError("RawDatagramSocket.bind"); + } +} + +patch class _SecureFilter { + patch factory _SecureFilter() { + throw new UnsupportedError("_SecureFilter._SecureFilter"); + } +} + +patch class _StdIOUtils { + patch static Stdin _getStdioInputStream() { + throw new UnsupportedError("StdIOUtils._getStdioInputStream"); + } + patch static _getStdioOutputStream(int fd) { + throw new UnsupportedError("StdIOUtils._getStdioOutputStream"); + } + patch static int _socketType(nativeSocket) { + throw new UnsupportedError("StdIOUtils._socketType"); + } + patch static _getStdioHandleType(int fd) { + throw new UnsupportedError("StdIOUtils._getStdioHandleType"); + } +} + +patch class _WindowsCodePageDecoder { + patch static String _decodeBytes(List bytes) { + throw new UnsupportedError("_WindowsCodePageDecoder._decodeBytes"); + } +} + +patch class _WindowsCodePageEncoder { + patch static List _encodeString(String string) { + throw new UnsupportedError("_WindowsCodePageEncoder._encodeString"); + } +} + +patch class _Filter { + patch static _Filter newZLibDeflateFilter(bool gzip, int level, + int windowBits, int memLevel, + int strategy, + List dictionary, bool raw) { + throw new UnsupportedError("newZLibDeflateFilter"); + } + patch static _Filter newZLibInflateFilter(int windowBits, + List dictionary, bool raw) { + throw new UnsupportedError("newZLibInflateFilter"); + } +} + +patch class Stdin { + patch int readByteSync() { + throw new UnsupportedError("Stdin.readByteSync"); + } + patch bool get echoMode { + throw new UnsupportedError("Stdin.echoMode"); + } + patch void set echoMode(bool enabled) { + throw new UnsupportedError("Stdin.echoMode"); + } + patch bool get lineMode { + throw new UnsupportedError("Stdin.lineMode"); + } + patch void set lineMode(bool enabled) { + throw new UnsupportedError("Stdin.lineMode"); + } +} + +patch class Stdout { + patch bool get hasTerminal { + throw new UnsupportedError("Stdout.hasTerminal"); + } + patch int get terminalColumns { + throw new UnsupportedError("Stdout.terminalColumns"); + } + patch int get terminalLines { + throw new UnsupportedError("Stdout.terminalLines"); + } +} + +patch class _FileSystemWatcher { + patch static Stream watch( + String path, int events, bool recursive) { + throw new UnsupportedError("_FileSystemWatcher.watch"); + } + patch static bool get isSupported { + throw new UnsupportedError("_FileSystemWatcher.isSupported"); + } +} + +patch class _IOService { + patch static Future dispatch(int request, List data) { + throw new UnsupportedError("_IOService.dispatch"); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/isolate_helper.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/isolate_helper.dart new file mode 100644 index 0000000..8a19dc6 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/isolate_helper.dart @@ -0,0 +1,1597 @@ +// Copyright (c) 2012, 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. + +library _isolate_helper; + +import 'dart:async'; +import 'dart:collection' show Queue, HashMap; +import 'dart:isolate'; +import 'dart:_js_helper' show + Closure, + Null, + Primitives, + convertDartClosureToJS, + random64; +import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS, + JS, + JS_CREATE_ISOLATE, + JS_CURRENT_ISOLATE_CONTEXT, + JS_CURRENT_ISOLATE, + JS_SET_CURRENT_ISOLATE, + IsolateContext; +import 'dart:_interceptors' show JSExtendableArray; + +/** + * Called by the compiler to support switching + * between isolates when we get a callback from the DOM. + */ +_callInIsolate(_IsolateContext isolate, Function function) { + var result = isolate.eval(function); + _globalState.topEventLoop.run(); + return result; +} + +/// Marks entering a JavaScript async operation to keep the worker alive. +/// +/// To be called by library code before starting an async operation controlled +/// by the JavaScript event handler. +/// +/// Also call [leaveJsAsync] in all callback handlers marking the end of that +/// async operation (also error handlers) so the worker can be released. +/// +/// These functions only has to be called for code that can be run from a +/// worker-isolate (so not for general dom operations). +enterJsAsync() { + _globalState.topEventLoop._activeJsAsyncCount++; +} + +/// Marks leaving a javascript async operation. +/// +/// See [enterJsAsync]. +leaveJsAsync() { + _globalState.topEventLoop._activeJsAsyncCount--; + assert(_globalState.topEventLoop._activeJsAsyncCount >= 0); +} + +/// Returns true if we are currently in a worker context. +bool isWorker() => _globalState.isWorker; + +/** + * Called by the compiler to fetch the current isolate context. + */ +_IsolateContext _currentIsolate() => _globalState.currentContext; + +/** + * Wrapper that takes the dart entry point and runs it within an isolate. The + * dart2js compiler will inject a call of the form + * [: startRootIsolate(main); :] when it determines that this wrapping + * is needed. For single-isolate applications (e.g. hello world), this + * call is not emitted. + */ +void startRootIsolate(entry, args) { + // The dartMainRunner can inject a new arguments array. We pass the arguments + // through a "JS", so that the type-inferrer loses track of it. + args = JS("", "#", args); + if (args == null) args = []; + if (args is! List) { + throw new ArgumentError("Arguments to main must be a List: $args"); + } + _globalState = new _Manager(entry); + + // Don't start the main loop again, if we are in a worker. + if (_globalState.isWorker) return; + final rootContext = new _IsolateContext(); + _globalState.rootContext = rootContext; + + // BUG(5151491): Setting currentContext should not be necessary, but + // because closures passed to the DOM as event handlers do not bind their + // isolate automatically we try to give them a reasonable context to live in + // by having a "default" isolate (the first one created). + _globalState.currentContext = rootContext; + + if (entry is _MainFunctionArgs) { + rootContext.eval(() { entry(args); }); + } else if (entry is _MainFunctionArgsMessage) { + rootContext.eval(() { entry(args, null); }); + } else { + rootContext.eval(entry); + } + _globalState.topEventLoop.run(); +} + +/******************************************************** + Inserted from lib/isolate/dart2js/isolateimpl.dart + ********************************************************/ + +/** + * Concepts used here: + * + * "manager" - A manager contains one or more isolates, schedules their + * execution, and performs other plumbing on their behalf. The isolate + * present at the creation of the manager is designated as its "root isolate". + * A manager may, for example, be implemented on a web Worker. + * + * [_Manager] - State present within a manager (exactly once, as a global). + * + * [_ManagerStub] - A handle held within one manager that allows interaction + * with another manager. A target manager may be addressed by zero or more + * [_ManagerStub]s. + * TODO(ahe): The _ManagerStub concept is broken. It was an attempt + * to create a common interface between the native Worker class and + * _MainManagerStub. + */ + +/** + * A native object that is shared across isolates. This object is visible to all + * isolates running under the same manager (either UI or background web worker). + * + * This is code that is intended to 'escape' the isolate boundaries in order to + * implement the semantics of isolates in JavaScript. Without this we would have + * been forced to implement more code (including the top-level event loop) in + * JavaScript itself. + */ +// TODO(eub, sigmund): move the "manager" to be entirely in JS. +// Running any Dart code outside the context of an isolate gives it +// the chance to break the isolate abstraction. +_Manager get _globalState => JS("_Manager", "init.globalState"); + +set _globalState(_Manager val) { + JS("void", "init.globalState = #", val); +} + +/** State associated with the current manager. See [globalState]. */ +// TODO(sigmund): split in multiple classes: global, thread, main-worker states? +class _Manager { + + /** Next available isolate id within this [_Manager]. */ + int nextIsolateId = 0; + + /** id assigned to this [_Manager]. */ + int currentManagerId = 0; + + /** + * Next available manager id. Only used by the main manager to assign a unique + * id to each manager created by it. + */ + int nextManagerId = 1; + + /** Context for the currently running [Isolate]. */ + _IsolateContext currentContext = null; + + /** Context for the root [Isolate] that first run in this [_Manager]. */ + _IsolateContext rootContext = null; + + /** The top-level event loop. */ + _EventLoop topEventLoop; + + /** Whether this program is running from the command line. */ + bool fromCommandLine; + + /** Whether this [_Manager] is running as a web worker. */ + bool isWorker; + + /** Whether we support spawning web workers. */ + bool supportsWorkers; + + /** + * Whether to use web workers when implementing isolates. Set to false for + * debugging/testing. + */ + bool get useWorkers => supportsWorkers; + + /** + * Whether to use the web-worker JSON-based message serialization protocol. By + * default this is only used with web workers. For debugging, you can force + * using this protocol by changing this field value to [:true:]. + */ + bool get needSerialization => useWorkers; + + /** + * Registry of isolates. Isolates must be registered if, and only if, receive + * ports are alive. Normally no open receive-ports means that the isolate is + * dead, but DOM callbacks could resurrect it. + */ + Map isolates; + + /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */ + _MainManagerStub mainManager; + + /// Registry of active Web Workers. Only used in the main [_Manager]. + Map managers; + + /** The entry point given by [startRootIsolate]. */ + final Function entry; + + _Manager(this.entry) { + _nativeDetectEnvironment(); + topEventLoop = new _EventLoop(); + isolates = new Map(); + managers = new Map(); + if (isWorker) { // "if we are not the main manager ourself" is the intent. + mainManager = new _MainManagerStub(); + _nativeInitWorkerMessageHandler(); + } + } + + void _nativeDetectEnvironment() { + bool isWindowDefined = globalWindow != null; + bool isWorkerDefined = globalWorker != null; + + isWorker = !isWindowDefined && globalPostMessageDefined; + supportsWorkers = isWorker + || (isWorkerDefined && IsolateNatives.thisScript != null); + fromCommandLine = !isWindowDefined && !isWorker; + } + + void _nativeInitWorkerMessageHandler() { + var function = JS('', + "function (e) { #(#, e); }", + DART_CLOSURE_TO_JS(IsolateNatives._processWorkerMessage), + mainManager); + JS("void", r"#.onmessage = #", globalThis, function); + // We define dartPrint so that the implementation of the Dart + // print method knows what to call. + // TODO(ngeoffray): Should we forward to the main isolate? What if + // it exited? + JS('void', r'#.dartPrint = function (object) {}', globalThis); + } + + + /** + * Close the worker running this code if all isolates are done and + * there are no active async JavaScript tasks still running. + */ + void maybeCloseWorker() { + if (isWorker + && isolates.isEmpty + && topEventLoop._activeJsAsyncCount == 0) { + mainManager.postMessage(_serializeMessage({'command': 'close'})); + } + } +} + +/** Context information tracked for each isolate. */ +class _IsolateContext implements IsolateContext { + /** Current isolate id. */ + final int id = _globalState.nextIsolateId++; + + /** Registry of receive ports currently active on this isolate. */ + final Map ports = new Map(); + + /** Registry of weak receive ports currently active on this isolate. */ + final Set weakPorts = new Set(); + + /** Holds isolate globals (statics and top-level properties). */ + // native object containing all globals of an isolate. + final isolateStatics = JS_CREATE_ISOLATE(); + + final RawReceivePortImpl controlPort = new RawReceivePortImpl._controlPort(); + + final Capability pauseCapability = new Capability(); + final Capability terminateCapability = new Capability(); // License to kill. + + // TODO(lrn): Store these in single "PauseState" object, so they don't take + // up as much room when not pausing. + bool isPaused = false; + List<_IsolateEvent> delayedEvents = []; + Set pauseTokens = new Set(); + + // Container with the "on exit" handler send-ports. + var doneHandlers; + + /** Whether errors are considered fatal. */ + // This doesn't do anything yet. We need to be able to catch uncaught errors + // (oxymoronically) in order to take lethal action. This is waiting for the + // same change as the uncaught error listeners. + bool errorsAreFatal = false; + + _IsolateContext() { + this.registerWeak(controlPort._id, controlPort); + } + + void addPause(Capability authentification, Capability resume) { + if (pauseCapability != authentification) return; + if (pauseTokens.add(resume) && !isPaused) { + isPaused = true; + } + _updateGlobalState(); + } + + void removePause(Capability resume) { + if (!isPaused) return; + pauseTokens.remove(resume); + if (pauseTokens.isEmpty) { + while(delayedEvents.isNotEmpty) { + _IsolateEvent event = delayedEvents.removeLast(); + _globalState.topEventLoop.prequeue(event); + } + isPaused = false; + } + _updateGlobalState(); + } + + void addDoneListener(SendPort responsePort) { + if (doneHandlers == null) { + doneHandlers = []; + } + // If necessary, we can switch doneHandlers to a Set if it gets larger. + // That is not expected to happen in practice. + if (doneHandlers.contains(responsePort)) return; + doneHandlers.add(responsePort); + } + + void removeDoneListener(SendPort responsePort) { + if (doneHandlers == null) return; + doneHandlers.remove(responsePort); + } + + void setErrorsFatal(Capability authentification, bool errorsAreFatal) { + if (terminateCapability != authentification) return; + this.errorsAreFatal = errorsAreFatal; + } + + void handlePing(SendPort responsePort, int pingType) { + if (pingType == Isolate.PING_EVENT) { + _globalState.topEventLoop.enqueue(this, () { + responsePort.send(null); + }, "ping"); + } else { + // There is no difference between PING_ALIVE and PING_CONTROL + // since we don't handle it before the control event queue. + responsePort.send(null); + } + } + + /** + * Run [code] in the context of the isolate represented by [this]. + */ + dynamic eval(Function code) { + var old = _globalState.currentContext; + _globalState.currentContext = this; + this._setGlobals(); + var result = null; + try { + result = code(); + } finally { + _globalState.currentContext = old; + if (old != null) old._setGlobals(); + } + return result; + } + + void _setGlobals() { + JS_SET_CURRENT_ISOLATE(isolateStatics); + } + + void handleControlMessage(message) { + switch (message[0]) { + case "pause": + addPause(message[1], message[2]); + break; + case "resume": + removePause(message[1]); + break; + case 'add-ondone': + addDoneListener(message[1]); + break; + case 'remove-ondone': + removeDoneListener(message[1]); + break; + case 'set-errors-fatal': + setErrorsFatal(message[1], message[2]); + break; + case "ping": + handlePing(message[1], message[2]); + break; + default: + print("UNKNOWN MESSAGE: $message"); + } + } + + /** Looks up a port registered for this isolate. */ + RawReceivePortImpl lookup(int portId) => ports[portId]; + + void _addRegistration(int portId, RawReceivePortImpl port) { + if (ports.containsKey(portId)) { + throw new Exception("Registry: ports must be registered only once."); + } + ports[portId] = port; + } + + /** Registers a port on this isolate. */ + void register(int portId, RawReceivePortImpl port) { + _addRegistration(portId, port); + _updateGlobalState(); + } + + /** + * Registers a weak port on this isolate. + * + * The port does not keep the isolate active. + */ + void registerWeak(int portId, RawReceivePortImpl port) { + weakPorts.add(portId); + _addRegistration(portId, port); + } + + void _updateGlobalState() { + if (ports.length - weakPorts.length > 0 || isPaused) { + _globalState.isolates[id] = this; // indicate this isolate is active + } else { + _shutdown(); + } + } + + void _shutdown() { + _globalState.isolates.remove(id); // indicate this isolate is not active + // Send "done" event to all listeners. This must be done after deactivating + // the current isolate, or it may get events if listening to itself. + if (doneHandlers != null) { + for (SendPort port in doneHandlers) { + port.send(null); + } + } + } + + /** Unregister a port on this isolate. */ + void unregister(int portId) { + ports.remove(portId); + weakPorts.remove(portId); + _updateGlobalState(); + } +} + +/** Represent the event loop on a javascript thread (DOM or worker). */ +class _EventLoop { + final Queue<_IsolateEvent> events = new Queue<_IsolateEvent>(); + + /// The number of waiting callbacks not controlled by the dart event loop. + /// + /// This could be timers or http requests. The worker will only be killed if + /// this count reaches 0. + /// Access this by using [enterJsAsync] before starting a JavaScript async + /// operation and [leaveJsAsync] when the callback has fired. + int _activeJsAsyncCount = 0; + + _EventLoop(); + + void enqueue(isolate, fn, msg) { + events.addLast(new _IsolateEvent(isolate, fn, msg)); + } + + void prequeue(_IsolateEvent event) { + events.addFirst(event); + } + + _IsolateEvent dequeue() { + if (events.isEmpty) return null; + return events.removeFirst(); + } + + void checkOpenReceivePortsFromCommandLine() { + if (_globalState.rootContext != null + && _globalState.isolates.containsKey(_globalState.rootContext.id) + && _globalState.fromCommandLine + && _globalState.rootContext.ports.isEmpty) { + // We want to reach here only on the main [_Manager] and only + // on the command-line. In the browser the isolate might + // still be alive due to DOM callbacks, but the presumption is + // that on the command-line, no future events can be injected + // into the event queue once it's empty. Node has setTimeout + // so this presumption is incorrect there. We think(?) that + // in d8 this assumption is valid. + throw new Exception("Program exited with open ReceivePorts."); + } + } + + /** Process a single event, if any. */ + bool runIteration() { + final event = dequeue(); + if (event == null) { + checkOpenReceivePortsFromCommandLine(); + _globalState.maybeCloseWorker(); + return false; + } + event.process(); + return true; + } + + /** + * Runs multiple iterations of the run-loop. If possible, each iteration is + * run asynchronously. + */ + void _runHelper() { + if (globalWindow != null) { + // Run each iteration from the browser's top event loop. + void next() { + if (!runIteration()) return; + Timer.run(next); + } + next(); + } else { + // Run synchronously until no more iterations are available. + while (runIteration()) {} + } + } + + /** + * Call [_runHelper] but ensure that worker exceptions are propragated. + */ + void run() { + if (!_globalState.isWorker) { + _runHelper(); + } else { + try { + _runHelper(); + } catch (e, trace) { + _globalState.mainManager.postMessage(_serializeMessage( + {'command': 'error', 'msg': '$e\n$trace' })); + } + } + } +} + +/** An event in the top-level event queue. */ +class _IsolateEvent { + _IsolateContext isolate; + Function fn; + String message; + + _IsolateEvent(this.isolate, this.fn, this.message); + + void process() { + if (isolate.isPaused) { + isolate.delayedEvents.add(this); + return; + } + isolate.eval(fn); + } +} + +/** A stub for interacting with the main manager. */ +class _MainManagerStub { + void postMessage(msg) { + // "self" is a way to refer to the global context object that + // works in HTML pages and in Web Workers. It does not work in d8 + // and Firefox jsshell, because that would have been too easy. + // + // See: http://www.w3.org/TR/workers/#the-global-scope + // and: http://www.w3.org/TR/Window/#dfn-self-attribute + JS("void", r"self.postMessage(#)", msg); + } +} + +const String _SPAWNED_SIGNAL = "spawned"; + +var globalThis = Primitives.computeGlobalThis(); +var globalWindow = JS('', "#.window", globalThis); +var globalWorker = JS('', "#.Worker", globalThis); +bool globalPostMessageDefined = + JS('', "#.postMessage !== (void 0)", globalThis); + +typedef _MainFunction(); +typedef _MainFunctionArgs(args); +typedef _MainFunctionArgsMessage(args, message); + +/// Note: IsolateNatives depends on _globalState which is only set up correctly +/// when 'dart:isolate' has been imported. +class IsolateNatives { + + static String thisScript = computeThisScript(); + + /// Associates an ID with a native worker object. + static final Expando workerIds = new Expando(); + + /** + * The src url for the script tag that loaded this Used to create + * JavaScript workers. + */ + static String computeThisScript() { + var currentScript = JS('', r'init.currentScript'); + if (currentScript != null) { + return JS('String', 'String(#.src)', currentScript); + } + if (Primitives.isD8) return computeThisScriptD8(); + if (Primitives.isJsshell) return computeThisScriptJsshell(); + // A worker has no script tag - so get an url from a stack-trace. + if (_globalState.isWorker) return computeThisScriptFromTrace(); + return null; + } + + static String computeThisScriptJsshell() { + return JS('String|Null', 'thisFilename()'); + } + + // TODO(ahe): The following is for supporting D8. We should move this code + // to a helper library that is only loaded when testing on D8. + static String computeThisScriptD8() => computeThisScriptFromTrace(); + + static String computeThisScriptFromTrace() { + var stack = JS('String|Null', 'new Error().stack'); + if (stack == null) { + // According to Internet Explorer documentation, the stack + // property is not set until the exception is thrown. The stack + // property was not provided until IE10. + stack = JS('String|Null', + '(function() {' + 'try { throw new Error() } catch(e) { return e.stack }' + '})()'); + if (stack == null) throw new UnsupportedError('No stack trace'); + } + var pattern, matches; + + // This pattern matches V8, Chrome, and Internet Explorer stack + // traces that look like this: + // Error + // at methodName (URI:LINE:COLUMN) + pattern = JS('', + r'new RegExp("^ *at [^(]*\\((.*):[0-9]*:[0-9]*\\)$", "m")'); + + + matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern); + if (matches != null) return JS('String', '#[1]', matches); + + // This pattern matches Firefox stack traces that look like this: + // methodName@URI:LINE + pattern = JS('', r'new RegExp("^[^@]*@(.*):[0-9]*$", "m")'); + + matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern); + if (matches != null) return JS('String', '#[1]', matches); + + throw new UnsupportedError('Cannot extract URI from "$stack"'); + } + + /** + * Assume that [e] is a browser message event and extract its message data. + * We don't import the dom explicitly so, when workers are disabled, this + * library can also run on top of nodejs. + */ + static _getEventData(e) => JS("", "#.data", e); + + /** + * Process messages on a worker, either to control the worker instance or to + * pass messages along to the isolate running in the worker. + */ + static void _processWorkerMessage(/* Worker */ sender, e) { + var msg = _deserializeMessage(_getEventData(e)); + switch (msg['command']) { + case 'start': + _globalState.currentManagerId = msg['id']; + String functionName = msg['functionName']; + Function entryPoint = (functionName == null) + ? _globalState.entry + : _getJSFunctionFromName(functionName); + var args = msg['args']; + var message = _deserializeMessage(msg['msg']); + var isSpawnUri = msg['isSpawnUri']; + var startPaused = msg['startPaused']; + var replyTo = _deserializeMessage(msg['replyTo']); + var context = new _IsolateContext(); + _globalState.topEventLoop.enqueue(context, () { + _startIsolate(entryPoint, args, message, + isSpawnUri, startPaused, replyTo); + }, 'worker-start'); + // Make sure we always have a current context in this worker. + // TODO(7907): This is currently needed because we're using + // Timers to implement Futures, and this isolate library + // implementation uses Futures. We should either stop using + // Futures in this library, or re-adapt if Futures get a + // different implementation. + _globalState.currentContext = context; + _globalState.topEventLoop.run(); + break; + case 'spawn-worker': + _spawnWorker(msg['functionName'], msg['uri'], + msg['args'], msg['msg'], + msg['isSpawnUri'], msg['startPaused'], + msg['replyPort']); + break; + case 'message': + SendPort port = msg['port']; + // If the port has been closed, we ignore the message. + if (port != null) { + msg['port'].send(msg['msg']); + } + _globalState.topEventLoop.run(); + break; + case 'close': + _globalState.managers.remove(workerIds[sender]); + JS('void', '#.terminate()', sender); + _globalState.topEventLoop.run(); + break; + case 'log': + _log(msg['msg']); + break; + case 'print': + if (_globalState.isWorker) { + _globalState.mainManager.postMessage( + _serializeMessage({'command': 'print', 'msg': msg})); + } else { + print(msg['msg']); + } + break; + case 'error': + throw msg['msg']; + } + } + + /** Log a message, forwarding to the main [_Manager] if appropriate. */ + static _log(msg) { + if (_globalState.isWorker) { + _globalState.mainManager.postMessage( + _serializeMessage({'command': 'log', 'msg': msg })); + } else { + try { + _consoleLog(msg); + } catch (e, trace) { + throw new Exception(trace); + } + } + } + + static void _consoleLog(msg) { + JS("void", r"#.console.log(#)", globalThis, msg); + } + + static _getJSFunctionFromName(String functionName) { + return JS("", "init.globalFunctions[#]()", functionName); + } + + /** + * Get a string name for the function, if possible. The result for + * anonymous functions is browser-dependent -- it may be "" or "anonymous" + * but you should probably not count on this. + */ + static String _getJSFunctionName(Function f) { + return (f is Closure) ? JS("String|Null", r'#.$name', f) : null; + } + + /** Create a new JavaScript object instance given its constructor. */ + static dynamic _allocate(var ctor) { + return JS("", "new #()", ctor); + } + + static Future spawnFunction(void topLevelFunction(message), + var message, + bool startPaused) { + final name = _getJSFunctionName(topLevelFunction); + if (name == null) { + throw new UnsupportedError( + "only top-level functions can be spawned."); + } + bool isLight = false; + bool isSpawnUri = false; + return spawn(name, null, null, message, isLight, isSpawnUri, startPaused); + } + + static Future spawnUri(Uri uri, List args, var message, + bool startPaused) { + bool isLight = false; + bool isSpawnUri = true; + return spawn(null, uri.toString(), args, message, + isLight, isSpawnUri, startPaused); + } + + // TODO(sigmund): clean up above, after we make the new API the default: + + /// If [uri] is `null` it is replaced with the current script. + static Future spawn(String functionName, String uri, + List args, message, + bool isLight, bool isSpawnUri, bool startPaused) { + // Assume that the compiled version of the Dart file lives just next to the + // dart file. + // TODO(floitsch): support precompiled version of dart2js output. + if (uri != null && uri.endsWith(".dart")) uri += ".js"; + + ReceivePort port = new ReceivePort(); + Future result = port.first.then((msg) { + assert(msg[0] == _SPAWNED_SIGNAL); + return msg; + }); + + SendPort signalReply = port.sendPort; + + if (_globalState.useWorkers && !isLight) { + _startWorker(functionName, uri, args, message, isSpawnUri, startPaused, + signalReply); + } else { + _startNonWorker( + functionName, uri, args, message, isSpawnUri, startPaused, + signalReply); + } + return result; + } + + static void _startWorker( + String functionName, String uri, + List args, message, + bool isSpawnUri, + bool startPaused, + SendPort replyPort) { + if (_globalState.isWorker) { + _globalState.mainManager.postMessage(_serializeMessage({ + 'command': 'spawn-worker', + 'functionName': functionName, + 'args': args, + 'msg': message, + 'uri': uri, + 'isSpawnUri': isSpawnUri, + 'startPaused': startPaused, + 'replyPort': replyPort})); + } else { + _spawnWorker(functionName, uri, args, message, + isSpawnUri, startPaused, replyPort); + } + } + + static void _startNonWorker( + String functionName, String uri, + List args, var message, + bool isSpawnUri, + bool startPaused, + SendPort replyPort) { + // TODO(eub): support IE9 using an iframe -- Dart issue 1702. + if (uri != null) { + throw new UnsupportedError( + "Currently spawnUri is not supported without web workers."); + } + _globalState.topEventLoop.enqueue(new _IsolateContext(), () { + final func = _getJSFunctionFromName(functionName); + _startIsolate(func, args, message, isSpawnUri, startPaused, replyPort); + }, 'nonworker start'); + } + + static void _startIsolate(Function topLevel, + List args, message, + bool isSpawnUri, + bool startPaused, + SendPort replyTo) { + _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT(); + Primitives.initializeStatics(context.id); + // The isolate's port does not keep the isolate open. + replyTo.send([_SPAWNED_SIGNAL, + context.controlPort.sendPort, + context.pauseCapability, + context.terminateCapability]); + + void runStartFunction() { + if (!isSpawnUri) { + topLevel(message); + } else if (topLevel is _MainFunctionArgsMessage) { + topLevel(args, message); + } else if (topLevel is _MainFunctionArgs) { + topLevel(args); + } else { + topLevel(); + } + } + + if (startPaused) { + context.addPause(context.pauseCapability, context.pauseCapability); + _globalState.topEventLoop.enqueue(context, runStartFunction, + 'start isolate'); + } else { + runStartFunction(); + } + } + + /** + * Spawns an isolate in a worker. [factoryName] is the Javascript constructor + * name for the isolate entry point class. + */ + static void _spawnWorker(functionName, String uri, + List args, message, + bool isSpawnUri, + bool startPaused, + SendPort replyPort) { + if (uri == null) uri = thisScript; + final worker = JS('var', 'new Worker(#)', uri); + + var processWorkerMessageTrampoline = + JS('', 'function(e) { #(#, e); }', + DART_CLOSURE_TO_JS(_processWorkerMessage), + worker); + JS('void', '#.onmessage = #', worker, processWorkerMessageTrampoline); + var workerId = _globalState.nextManagerId++; + // We also store the id on the worker itself so that we can unregister it. + workerIds[worker] = workerId; + _globalState.managers[workerId] = worker; + JS('void', '#.postMessage(#)', worker, _serializeMessage({ + 'command': 'start', + 'id': workerId, + // Note: we serialize replyPort twice because the child worker needs to + // first deserialize the worker id, before it can correctly deserialize + // the port (port deserialization is sensitive to what is the current + // workerId). + 'replyTo': _serializeMessage(replyPort), + 'args': args, + 'msg': _serializeMessage(message), + 'isSpawnUri': isSpawnUri, + 'startPaused': startPaused, + 'functionName': functionName })); + } +} + +/******************************************************** + Inserted from lib/isolate/dart2js/ports.dart + ********************************************************/ + +/** Common functionality to all send ports. */ +abstract class _BaseSendPort implements SendPort { + /** Id for the destination isolate. */ + final int _isolateId; + + const _BaseSendPort(this._isolateId); + + void _checkReplyTo(SendPort replyTo) { + if (replyTo != null + && replyTo is! _NativeJsSendPort + && replyTo is! _WorkerSendPort) { + throw new Exception("SendPort.send: Illegal replyTo port type"); + } + } + + void send(var message); + bool operator ==(var other); + int get hashCode; +} + +/** A send port that delivers messages in-memory via native JavaScript calls. */ +class _NativeJsSendPort extends _BaseSendPort implements SendPort { + final RawReceivePortImpl _receivePort; + + const _NativeJsSendPort(this._receivePort, int isolateId) : super(isolateId); + + void send(var message) { + // Check that the isolate still runs and the port is still open + final isolate = _globalState.isolates[_isolateId]; + if (isolate == null) return; + if (_receivePort._isClosed) return; + // We force serialization/deserialization as a simple way to ensure + // isolate communication restrictions are respected between isolates that + // live in the same worker. [_NativeJsSendPort] delivers both messages + // from the same worker and messages from other workers. In particular, + // messages sent from a worker via a [_WorkerSendPort] are received at + // [_processWorkerMessage] and forwarded to a native port. In such cases, + // here we'll see [_globalState.currentContext == null]. + final shouldSerialize = _globalState.currentContext != null + && _globalState.currentContext.id != _isolateId; + var msg = message; + if (shouldSerialize) { + msg = _serializeMessage(msg); + } + if (isolate.controlPort == _receivePort) { + isolate.handleControlMessage(msg); + return; + } + _globalState.topEventLoop.enqueue(isolate, () { + if (!_receivePort._isClosed) { + if (shouldSerialize) { + msg = _deserializeMessage(msg); + } + _receivePort._add(msg); + } + }, 'receive $message'); + } + + bool operator ==(var other) => (other is _NativeJsSendPort) && + (_receivePort == other._receivePort); + + int get hashCode => _receivePort._id; +} + +/** A send port that delivers messages via worker.postMessage. */ +// TODO(eub): abstract this for iframes. +class _WorkerSendPort extends _BaseSendPort implements SendPort { + final int _workerId; + final int _receivePortId; + + const _WorkerSendPort(this._workerId, int isolateId, this._receivePortId) + : super(isolateId); + + void send(var message) { + final workerMessage = _serializeMessage({ + 'command': 'message', + 'port': this, + 'msg': message}); + + if (_globalState.isWorker) { + // Communication from one worker to another go through the + // main worker. + _globalState.mainManager.postMessage(workerMessage); + } else { + // Deliver the message only if the worker is still alive. + /* Worker */ var manager = _globalState.managers[_workerId]; + if (manager != null) { + JS('void', '#.postMessage(#)', manager, workerMessage); + } + } + } + + bool operator ==(var other) { + return (other is _WorkerSendPort) && + (_workerId == other._workerId) && + (_isolateId == other._isolateId) && + (_receivePortId == other._receivePortId); + } + + int get hashCode { + // TODO(sigmund): use a standard hash when we get one available in corelib. + return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; + } +} + +class RawReceivePortImpl implements RawReceivePort { + static int _nextFreeId = 1; + + final int _id; + Function _handler; + bool _isClosed = false; + + RawReceivePortImpl(this._handler) : _id = _nextFreeId++ { + _globalState.currentContext.register(_id, this); + } + + RawReceivePortImpl.weak(this._handler) : _id = _nextFreeId++ { + _globalState.currentContext.registerWeak(_id, this); + } + + // Creates the control port of an isolate. + // This is created before the isolate context object itself, + // so it cannot access the static _nextFreeId field. + RawReceivePortImpl._controlPort() : _handler = null, _id = 0; + + void set handler(Function newHandler) { + _handler = newHandler; + } + + void close() { + if (_isClosed) return; + _isClosed = true; + _handler = null; + _globalState.currentContext.unregister(_id); + } + + void _add(dataEvent) { + if (_isClosed) return; + _handler(dataEvent); + } + + SendPort get sendPort { + return new _NativeJsSendPort(this, _globalState.currentContext.id); + } +} + +class ReceivePortImpl extends Stream implements ReceivePort { + final RawReceivePort _rawPort; + StreamController _controller; + + ReceivePortImpl() : this.fromRawReceivePort(new RawReceivePortImpl(null)); + + ReceivePortImpl.weak() + : this.fromRawReceivePort(new RawReceivePortImpl.weak(null)); + + ReceivePortImpl.fromRawReceivePort(this._rawPort) { + _controller = new StreamController(onCancel: close, sync: true); + _rawPort.handler = _controller.add; + } + + StreamSubscription listen(void onData(var event), + {Function onError, + void onDone(), + bool cancelOnError}) { + return _controller.stream.listen(onData, onError: onError, onDone: onDone, + cancelOnError: cancelOnError); + } + + void close() { + _rawPort.close(); + _controller.close(); + } + + SendPort get sendPort => _rawPort.sendPort; +} + + +/******************************************************** + Inserted from lib/isolate/dart2js/messages.dart + ********************************************************/ + +// Defines message visitors, serialization, and deserialization. + +/** Serialize [message] (or simulate serialization). */ +_serializeMessage(message) { + if (_globalState.needSerialization) { + return new _JsSerializer().traverse(message); + } else { + return new _JsCopier().traverse(message); + } +} + +/** Deserialize [message] (or simulate deserialization). */ +_deserializeMessage(message) { + if (_globalState.needSerialization) { + return new _JsDeserializer().deserialize(message); + } else { + // Nothing more to do. + return message; + } +} + +class _JsSerializer extends _Serializer { + + _JsSerializer() : super() { _visited = new _JsVisitedMap(); } + + visitSendPort(SendPort x) { + if (x is _NativeJsSendPort) return visitNativeJsSendPort(x); + if (x is _WorkerSendPort) return visitWorkerSendPort(x); + throw "Illegal underlying port $x"; + } + + visitCapability(Capability x) { + if (x is CapabilityImpl) { + return ['capability', x._id]; + } + throw "Capability not serializable: $x"; + } + + visitNativeJsSendPort(_NativeJsSendPort port) { + return ['sendport', _globalState.currentManagerId, + port._isolateId, port._receivePort._id]; + } + + visitWorkerSendPort(_WorkerSendPort port) { + return ['sendport', port._workerId, port._isolateId, port._receivePortId]; + } +} + + +class _JsCopier extends _Copier { + + _JsCopier() : super() { _visited = new _JsVisitedMap(); } + + visitSendPort(SendPort x) { + if (x is _NativeJsSendPort) return visitNativeJsSendPort(x); + if (x is _WorkerSendPort) return visitWorkerSendPort(x); + throw "Illegal underlying port $x"; + } + + visitCapability(Capability x) { + if (x is CapabilityImpl) { + return new CapabilityImpl._internal(x._id); + } + throw "Capability not serializable: $x"; + } + + SendPort visitNativeJsSendPort(_NativeJsSendPort port) { + return new _NativeJsSendPort(port._receivePort, port._isolateId); + } + + SendPort visitWorkerSendPort(_WorkerSendPort port) { + return new _WorkerSendPort( + port._workerId, port._isolateId, port._receivePortId); + } +} + +class _JsDeserializer extends _Deserializer { + + SendPort deserializeSendPort(List list) { + int managerId = list[1]; + int isolateId = list[2]; + int receivePortId = list[3]; + // If two isolates are in the same manager, we use NativeJsSendPorts to + // deliver messages directly without using postMessage. + if (managerId == _globalState.currentManagerId) { + var isolate = _globalState.isolates[isolateId]; + if (isolate == null) return null; // Isolate has been closed. + var receivePort = isolate.lookup(receivePortId); + if (receivePort == null) return null; // Port has been closed. + return new _NativeJsSendPort(receivePort, isolateId); + } else { + return new _WorkerSendPort(managerId, isolateId, receivePortId); + } + } + + Capability deserializeCapability(List list) { + return new CapabilityImpl._internal(list[1]); + } +} + +class _JsVisitedMap implements _MessageTraverserVisitedMap { + List tagged; + + /** Retrieves any information stored in the native object [object]. */ + operator[](var object) { + return _getAttachedInfo(object); + } + + /** Injects some information into the native [object]. */ + void operator[]=(var object, var info) { + tagged.add(object); + _setAttachedInfo(object, info); + } + + /** Get ready to rumble. */ + void reset() { + assert(tagged == null); + tagged = new List(); + } + + /** Remove all information injected in the native objects. */ + void cleanup() { + for (int i = 0, length = tagged.length; i < length; i++) { + _clearAttachedInfo(tagged[i]); + } + tagged = null; + } + + void _clearAttachedInfo(var o) { + JS("void", "#['__MessageTraverser__attached_info__'] = #", o, null); + } + + void _setAttachedInfo(var o, var info) { + JS("void", "#['__MessageTraverser__attached_info__'] = #", o, info); + } + + _getAttachedInfo(var o) { + return JS("", "#['__MessageTraverser__attached_info__']", o); + } +} + +// only visible for testing purposes +// TODO(sigmund): remove once we can disable privacy for testing (bug #1882) +class TestingOnly { + static copy(x) { + return new _JsCopier().traverse(x); + } + + // only visible for testing purposes + static serialize(x) { + _Serializer serializer = new _JsSerializer(); + _Deserializer deserializer = new _JsDeserializer(); + return deserializer.deserialize(serializer.traverse(x)); + } +} + +/******************************************************** + Inserted from lib/isolate/serialization.dart + ********************************************************/ + +class _MessageTraverserVisitedMap { + + operator[](var object) => null; + void operator[]=(var object, var info) { } + + void reset() { } + void cleanup() { } + +} + +/** Abstract visitor for dart objects that can be sent as isolate messages. */ +abstract class _MessageTraverser { + + _MessageTraverserVisitedMap _visited; + _MessageTraverser() : _visited = new _MessageTraverserVisitedMap(); + + /** Visitor's entry point. */ + traverse(var x) { + if (isPrimitive(x)) return visitPrimitive(x); + _visited.reset(); + var result; + try { + result = _dispatch(x); + } finally { + _visited.cleanup(); + } + return result; + } + + _dispatch(var x) { + // This code likely fails for user classes implementing + // SendPort and Capability because it assumes the internal classes. + if (isPrimitive(x)) return visitPrimitive(x); + if (x is List) return visitList(x); + if (x is Map) return visitMap(x); + if (x is SendPort) return visitSendPort(x); + if (x is Capability) return visitCapability(x); + + // Overridable fallback. + return visitObject(x); + } + + visitPrimitive(x); + visitList(List x); + visitMap(Map x); + visitSendPort(SendPort x); + visitCapability(Capability x); + + visitObject(Object x) { + // TODO(floitsch): make this a real exception. (which one)? + throw "Message serialization: Illegal value $x passed"; + } + + static bool isPrimitive(x) { + return (x == null) || (x is String) || (x is num) || (x is bool); + } +} + + +/** A visitor that recursively copies a message. */ +class _Copier extends _MessageTraverser { + + visitPrimitive(x) => x; + + List visitList(List list) { + List copy = _visited[list]; + if (copy != null) return copy; + + int len = list.length; + + // TODO(floitsch): we loose the generic type of the List. + copy = new List(len); + _visited[list] = copy; + for (int i = 0; i < len; i++) { + copy[i] = _dispatch(list[i]); + } + return copy; + } + + Map visitMap(Map map) { + Map copy = _visited[map]; + if (copy != null) return copy; + + // TODO(floitsch): we loose the generic type of the map. + copy = new Map(); + _visited[map] = copy; + map.forEach((key, val) { + copy[_dispatch(key)] = _dispatch(val); + }); + return copy; + } + + visitSendPort(SendPort x) => throw new UnimplementedError(); + + visitCapability(Capability x) => throw new UnimplementedError(); +} + +/** Visitor that serializes a message as a JSON array. */ +class _Serializer extends _MessageTraverser { + int _nextFreeRefId = 0; + + visitPrimitive(x) => x; + + visitList(List list) { + int copyId = _visited[list]; + if (copyId != null) return ['ref', copyId]; + + int id = _nextFreeRefId++; + _visited[list] = id; + var jsArray = _serializeList(list); + // TODO(floitsch): we are losing the generic type. + return ['list', id, jsArray]; + } + + visitMap(Map map) { + int copyId = _visited[map]; + if (copyId != null) return ['ref', copyId]; + + int id = _nextFreeRefId++; + _visited[map] = id; + var keys = _serializeList(map.keys.toList()); + var values = _serializeList(map.values.toList()); + // TODO(floitsch): we are losing the generic type. + return ['map', id, keys, values]; + } + + _serializeList(List list) { + int len = list.length; + // Use a growable list because we do not add extra properties on + // them. + var result = new List()..length = len; + for (int i = 0; i < len; i++) { + result[i] = _dispatch(list[i]); + } + return result; + } + + visitSendPort(SendPort x) => throw new UnimplementedError(); + + visitCapability(Capability x) => throw new UnimplementedError(); +} + +/** Deserializes arrays created with [_Serializer]. */ +abstract class _Deserializer { + Map _deserialized; + + _Deserializer(); + + static bool isPrimitive(x) { + return (x == null) || (x is String) || (x is num) || (x is bool); + } + + deserialize(x) { + if (isPrimitive(x)) return x; + // TODO(floitsch): this should be new HashMap() + _deserialized = new HashMap(); + return _deserializeHelper(x); + } + + _deserializeHelper(x) { + if (isPrimitive(x)) return x; + assert(x is List); + switch (x[0]) { + case 'ref': return _deserializeRef(x); + case 'list': return _deserializeList(x); + case 'map': return _deserializeMap(x); + case 'sendport': return deserializeSendPort(x); + case 'capability': return deserializeCapability(x); + default: return deserializeObject(x); + } + } + + _deserializeRef(List x) { + int id = x[1]; + var result = _deserialized[id]; + assert(result != null); + return result; + } + + List _deserializeList(List x) { + int id = x[1]; + // We rely on the fact that Dart-lists are directly mapped to Js-arrays. + List dartList = x[2]; + _deserialized[id] = dartList; + int len = dartList.length; + for (int i = 0; i < len; i++) { + dartList[i] = _deserializeHelper(dartList[i]); + } + return dartList; + } + + Map _deserializeMap(List x) { + Map result = new Map(); + int id = x[1]; + _deserialized[id] = result; + List keys = x[2]; + List values = x[3]; + int len = keys.length; + assert(len == values.length); + for (int i = 0; i < len; i++) { + var key = _deserializeHelper(keys[i]); + var value = _deserializeHelper(values[i]); + result[key] = value; + } + return result; + } + + deserializeSendPort(List x); + + deserializeCapability(List x); + + deserializeObject(List x) { + // TODO(floitsch): Use real exception (which one?). + throw "Unexpected serialized object"; + } +} + +class TimerImpl implements Timer { + final bool _once; + bool _inEventLoop = false; + int _handle; + + TimerImpl(int milliseconds, void callback()) + : _once = true { + if (milliseconds == 0 && (!hasTimer() || _globalState.isWorker)) { + + void internalCallback() { + _handle = null; + callback(); + } + + // Setting _handle to something different from null indicates that the + // callback has not been run. Hence, the choice of 1 is arbitrary. + _handle = 1; + + // This makes a dependency between the async library and the + // event loop of the isolate library. The compiler makes sure + // that the event loop is compiled if [Timer] is used. + // TODO(7907): In case of web workers, we need to use the event + // loop instead of setTimeout, to make sure the futures get executed in + // order. + _globalState.topEventLoop.enqueue( + _globalState.currentContext, internalCallback, 'timer'); + _inEventLoop = true; + } else if (hasTimer()) { + + void internalCallback() { + _handle = null; + leaveJsAsync(); + callback(); + } + + enterJsAsync(); + + _handle = JS('int', '#.setTimeout(#, #)', + globalThis, + convertDartClosureToJS(internalCallback, 0), + milliseconds); + } else { + assert(milliseconds > 0); + throw new UnsupportedError("Timer greater than 0."); + } + } + + TimerImpl.periodic(int milliseconds, void callback(Timer timer)) + : _once = false { + if (hasTimer()) { + enterJsAsync(); + _handle = JS('int', '#.setInterval(#, #)', + globalThis, + convertDartClosureToJS(() { callback(this); }, 0), + milliseconds); + } else { + throw new UnsupportedError("Periodic timer."); + } + } + + void cancel() { + if (hasTimer()) { + if (_inEventLoop) { + throw new UnsupportedError("Timer in event loop cannot be canceled."); + } + if (_handle == null) return; + leaveJsAsync(); + if (_once) { + JS('void', '#.clearTimeout(#)', globalThis, _handle); + } else { + JS('void', '#.clearInterval(#)', globalThis, _handle); + } + _handle = null; + } else { + throw new UnsupportedError("Canceling a timer."); + } + } + + bool get isActive => _handle != null; +} + +bool hasTimer() => JS('', '#.setTimeout', globalThis) != null; + + +/** + * Implementation class for [Capability]. + * + * It has the same name to make it harder for users to distinguish. + */ +class CapabilityImpl implements Capability { + /** Internal random secret identifying the capability. */ + final int _id; + + CapabilityImpl() : this._internal(random64()); + + CapabilityImpl._internal(this._id); + + int get hashCode { + // Thomas Wang 32 bit Mix. + // http://www.concentric.net/~Ttwang/tech/inthash.htm + // (via https://gist.github.com/badboy/6267743) + int hash = _id; + hash = (hash >> 0) ^ (hash ~/ 0x100000000); // To 32 bit from ~64. + hash = (~hash + (hash << 15)) & 0xFFFFFFFF; + hash ^= hash >> 12; + hash = (hash * 5) & 0xFFFFFFFF; + hash ^= hash >> 4; + hash = (hash * 2057) & 0xFFFFFFFF; + hash ^= hash >> 16; + return hash; + } + + bool operator==(Object other) { + if (identical(other, this)) return true; + if (other is CapabilityImpl) { + return identical(_id, other._id); + } + return false; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/isolate_patch.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/isolate_patch.dart new file mode 100644 index 0000000..4959a0d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/isolate_patch.dart @@ -0,0 +1,66 @@ +// Copyright (c) 2012, 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. + +// Patch file for the dart:isolate library. + +import 'dart:_isolate_helper' show CapabilityImpl, + CloseToken, + IsolateNatives, + JsIsolateSink, + ReceivePortImpl, + RawReceivePortImpl; + +patch class Isolate { + patch static Future spawn(void entryPoint(message), var message, + { bool paused: false }) { + try { + return IsolateNatives.spawnFunction(entryPoint, message, paused) + .then((msg) => new Isolate(msg[1], + pauseCapability: msg[2], + terminateCapability: msg[3])); + } catch (e, st) { + return new Future.error(e, st); + } + } + + patch static Future spawnUri( + Uri uri, List args, var message, { bool paused: false }) { + try { + if (args is List) { + for (int i = 0; i < args.length; i++) { + if (args[i] is! String) { + throw new ArgumentError("Args must be a list of Strings $args"); + } + } + } else if (args != null) { + throw new ArgumentError("Args must be a list of Strings $args"); + } + return IsolateNatives.spawnUri(uri, args, message, paused) + .then((msg) => new Isolate(msg[1], + pauseCapability: msg[2], + terminateCapability: msg[3])); + } catch (e, st) { + return new Future.error(e, st); + } + } +} + +/** Default factory for receive ports. */ +patch class ReceivePort { + patch factory ReceivePort() = ReceivePortImpl; + + patch factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort) { + return new ReceivePortImpl.fromRawReceivePort(rawPort); + } +} + +patch class RawReceivePort { + patch factory RawReceivePort([void handler(event)]) { + return new RawReceivePortImpl(handler); + } +} + +patch class Capability { + patch factory Capability() = CapabilityImpl; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_array.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_array.dart new file mode 100644 index 0000000..730d063 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_array.dart @@ -0,0 +1,379 @@ +// Copyright (c) 2012, 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. + +part of _interceptors; + +/** + * The interceptor class for [List]. The compiler recognizes this + * class as an interceptor, and changes references to [:this:] to + * actually use the receiver of the method, which is generated as an extra + * argument added to each member. + */ +class JSArray extends Interceptor implements List, JSIndexable { + + const JSArray(); + + /** + * Returns a fresh JavaScript Array, marked as fixed-length. + * + * [length] must be a non-negative integer. + */ + factory JSArray.fixed(int length) { + // Explicit type test is necessary to guard against JavaScript conversions + // in unchecked mode. + if ((length is !int) || (length < 0)) { + throw new ArgumentError("Length must be a non-negative integer: $length"); + } + return new JSArray.markFixed(JS('', 'new Array(#)', length)); + } + + /** + * Returns a fresh growable JavaScript Array of zero length length. + */ + factory JSArray.emptyGrowable() => new JSArray.markGrowable(JS('', '[]')); + + /** + * Returns a fresh growable JavaScript Array with initial length. + * + * [validatedLength] must be a non-negative integer. + */ + factory JSArray.growable(int length) { + // Explicit type test is necessary to guard against JavaScript conversions + // in unchecked mode. + if ((length is !int) || (length < 0)) { + throw new ArgumentError("Length must be a non-negative integer: $length"); + } + return new JSArray.markGrowable(JS('', 'new Array(#)', length)); + } + + /** + * Constructor for adding type parameters to an existing JavaScript Array. + * The compiler specially recognizes this constructor. + * + * var a = new JSArray.typed(JS('JSExtendableArray', '[]')); + * a is List --> true + * a is List --> false + * + * Usually either the [JSArray.markFixed] or [JSArray.markGrowable] + * constructors is used instead. + * + * The input must be a JavaScript Array. The JS form is just a re-assertion + * to help type analysis when the input type is sloppy. + */ + factory JSArray.typed(allocation) => JS('JSArray', '#', allocation); + + factory JSArray.markFixed(allocation) => + JS('JSFixedArray', '#', markFixedList(new JSArray.typed(allocation))); + + factory JSArray.markGrowable(allocation) => + JS('JSExtendableArray', '#', new JSArray.typed(allocation)); + + static List markFixedList(List list) { + JS('void', r'#.fixed$length = init', list); + return JS('JSFixedArray', '#', list); + } + + checkMutable(reason) { + if (this is !JSMutableArray) { + throw new UnsupportedError(reason); + } + } + + checkGrowable(reason) { + if (this is !JSExtendableArray) { + throw new UnsupportedError(reason); + } + } + + void add(E value) { + checkGrowable('add'); + JS('void', r'#.push(#)', this, value); + } + + E removeAt(int index) { + if (index is !int) throw new ArgumentError(index); + if (index < 0 || index >= length) { + throw new RangeError.value(index); + } + checkGrowable('removeAt'); + return JS('var', r'#.splice(#, 1)[0]', this, index); + } + + void insert(int index, E value) { + if (index is !int) throw new ArgumentError(index); + if (index < 0 || index > length) { + throw new RangeError.value(index); + } + checkGrowable('insert'); + JS('void', r'#.splice(#, 0, #)', this, index, value); + } + + void insertAll(int index, Iterable iterable) { + checkGrowable('insertAll'); + IterableMixinWorkaround.insertAllList(this, index, iterable); + } + + void setAll(int index, Iterable iterable) { + checkMutable('setAll'); + IterableMixinWorkaround.setAllList(this, index, iterable); + } + + E removeLast() { + checkGrowable('removeLast'); + if (length == 0) throw new RangeError.value(-1); + return JS('var', r'#.pop()', this); + } + + bool remove(Object element) { + checkGrowable('remove'); + for (int i = 0; i < this.length; i++) { + if (this[i] == element) { + JS('var', r'#.splice(#, 1)', this, i); + return true; + } + } + return false; + } + + void removeWhere(bool test(E element)) { + // This could, and should, be optimized. + IterableMixinWorkaround.removeWhereList(this, test); + } + + void retainWhere(bool test(E element)) { + IterableMixinWorkaround.removeWhereList(this, + (E element) => !test(element)); + } + + Iterable where(bool f(E element)) { + return IterableMixinWorkaround.where(this, f); + } + + Iterable expand(Iterable f(E element)) { + return IterableMixinWorkaround.expand(this, f); + } + + void addAll(Iterable collection) { + for (E e in collection) { + this.add(e); + } + } + + void clear() { + length = 0; + } + + void forEach(void f(E element)) { + return IterableMixinWorkaround.forEach(this, f); + } + + Iterable map(f(E element)) { + return IterableMixinWorkaround.mapList(this, f); + } + + String join([String separator = ""]) { + var list = new List(this.length); + for (int i = 0; i < this.length; i++) { + list[i] = "${this[i]}"; + } + return JS('String', "#.join(#)", list, separator); + } + + Iterable take(int n) { + return IterableMixinWorkaround.takeList(this, n); + } + + Iterable takeWhile(bool test(E value)) { + return IterableMixinWorkaround.takeWhile(this, test); + } + + Iterable skip(int n) { + return IterableMixinWorkaround.skipList(this, n); + } + + Iterable skipWhile(bool test(E value)) { + return IterableMixinWorkaround.skipWhile(this, test); + } + + E reduce(E combine(E value, E element)) { + return IterableMixinWorkaround.reduce(this, combine); + } + + fold(initialValue, combine(previousValue, E element)) { + return IterableMixinWorkaround.fold(this, initialValue, combine); + } + + dynamic firstWhere(bool test(E value), {Object orElse()}) { + return IterableMixinWorkaround.firstWhere(this, test, orElse); + } + + dynamic lastWhere(bool test(E value), {Object orElse()}) { + return IterableMixinWorkaround.lastWhereList(this, test, orElse); + } + + E singleWhere(bool test(E value)) { + return IterableMixinWorkaround.singleWhere(this, test); + } + + E elementAt(int index) { + return this[index]; + } + + List sublist(int start, [int end]) { + checkNull(start); // TODO(ahe): This is not specified but co19 tests it. + if (start is !int) throw new ArgumentError(start); + if (start < 0 || start > length) { + throw new RangeError.range(start, 0, length); + } + if (end == null) { + end = length; + } else { + if (end is !int) throw new ArgumentError(end); + if (end < start || end > length) { + throw new RangeError.range(end, start, length); + } + } + if (start == end) return []; + return new JSArray.markGrowable( + JS('', r'#.slice(#, #)', this, start, end)); + } + + + Iterable getRange(int start, int end) { + return IterableMixinWorkaround.getRangeList(this, start, end); + } + + E get first { + if (length > 0) return this[0]; + throw new StateError("No elements"); + } + + E get last { + if (length > 0) return this[length - 1]; + throw new StateError("No elements"); + } + + E get single { + if (length == 1) return this[0]; + if (length == 0) throw new StateError("No elements"); + throw new StateError("More than one element"); + } + + void removeRange(int start, int end) { + checkGrowable('removeRange'); + int receiverLength = this.length; + if (start < 0 || start > receiverLength) { + throw new RangeError.range(start, 0, receiverLength); + } + if (end < start || end > receiverLength) { + throw new RangeError.range(end, start, receiverLength); + } + Lists.copy(this, + end, + this, + start, + receiverLength - end); + this.length = receiverLength - (end - start); + } + + void setRange(int start, int end, Iterable iterable, [int skipCount = 0]) { + checkMutable('set range'); + IterableMixinWorkaround.setRangeList(this, start, end, iterable, skipCount); + } + + void fillRange(int start, int end, [E fillValue]) { + checkMutable('fill range'); + IterableMixinWorkaround.fillRangeList(this, start, end, fillValue); + } + + void replaceRange(int start, int end, Iterable iterable) { + checkGrowable('removeRange'); + IterableMixinWorkaround.replaceRangeList(this, start, end, iterable); + } + + bool any(bool f(E element)) => IterableMixinWorkaround.any(this, f); + + bool every(bool f(E element)) => IterableMixinWorkaround.every(this, f); + + Iterable get reversed => IterableMixinWorkaround.reversedList(this); + + void sort([int compare(E a, E b)]) { + checkMutable('sort'); + IterableMixinWorkaround.sortList(this, compare); + } + + void shuffle([Random random]) { + IterableMixinWorkaround.shuffleList(this, random); + } + + int indexOf(Object element, [int start = 0]) { + return IterableMixinWorkaround.indexOfList(this, element, start); + } + + int lastIndexOf(Object element, [int start]) { + return IterableMixinWorkaround.lastIndexOfList(this, element, start); + } + + bool contains(Object other) { + for (int i = 0; i < length; i++) { + if (this[i] == other) return true; + } + return false; + } + + bool get isEmpty => length == 0; + + bool get isNotEmpty => !isEmpty; + + String toString() => IterableMixinWorkaround.toStringIterable(this, '[', ']'); + + List toList({ bool growable: true }) { + if (growable) { + return new JSArray.markGrowable(JS('', '#.slice()', this)); + } else { + return new JSArray.markFixed(JS('', '#.slice()', this)); + } + } + + Set toSet() => new Set.from(this); + + Iterator get iterator => new ListIterator(this); + + int get hashCode => Primitives.objectHashCode(this); + + int get length => JS('JSUInt32', r'#.length', this); + + void set length(int newLength) { + if (newLength is !int) throw new ArgumentError(newLength); + if (newLength < 0) throw new RangeError.value(newLength); + checkGrowable('set length'); + JS('void', r'#.length = #', this, newLength); + } + + E operator [](int index) { + if (index is !int) throw new ArgumentError(index); + if (index >= length || index < 0) throw new RangeError.value(index); + return JS('var', '#[#]', this, index); + } + + void operator []=(int index, E value) { + checkMutable('indexed set'); + if (index is !int) throw new ArgumentError(index); + if (index >= length || index < 0) throw new RangeError.value(index); + JS('void', r'#[#] = #', this, index, value); + } + + Map asMap() { + return IterableMixinWorkaround.asMapList(this); + } +} + +/** + * Dummy subclasses that allow the backend to track more precise + * information about arrays through their type. The CPA type inference + * relies on the fact that these classes do not override [] nor []=. + */ +class JSMutableArray extends JSArray implements JSMutableIndexable {} +class JSFixedArray extends JSMutableArray {} +class JSExtendableArray extends JSMutableArray {} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_helper.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_helper.dart new file mode 100644 index 0000000..1ee856e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_helper.dart @@ -0,0 +1,3325 @@ +// Copyright (c) 2013, 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. + +library _js_helper; + +import 'dart:collection'; +import 'dart:_isolate_helper' show + IsolateNatives, + leaveJsAsync, + enterJsAsync, + isWorker; + +import 'dart:async' show Future, DeferredLoadException, Completer; + +import 'dart:_foreign_helper' show + DART_CLOSURE_TO_JS, + JS, + JS_CALL_IN_ISOLATE, + JS_CONST, + JS_CURRENT_ISOLATE, + JS_CURRENT_ISOLATE_CONTEXT, + JS_DART_OBJECT_CONSTRUCTOR, + JS_EFFECT, + JS_FUNCTION_CLASS_NAME, + JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG, + JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG, + JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG, + JS_FUNCTION_TYPE_RETURN_TYPE_TAG, + JS_FUNCTION_TYPE_TAG, + JS_FUNCTION_TYPE_VOID_RETURN_TAG, + JS_GET_NAME, + JS_GET_FLAG, + JS_HAS_EQUALS, + JS_IS_INDEXABLE_FIELD_NAME, + JS_NULL_CLASS_NAME, + JS_OBJECT_CLASS_NAME, + JS_OPERATOR_AS_PREFIX, + JS_OPERATOR_IS_PREFIX, + JS_SIGNATURE_NAME, + RAW_DART_FUNCTION_REF; + +import 'dart:_interceptors'; +import 'dart:_internal' as _symbol_dev; +import 'dart:_internal' show MappedIterable; + +import 'dart:_js_names' show + extractKeys, + mangledNames, + unmangleGlobalNameIfPreservedAnyways; + +part 'annotations.dart'; +part 'constant_map.dart'; +part 'native_helper.dart'; +part 'regexp_helper.dart'; +part 'string_helper.dart'; +part 'js_rti.dart'; + +bool isJsIndexable(var object, var record) { + if (record != null) { + var result = dispatchRecordIndexability(record); + if (result != null) return result; + } + return object is JavaScriptIndexingBehavior; +} + +String S(value) { + if (value is String) return value; + if (value is num) { + if (value != 0) { + // ""+x is faster than String(x) for integers on most browsers. + return JS('String', r'"" + (#)', value); + } + } else if (true == value) { + return 'true'; + } else if (false == value) { + return 'false'; + } else if (value == null) { + return 'null'; + } + var res = value.toString(); + if (res is !String) throw new ArgumentError(value); + return res; +} + +createInvocationMirror(String name, internalName, kind, arguments, + argumentNames) { + return new JSInvocationMirror(name, + internalName, + kind, + arguments, + argumentNames); +} + +createUnmangledInvocationMirror(Symbol symbol, internalName, kind, arguments, + argumentNames) { + return new JSInvocationMirror(symbol, + internalName, + kind, + arguments, + argumentNames); +} + +void throwInvalidReflectionError(String memberName) { + throw new UnsupportedError("Can't use '$memberName' in reflection " + "because it is not included in a @MirrorsUsed annotation."); +} + +bool hasReflectableProperty(var jsFunction) { + return JS('bool', '# in #', JS_GET_NAME("REFLECTABLE"), jsFunction); +} + +class JSInvocationMirror implements Invocation { + static const METHOD = 0; + static const GETTER = 1; + static const SETTER = 2; + + /// When [_memberName] is a String, it holds the mangled name of this + /// invocation. When it is a Symbol, it holds the unmangled name. + var /* String or Symbol */ _memberName; + final String _internalName; + final int _kind; + final List _arguments; + final List _namedArgumentNames; + /** Map from argument name to index in _arguments. */ + Map _namedIndices = null; + + JSInvocationMirror(this._memberName, + this._internalName, + this._kind, + this._arguments, + this._namedArgumentNames); + + Symbol get memberName { + if (_memberName is Symbol) return _memberName; + String name = _memberName; + String unmangledName = mangledNames[name]; + if (unmangledName != null) { + name = unmangledName.split(':')[0]; + } + _memberName = new _symbol_dev.Symbol.unvalidated(name); + return _memberName; + } + + bool get isMethod => _kind == METHOD; + bool get isGetter => _kind == GETTER; + bool get isSetter => _kind == SETTER; + bool get isAccessor => _kind != METHOD; + + List get positionalArguments { + if (isGetter) return const []; + var argumentCount = + _arguments.length - _namedArgumentNames.length; + if (argumentCount == 0) return const []; + var list = []; + for (var index = 0 ; index < argumentCount ; index++) { + list.add(_arguments[index]); + } + return makeLiteralListConst(list); + } + + Map get namedArguments { + // TODO: Make maps const (issue 10471) + if (isAccessor) return {}; + int namedArgumentCount = _namedArgumentNames.length; + int namedArgumentsStartIndex = _arguments.length - namedArgumentCount; + if (namedArgumentCount == 0) return {}; + var map = new Map(); + for (int i = 0; i < namedArgumentCount; i++) { + map[new _symbol_dev.Symbol.unvalidated(_namedArgumentNames[i])] = + _arguments[namedArgumentsStartIndex + i]; + } + return map; + } + + _getCachedInvocation(Object object) { + var interceptor = getInterceptor(object); + var receiver = object; + var name = _internalName; + var arguments = _arguments; + // TODO(ngeoffray): If this functionality ever become performance + // critical, we might want to dynamically change [interceptedNames] + // to be a JavaScript object with intercepted names as property + // instead of a JavaScript array. + // TODO(floitsch): we already add stubs (tear-off getters) as properties + // in init.interceptedNames. + // Finish the transition and always use the object as hashtable. + bool isIntercepted = + JS("bool", + 'Object.prototype.hasOwnProperty.call(init.interceptedNames, #) ||' + '#.indexOf(#) !== -1', + name, interceptedNames, name); + if (isIntercepted) { + receiver = interceptor; + if (JS('bool', '# === #', object, interceptor)) { + interceptor = null; + } + } else { + interceptor = null; + } + bool isCatchAll = false; + var method = JS('var', '#[#]', receiver, name); + if (JS('bool', 'typeof # != "function"', method) ) { + String baseName = _symbol_dev.Symbol.getName(memberName); + method = JS('', '#[# + "*"]', receiver, baseName); + if (method == null) { + interceptor = getInterceptor(object); + method = JS('', '#[# + "*"]', interceptor, baseName); + if (method != null) { + isIntercepted = true; + receiver = interceptor; + } else { + interceptor = null; + } + } + isCatchAll = true; + } + if (JS('bool', 'typeof # == "function"', method)) { + // TODO(floitsch): bound or tear-off closure does not guarantee that the + // function is reflectable. + bool isReflectable = hasReflectableProperty(method) || + object is BoundClosure || + object is TearOffClosure; + if (!isReflectable) { + throwInvalidReflectionError(_symbol_dev.Symbol.getName(memberName)); + } + if (isCatchAll) { + return new CachedCatchAllInvocation( + name, method, isIntercepted, interceptor); + } else { + return new CachedInvocation(name, method, isIntercepted, interceptor); + } + } else { + // In this case, receiver doesn't implement name. So we should + // invoke noSuchMethod instead (which will often throw a + // NoSuchMethodError). + return new CachedNoSuchMethodInvocation(interceptor); + } + } + + /// This method is called by [InstanceMirror.delegate]. + static invokeFromMirror(JSInvocationMirror invocation, Object victim) { + var cached = invocation._getCachedInvocation(victim); + if (cached.isNoSuchMethod) { + return cached.invokeOn(victim, invocation); + } else { + return cached.invokeOn(victim, invocation._arguments); + } + } + + static getCachedInvocation(JSInvocationMirror invocation, Object victim) { + return invocation._getCachedInvocation(victim); + } +} + +class CachedInvocation { + // The mangled name of this invocation. + String mangledName; + + /// The JS function to call. + var jsFunction; + + /// True if this is an intercepted call. + bool isIntercepted; + + /// Non-null interceptor if this is an intercepted call through an + /// [Interceptor]. + Interceptor cachedInterceptor; + + CachedInvocation(this.mangledName, + this.jsFunction, + this.isIntercepted, + this.cachedInterceptor); + + bool get isNoSuchMethod => false; + + /// Applies [jsFunction] to [victim] with [arguments]. + /// Users of this class must take care to check the arguments first. + invokeOn(Object victim, List arguments) { + var receiver = victim; + if (!isIntercepted) { + if (arguments is! JSArray) arguments = new List.from(arguments); + } else { + arguments = [victim]..addAll(arguments); + if (cachedInterceptor != null) receiver = cachedInterceptor; + } + return JS("var", "#.apply(#, #)", jsFunction, receiver, arguments); + } +} + +class CachedCatchAllInvocation extends CachedInvocation { + final ReflectionInfo info; + + CachedCatchAllInvocation(String name, + jsFunction, + bool isIntercepted, + Interceptor cachedInterceptor) + : info = new ReflectionInfo(jsFunction), + super(name, jsFunction, isIntercepted, cachedInterceptor); + + invokeOn(Object victim, List arguments) { + var receiver = victim; + int providedArgumentCount; + int fullParameterCount = + info.requiredParameterCount + info.optionalParameterCount; + if (!isIntercepted) { + if (arguments is JSArray) { + providedArgumentCount = arguments.length; + // If we need to add extra arguments before calling, we have + // to copy the arguments array. + if (providedArgumentCount < fullParameterCount) { + arguments = new List.from(arguments); + } + } else { + arguments = new List.from(arguments); + providedArgumentCount = arguments.length; + } + } else { + arguments = [victim]..addAll(arguments); + if (cachedInterceptor != null) receiver = cachedInterceptor; + providedArgumentCount = arguments.length - 1; + } + if (info.areOptionalParametersNamed && + (providedArgumentCount > info.requiredParameterCount)) { + throw new UnimplementedNoSuchMethodError( + "Invocation of unstubbed method '${info.reflectionName}'" + " with ${arguments.length} arguments."); + } else if (providedArgumentCount < info.requiredParameterCount) { + throw new UnimplementedNoSuchMethodError( + "Invocation of unstubbed method '${info.reflectionName}'" + " with $providedArgumentCount arguments (too few)."); + } else if (providedArgumentCount > fullParameterCount) { + throw new UnimplementedNoSuchMethodError( + "Invocation of unstubbed method '${info.reflectionName}'" + " with $providedArgumentCount arguments (too many)."); + } + for (int i = providedArgumentCount; i < fullParameterCount; i++) { + arguments.add(getMetadata(info.defaultValue(i))); + } + return JS("var", "#.apply(#, #)", jsFunction, receiver, arguments); + } +} + +class CachedNoSuchMethodInvocation { + /// Non-null interceptor if this is an intercepted call through an + /// [Interceptor]. + var interceptor; + + CachedNoSuchMethodInvocation(this.interceptor); + + bool get isNoSuchMethod => true; + + invokeOn(Object victim, Invocation invocation) { + var receiver = (interceptor == null) ? victim : interceptor; + return receiver.noSuchMethod(invocation); + } +} + +class ReflectionInfo { + static const int REQUIRED_PARAMETERS_INFO = 0; + static const int OPTIONAL_PARAMETERS_INFO = 1; + static const int FUNCTION_TYPE_INDEX = 2; + static const int FIRST_DEFAULT_ARGUMENT = 3; + + /// A JavaScript function object. + final jsFunction; + + /// Raw reflection information. + final List data; + + /// Is this a getter or a setter. + final bool isAccessor; + + /// Number of required parameters. + final int requiredParameterCount; + + /// Number of optional parameters. + final int optionalParameterCount; + + /// Are optional parameters named. + final bool areOptionalParametersNamed; + + /// Either an index to the function type in [:init.metadata:] or a JavaScript + /// function object which can compute such a type (presumably due to free + /// type variables). + final functionType; + + List cachedSortedIndices; + + ReflectionInfo.internal(this.jsFunction, + this.data, + this.isAccessor, + this.requiredParameterCount, + this.optionalParameterCount, + this.areOptionalParametersNamed, + this.functionType); + + factory ReflectionInfo(jsFunction) { + List data = JS('JSExtendableArray|Null', r'#.$reflectionInfo', jsFunction); + if (data == null) return null; + data = JSArray.markFixedList(data); + + int requiredParametersInfo = + JS('int', '#[#]', data, REQUIRED_PARAMETERS_INFO); + int requiredParameterCount = JS('int', '# >> 1', requiredParametersInfo); + bool isAccessor = (requiredParametersInfo & 1) == 1; + + int optionalParametersInfo = + JS('int', '#[#]', data, OPTIONAL_PARAMETERS_INFO); + int optionalParameterCount = JS('int', '# >> 1', optionalParametersInfo); + bool areOptionalParametersNamed = (optionalParametersInfo & 1) == 1; + + var functionType = JS('', '#[#]', data, FUNCTION_TYPE_INDEX); + return new ReflectionInfo.internal( + jsFunction, data, isAccessor, requiredParameterCount, + optionalParameterCount, areOptionalParametersNamed, functionType); + } + + String parameterName(int parameter) { + int metadataIndex; + if (JS_GET_FLAG('MUST_RETAIN_METADATA')) { + metadataIndex = JS('int', '#[2 * # + # + #]', data, + parameter, optionalParameterCount, FIRST_DEFAULT_ARGUMENT); + } else { + metadataIndex = JS('int', '#[# + # + #]', data, + parameter, optionalParameterCount, FIRST_DEFAULT_ARGUMENT); + } + return JS('String', 'init.metadata[#]', metadataIndex); + } + + List parameterMetadataAnnotations(int parameter) { + if (!JS_GET_FLAG('MUST_RETAIN_METADATA')) { + throw new StateError('metadata has not been preserved'); + } else { + return JS('', '#[2 * # + # + # + 1]', data, parameter, + optionalParameterCount, FIRST_DEFAULT_ARGUMENT); + } + } + + int defaultValue(int parameter) { + if (parameter < requiredParameterCount) return null; + return JS('int', '#[# + # - #]', data, + FIRST_DEFAULT_ARGUMENT, parameter, requiredParameterCount); + } + + /// Returns the default value of the [parameter]th entry of the list of + /// parameters sorted by name. + int defaultValueInOrder(int parameter) { + if (parameter < requiredParameterCount) return null; + + if (!areOptionalParametersNamed || optionalParameterCount == 1) { + return defaultValue(parameter); + } + + int index = sortedIndex(parameter - requiredParameterCount); + return defaultValue(index); + } + + /// Returns the default value of the [parameter]th entry of the list of + /// parameters sorted by name. + String parameterNameInOrder(int parameter) { + if (parameter < requiredParameterCount) return null; + + if (!areOptionalParametersNamed || + optionalParameterCount == 1) { + return parameterName(parameter); + } + + int index = sortedIndex(parameter - requiredParameterCount); + return parameterName(index); + } + + /// Computes the index of the parameter in the list of named parameters sorted + /// by their name. + int sortedIndex(int unsortedIndex) { + if (cachedSortedIndices == null) { + // TODO(karlklose): cache this between [ReflectionInfo] instances or cache + // [ReflectionInfo] instances by [jsFunction]. + cachedSortedIndices = new List(optionalParameterCount); + Map positions = {}; + for (int i = 0; i < optionalParameterCount; i++) { + int index = requiredParameterCount + i; + positions[parameterName(index)] = index; + } + int index = 0; + (positions.keys.toList()..sort()).forEach((String name) { + cachedSortedIndices[index++] = positions[name]; + }); + } + return cachedSortedIndices[unsortedIndex]; + } + + @NoInline() + computeFunctionRti(jsConstructor) { + if (JS('bool', 'typeof # == "number"', functionType)) { + return getMetadata(functionType); + } else if (JS('bool', 'typeof # == "function"', functionType)) { + var fakeInstance = JS('', 'new #()', jsConstructor); + setRuntimeTypeInfo( + fakeInstance, JS('JSExtendableArray', '#["<>"]', fakeInstance)); + return JS('=Object|Null', r'#.apply({$receiver:#})', + functionType, fakeInstance); + } else { + throw new RuntimeError('Unexpected function type'); + } + } + + String get reflectionName => JS('String', r'#.$reflectionName', jsFunction); +} + +getMetadata(int index) => JS('', 'init.metadata[#]', index); + +class Primitives { + /// Isolate-unique ID for caching [JsClosureMirror.function]. + /// Note the initial value is used by the first isolate (or if there are no + /// isolates), new isolates will update this value to avoid conflicts by + /// calling [initializeStatics]. + static String mirrorFunctionCacheName = '\$cachedFunction'; + + /// Isolate-unique ID for caching [JsInstanceMirror._invoke]. + static String mirrorInvokeCacheName = '\$cachedInvocation'; + + /// Called when creating a new isolate (see _IsolateContext constructor in + /// isolate_helper.dart). + /// Please don't add complicated code to this method, as it will impact + /// start-up performance. + static void initializeStatics(int id) { + // Benchmarking shows significant performance improvements if this is a + // fixed value. + mirrorFunctionCacheName += '_$id'; + mirrorInvokeCacheName += '_$id'; + } + + static int objectHashCode(object) { + int hash = JS('int|Null', r'#.$identityHash', object); + if (hash == null) { + hash = JS('int', '(Math.random() * 0x3fffffff) | 0'); + JS('void', r'#.$identityHash = #', object, hash); + } + return JS('int', '#', hash); + } + + static computeGlobalThis() => JS('', 'function() { return this; }()'); + + static _throwFormatException(String string) { + throw new FormatException(string); + } + + static int parseInt(String source, + int radix, + int handleError(String source)) { + if (handleError == null) handleError = _throwFormatException; + + checkString(source); + var match = JS('JSExtendableArray|Null', + r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i.exec(#)', + source); + int digitsIndex = 1; + int hexIndex = 2; + int decimalIndex = 3; + int nonDecimalHexIndex = 4; + if (radix == null) { + radix = 10; + if (match != null) { + if (match[hexIndex] != null) { + // Cannot fail because we know that the digits are all hex. + return JS('num', r'parseInt(#, 16)', source); + } + if (match[decimalIndex] != null) { + // Cannot fail because we know that the digits are all decimal. + return JS('num', r'parseInt(#, 10)', source); + } + return handleError(source); + } + } else { + if (radix is! int) throw new ArgumentError("Radix is not an integer"); + if (radix < 2 || radix > 36) { + throw new RangeError("Radix $radix not in range 2..36"); + } + if (match != null) { + if (radix == 10 && match[decimalIndex] != null) { + // Cannot fail because we know that the digits are all decimal. + return JS('num', r'parseInt(#, 10)', source); + } + if (radix < 10 || match[decimalIndex] == null) { + // We know that the characters must be ASCII as otherwise the + // regexp wouldn't have matched. Lowercasing by doing `| 0x20` is thus + // guaranteed to be a safe operation, since it preserves digits + // and lower-cases ASCII letters. + int maxCharCode; + if (radix <= 10) { + // Allow all digits less than the radix. For example 0, 1, 2 for + // radix 3. + // "0".codeUnitAt(0) + radix - 1; + maxCharCode = 0x30 + radix - 1; + } else { + // Letters are located after the digits in ASCII. Therefore we + // only check for the character code. The regexp above made already + // sure that the string does not contain anything but digits or + // letters. + // "a".codeUnitAt(0) + (radix - 10) - 1; + maxCharCode = 0x61 + radix - 10 - 1; + } + String digitsPart = match[digitsIndex]; + for (int i = 0; i < digitsPart.length; i++) { + int characterCode = digitsPart.codeUnitAt(0) | 0x20; + if (digitsPart.codeUnitAt(i) > maxCharCode) { + return handleError(source); + } + } + } + } + } + if (match == null) return handleError(source); + return JS('num', r'parseInt(#, #)', source, radix); + } + + static double parseDouble(String source, double handleError(String source)) { + checkString(source); + if (handleError == null) handleError = _throwFormatException; + // Notice that JS parseFloat accepts garbage at the end of the string. + // Accept only: + // - [+/-]NaN + // - [+/-]Infinity + // - a Dart double literal + // We do allow leading or trailing whitespace. + if (!JS('bool', + r'/^\s*[+-]?(?:Infinity|NaN|' + r'(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(#)', + source)) { + return handleError(source); + } + var result = JS('num', r'parseFloat(#)', source); + if (result.isNaN) { + var trimmed = source.trim(); + if (trimmed == 'NaN' || trimmed == '+NaN' || trimmed == '-NaN') { + return result; + } + return handleError(source); + } + return result; + } + + /** [: r"$".codeUnitAt(0) :] */ + static const int DOLLAR_CHAR_VALUE = 36; + + /// Creates a string containing the complete type for the class [className] + /// with the given type arguments. + static String formatType(String className, List typeArguments) { + return '$className${joinArguments(typeArguments, 0)}'; + } + + /// Returns the type of [object] as a string (including type arguments). + static String objectTypeName(Object object) { + String name = constructorNameFallback(getInterceptor(object)); + if (name == 'Object') { + // Try to decompile the constructor by turning it into a string + // and get the name out of that. If the decompiled name is a + // string, we use that instead of the very generic 'Object'. + var decompiled = JS('var', r'#.match(/^\s*function\s*(\S*)\s*\(/)[1]', + JS('var', r'String(#.constructor)', object)); + if (decompiled is String) name = decompiled; + } + // TODO(kasperl): If the namer gave us a fresh global name, we may + // want to remove the numeric suffix that makes it unique too. + if (identical(name.codeUnitAt(0), DOLLAR_CHAR_VALUE)) { + name = name.substring(1); + } + return formatType(name, getRuntimeTypeInfo(object)); + } + + static String objectToString(Object object) { + String name = objectTypeName(object); + return "Instance of '$name'"; + } + + static num dateNow() => JS('num', r'Date.now()'); + + static num numMicroseconds() { + if (JS('bool', 'typeof window != "undefined" && window !== null')) { + var performance = JS('var', 'window.performance'); + if (performance != null && + JS('bool', 'typeof #.webkitNow == "function"', performance)) { + return (1000 * JS('num', '#.webkitNow()', performance)).floor(); + } + } + return 1000 * dateNow(); + } + + static bool get isD8 { + return JS('bool', + 'typeof version == "function"' + ' && typeof os == "object" && "system" in os'); + } + + static bool get isJsshell { + return JS('bool', + 'typeof version == "function" && typeof system == "function"'); + } + + static String currentUri() { + // In a browser return self.location.href. + if (JS('bool', 'typeof self != "undefined"')) { + return JS('String', 'self.location.href'); + } + + // In JavaScript shells try to determine the current working + // directory. + var workingDirectory; + if (isD8) { + // TODO(sgjesse): This does not work on Windows. + workingDirectory = JS('String', 'os.system("pwd")'); + var length = workingDirectory.length; + if (workingDirectory[length - 1] == '\n') { + workingDirectory = workingDirectory.substring(0, length - 1); + } + } + + if (isJsshell) { + // TODO(sgjesse): This does not work on Windows. + workingDirectory = JS('String', 'environment["PWD"]'); + } + + return workingDirectory != null + ? "file://" + workingDirectory + "/" + : null; + } + + // This is to avoid stack overflows due to very large argument arrays in + // apply(). It fixes http://dartbug.com/6919 + static String _fromCharCodeApply(List array) { + String result = ""; + const kMaxApply = 500; + int end = array.length; + for (var i = 0; i < end; i += kMaxApply) { + var subarray; + if (end <= kMaxApply) { + subarray = array; + } else { + subarray = JS('JSExtendableArray', r'#.slice(#, #)', array, + i, i + kMaxApply < end ? i + kMaxApply : end); + } + result = JS('String', '# + String.fromCharCode.apply(#, #)', + result, null, subarray); + } + return result; + } + + static String stringFromCodePoints(codePoints) { + List a = []; + for (var i in codePoints) { + if (i is !int) throw new ArgumentError(i); + if (i <= 0xffff) { + a.add(i); + } else if (i <= 0x10ffff) { + a.add(0xd800 + ((((i - 0x10000) >> 10) & 0x3ff))); + a.add(0xdc00 + (i & 0x3ff)); + } else { + throw new ArgumentError(i); + } + } + return _fromCharCodeApply(a); + } + + static String stringFromCharCodes(charCodes) { + for (var i in charCodes) { + if (i is !int) throw new ArgumentError(i); + if (i < 0) throw new ArgumentError(i); + if (i > 0xffff) return stringFromCodePoints(charCodes); + } + return _fromCharCodeApply(charCodes); + } + + static String stringConcatUnchecked(String string1, String string2) { + return JS('String', r'# + #', string1, string2); + } + + static String getTimeZoneName(receiver) { + // Firefox and Chrome emit the timezone in parenthesis. + // Example: "Wed May 16 2012 21:13:00 GMT+0200 (CEST)". + // We extract this name using a regexp. + var d = lazyAsJsDate(receiver); + List match = JS('JSArray|Null', r'/\((.*)\)/.exec(#.toString())', d); + if (match != null) return match[1]; + + // Internet Explorer 10+ emits the zone name without parenthesis: + // Example: Thu Oct 31 14:07:44 PDT 2013 + match = JS('JSArray|Null', + // Thu followed by a space. + r'/^[A-Z,a-z]{3}\s' + // Oct 31 followed by space. + r'[A-Z,a-z]{3}\s\d+\s' + // Time followed by a space. + r'\d{2}:\d{2}:\d{2}\s' + // The time zone name followed by a space. + r'([A-Z]{3,5})\s' + // The year. + r'\d{4}$/' + '.exec(#.toString())', + d); + if (match != null) return match[1]; + + // IE 9 and Opera don't provide the zone name. We fall back to emitting the + // UTC/GMT offset. + // Example (IE9): Wed Nov 20 09:51:00 UTC+0100 2013 + // (Opera): Wed Nov 20 2013 11:03:38 GMT+0100 + match = JS('JSArray|Null', r'/(?:GMT|UTC)[+-]\d{4}/.exec(#.toString())', d); + if (match != null) return match[0]; + return ""; + } + + static int getTimeZoneOffsetInMinutes(receiver) { + // Note that JS and Dart disagree on the sign of the offset. + return -JS('int', r'#.getTimezoneOffset()', lazyAsJsDate(receiver)); + } + + static valueFromDecomposedDate(years, month, day, hours, minutes, seconds, + milliseconds, isUtc) { + final int MAX_MILLISECONDS_SINCE_EPOCH = 8640000000000000; + checkInt(years); + checkInt(month); + checkInt(day); + checkInt(hours); + checkInt(minutes); + checkInt(seconds); + checkInt(milliseconds); + checkBool(isUtc); + var jsMonth = month - 1; + var value; + if (isUtc) { + value = JS('num', r'Date.UTC(#, #, #, #, #, #, #)', + years, jsMonth, day, hours, minutes, seconds, milliseconds); + } else { + value = JS('num', r'new Date(#, #, #, #, #, #, #).valueOf()', + years, jsMonth, day, hours, minutes, seconds, milliseconds); + } + if (value.isNaN || + value < -MAX_MILLISECONDS_SINCE_EPOCH || + value > MAX_MILLISECONDS_SINCE_EPOCH) { + throw new ArgumentError(); + } + if (years <= 0 || years < 100) return patchUpY2K(value, years, isUtc); + return value; + } + + static patchUpY2K(value, years, isUtc) { + var date = JS('', r'new Date(#)', value); + if (isUtc) { + JS('num', r'#.setUTCFullYear(#)', date, years); + } else { + JS('num', r'#.setFullYear(#)', date, years); + } + return JS('num', r'#.valueOf()', date); + } + + // Lazily keep a JS Date stored in the JS object. + static lazyAsJsDate(receiver) { + if (JS('bool', r'#.date === (void 0)', receiver)) { + JS('void', r'#.date = new Date(#)', receiver, + receiver.millisecondsSinceEpoch); + } + return JS('var', r'#.date', receiver); + } + + // The getters for date and time parts below add a positive integer to ensure + // that the result is really an integer, because the JavaScript implementation + // may return -0.0 instead of 0. + + static getYear(receiver) { + return (receiver.isUtc) + ? JS('int', r'(#.getUTCFullYear() + 0)', lazyAsJsDate(receiver)) + : JS('int', r'(#.getFullYear() + 0)', lazyAsJsDate(receiver)); + } + + static getMonth(receiver) { + return (receiver.isUtc) + ? JS('int', r'#.getUTCMonth() + 1', lazyAsJsDate(receiver)) + : JS('int', r'#.getMonth() + 1', lazyAsJsDate(receiver)); + } + + static getDay(receiver) { + return (receiver.isUtc) + ? JS('int', r'(#.getUTCDate() + 0)', lazyAsJsDate(receiver)) + : JS('int', r'(#.getDate() + 0)', lazyAsJsDate(receiver)); + } + + static getHours(receiver) { + return (receiver.isUtc) + ? JS('int', r'(#.getUTCHours() + 0)', lazyAsJsDate(receiver)) + : JS('int', r'(#.getHours() + 0)', lazyAsJsDate(receiver)); + } + + static getMinutes(receiver) { + return (receiver.isUtc) + ? JS('int', r'(#.getUTCMinutes() + 0)', lazyAsJsDate(receiver)) + : JS('int', r'(#.getMinutes() + 0)', lazyAsJsDate(receiver)); + } + + static getSeconds(receiver) { + return (receiver.isUtc) + ? JS('int', r'(#.getUTCSeconds() + 0)', lazyAsJsDate(receiver)) + : JS('int', r'(#.getSeconds() + 0)', lazyAsJsDate(receiver)); + } + + static getMilliseconds(receiver) { + return (receiver.isUtc) + ? JS('int', r'(#.getUTCMilliseconds() + 0)', lazyAsJsDate(receiver)) + : JS('int', r'(#.getMilliseconds() + 0)', lazyAsJsDate(receiver)); + } + + static getWeekday(receiver) { + int weekday = (receiver.isUtc) + ? JS('int', r'#.getUTCDay() + 0', lazyAsJsDate(receiver)) + : JS('int', r'#.getDay() + 0', lazyAsJsDate(receiver)); + // Adjust by one because JS weeks start on Sunday. + return (weekday + 6) % 7 + 1; + } + + static valueFromDateString(str) { + if (str is !String) throw new ArgumentError(str); + var value = JS('num', r'Date.parse(#)', str); + if (value.isNaN) throw new ArgumentError(str); + return value; + } + + static getProperty(object, key) { + if (object == null || object is bool || object is num || object is String) { + throw new ArgumentError(object); + } + return JS('var', '#[#]', object, key); + } + + static void setProperty(object, key, value) { + if (object == null || object is bool || object is num || object is String) { + throw new ArgumentError(object); + } + JS('void', '#[#] = #', object, key, value); + } + + static functionNoSuchMethod(function, + List positionalArguments, + Map namedArguments) { + int argumentCount = 0; + List arguments = []; + List namedArgumentList = []; + + if (positionalArguments != null) { + argumentCount += positionalArguments.length; + arguments.addAll(positionalArguments); + } + + String names = ''; + if (namedArguments != null && !namedArguments.isEmpty) { + namedArguments.forEach((String name, argument) { + names = '$names\$$name'; + namedArgumentList.add(name); + arguments.add(argument); + argumentCount++; + }); + } + + String selectorName = 'call\$$argumentCount$names'; + + return function.noSuchMethod( + createUnmangledInvocationMirror( + #call, + selectorName, + JSInvocationMirror.METHOD, + arguments, + namedArgumentList)); + } + + static applyFunction(Function function, + List positionalArguments, + Map namedArguments) { + if (namedArguments != null && !namedArguments.isEmpty) { + // TODO(ahe): The following code can be shared with + // JsInstanceMirror.invoke. + var interceptor = getInterceptor(function); + var jsFunction = JS('', '#["call*"]', interceptor); + + if (jsFunction == null) { + return functionNoSuchMethod( + function, positionalArguments, namedArguments); + } + ReflectionInfo info = new ReflectionInfo(jsFunction); + if (info == null || !info.areOptionalParametersNamed) { + return functionNoSuchMethod( + function, positionalArguments, namedArguments); + } + + if (positionalArguments != null) { + positionalArguments = new List.from(positionalArguments); + } else { + positionalArguments = []; + } + // Check the number of positional arguments is valid. + if (info.requiredParameterCount != positionalArguments.length) { + return functionNoSuchMethod( + function, positionalArguments, namedArguments); + } + var defaultArguments = new Map(); + for (int i = 0; i < info.optionalParameterCount; i++) { + int index = i + info.requiredParameterCount; + var parameterName = info.parameterNameInOrder(index); + var value = info.defaultValueInOrder(index); + var defaultValue = getMetadata(value); + defaultArguments[parameterName] = defaultValue; + } + bool bad = false; + namedArguments.forEach((String parameter, value) { + if (defaultArguments.containsKey(parameter)) { + defaultArguments[parameter] = value; + } else { + // Extraneous named argument. + bad = true; + } + }); + if (bad) { + return functionNoSuchMethod( + function, positionalArguments, namedArguments); + } + positionalArguments.addAll(defaultArguments.values); + return JS('', '#.apply(#, #)', jsFunction, function, positionalArguments); + } + + int argumentCount = 0; + List arguments = []; + + if (positionalArguments != null) { + argumentCount += positionalArguments.length; + arguments.addAll(positionalArguments); + } + + String selectorName = 'call\$$argumentCount'; + var jsFunction = JS('var', '#[#]', function, selectorName); + if (jsFunction == null) { + + // TODO(ahe): This might occur for optional arguments if there is no call + // selector with that many arguments. + + return + functionNoSuchMethod(function, positionalArguments, namedArguments); + } + // We bound 'this' to [function] because of how we compile + // closures: escaped local variables are stored and accessed through + // [function]. + return JS('var', '#.apply(#, #)', jsFunction, function, arguments); + } + + static getConstructorOrInterceptorToken(String className) { + // TODO(ahe): Generalize this and improve test coverage of + // reflecting on intercepted classes. + + // We should probably not be mappling the dart:core interface names to the + // interceptor library implementation classes like this. `JSArray` is just + // one implementation of `List`, there are others that have no relationship + // with JSArray other than implementing a common interface. + // + // For now `List` in dart:core and `JSArray` is in dart:_interceptors. We + // need to maintain a distinction to get the correct library mirror. + // + // TODO(17394): Short term: Refactor to avoid two copies of the list of + // known interceptor implementations. + // + // TODO(17394): Longer term: The proper interfaces with abstract methods + // should be emitted. + + if (JS('bool', '# == "String"', className)) return const JSString(); + if (JS('bool', '# == "int"', className)) return const JSInt(); + if (JS('bool', '# == "double"', className)) return const JSDouble(); + if (JS('bool', '# == "num"', className)) return const JSNumber(); + if (JS('bool', '# == "bool"', className)) return const JSBool(); + if (JS('bool', '# == "List"', className)) return const JSArray(); + if (JS('bool', '# == "Null"', className)) return const JSNull(); + return JS('var', 'init.allClasses[#]', className); + } + + static bool isInterceptorToken(var object) { + // This must match the list of tokens returned by + // [getConstructorOrInterceptorToken] above. + return JS('bool', '# === #', object, const JSString()) + || JS('bool', '# === #', object, const JSInt()) + || JS('bool', '# === #', object, const JSDouble()) + || JS('bool', '# === #', object, const JSNumber()) + || JS('bool', '# === #', object, const JSBool()) + || JS('bool', '# === #', object, const JSArray()) + || JS('bool', '# === #', object, const JSNull()); + } + + static bool identicalImplementation(a, b) { + return JS('bool', '# == null', a) + ? JS('bool', '# == null', b) + : JS('bool', '# === #', a, b); + } + + static StackTrace extractStackTrace(Error error) { + return getTraceFromException(JS('', r'#.$thrownJsError', error)); + } +} + +/// Helper class for allocating and using JS object literals as caches. +class JsCache { + /// Returns a JavaScript object suitable for use as a cache. + static allocate() { + var result = JS('=Object', '{x:0}'); + // Deleting a property makes V8 assume that it shouldn't create a hidden + // class for [result] and map transitions. Although these map transitions + // pay off if there are many cache hits for the same keys, it becomes + // really slow when there aren't many repeated hits. + JS('void', 'delete #.x', result); + return result; + } + + static fetch(cache, String key) => JS('', '#[#]', cache, key); + + static void update(cache, String key, value) { + JS('void', '#[#] = #', cache, key, value); + } +} + +/** + * Called by generated code to throw an illegal-argument exception, + * for example, if a non-integer index is given to an optimized + * indexed access. + */ +iae(argument) { + throw new ArgumentError(argument); +} + +/** + * Called by generated code to throw an index-out-of-range exception, + * for example, if a bounds check fails in an optimized indexed + * access. This may also be called when the index is not an integer, in + * which case it throws an illegal-argument exception instead, like + * [iae], or when the receiver is null. + */ +ioore(receiver, index) { + if (receiver == null) receiver.length; // Force a NoSuchMethodError. + if (index is !int) iae(index); + throw new RangeError.value(index); +} + +stringLastIndexOfUnchecked(receiver, element, start) + => JS('int', r'#.lastIndexOf(#, #)', receiver, element, start); + + +checkNull(object) { + if (object == null) throw new ArgumentError(null); + return object; +} + +checkNum(value) { + if (value is !num) { + throw new ArgumentError(value); + } + return value; +} + +checkInt(value) { + if (value is !int) { + throw new ArgumentError(value); + } + return value; +} + +checkBool(value) { + if (value is !bool) { + throw new ArgumentError(value); + } + return value; +} + +checkString(value) { + if (value is !String) { + throw new ArgumentError(value); + } + return value; +} + +/** + * Wrap the given Dart object and record a stack trace. + * + * The code in [unwrapException] deals with getting the original Dart + * object out of the wrapper again. + */ +wrapException(ex) { + if (ex == null) ex = new NullThrownError(); + var wrapper = JS('', 'new Error()'); + // [unwrapException] looks for the property 'dartException'. + JS('void', '#.dartException = #', wrapper, ex); + + if (JS('bool', '"defineProperty" in Object')) { + // Define a JavaScript getter for 'message'. This is to work around V8 bug + // (https://code.google.com/p/v8/issues/detail?id=2519). The default + // toString on Error returns the value of 'message' if 'name' is + // empty. Setting toString directly doesn't work, see the bug. + JS('void', 'Object.defineProperty(#, "message", { get: # })', + wrapper, DART_CLOSURE_TO_JS(toStringWrapper)); + JS('void', '#.name = ""', wrapper); + } else { + // In the unlikely event the browser doesn't support Object.defineProperty, + // hope that it just calls toString. + JS('void', '#.toString = #', wrapper, DART_CLOSURE_TO_JS(toStringWrapper)); + } + + return wrapper; +} + +/// Do not call directly. +toStringWrapper() { + // This method gets installed as toString on a JavaScript object. Due to the + // weird scope rules of JavaScript, JS 'this' will refer to that object. + return JS('', r'this.dartException').toString(); +} + +/** + * This wraps the exception and does the throw. It is possible to call this in + * a JS expression context, where the throw statement is not allowed. Helpers + * are never inlined, so we don't risk inlining the throw statement into an + * expression context. + */ +throwExpression(ex) { + JS('void', 'throw #', wrapException(ex)); +} + +makeLiteralListConst(list) { + JS('bool', r'#.immutable$list = #', list, true); + JS('bool', r'#.fixed$length = #', list, true); + return list; +} + +throwRuntimeError(message) { + throw new RuntimeError(message); +} + +throwAbstractClassInstantiationError(className) { + throw new AbstractClassInstantiationError(className); +} + + +/** + * Helper class for building patterns recognizing native type errors. + */ +class TypeErrorDecoder { + // Field names are private to help tree-shaking. + + /// A regular expression which matches is matched against an error message. + final String _pattern; + + /// The group index of "arguments" in [_pattern], or -1 if _pattern has no + /// match for "arguments". + final int _arguments; + + /// The group index of "argumentsExpr" in [_pattern], or -1 if _pattern has + /// no match for "argumentsExpr". + final int _argumentsExpr; + + /// The group index of "expr" in [_pattern], or -1 if _pattern has no match + /// for "expr". + final int _expr; + + /// The group index of "method" in [_pattern], or -1 if _pattern has no match + /// for "method". + final int _method; + + /// The group index of "receiver" in [_pattern], or -1 if _pattern has no + /// match for "receiver". + final int _receiver; + + /// Pattern used to recognize a NoSuchMethodError error (and + /// possibly extract the method name). + static final TypeErrorDecoder noSuchMethodPattern = + extractPattern(provokeCallErrorOn(buildJavaScriptObject())); + + /// Pattern used to recognize an "object not a closure" error (and + /// possibly extract the method name). + static final TypeErrorDecoder notClosurePattern = + extractPattern(provokeCallErrorOn(buildJavaScriptObjectWithNonClosure())); + + /// Pattern used to recognize a NoSuchMethodError on JavaScript null + /// call. + static final TypeErrorDecoder nullCallPattern = + extractPattern(provokeCallErrorOn(JS('', 'null'))); + + /// Pattern used to recognize a NoSuchMethodError on JavaScript literal null + /// call. + static final TypeErrorDecoder nullLiteralCallPattern = + extractPattern(provokeCallErrorOnNull()); + + /// Pattern used to recognize a NoSuchMethodError on JavaScript + /// undefined call. + static final TypeErrorDecoder undefinedCallPattern = + extractPattern(provokeCallErrorOn(JS('', 'void 0'))); + + /// Pattern used to recognize a NoSuchMethodError on JavaScript literal + /// undefined call. + static final TypeErrorDecoder undefinedLiteralCallPattern = + extractPattern(provokeCallErrorOnUndefined()); + + /// Pattern used to recognize a NoSuchMethodError on JavaScript null + /// property access. + static final TypeErrorDecoder nullPropertyPattern = + extractPattern(provokePropertyErrorOn(JS('', 'null'))); + + /// Pattern used to recognize a NoSuchMethodError on JavaScript literal null + /// property access. + static final TypeErrorDecoder nullLiteralPropertyPattern = + extractPattern(provokePropertyErrorOnNull()); + + /// Pattern used to recognize a NoSuchMethodError on JavaScript + /// undefined property access. + static final TypeErrorDecoder undefinedPropertyPattern = + extractPattern(provokePropertyErrorOn(JS('', 'void 0'))); + + /// Pattern used to recognize a NoSuchMethodError on JavaScript literal + /// undefined property access. + static final TypeErrorDecoder undefinedLiteralPropertyPattern = + extractPattern(provokePropertyErrorOnUndefined()); + + TypeErrorDecoder(this._arguments, + this._argumentsExpr, + this._expr, + this._method, + this._receiver, + this._pattern); + + /// Returns a JavaScript object literal (map) with at most the + /// following keys: + /// + /// * arguments: The arguments as formatted by the JavaScript + /// engine. No browsers are known to provide this information. + /// + /// * argumentsExpr: The syntax of the arguments (JavaScript source + /// code). No browsers are known to provide this information. + /// + /// * expr: The syntax of the receiver expression (JavaScript source + /// code). Firefox provides this information, for example: "$expr$.$method$ + /// is not a function". + /// + /// * method: The name of the called method (mangled name). At least Firefox + /// and Chrome/V8 provides this information, for example, "Object [object + /// Object] has no method '$method$'". + /// + /// * receiver: The string representation of the receiver. Chrome/V8 + /// used to provide this information (by calling user-defined + /// JavaScript toString on receiver), but it has degenerated into + /// "[object Object]" in recent versions. + matchTypeError(message) { + var match = JS('JSExtendableArray|Null', + 'new RegExp(#).exec(#)', _pattern, message); + if (match == null) return null; + var result = JS('', '{}'); + if (_arguments != -1) { + JS('', '#.arguments = #[# + 1]', result, match, _arguments); + } + if (_argumentsExpr != -1) { + JS('', '#.argumentsExpr = #[# + 1]', result, match, _argumentsExpr); + } + if (_expr != -1) { + JS('', '#.expr = #[# + 1]', result, match, _expr); + } + if (_method != -1) { + JS('', '#.method = #[# + 1]', result, match, _method); + } + if (_receiver != -1) { + JS('', '#.receiver = #[# + 1]', result, match, _receiver); + } + + return result; + } + + /// Builds a JavaScript Object with a toString method saying + /// r"$receiver$". + static buildJavaScriptObject() { + return JS('', r'{ toString: function() { return "$receiver$"; } }'); + } + + /// Builds a JavaScript Object with a toString method saying + /// r"$receiver$". The property "$method" is defined, but is not a function. + static buildJavaScriptObjectWithNonClosure() { + return JS('', r'{ $method$: null, ' + r'toString: function() { return "$receiver$"; } }'); + } + + /// Extract a pattern from a JavaScript TypeError message. + /// + /// The patterns are extracted by forcing TypeErrors on known + /// objects thus forcing known strings into the error message. The + /// known strings are then replaced with wildcards which in theory + /// makes it possible to recognize the desired information even if + /// the error messages are reworded or translated. + static extractPattern(String message) { + // Some JavaScript implementations (V8 at least) include a + // representation of the receiver in the error message, however, + // this representation is not always [: receiver.toString() :], + // sometimes it is [: Object.prototype.toString(receiver) :], and + // sometimes it is an implementation specific method (but that + // doesn't seem to happen for object literals). So sometimes we + // get the text "[object Object]". The shortest way to get that + // string is using "String({})". + // See: http://code.google.com/p/v8/issues/detail?id=2519. + message = JS('String', r"#.replace(String({}), '$receiver$')", message); + + // Since we want to create a new regular expression from an unknown string, + // we must escape all regular expression syntax. + message = JS('String', r"#.replace(new RegExp(#, 'g'), '\\$&')", + message, ESCAPE_REGEXP); + + // Look for the special pattern \$camelCase\$ (all the $ symbols + // have been escaped already), as we will soon be inserting + // regular expression syntax that we want interpreted by RegExp. + List match = + JS('JSExtendableArray|Null', r"#.match(/\\\$[a-zA-Z]+\\\$/g)", message); + if (match == null) match = []; + + // Find the positions within the substring matches of the error message + // components. This will help us extract information later, such as the + // method name. + int arguments = JS('int', '#.indexOf(#)', match, r'\$arguments\$'); + int argumentsExpr = JS('int', '#.indexOf(#)', match, r'\$argumentsExpr\$'); + int expr = JS('int', '#.indexOf(#)', match, r'\$expr\$'); + int method = JS('int', '#.indexOf(#)', match, r'\$method\$'); + int receiver = JS('int', '#.indexOf(#)', match, r'\$receiver\$'); + + // Replace the patterns with a regular expression wildcard. + // Note: in a perfect world, one would use "(.*)", but not in + // JavaScript, "." does not match newlines. + String pattern = JS('String', + r"#.replace('\\$arguments\\$', '((?:x|[^x])*)')" + r".replace('\\$argumentsExpr\\$', '((?:x|[^x])*)')" + r".replace('\\$expr\\$', '((?:x|[^x])*)')" + r".replace('\\$method\\$', '((?:x|[^x])*)')" + r".replace('\\$receiver\\$', '((?:x|[^x])*)')", + message); + + return new TypeErrorDecoder(arguments, + argumentsExpr, + expr, + method, + receiver, + pattern); + } + + /// Provokes a TypeError and returns its message. + /// + /// The error is provoked so all known variable content can be recognized and + /// a pattern can be inferred. + static String provokeCallErrorOn(expression) { + // This function is carefully created to maximize the possibility + // of decoding the TypeError message and turning it into a general + // pattern. + // + // The idea is to inject something known into something unknown. The + // unknown entity is the error message that the browser provides with a + // TypeError. It is a human readable message, possibly localized in a + // language no dart2js engineer understand. We assume that $name$ would + // never naturally occur in a human readable error message, yet it is easy + // to decode. + // + // For example, evaluate this in V8 version 3.13.7.6: + // + // var $expr$ = null; $expr$.$method$() + // + // The VM throws an instance of TypeError whose message property contains + // "Cannot call method '$method$' of null". We can then reasonably assume + // that if the string contains $method$, that's where the method name will + // be in general. Call this automatically reverse engineering the error + // format string in V8. + // + // So the error message from V8 is turned into this regular expression: + // + // "Cannot call method '(.*)' of null" + // + // Similarly, if we evaluate: + // + // var $expr$ = {toString: function() { return '$receiver$'; }}; + // $expr$.$method$() + // + // We get this message: "Object $receiver$ has no method '$method$'" + // + // Which is turned into this regular expression: + // + // "Object (.*) has no method '(.*)'" + // + // Firefox/jsshell is slightly different, it tries to include the source + // code that caused the exception, so we get this message: "$expr$.$method$ + // is not a function" which is turned into this regular expression: + // + // "(.*)\\.(.*) is not a function" + + var function = JS('', r"""function($expr$) { + var $argumentsExpr$ = '$arguments$' + try { + $expr$.$method$($argumentsExpr$); + } catch (e) { + return e.message; + } +}"""); + return JS('String', '(#)(#)', function, expression); + } + + /// Similar to [provokeCallErrorOn], but provokes an error directly on + /// literal "null" expression. + static String provokeCallErrorOnNull() { + // See [provokeCallErrorOn] for a detailed explanation. + var function = JS('', r"""function() { + var $argumentsExpr$ = '$arguments$' + try { + null.$method$($argumentsExpr$); + } catch (e) { + return e.message; + } +}"""); + return JS('String', '(#)()', function); + } + + /// Similar to [provokeCallErrorOnNull], but provokes an error directly on + /// (void 0), that is, "undefined". + static String provokeCallErrorOnUndefined() { + // See [provokeCallErrorOn] for a detailed explanation. + var function = JS('', r"""function() { + var $argumentsExpr$ = '$arguments$' + try { + (void 0).$method$($argumentsExpr$); + } catch (e) { + return e.message; + } +}"""); + return JS('String', '(#)()', function); + } + + /// Similar to [provokeCallErrorOn], but provokes a property access + /// error. + static String provokePropertyErrorOn(expression) { + // See [provokeCallErrorOn] for a detailed explanation. + var function = JS('', r"""function($expr$) { + try { + $expr$.$method$; + } catch (e) { + return e.message; + } +}"""); + return JS('String', '(#)(#)', function, expression); + } + + /// Similar to [provokePropertyErrorOn], but provokes an property access + /// error directly on literal "null" expression. + static String provokePropertyErrorOnNull() { + // See [provokeCallErrorOn] for a detailed explanation. + var function = JS('', r"""function() { + try { + null.$method$; + } catch (e) { + return e.message; + } +}"""); + return JS('String', '(#)()', function); + } + + /// Similar to [provokePropertyErrorOnNull], but provokes an property access + /// error directly on (void 0), that is, "undefined". + static String provokePropertyErrorOnUndefined() { + // See [provokeCallErrorOn] for a detailed explanation. + var function = JS('', r"""function() { + try { + (void 0).$method$; + } catch (e) { + return e.message; + } +}"""); + return JS('String', '(#)()', function); + } +} + +class NullError extends Error implements NoSuchMethodError { + final String _message; + final String _method; + + NullError(this._message, match) + : _method = match == null ? null : JS('', '#.method', match); + + String toString() { + if (_method == null) return 'NullError: $_message'; + return 'NullError: Cannot call "$_method" on null'; + } +} + +class JsNoSuchMethodError extends Error implements NoSuchMethodError { + final String _message; + final String _method; + final String _receiver; + + JsNoSuchMethodError(this._message, match) + : _method = match == null ? null : JS('String|Null', '#.method', match), + _receiver = + match == null ? null : JS('String|Null', '#.receiver', match); + + String toString() { + if (_method == null) return 'NoSuchMethodError: $_message'; + if (_receiver == null) { + return 'NoSuchMethodError: Cannot call "$_method" ($_message)'; + } + return 'NoSuchMethodError: Cannot call "$_method" on "$_receiver" ' + '($_message)'; + } +} + +class UnknownJsTypeError extends Error { + final String _message; + + UnknownJsTypeError(this._message); + + String toString() => _message.isEmpty ? 'Error' : 'Error: $_message'; +} + +/** + * Called from catch blocks in generated code to extract the Dart + * exception from the thrown value. The thrown value may have been + * created by [wrapException] or it may be a 'native' JS exception. + * + * Some native exceptions are mapped to new Dart instances, others are + * returned unmodified. + */ +unwrapException(ex) { + /// If error implements Error, save [ex] in [error.$thrownJsError]. + /// Otherwise, do nothing. Later, the stack trace can then be extraced from + /// [ex]. + saveStackTrace(error) { + if (error is Error) { + var thrownStackTrace = JS('', r'#.$thrownJsError', error); + if (thrownStackTrace == null) { + JS('void', r'#.$thrownJsError = #', error, ex); + } + } + return error; + } + + // Note that we are checking if the object has the property. If it + // has, it could be set to null if the thrown value is null. + if (ex == null) return null; + if (JS('bool', 'typeof # !== "object"', ex)) return ex; + + if (JS('bool', r'"dartException" in #', ex)) { + return saveStackTrace(JS('', r'#.dartException', ex)); + } else if (!JS('bool', r'"message" in #', ex)) { + return ex; + } + + // Grab hold of the exception message. This field is available on + // all supported browsers. + var message = JS('var', r'#.message', ex); + + // Internet Explorer has an error number. This is the most reliable way to + // detect specific errors, so check for this first. + if (JS('bool', '"number" in #', ex) + && JS('bool', 'typeof #.number == "number"', ex)) { + int number = JS('int', '#.number', ex); + + // From http://msdn.microsoft.com/en-us/library/ie/hc53e755(v=vs.94).aspx + // "number" is a 32-bit word. The error code is the low 16 bits, and the + // facility code is the upper 16 bits. + var ieErrorCode = number & 0xffff; + var ieFacilityNumber = (number >> 16) & 0x1fff; + + // http://msdn.microsoft.com/en-us/library/aa264975(v=vs.60).aspx + // http://msdn.microsoft.com/en-us/library/ie/1dk3k160(v=vs.94).aspx + if (ieFacilityNumber == 10) { + switch (ieErrorCode) { + case 438: + return saveStackTrace( + new JsNoSuchMethodError('$message (Error $ieErrorCode)', null)); + case 445: + case 5007: + return saveStackTrace( + new NullError('$message (Error $ieErrorCode)', null)); + } + } + } + + if (JS('bool', r'# instanceof TypeError', ex)) { + var match; + // Using JS to give type hints to the compiler to help tree-shaking. + // TODO(ahe): That should be unnecessary due to type inference. + var nsme = + JS('TypeErrorDecoder', '#', TypeErrorDecoder.noSuchMethodPattern); + var notClosure = + JS('TypeErrorDecoder', '#', TypeErrorDecoder.notClosurePattern); + var nullCall = + JS('TypeErrorDecoder', '#', TypeErrorDecoder.nullCallPattern); + var nullLiteralCall = + JS('TypeErrorDecoder', '#', TypeErrorDecoder.nullLiteralCallPattern); + var undefCall = + JS('TypeErrorDecoder', '#', TypeErrorDecoder.undefinedCallPattern); + var undefLiteralCall = + JS('TypeErrorDecoder', '#', + TypeErrorDecoder.undefinedLiteralCallPattern); + var nullProperty = + JS('TypeErrorDecoder', '#', TypeErrorDecoder.nullPropertyPattern); + var nullLiteralProperty = + JS('TypeErrorDecoder', '#', + TypeErrorDecoder.nullLiteralPropertyPattern); + var undefProperty = + JS('TypeErrorDecoder', '#', TypeErrorDecoder.undefinedPropertyPattern); + var undefLiteralProperty = + JS('TypeErrorDecoder', '#', + TypeErrorDecoder.undefinedLiteralPropertyPattern); + if ((match = nsme.matchTypeError(message)) != null) { + return saveStackTrace(new JsNoSuchMethodError(message, match)); + } else if ((match = notClosure.matchTypeError(message)) != null) { + // notClosure may match "({c:null}).c()" or "({c:1}).c()", so we + // cannot tell if this an attempt to invoke call on null or a + // non-function object. + // But we do know the method name is "call". + JS('', '#.method = "call"', match); + return saveStackTrace(new JsNoSuchMethodError(message, match)); + } else if ((match = nullCall.matchTypeError(message)) != null || + (match = nullLiteralCall.matchTypeError(message)) != null || + (match = undefCall.matchTypeError(message)) != null || + (match = undefLiteralCall.matchTypeError(message)) != null || + (match = nullProperty.matchTypeError(message)) != null || + (match = nullLiteralCall.matchTypeError(message)) != null || + (match = undefProperty.matchTypeError(message)) != null || + (match = undefLiteralProperty.matchTypeError(message)) != null) { + return saveStackTrace(new NullError(message, match)); + } + + // If we cannot determine what kind of error this is, we fall back + // to reporting this as a generic error. It's probably better than + // nothing. + return saveStackTrace( + new UnknownJsTypeError(message is String ? message : '')); + } + + if (JS('bool', r'# instanceof RangeError', ex)) { + if (message is String && contains(message, 'call stack')) { + return new StackOverflowError(); + } + + // In general, a RangeError is thrown when trying to pass a number + // as an argument to a function that does not allow a range that + // includes that number. + return saveStackTrace(new ArgumentError()); + } + + // Check for the Firefox specific stack overflow signal. + if (JS('bool', + r'typeof InternalError == "function" && # instanceof InternalError', + ex)) { + if (message is String && message == 'too much recursion') { + return new StackOverflowError(); + } + } + + // Just return the exception. We should not wrap it because in case + // the exception comes from the DOM, it is a JavaScript + // object backed by a native Dart class. + return ex; +} + +/** + * Called by generated code to fetch the stack trace from an + * exception. Should never return null. + */ +StackTrace getTraceFromException(exception) => new _StackTrace(exception); + +class _StackTrace implements StackTrace { + var _exception; + String _trace; + _StackTrace(this._exception); + + String toString() { + if (_trace != null) return _trace; + + String trace; + if (JS('bool', 'typeof # === "object"', _exception)) { + trace = JS("String|Null", r"#.stack", _exception); + } + return _trace = (trace == null) ? '' : trace; + } +} + +int objectHashCode(var object) { + if (object == null || JS('bool', "typeof # != 'object'", object)) { + return object.hashCode; + } else { + return Primitives.objectHashCode(object); + } +} + +/** + * Called by generated code to build a map literal. [keyValuePairs] is + * a list of key, value, key, value, ..., etc. + */ +makeLiteralMap(keyValuePairs) { + return fillLiteralMap(keyValuePairs, new LinkedHashMap()); +} + +fillLiteralMap(keyValuePairs, Map result) { + // TODO(johnniwinther): Use JSArray to optimize this code instead of calling + // [getLength] and [getIndex]. + int index = 0; + int length = getLength(keyValuePairs); + while (index < length) { + var key = getIndex(keyValuePairs, index++); + var value = getIndex(keyValuePairs, index++); + result[key] = value; + } + return result; +} + +invokeClosure(Function closure, + var isolate, + int numberOfArguments, + var arg1, + var arg2, + var arg3, + var arg4) { + if (numberOfArguments == 0) { + return JS_CALL_IN_ISOLATE(isolate, () => closure()); + } else if (numberOfArguments == 1) { + return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1)); + } else if (numberOfArguments == 2) { + return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1, arg2)); + } else if (numberOfArguments == 3) { + return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1, arg2, arg3)); + } else if (numberOfArguments == 4) { + return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1, arg2, arg3, arg4)); + } else { + throw new Exception( + 'Unsupported number of arguments for wrapped closure'); + } +} + +/** + * Called by generated code to convert a Dart closure to a JS + * closure when the Dart closure is passed to the DOM. + */ +convertDartClosureToJS(closure, int arity) { + if (closure == null) return null; + var function = JS('var', r'#.$identity', closure); + if (JS('bool', r'!!#', function)) return function; + + // We use $0 and $1 to not clash with variable names used by the + // compiler and/or minifier. + function = JS('var', + '(function(closure, arity, context, invoke) {' + ' return function(a1, a2, a3, a4) {' + ' return invoke(closure, context, arity, a1, a2, a3, a4);' + ' };' + '})(#,#,#,#)', + closure, + arity, + // Capture the current isolate now. Remember that "#" + // in JS is simply textual substitution of compiled + // expressions. + JS_CURRENT_ISOLATE_CONTEXT(), + DART_CLOSURE_TO_JS(invokeClosure)); + + JS('void', r'#.$identity = #', closure, function); + return function; +} + +/** + * Super class for Dart closures. + */ +abstract class Closure implements Function { + // TODO(ahe): These constants must be in sync with + // reflection_data_parser.dart. + static const FUNCTION_INDEX = 0; + static const NAME_INDEX = 1; + static const CALL_NAME_INDEX = 2; + static const REQUIRED_PARAMETER_INDEX = 3; + static const OPTIONAL_PARAMETER_INDEX = 4; + static const DEFAULT_ARGUMENTS_INDEX = 5; + + /** + * Global counter to prevent reusing function code objects. + * + * V8 will share the underlying function code objects when the same string is + * passed to "new Function". Shared function code objects can lead to + * sub-optimal performance due to polymorhism, and can be prevented by + * ensuring the strings are different. + */ + static int functionCounter = 0; + + Closure(); + + /** + * Creates a new closure class for use by implicit getters associated with a + * method. + * + * In other words, creates a tear-off closure. + * + * Called from [closureFromTearOff] as well as from reflection when tearing + * of a method via [:getField:]. + * + * This method assumes that [functions] was created by the JavaScript function + * `addStubs` in `reflection_data_parser.dart`. That is, a list of JavaScript + * function objects with properties `$stubName` and `$callName`. + * + * Further assumes that [reflectionInfo] is the end of the array created by + * [dart2js.js_emitter.ContainerBuilder.addMemberMethod] starting with + * required parameter count. + * + * Caution: this function may be called when building constants. + * TODO(ahe): Don't call this function when building constants. + */ + static fromTearOff(receiver, + List functions, + List reflectionInfo, + bool isStatic, + jsArguments, + String propertyName) { + JS_EFFECT(() { + BoundClosure.receiverOf(JS('BoundClosure', 'void 0')); + BoundClosure.selfOf(JS('BoundClosure', 'void 0')); + }); + // TODO(ahe): All the place below using \$ should be rewritten to go + // through the namer. + var function = JS('', '#[#]', functions, 0); + String name = JS('String|Null', '#.\$stubName', function); + String callName = JS('String|Null', '#.\$callName', function); + + JS('', '#.\$reflectionInfo = #', function, reflectionInfo); + ReflectionInfo info = new ReflectionInfo(function); + + var functionType = info.functionType; + + // function tmp() {}; + // tmp.prototype = BC.prototype; + // var proto = new tmp; + // for each computed prototype property: + // proto[property] = ...; + // proto._init = BC; + // var dynClosureConstructor = + // new Function('self', 'target', 'receiver', 'name', + // 'this._init(self, target, receiver, name)'); + // proto.constructor = dynClosureConstructor; // Necessary? + // dynClosureConstructor.prototype = proto; + // return dynClosureConstructor; + + // We need to create a new subclass of either TearOffClosure or + // BoundClosure. For this, we need to create an object whose prototype is + // the prototype is either TearOffClosure.prototype or + // BoundClosure.prototype, respectively in pseudo JavaScript code. The + // simplest way to access the JavaScript construction function of a Dart + // class is to create an instance and access its constructor property. The + // newly created instance could in theory be used directly as the + // prototype, but it might include additional fields that we don't need. + // So we only use the new instance to access the constructor property and + // use Object.create to create the desired prototype. + var prototype = isStatic + // TODO(ahe): Safe to use Object.create? + ? JS('TearOffClosure', 'Object.create(#.constructor.prototype)', + new TearOffClosure()) + : JS('BoundClosure', 'Object.create(#.constructor.prototype)', + new BoundClosure(null, null, null, null)); + + JS('', '#.\$initialize = #', prototype, JS('', '#.constructor', prototype)); + var constructor = isStatic + ? JS('', 'function(){this.\$initialize()}') + : isCsp + ? JS('', 'function(a,b,c,d) {this.\$initialize(a,b,c,d)}') + : JS('', + 'new Function("a","b","c","d",' + '"this.\$initialize(a,b,c,d);"+#)', + functionCounter++); + + // TODO(ahe): Is it necessary to set the constructor property? + JS('', '#.constructor = #', prototype, constructor); + + JS('', '#.prototype = #', constructor, prototype); + + // Create a closure and "monkey" patch it with call stubs. + var trampoline = function; + var isIntercepted = false; + if (!isStatic) { + if (JS('bool', '#.length == 1', jsArguments)) { + // Intercepted call. + isIntercepted = true; + } + trampoline = forwardCallTo(receiver, function, isIntercepted); + JS('', '#.\$reflectionInfo = #', trampoline, reflectionInfo); + } else { + JS('', '#.\$name = #', prototype, propertyName); + } + + var signatureFunction; + if (JS('bool', 'typeof # == "number"', functionType)) { + signatureFunction = + JS('', '(function(s){return function(){return init.metadata[s]}})(#)', + functionType); + } else if (!isStatic + && JS('bool', 'typeof # == "function"', functionType)) { + var getReceiver = isIntercepted + ? RAW_DART_FUNCTION_REF(BoundClosure.receiverOf) + : RAW_DART_FUNCTION_REF(BoundClosure.selfOf); + signatureFunction = JS( + '', + 'function(f,r){' + 'return function(){' + 'return f.apply({\$receiver:r(this)},arguments)' + '}' + '}(#,#)', functionType, getReceiver); + } else { + throw 'Error in reflectionInfo.'; + } + + JS('', '#.\$signature = #', prototype, signatureFunction); + + JS('', '#[#] = #', prototype, callName, trampoline); + for (int i = 1; i < functions.length; i++) { + var stub = functions[i]; + var stubCallName = JS('String|Null', '#.\$callName', stub); + if (stubCallName != null) { + JS('', '#[#] = #', prototype, stubCallName, + isStatic ? stub : forwardCallTo(receiver, stub, isIntercepted)); + } + } + + JS('', '#["call*"] = #', prototype, trampoline); + + return constructor; + } + + static cspForwardCall(int arity, bool isSuperCall, String stubName, + function) { + var getSelf = RAW_DART_FUNCTION_REF(BoundClosure.selfOf); + // Handle intercepted stub-names with the default slow case. + if (isSuperCall) arity = -1; + switch (arity) { + case 0: + return JS( + '', + 'function(n,S){' + 'return function(){' + 'return S(this)[n]()' + '}' + '}(#,#)', stubName, getSelf); + case 1: + return JS( + '', + 'function(n,S){' + 'return function(a){' + 'return S(this)[n](a)' + '}' + '}(#,#)', stubName, getSelf); + case 2: + return JS( + '', + 'function(n,S){' + 'return function(a,b){' + 'return S(this)[n](a,b)' + '}' + '}(#,#)', stubName, getSelf); + case 3: + return JS( + '', + 'function(n,S){' + 'return function(a,b,c){' + 'return S(this)[n](a,b,c)' + '}' + '}(#,#)', stubName, getSelf); + case 4: + return JS( + '', + 'function(n,S){' + 'return function(a,b,c,d){' + 'return S(this)[n](a,b,c,d)' + '}' + '}(#,#)', stubName, getSelf); + case 5: + return JS( + '', + 'function(n,S){' + 'return function(a,b,c,d,e){' + 'return S(this)[n](a,b,c,d,e)' + '}' + '}(#,#)', stubName, getSelf); + default: + return JS( + '', + 'function(f,s){' + 'return function(){' + 'return f.apply(s(this),arguments)' + '}' + '}(#,#)', function, getSelf); + } + } + + static bool get isCsp => JS('bool', 'typeof dart_precompiled == "function"'); + + static forwardCallTo(receiver, function, bool isIntercepted) { + if (isIntercepted) return forwardInterceptedCallTo(receiver, function); + String stubName = JS('String|Null', '#.\$stubName', function); + int arity = JS('int', '#.length', function); + var lookedUpFunction = JS("", "#[#]", receiver, stubName); + // The receiver[stubName] may not be equal to the function if we try to + // forward to a super-method. Especially when we create a bound closure + // of a super-call we need to make sure that we don't forward back to the + // dynamically looked up function. + bool isSuperCall = !identical(function, lookedUpFunction); + + if (isCsp || isSuperCall || arity >= 27) { + return cspForwardCall(arity, isSuperCall, stubName, function); + } + + if (arity == 0) { + return JS( + '', + '(new Function(#))()', + 'return function(){' + 'return this.${BoundClosure.selfFieldName()}.$stubName();' + '${functionCounter++}' + '}'); + } + assert (1 <= arity && arity < 27); + String arguments = JS( + 'String', + '"abcdefghijklmnopqrstuvwxyz".split("").splice(0,#).join(",")', + arity); + return JS( + '', + '(new Function(#))()', + 'return function($arguments){' + 'return this.${BoundClosure.selfFieldName()}.$stubName($arguments);' + '${functionCounter++}' + '}'); + } + + static cspForwardInterceptedCall(int arity, bool isSuperCall, + String name, function) { + var getSelf = RAW_DART_FUNCTION_REF(BoundClosure.selfOf); + var getReceiver = RAW_DART_FUNCTION_REF(BoundClosure.receiverOf); + // Handle intercepted stub-names with the default slow case. + if (isSuperCall) arity = -1; + switch (arity) { + case 0: + // Intercepted functions always takes at least one argument (the + // receiver). + throw new RuntimeError('Intercepted function with no arguments.'); + case 1: + return JS( + '', + 'function(n,s,r){' + 'return function(){' + 'return s(this)[n](r(this))' + '}' + '}(#,#,#)', name, getSelf, getReceiver); + case 2: + return JS( + '', + 'function(n,s,r){' + 'return function(a){' + 'return s(this)[n](r(this),a)' + '}' + '}(#,#,#)', name, getSelf, getReceiver); + case 3: + return JS( + '', + 'function(n,s,r){' + 'return function(a,b){' + 'return s(this)[n](r(this),a,b)' + '}' + '}(#,#,#)', name, getSelf, getReceiver); + case 4: + return JS( + '', + 'function(n,s,r){' + 'return function(a,b,c){' + 'return s(this)[n](r(this),a,b,c)' + '}' + '}(#,#,#)', name, getSelf, getReceiver); + case 5: + return JS( + '', + 'function(n,s,r){' + 'return function(a,b,c,d){' + 'return s(this)[n](r(this),a,b,c,d)' + '}' + '}(#,#,#)', name, getSelf, getReceiver); + case 6: + return JS( + '', + 'function(n,s,r){' + 'return function(a,b,c,d,e){' + 'return s(this)[n](r(this),a,b,c,d,e)' + '}' + '}(#,#,#)', name, getSelf, getReceiver); + default: + return JS( + '', + 'function(f,s,r,a){' + 'return function(){' + 'a=[r(this)];' + 'Array.prototype.push.apply(a,arguments);' + 'return f.apply(s(this),a)' + '}' + '}(#,#,#)', function, getSelf, getReceiver); + } + } + + static forwardInterceptedCallTo(receiver, function) { + String selfField = BoundClosure.selfFieldName(); + String receiverField = BoundClosure.receiverFieldName(); + String stubName = JS('String|Null', '#.\$stubName', function); + int arity = JS('int', '#.length', function); + bool isCsp = JS('bool', 'typeof dart_precompiled == "function"'); + var lookedUpFunction = JS("", "#[#]", receiver, stubName); + // The receiver[stubName] may not be equal to the function if we try to + // forward to a super-method. Especially when we create a bound closure + // of a super-call we need to make sure that we don't forward back to the + // dynamically looked up function. + bool isSuperCall = !identical(function, lookedUpFunction); + + if (isCsp || isSuperCall || arity >= 28) { + return cspForwardInterceptedCall(arity, isSuperCall, stubName, + function); + } + if (arity == 1) { + return JS( + '', + '(new Function(#))()', + 'return function(){' + 'return this.$selfField.$stubName(this.$receiverField);' + '${functionCounter++}' + '}'); + } + assert(1 < arity && arity < 28); + String arguments = JS( + 'String', + '"abcdefghijklmnopqrstuvwxyz".split("").splice(0,#).join(",")', + arity - 1); + return JS( + '', + '(new Function(#))()', + 'return function($arguments){' + 'return this.$selfField.$stubName(this.$receiverField, $arguments);' + '${functionCounter++}' + '}'); + } + + String toString() => "Closure"; +} + +/// Called from implicit method getter (aka tear-off). +closureFromTearOff(receiver, + functions, + reflectionInfo, + isStatic, + jsArguments, + name) { + return Closure.fromTearOff( + receiver, + JSArray.markFixedList(functions), + JSArray.markFixedList(reflectionInfo), + JS('bool', '!!#', isStatic), + jsArguments, + JS('String', '#', name)); +} + +/// Represents an implicit closure of a function. +class TearOffClosure extends Closure { +} + +/// Represents a 'tear-off' closure, that is an instance method bound +/// to a specific receiver (instance). +class BoundClosure extends TearOffClosure { + /// The receiver or interceptor. + // TODO(ahe): This could just be the interceptor, we always know if + // we need the interceptor when generating the call method. + final _self; + + /// The method. + final _target; + + /// The receiver. Null if [_self] is not an interceptor. + final _receiver; + + /// The name of the function. Only used by the mirror system. + final String _name; + + BoundClosure(this._self, this._target, this._receiver, this._name); + + bool operator==(other) { + if (identical(this, other)) return true; + if (other is! BoundClosure) return false; + return JS('bool', '# === # && # === # && # === #', + _self, other._self, + _target, other._target, + _receiver, other._receiver); + } + + int get hashCode { + int receiverHashCode; + if (_receiver == null) { + // A bound closure on a regular Dart object, just use the + // identity hash code. + receiverHashCode = Primitives.objectHashCode(_self); + } else if (JS('String', 'typeof #', _receiver) != 'object') { + // A bound closure on a primitive JavaScript type. We + // use the hashCode method we define for those primitive types. + receiverHashCode = _receiver.hashCode; + } else { + // A bound closure on an intercepted native class, just use the + // identity hash code. + receiverHashCode = Primitives.objectHashCode(_receiver); + } + return receiverHashCode ^ Primitives.objectHashCode(_target); + } + + @NoInline() + static selfOf(BoundClosure closure) => closure._self; + + static targetOf(BoundClosure closure) => closure._target; + + @NoInline() + static receiverOf(BoundClosure closure) => closure._receiver; + + static nameOf(BoundClosure closure) => closure._name; + + static String selfFieldNameCache; + + static String selfFieldName() { + if (selfFieldNameCache == null) { + selfFieldNameCache = computeFieldNamed('self'); + } + return selfFieldNameCache; + } + + static String receiverFieldNameCache; + + static String receiverFieldName() { + if (receiverFieldNameCache == null) { + receiverFieldNameCache = computeFieldNamed('receiver'); + } + return receiverFieldNameCache; + } + + @NoInline() @NoSideEffects() + static String computeFieldNamed(String fieldName) { + var template = new BoundClosure('self', 'target', 'receiver', 'name'); + var names = JSArray.markFixedList( + JS('', 'Object.getOwnPropertyNames(#)', template)); + for (int i = 0; i < names.length; i++) { + var name = names[i]; + if (JS('bool', '#[#] === #', template, name, fieldName)) { + return JS('String', '#', name); + } + } + } +} + +bool jsHasOwnProperty(var jsObject, String property) { + return JS('bool', r'#.hasOwnProperty(#)', jsObject, property); +} + +jsPropertyAccess(var jsObject, String property) { + return JS('var', r'#[#]', jsObject, property); +} + +/** + * Called at the end of unaborted switch cases to get the singleton + * FallThroughError exception that will be thrown. + */ +getFallThroughError() => new FallThroughErrorImplementation(); + +/** + * Represents the type dynamic. The compiler treats this specially. + */ +abstract class Dynamic_ { +} + +/** + * A metadata annotation describing the types instantiated by a native element. + * + * The annotation is valid on a native method and a field of a native class. + * + * By default, a field of a native class is seen as an instantiation point for + * all native classes that are a subtype of the field's type, and a native + * method is seen as an instantiation point fo all native classes that are a + * subtype of the method's return type, or the argument types of the declared + * type of the method's callback parameter. + * + * An @[Creates] annotation overrides the default set of instantiated types. If + * one or more @[Creates] annotations are present, the type of the native + * element is ignored, and the union of @[Creates] annotations is used instead. + * The names in the strings are resolved and the program will fail to compile + * with dart2js if they do not name types. + * + * The argument to [Creates] is a string. The string is parsed as the names of + * one or more types, separated by vertical bars `|`. There are some special + * names: + * + * * `=Object`. This means 'exactly Object', which is a plain JavaScript object + * with properties and none of the subtypes of Object. + * + * Example: we may know that a method always returns a specific implementation: + * + * @Creates('_NodeList') + * List getElementsByTagName(String tag) native; + * + * Useful trick: A method can be marked as not instantiating any native classes + * with the annotation `@Creates('Null')`. This is useful for fields on native + * classes that are used only in Dart code. + * + * @Creates('Null') + * var _cachedFoo; + */ +class Creates { + final String types; + const Creates(this.types); +} + +/** + * A metadata annotation describing the types returned or yielded by a native + * element. + * + * The annotation is valid on a native method and a field of a native class. + * + * By default, a native method or field is seen as returning or yielding all + * subtypes if the method return type or field type. This annotation allows a + * more precise set of types to be specified. + * + * See [Creates] for the syntax of the argument. + * + * Example: IndexedDB keys are numbers, strings and JavaScript Arrays of keys. + * + * @Returns('String|num|JSExtendableArray') + * dynamic key; + * + * // Equivalent: + * @Returns('String') @Returns('num') @Returns('JSExtendableArray') + * dynamic key; + */ +class Returns { + final String types; + const Returns(this.types); +} + +/** + * A metadata annotation placed on native methods and fields of native classes + * to specify the JavaScript name. + * + * This example declares a Dart field + getter + setter called `$dom_title` that + * corresponds to the JavaScript property `title`. + * + * class Docmument native "*Foo" { + * @JSName('title') + * String $dom_title; + * } + */ +class JSName { + final String name; + const JSName(this.name); +} + +/** + * The following methods are called by the runtime to implement + * checked mode and casts. We specialize each primitive type (eg int, bool), and + * use the compiler's convention to do is-checks on regular objects. + */ +boolConversionCheck(value) { + if (value is bool) return value; + // One of the following checks will always fail. + boolTypeCheck(value); + assert(value != null); + return false; +} + +stringTypeCheck(value) { + if (value == null) return value; + if (value is String) return value; + throw new TypeErrorImplementation(value, 'String'); +} + +stringTypeCast(value) { + if (value is String || value == null) return value; + // TODO(lrn): When reified types are available, pass value.class and String. + throw new CastErrorImplementation( + Primitives.objectTypeName(value), 'String'); +} + +doubleTypeCheck(value) { + if (value == null) return value; + if (value is double) return value; + throw new TypeErrorImplementation(value, 'double'); +} + +doubleTypeCast(value) { + if (value is double || value == null) return value; + throw new CastErrorImplementation( + Primitives.objectTypeName(value), 'double'); +} + +numTypeCheck(value) { + if (value == null) return value; + if (value is num) return value; + throw new TypeErrorImplementation(value, 'num'); +} + +numTypeCast(value) { + if (value is num || value == null) return value; + throw new CastErrorImplementation( + Primitives.objectTypeName(value), 'num'); +} + +boolTypeCheck(value) { + if (value == null) return value; + if (value is bool) return value; + throw new TypeErrorImplementation(value, 'bool'); +} + +boolTypeCast(value) { + if (value is bool || value == null) return value; + throw new CastErrorImplementation( + Primitives.objectTypeName(value), 'bool'); +} + +intTypeCheck(value) { + if (value == null) return value; + if (value is int) return value; + throw new TypeErrorImplementation(value, 'int'); +} + +intTypeCast(value) { + if (value is int || value == null) return value; + throw new CastErrorImplementation( + Primitives.objectTypeName(value), 'int'); +} + +void propertyTypeError(value, property) { + // Cuts the property name to the class name. + String name = property.substring(3, property.length); + throw new TypeErrorImplementation(value, name); +} + +void propertyTypeCastError(value, property) { + // Cuts the property name to the class name. + String actualType = Primitives.objectTypeName(value); + String expectedType = property.substring(3, property.length); + throw new CastErrorImplementation(actualType, expectedType); +} + +/** + * For types that are not supertypes of native (eg DOM) types, + * we emit a simple property check to check that an object implements + * that type. + */ +propertyTypeCheck(value, property) { + if (value == null) return value; + if (JS('bool', '!!#[#]', value, property)) return value; + propertyTypeError(value, property); +} + +/** + * For types that are not supertypes of native (eg DOM) types, + * we emit a simple property check to check that an object implements + * that type. + */ +propertyTypeCast(value, property) { + if (value == null || JS('bool', '!!#[#]', value, property)) return value; + propertyTypeCastError(value, property); +} + +/** + * For types that are supertypes of native (eg DOM) types, we use the + * interceptor for the class because we cannot add a JS property to the + * prototype at load time. + */ +interceptedTypeCheck(value, property) { + if (value == null) return value; + if ((identical(JS('String', 'typeof #', value), 'object')) + && JS('bool', '#[#]', getInterceptor(value), property)) { + return value; + } + propertyTypeError(value, property); +} + +/** + * For types that are supertypes of native (eg DOM) types, we use the + * interceptor for the class because we cannot add a JS property to the + * prototype at load time. + */ +interceptedTypeCast(value, property) { + if (value == null + || ((JS('bool', 'typeof # === "object"', value)) + && JS('bool', '#[#]', getInterceptor(value), property))) { + return value; + } + propertyTypeCastError(value, property); +} + +/** + * Specialization of the type check for num and String and their + * supertype since [value] can be a JS primitive. + */ +numberOrStringSuperTypeCheck(value, property) { + if (value == null) return value; + if (value is String) return value; + if (value is num) return value; + if (JS('bool', '!!#[#]', value, property)) return value; + propertyTypeError(value, property); +} + +numberOrStringSuperTypeCast(value, property) { + if (value is String) return value; + if (value is num) return value; + return propertyTypeCast(value, property); +} + +numberOrStringSuperNativeTypeCheck(value, property) { + if (value == null) return value; + if (value is String) return value; + if (value is num) return value; + if (JS('bool', '#[#]', getInterceptor(value), property)) return value; + propertyTypeError(value, property); +} + +numberOrStringSuperNativeTypeCast(value, property) { + if (value == null) return value; + if (value is String) return value; + if (value is num) return value; + if (JS('bool', '#[#]', getInterceptor(value), property)) return value; + propertyTypeCastError(value, property); +} + +/** + * Specialization of the type check for String and its supertype + * since [value] can be a JS primitive. + */ +stringSuperTypeCheck(value, property) { + if (value == null) return value; + if (value is String) return value; + if (JS('bool', '!!#[#]', value, property)) return value; + propertyTypeError(value, property); +} + +stringSuperTypeCast(value, property) { + if (value is String) return value; + return propertyTypeCast(value, property); +} + +stringSuperNativeTypeCheck(value, property) { + if (value == null) return value; + if (value is String) return value; + if (JS('bool', '#[#]', getInterceptor(value), property)) return value; + propertyTypeError(value, property); +} + +stringSuperNativeTypeCast(value, property) { + if (value is String || value == null) return value; + if (JS('bool', '#[#]', getInterceptor(value), property)) return value; + propertyTypeCastError(value, property); +} + +/** + * Specialization of the type check for List and its supertypes, + * since [value] can be a JS array. + */ +listTypeCheck(value) { + if (value == null) return value; + if (value is List) return value; + throw new TypeErrorImplementation(value, 'List'); +} + +listTypeCast(value) { + if (value is List || value == null) return value; + throw new CastErrorImplementation( + Primitives.objectTypeName(value), 'List'); +} + +listSuperTypeCheck(value, property) { + if (value == null) return value; + if (value is List) return value; + if (JS('bool', '!!#[#]', value, property)) return value; + propertyTypeError(value, property); +} + +listSuperTypeCast(value, property) { + if (value is List) return value; + return propertyTypeCast(value, property); +} + +listSuperNativeTypeCheck(value, property) { + if (value == null) return value; + if (value is List) return value; + if (JS('bool', '#[#]', getInterceptor(value), property)) return value; + propertyTypeError(value, property); +} + +listSuperNativeTypeCast(value, property) { + if (value is List || value == null) return value; + if (JS('bool', '#[#]', getInterceptor(value), property)) return value; + propertyTypeCastError(value, property); +} + +voidTypeCheck(value) { + if (value == null) return value; + throw new TypeErrorImplementation(value, 'void'); +} + +checkMalformedType(value, message) { + if (value == null) return value; + throw new TypeErrorImplementation.fromMessage(message); +} + +/** + * Special interface recognized by the compiler and implemented by DOM + * objects that support integer indexing. This interface is not + * visible to anyone, and is only injected into special libraries. + */ +abstract class JavaScriptIndexingBehavior extends JSMutableIndexable { +} + +// TODO(lrn): These exceptions should be implemented in core. +// When they are, remove the 'Implementation' here. + +/** Thrown by type assertions that fail. */ +class TypeErrorImplementation extends Error implements TypeError { + final String message; + + /** + * Normal type error caused by a failed subtype test. + */ + TypeErrorImplementation(Object value, String type) + : message = "type '${Primitives.objectTypeName(value)}' is not a subtype " + "of type '$type'"; + + TypeErrorImplementation.fromMessage(String this.message); + + String toString() => message; +} + +/** Thrown by the 'as' operator if the cast isn't valid. */ +class CastErrorImplementation extends Error implements CastError { + // TODO(lrn): Rename to CastError (and move implementation into core). + final String message; + + /** + * Normal cast error caused by a failed type cast. + */ + CastErrorImplementation(Object actualType, Object expectedType) + : message = "CastError: Casting value of type $actualType to" + " incompatible type $expectedType"; + + String toString() => message; +} + +class FallThroughErrorImplementation extends FallThroughError { + FallThroughErrorImplementation(); + String toString() => "Switch case fall-through."; +} + +/** + * Helper function for implementing asserts. The compiler treats this specially. + */ +void assertHelper(condition) { + // Do a bool check first because it is common and faster than 'is Function'. + if (condition is !bool) { + if (condition is Function) condition = condition(); + if (condition is !bool) { + throw new TypeErrorImplementation(condition, 'bool'); + } + } + // Compare to true to avoid boolean conversion check in checked + // mode. + if (true != condition) throw new AssertionError(); +} + +/** + * Called by generated code when a method that must be statically + * resolved cannot be found. + */ +void throwNoSuchMethod(obj, name, arguments, expectedArgumentNames) { + Symbol memberName = new _symbol_dev.Symbol.unvalidated(name); + throw new NoSuchMethodError(obj, memberName, arguments, + new Map(), + expectedArgumentNames); +} + +/** + * Called by generated code when a static field's initializer references the + * field that is currently being initialized. + */ +void throwCyclicInit(String staticName) { + throw new CyclicInitializationError( + "Cyclic initialization for static $staticName"); +} + +/** + * Error thrown when a runtime error occurs. + */ +class RuntimeError extends Error { + final message; + RuntimeError(this.message); + String toString() => "RuntimeError: $message"; +} + +abstract class RuntimeType { + const RuntimeType(); + + toRti(); +} + +class RuntimeFunctionType extends RuntimeType { + final RuntimeType returnType; + final List parameterTypes; + final List optionalParameterTypes; + final namedParameters; + + static var /* bool */ inAssert = false; + + RuntimeFunctionType(this.returnType, + this.parameterTypes, + this.optionalParameterTypes, + this.namedParameters); + + bool get isVoid => returnType is VoidRuntimeType; + + /// Called from generated code. [expression] is a Dart object and this method + /// returns true if [this] is a supertype of [expression]. + @NoInline() @NoSideEffects() + bool _isTest(expression) { + var functionTypeObject = _extractFunctionTypeObjectFrom(expression); + return functionTypeObject == null + ? false + : isFunctionSubtype(functionTypeObject, toRti()); + } + + @NoInline() @NoSideEffects() + _asCheck(expression) { + // Type inferrer doesn't think this is called with dynamic arguments. + return _check(JS('', '#', expression), true); + } + + @NoInline() @NoSideEffects() + _assertCheck(expression) { + if (inAssert) return null; + inAssert = true; // Don't try to check this library itself. + try { + // Type inferrer don't think this is called with dynamic arguments. + return _check(JS('', '#', expression), false); + } finally { + inAssert = false; + } + } + + _check(expression, bool isCast) { + if (expression == null) return null; + if (_isTest(expression)) return expression; + + var self = new FunctionTypeInfoDecoderRing(toRti()).toString(); + if (isCast) { + var functionTypeObject = _extractFunctionTypeObjectFrom(expression); + var pretty; + if (functionTypeObject != null) { + pretty = new FunctionTypeInfoDecoderRing(functionTypeObject).toString(); + } else { + pretty = Primitives.objectTypeName(expression); + } + throw new CastErrorImplementation(pretty, self); + } else { + // TODO(ahe): Pass "pretty" function-type to TypeErrorImplementation? + throw new TypeErrorImplementation(expression, self); + } + } + + _extractFunctionTypeObjectFrom(o) { + var interceptor = getInterceptor(o); + return JS('bool', '# in #', JS_SIGNATURE_NAME(), interceptor) + ? JS('', '#[#]()', interceptor, JS_SIGNATURE_NAME()) + : null; + } + + toRti() { + var result = JS('=Object', '{ #: "dynafunc" }', JS_FUNCTION_TYPE_TAG()); + if (isVoid) { + JS('', '#[#] = true', result, JS_FUNCTION_TYPE_VOID_RETURN_TAG()); + } else { + if (returnType is! DynamicRuntimeType) { + JS('', '#[#] = #', result, JS_FUNCTION_TYPE_RETURN_TYPE_TAG(), + returnType.toRti()); + } + } + if (parameterTypes != null && !parameterTypes.isEmpty) { + JS('', '#[#] = #', result, JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG(), + listToRti(parameterTypes)); + } + + if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) { + JS('', '#[#] = #', result, JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG(), + listToRti(optionalParameterTypes)); + } + + if (namedParameters != null) { + var namedRti = JS('=Object', '{}'); + var keys = extractKeys(namedParameters); + for (var i = 0; i < keys.length; i++) { + var name = keys[i]; + var rti = JS('', '#[#]', namedParameters, name).toRti(); + JS('', '#[#] = #', namedRti, name, rti); + } + JS('', '#[#] = #', result, JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG(), + namedRti); + } + + return result; + } + + static listToRti(list) { + list = JS('JSFixedArray', '#', list); + var result = JS('JSExtendableArray', '[]'); + for (var i = 0; i < list.length; i++) { + JS('', '#.push(#)', result, list[i].toRti()); + } + return result; + } + + String toString() { + String result = '('; + bool needsComma = false; + if (parameterTypes != null) { + for (var i = 0; i < parameterTypes.length; i++) { + RuntimeType type = parameterTypes[i]; + if (needsComma) result += ', '; + result += '$type'; + needsComma = true; + } + } + if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) { + if (needsComma) result += ', '; + needsComma = false; + result += '['; + for (var i = 0; i < optionalParameterTypes.length; i++) { + RuntimeType type = optionalParameterTypes[i]; + if (needsComma) result += ', '; + result += '$type'; + needsComma = true; + } + result += ']'; + } else if (namedParameters != null) { + if (needsComma) result += ', '; + needsComma = false; + result += '{'; + var keys = extractKeys(namedParameters); + for (var i = 0; i < keys.length; i++) { + var name = keys[i]; + if (needsComma) result += ', '; + var rti = JS('', '#[#]', namedParameters, name).toRti(); + result += '$rti ${JS("String", "#", name)}'; + needsComma = true; + } + result += '}'; + } + + result += ') -> $returnType'; + return result; + } +} + +RuntimeFunctionType buildFunctionType(returnType, + parameterTypes, + optionalParameterTypes) { + return new RuntimeFunctionType( + returnType, + parameterTypes, + optionalParameterTypes, + null); +} + +RuntimeFunctionType buildNamedFunctionType(returnType, + parameterTypes, + namedParameters) { + return new RuntimeFunctionType( + returnType, + parameterTypes, + null, + namedParameters); +} + +RuntimeType buildInterfaceType(rti, typeArguments) { + String name = JS('String|Null', r'#.name', rti); + if (typeArguments == null || typeArguments.isEmpty) { + return new RuntimeTypePlain(name); + } + return new RuntimeTypeGeneric(name, typeArguments, null); +} + +class DynamicRuntimeType extends RuntimeType { + const DynamicRuntimeType(); + + String toString() => 'dynamic'; + + toRti() => null; +} + +RuntimeType getDynamicRuntimeType() => const DynamicRuntimeType(); + +class VoidRuntimeType extends RuntimeType { + const VoidRuntimeType(); + + String toString() => 'void'; + + toRti() => throw 'internal error'; +} + +RuntimeType getVoidRuntimeType() => const VoidRuntimeType(); + +/** + * Meta helper for function type tests. + * + * A "meta helper" is a helper function that is never called but simulates how + * generated code behaves as far as resolution and type inference is concerned. + */ +functionTypeTestMetaHelper() { + var dyn = JS('', 'x'); + var dyn2 = JS('', 'x'); + List fixedListOrNull = JS('JSFixedArray|Null', 'x'); + List fixedListOrNull2 = JS('JSFixedArray|Null', 'x'); + List fixedList = JS('JSFixedArray', 'x'); + // TODO(ahe): Can we use [UnknownJavaScriptObject] below? + var /* UnknownJavaScriptObject */ jsObject = JS('=Object', 'x'); + + buildFunctionType(dyn, fixedListOrNull, fixedListOrNull2); + buildNamedFunctionType(dyn, fixedList, jsObject); + buildInterfaceType(dyn, fixedListOrNull); + getDynamicRuntimeType(); + getVoidRuntimeType(); + convertRtiToRuntimeType(dyn); + dyn._isTest(dyn2); + dyn._asCheck(dyn2); + dyn._assertCheck(dyn2); +} + +RuntimeType convertRtiToRuntimeType(rti) { + if (rti == null) { + return getDynamicRuntimeType(); + } else if (JS('bool', 'typeof # == "function"', rti)) { + return new RuntimeTypePlain(JS('String', r'rti.name')); + } else if (JS('bool', '#.constructor == Array', rti)) { + List list = JS('JSFixedArray', '#', rti); + String name = JS('String', r'#.name', list[0]); + List arguments = []; + for (int i = 1; i < list.length; i++) { + arguments.add(convertRtiToRuntimeType(list[i])); + } + return new RuntimeTypeGeneric(name, arguments, rti); + } else if (JS('bool', '"func" in #', rti)) { + return new FunctionTypeInfoDecoderRing(rti).toRuntimeType(); + } else { + throw new RuntimeError( + "Cannot convert " + "'${JS('String', 'JSON.stringify(#)', rti)}' to RuntimeType."); + } +} + +class RuntimeTypePlain extends RuntimeType { + final String name; + + RuntimeTypePlain(this.name); + + toRti() { + var rti = JS('', 'init.allClasses[#]', name); + if (rti == null) throw "no type for '$name'"; + return rti; + } + + String toString() => name; +} + +class RuntimeTypeGeneric extends RuntimeType { + final String name; + final List arguments; + var rti; + + RuntimeTypeGeneric(this.name, this.arguments, this.rti); + + toRti() { + if (rti != null) return rti; + var result = JS('JSExtendableArray', '[init.allClasses[#]]', name); + if (result[0] == null) { + throw "no type for '$name<...>'"; + } + for (RuntimeType argument in arguments) { + JS('', '#.push(#)', result, argument.toRti()); + } + return rti = result; + } + + String toString() => '$name<${arguments.join(", ")}>'; +} + +class FunctionTypeInfoDecoderRing { + final _typeData; + String _cachedToString; + + FunctionTypeInfoDecoderRing(this._typeData); + + bool get _hasReturnType => JS('bool', '"ret" in #', _typeData); + get _returnType => JS('', '#.ret', _typeData); + + bool get _isVoid => JS('bool', '!!#.void', _typeData); + + bool get _hasArguments => JS('bool', '"args" in #', _typeData); + List get _arguments => JS('JSExtendableArray', '#.args', _typeData); + + bool get _hasOptionalArguments => JS('bool', '"opt" in #', _typeData); + List get _optionalArguments => JS('JSExtendableArray', '#.opt', _typeData); + + bool get _hasNamedArguments => JS('bool', '"named" in #', _typeData); + get _namedArguments => JS('=Object', '#.named', _typeData); + + RuntimeType toRuntimeType() { + // TODO(ahe): Implement this (and update return type). + return const DynamicRuntimeType(); + } + + String _convert(type) { + String result = runtimeTypeToString(type); + if (result != null) return result; + if (JS('bool', '"func" in #', type)) { + return new FunctionTypeInfoDecoderRing(type).toString(); + } else { + throw 'bad type'; + } + } + + String toString() { + if (_cachedToString != null) return _cachedToString; + var s = "("; + var sep = ''; + if (_hasArguments) { + for (var argument in _arguments) { + s += sep; + s += _convert(argument); + sep = ', '; + } + } + if (_hasOptionalArguments) { + s += '$sep['; + sep = ''; + for (var argument in _optionalArguments) { + s += sep; + s += _convert(argument); + sep = ', '; + } + s += ']'; + } + if (_hasNamedArguments) { + s += '$sep{'; + sep = ''; + for (var name in extractKeys(_namedArguments)) { + s += sep; + s += '$name: '; + s += _convert(JS('', '#[#]', _namedArguments, name)); + sep = ', '; + } + s += '}'; + } + s += ') -> '; + if (_isVoid) { + s += 'void'; + } else if (_hasReturnType) { + s += _convert(_returnType); + } else { + s += 'dynamic'; + } + return _cachedToString = "$s"; + } +} + +// TODO(ahe): Remove this class and call noSuchMethod instead. +class UnimplementedNoSuchMethodError extends Error + implements NoSuchMethodError { + final String _message; + + UnimplementedNoSuchMethodError(this._message); + + String toString() => "Unsupported operation: $_message"; +} + +/** + * Creates a random number with 64 bits of randomness. + * + * This will be truncated to the 53 bits available in a double. + */ +int random64() { + // TODO(lrn): Use a secure random source. + int int32a = JS("int", "(Math.random() * 0x100000000) >>> 0"); + int int32b = JS("int", "(Math.random() * 0x100000000) >>> 0"); + return int32a + int32b * 0x100000000; +} + +/** + * Returns a property name for placing data on JavaScript objects shared between + * DOM isolates. This happens when multiple programs are loaded in the same + * JavaScript context (i.e. page). The name is based on [name] but with an + * additional part that is unique for each isolate. + * + * The form of the name is '___dart_$name_$id'. + */ +String getIsolateAffinityTag(String name) { + return JS('String', 'init.getIsolateTag(#)', name); +} + +typedef Future LoadLibraryFunctionType(); + +LoadLibraryFunctionType _loadLibraryWrapper(String loadId) { + return () => loadDeferredLibrary(loadId); +} + +final Map> _loadedLibraries = >{}; + +Future loadDeferredLibrary(String loadId, [String uri]) { + List hunkNames = new List(); + if (JS('bool', '\$.libraries_to_load[#] === undefined', loadId)) { + return new Future(() => false); + } + for (int index = 0; + index < JS('int', '\$.libraries_to_load[#].length', loadId); + ++index) { + hunkNames.add(JS('String', '\$.libraries_to_load[#][#]', + loadId, index)); + } + Iterable> allLoads = + hunkNames.map((hunkName) => _loadHunk(hunkName, uri)); + return Future.wait(allLoads).then((results) { + return results.any((x) => x); + }); +} + +Future _loadHunk(String hunkName, String uri) { + // TODO(ahe): Validate libraryName. Kasper points out that you want + // to be able to experiment with the effect of toggling @DeferLoad, + // so perhaps we should silently ignore "bad" library names. + Future future = _loadedLibraries[hunkName]; + if (future != null) { + return future.then((_) => false); + } + + if (uri == null) { + uri = IsolateNatives.thisScript; + } + int index = uri.lastIndexOf('/'); + uri = '${uri.substring(0, index + 1)}$hunkName'; + + if (Primitives.isJsshell || Primitives.isD8) { + // TODO(ahe): Move this code to a JavaScript command helper script that is + // not included in generated output. + return _loadedLibraries[hunkName] = new Future(() { + try { + // Create a new function to avoid getting access to current function + // context. + JS('void', '(new Function(#))()', 'load("$uri")'); + } catch (error, stackTrace) { + throw new DeferredLoadException("Loading $uri failed."); + } + return true; + }); + } else if (isWorker()) { + // We are in a web worker. Load the code with an XMLHttpRequest. + return _loadedLibraries[hunkName] = new Future(() { + Completer completer = new Completer(); + enterJsAsync(); + Future leavingFuture = completer.future.whenComplete(() { + leaveJsAsync(); + }); + + int index = uri.lastIndexOf('/'); + uri = '${uri.substring(0, index + 1)}$hunkName'; + var xhr = JS('dynamic', 'new XMLHttpRequest()'); + JS('void', '#.open("GET", #)', xhr, uri); + JS('void', '#.addEventListener("load", #, false)', + xhr, convertDartClosureToJS((event) { + if (JS('int', '#.status', xhr) != 200) { + completer.completeError( + new DeferredLoadException("Loading $uri failed.")); + return; + } + String code = JS('String', '#.responseText', xhr); + try { + // Create a new function to avoid getting access to current function + // context. + JS('void', '(new Function(#))()', code); + } catch (error, stackTrace) { + completer.completeError( + new DeferredLoadException("Evaluating $uri failed.")); + return; + } + completer.complete(true); + }, 1)); + + var fail = convertDartClosureToJS((event) { + new DeferredLoadException("Loading $uri failed."); + }, 1); + JS('void', '#.addEventListener("error", #, false)', xhr, fail); + JS('void', '#.addEventListener("abort", #, false)', xhr, fail); + + JS('void', '#.send()', xhr); + return leavingFuture; + }); + } + // We are in a dom-context. + return _loadedLibraries[hunkName] = new Future(() { + Completer completer = new Completer(); + // Inject a script tag. + var script = JS('', 'document.createElement("script")'); + JS('', '#.type = "text/javascript"', script); + JS('', '#.src = #', script, uri); + JS('', '#.addEventListener("load", #, false)', + script, convertDartClosureToJS((event) { + completer.complete(true); + }, 1)); + JS('', '#.addEventListener("error", #, false)', + script, convertDartClosureToJS((event) { + completer.completeError( + new DeferredLoadException("Loading $uri failed.")); + }, 1)); + JS('', 'document.body.appendChild(#)', script); + + return completer.future; + }); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_mirrors.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_mirrors.dart new file mode 100644 index 0000000..f42ea42 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_mirrors.dart @@ -0,0 +1,2814 @@ +// Copyright (c) 2013, 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. + +library dart._js_mirrors; + +import 'dart:async'; + +import 'dart:collection' show + UnmodifiableListView; + +import 'dart:mirrors'; + +import 'dart:_foreign_helper' show + JS, + JS_CURRENT_ISOLATE, + JS_CURRENT_ISOLATE_CONTEXT, + JS_GET_NAME; + +import 'dart:_internal' as _symbol_dev; + +import 'dart:_js_helper' show + BoundClosure, + Closure, + JSInvocationMirror, + JsCache, + Null, + Primitives, + ReflectionInfo, + RuntimeError, + TypeVariable, + UnimplementedNoSuchMethodError, + createRuntimeType, + createUnmangledInvocationMirror, + getMangledTypeName, + getMetadata, + hasReflectableProperty, + runtimeTypeToString, + setRuntimeTypeInfo, + throwInvalidReflectionError; + +import 'dart:_interceptors' show + Interceptor, + JSArray, + JSExtendableArray, + getInterceptor; + +import 'dart:_js_names'; + +const String METHODS_WITH_OPTIONAL_ARGUMENTS = r'$methodsWithOptionalArguments'; + +/// No-op method that is called to inform the compiler that tree-shaking needs +/// to be disabled. +disableTreeShaking() => preserveNames(); + +/// No-op method that is called to inform the compiler that metadata must be +/// preserved at runtime. +preserveMetadata() {} + +String getName(Symbol symbol) { + preserveNames(); + return n(symbol); +} + +class JsMirrorSystem implements MirrorSystem { + UnmodifiableMapView _cachedLibraries; + + final IsolateMirror isolate = new JsIsolateMirror(); + + JsTypeMirror get dynamicType => _dynamicType; + JsTypeMirror get voidType => _voidType; + + static final JsTypeMirror _dynamicType = + new JsTypeMirror(const Symbol('dynamic')); + static final JsTypeMirror _voidType = new JsTypeMirror(const Symbol('void')); + + static final Map> librariesByName = + computeLibrariesByName(); + + Map get libraries { + if (_cachedLibraries != null) return _cachedLibraries; + Map result = new Map(); + for (List list in librariesByName.values) { + for (LibraryMirror library in list) { + result[library.uri] = library; + } + } + return _cachedLibraries = + new UnmodifiableMapView(result); + } + + LibraryMirror findLibrary(Symbol libraryName) { + return librariesByName[n(libraryName)].single; + } + + static Map> computeLibrariesByName() { + disableTreeShaking(); + var result = new Map>(); + var jsLibraries = JS('JSExtendableArray|Null', 'init.libraries'); + if (jsLibraries == null) return result; + for (List data in jsLibraries) { + String name = data[0]; + Uri uri = Uri.parse(data[1]); + List classes = data[2]; + List functions = data[3]; + var metadataFunction = data[4]; + var fields = data[5]; + bool isRoot = data[6]; + var globalObject = data[7]; + List metadata = (metadataFunction == null) + ? const [] : JS('List', '#()', metadataFunction); + var libraries = result.putIfAbsent(name, () => []); + libraries.add( + new JsLibraryMirror( + s(name), uri, classes, functions, metadata, fields, isRoot, + globalObject)); + } + return result; + } +} + +abstract class JsMirror implements Mirror { + const JsMirror(); + + String get _prettyName; + + String toString() => _prettyName; + + // TODO(ahe): Remove this method from the API. + MirrorSystem get mirrors => currentJsMirrorSystem; + + _getField(JsMirror receiver) { + throw new UnimplementedError(); + } + + void _setField(JsMirror receiver, Object arg) { + throw new UnimplementedError(); + } + + _loadField(String name) { + throw new UnimplementedError(); + } + + void _storeField(String name, Object arg) { + throw new UnimplementedError(); + } +} + +// This class is somewhat silly in the current implementation. +class JsIsolateMirror extends JsMirror implements IsolateMirror { + final _isolateContext = JS_CURRENT_ISOLATE_CONTEXT(); + + String get _prettyName => 'Isolate'; + + String get debugName { + String id = _isolateContext == null ? 'X' : _isolateContext.id.toString(); + // Using name similar to what the VM uses. + return '${n(rootLibrary.simpleName)}-$id'; + } + + bool get isCurrent => JS_CURRENT_ISOLATE_CONTEXT() == _isolateContext; + + LibraryMirror get rootLibrary { + return currentJsMirrorSystem.libraries.values.firstWhere( + (JsLibraryMirror library) => library._isRoot); + } +} + +abstract class JsDeclarationMirror extends JsMirror + implements DeclarationMirror { + final Symbol simpleName; + + const JsDeclarationMirror(this.simpleName); + + Symbol get qualifiedName => computeQualifiedName(owner, simpleName); + + bool get isPrivate => n(simpleName).startsWith('_'); + + bool get isTopLevel => owner != null && owner is LibraryMirror; + + // TODO(ahe): This should use qualifiedName. + String toString() => "$_prettyName on '${n(simpleName)}'"; + + List get _methods { + throw new RuntimeError('Should not call _methods'); + } + + _invoke(List positionalArguments, Map namedArguments) { + throw new RuntimeError('Should not call _invoke'); + } + + // TODO(ahe): Implement this. + SourceLocation get location => throw new UnimplementedError(); +} + +class JsTypeVariableMirror extends JsTypeMirror implements TypeVariableMirror { + final DeclarationMirror owner; + final TypeVariable _typeVariable; + final int _metadataIndex; + TypeMirror _cachedUpperBound; + + JsTypeVariableMirror(TypeVariable typeVariable, this.owner, + this._metadataIndex) + : this._typeVariable = typeVariable, + super(s(typeVariable.name)); + + bool operator ==(other) { + return (other is JsTypeVariableMirror && + simpleName == other.simpleName && + owner == other.owner); + } + + int get hashCode { + int code = 0x3FFFFFFF & (JsTypeVariableMirror).hashCode; + code ^= 17 * simpleName.hashCode; + code ^= 19 * owner.hashCode; + return code; + } + + String get _prettyName => 'TypeVariableMirror'; + + bool get isTopLevel => false; + bool get isStatic => false; + + TypeMirror get upperBound { + if (_cachedUpperBound != null) return _cachedUpperBound; + return _cachedUpperBound = typeMirrorFromRuntimeTypeRepresentation( + owner, getMetadata(_typeVariable.bound)); + } + + bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); + bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); + + _asRuntimeType() => _metadataIndex; +} + +class JsTypeMirror extends JsDeclarationMirror implements TypeMirror { + JsTypeMirror(Symbol simpleName) + : super(simpleName); + + String get _prettyName => 'TypeMirror'; + + DeclarationMirror get owner => null; + + // TODO(ahe): Doesn't match the specification, see http://dartbug.com/11569. + bool get isTopLevel => true; + + // TODO(ahe): Implement these. + List get metadata => throw new UnimplementedError(); + + bool get hasReflectedType => false; + Type get reflectedType { + throw new UnsupportedError("This type does not support reflectedType"); + } + + List get typeVariables => const []; + List get typeArguments => const []; + + bool get isOriginalDeclaration => true; + TypeMirror get originalDeclaration => this; + + bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); + bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); + + _asRuntimeType() { + if (this == JsMirrorSystem._dynamicType) return null; + if (this == JsMirrorSystem._voidType) return null; + throw new RuntimeError('Should not call _asRuntimeType'); + } +} + +class JsLibraryMirror extends JsDeclarationMirror with JsObjectMirror + implements LibraryMirror { + final Uri uri; + final List _classes; + final List _functions; + final List _metadata; + final String _compactFieldSpecification; + final bool _isRoot; + final _globalObject; + List _cachedFunctionMirrors; + List _cachedFields; + UnmodifiableMapView _cachedClasses; + UnmodifiableMapView _cachedFunctions; + UnmodifiableMapView _cachedGetters; + UnmodifiableMapView _cachedSetters; + UnmodifiableMapView _cachedVariables; + UnmodifiableMapView _cachedMembers; + UnmodifiableMapView _cachedDeclarations; + UnmodifiableListView _cachedMetadata; + + JsLibraryMirror(Symbol simpleName, + this.uri, + this._classes, + this._functions, + this._metadata, + this._compactFieldSpecification, + this._isRoot, + this._globalObject) + : super(simpleName); + + String get _prettyName => 'LibraryMirror'; + + Symbol get qualifiedName => simpleName; + + List get _methods => _functionMirrors; + + Map get __classes { + if (_cachedClasses != null) return _cachedClasses; + var result = new Map(); + for (String className in _classes) { + var cls = reflectClassByMangledName(className); + if (cls is ClassMirror) { + cls = cls.originalDeclaration; + if (cls is JsClassMirror) { + result[cls.simpleName] = cls; + cls._owner = this; + } + } + } + return _cachedClasses = + new UnmodifiableMapView(result); + } + + InstanceMirror setField(Symbol fieldName, Object arg) { + String name = n(fieldName); + if (name.endsWith('=')) throw new ArgumentError(''); + var mirror = __functions[s('$name=')]; + if (mirror == null) mirror = __variables[fieldName]; + if (mirror == null) { + // TODO(ahe): What receiver to use? + throw new NoSuchMethodError(this, setterSymbol(fieldName), [arg], null); + } + mirror._setField(this, arg); + return reflect(arg); + } + + InstanceMirror getField(Symbol fieldName) { + JsMirror mirror = __members[fieldName]; + if (mirror == null) { + // TODO(ahe): What receiver to use? + throw new NoSuchMethodError(this, fieldName, [], null); + } + return reflect(mirror._getField(this)); + } + + InstanceMirror invoke(Symbol memberName, + List positionalArguments, + [Map namedArguments]) { + if (namedArguments != null && !namedArguments.isEmpty) { + throw new UnsupportedError('Named arguments are not implemented.'); + } + JsDeclarationMirror mirror = __members[memberName]; + if (mirror == null) { + // TODO(ahe): What receiver to use? + throw new NoSuchMethodError( + this, memberName, positionalArguments, namedArguments); + } + if (mirror is JsMethodMirror && !mirror.canInvokeReflectively()) { + throwInvalidReflectionError(n(memberName)); + } + return reflect(mirror._invoke(positionalArguments, namedArguments)); + } + + _loadField(String name) { + // TODO(ahe): What about lazily initialized fields? See + // [JsClassMirror.getField]. + + // '$' (JS_CURRENT_ISOLATE()) stores state which is read directly, so we + // shouldn't use [_globalObject] here. + assert(JS('bool', '# in #', name, JS_CURRENT_ISOLATE())); + return JS('', '#[#]', JS_CURRENT_ISOLATE(), name); + } + + void _storeField(String name, Object arg) { + // '$' (JS_CURRENT_ISOLATE()) stores state which is stored directly, so we + // shouldn't use [_globalObject] here. + assert(JS('bool', '# in #', name, JS_CURRENT_ISOLATE())); + JS('void', '#[#] = #', JS_CURRENT_ISOLATE(), name, arg); + } + + List get _functionMirrors { + if (_cachedFunctionMirrors != null) return _cachedFunctionMirrors; + var result = new List(); + for (int i = 0; i < _functions.length; i++) { + String name = _functions[i]; + var jsFunction = JS('', '#[#]', _globalObject, name); + String unmangledName = mangledGlobalNames[name]; + if (unmangledName == null || + JS('bool', "!!#['getterStub']", jsFunction)) { + // If there is no unmangledName, [jsFunction] is either a synthetic + // implementation detail, or something that is excluded + // by @MirrorsUsed. + // If it has a getterStub property it is a synthetic stub. + // TODO(floitsch): Remove the getterStub hack. + continue; + } + bool isConstructor = unmangledName.startsWith('new '); + bool isStatic = !isConstructor; // Top-level functions are static, but + // constructors are not. + if (isConstructor) { + unmangledName = unmangledName.substring(4).replaceAll(r'$', '.'); + } + JsMethodMirror mirror = + new JsMethodMirror.fromUnmangledName( + unmangledName, jsFunction, isStatic, isConstructor); + result.add(mirror); + mirror._owner = this; + } + return _cachedFunctionMirrors = result; + } + + List get _fields { + if (_cachedFields != null) return _cachedFields; + var result = []; + parseCompactFieldSpecification( + this, _compactFieldSpecification, true, result); + return _cachedFields = result; + } + + Map get __functions { + if (_cachedFunctions != null) return _cachedFunctions; + var result = new Map(); + for (JsMethodMirror mirror in _functionMirrors) { + if (!mirror.isConstructor) result[mirror.simpleName] = mirror; + } + return _cachedFunctions = + new UnmodifiableMapView(result); + } + + Map get __getters { + if (_cachedGetters != null) return _cachedGetters; + var result = new Map(); + // TODO(ahe): Implement this. + return _cachedGetters = + new UnmodifiableMapView(result); + } + + Map get __setters { + if (_cachedSetters != null) return _cachedSetters; + var result = new Map(); + // TODO(ahe): Implement this. + return _cachedSetters = + new UnmodifiableMapView(result); + } + + Map get __variables { + if (_cachedVariables != null) return _cachedVariables; + var result = new Map(); + for (JsVariableMirror mirror in _fields) { + result[mirror.simpleName] = mirror; + } + return _cachedVariables = + new UnmodifiableMapView(result); + } + + Map get __members { + if (_cachedMembers != null) return _cachedMembers; + Map result = new Map.from(__classes); + addToResult(Symbol key, Mirror value) { + result[key] = value; + } + __functions.forEach(addToResult); + __getters.forEach(addToResult); + __setters.forEach(addToResult); + __variables.forEach(addToResult); + return _cachedMembers = new UnmodifiableMapView(result); + } + + Map get declarations { + if (_cachedDeclarations != null) return _cachedDeclarations; + var result = new Map(); + addToResult(Symbol key, Mirror value) { + result[key] = value; + } + __members.forEach(addToResult); + return _cachedDeclarations = + new UnmodifiableMapView(result); + } + + List get metadata { + if (_cachedMetadata != null) return _cachedMetadata; + preserveMetadata(); + return _cachedMetadata = + new UnmodifiableListView(_metadata.map(reflect)); + } + + // TODO(ahe): Test this getter. + DeclarationMirror get owner => null; + + List get libraryDependencies + => throw new UnimplementedError(); +} + +String n(Symbol symbol) => _symbol_dev.Symbol.getName(symbol); + +Symbol s(String name) { + if (name == null) return null; + return new _symbol_dev.Symbol.unvalidated(name); +} + +Symbol setterSymbol(Symbol symbol) => s("${n(symbol)}="); + +final JsMirrorSystem currentJsMirrorSystem = new JsMirrorSystem(); + +InstanceMirror reflect(Object reflectee) { + if (reflectee is Closure) { + return new JsClosureMirror(reflectee); + } else { + return new JsInstanceMirror(reflectee); + } +} + +TypeMirror reflectType(Type key) { + return reflectClassByMangledName(getMangledTypeName(key)); +} + +TypeMirror reflectClassByMangledName(String mangledName) { + String unmangledName = mangledGlobalNames[mangledName]; + if (mangledName == 'dynamic') return JsMirrorSystem._dynamicType; + if (mangledName == 'void') return JsMirrorSystem._voidType; + if (unmangledName == null) unmangledName = mangledName; + return reflectClassByName(s(unmangledName), mangledName); +} + +var classMirrors; + +TypeMirror reflectClassByName(Symbol symbol, String mangledName) { + int separatorIndex = mangledName.indexOf('/'); + if (separatorIndex > -1) { + // This is an interceptor name, where the first part is the nice name used + // for printing the type name. + mangledName = mangledName.substring(separatorIndex + 1); + } + if (classMirrors == null) classMirrors = JsCache.allocate(); + var mirror = JsCache.fetch(classMirrors, mangledName); + if (mirror != null) return mirror; + disableTreeShaking(); + int typeArgIndex = mangledName.indexOf("<"); + if (typeArgIndex != -1) { + mirror = new JsTypeBoundClassMirror(reflectClassByMangledName( + mangledName.substring(0, typeArgIndex)).originalDeclaration, + // Remove the angle brackets enclosing the type arguments. + mangledName.substring(typeArgIndex + 1, mangledName.length - 1)); + JsCache.update(classMirrors, mangledName, mirror); + return mirror; + } + var constructorOrInterceptor = + Primitives.getConstructorOrInterceptorToken(mangledName); + if (constructorOrInterceptor == null) { + int index = JS('int|Null', 'init.functionAliases[#]', mangledName); + if (index != null) { + mirror = new JsTypedefMirror(symbol, mangledName, getMetadata(index)); + JsCache.update(classMirrors, mangledName, mirror); + return mirror; + } + // Probably an intercepted class. + // TODO(ahe): How to handle intercepted classes? + throw new UnsupportedError('Cannot find class for: ${n(symbol)}'); + } + var constructor = (Primitives.isInterceptorToken(constructorOrInterceptor)) + ? JS('', '#.constructor', constructorOrInterceptor) + : constructorOrInterceptor; + var descriptor = JS('', '#["@"]', constructor); + var fields; + var fieldsMetadata; + if (descriptor == null) { + // This is a native class, or an intercepted class. + // TODO(ahe): Preserve descriptor for such classes. + } else { + fields = JS('', '#[#]', descriptor, + JS_GET_NAME('CLASS_DESCRIPTOR_PROPERTY')); + if (fields is List) { + fieldsMetadata = fields.getRange(1, fields.length).toList(); + fields = fields[0]; + } + if (fields is! String) { + // TODO(ahe): This is CSP mode. Find a way to determine the + // fields of this class. + fields = ''; + } + } + + var superclassName = fields.split(';')[0]; + var mixins = superclassName.split('+'); + if (mixins.length > 1 && mangledGlobalNames[mangledName] == null) { + mirror = reflectMixinApplication(mixins, mangledName); + } else { + ClassMirror classMirror = new JsClassMirror( + symbol, mangledName, constructorOrInterceptor, fields, fieldsMetadata); + List typeVariables = + JS('JSExtendableArray|Null', '#.prototype["<>"]', constructor); + if (typeVariables == null || typeVariables.length == 0) { + mirror = classMirror; + } else { + String typeArguments = 'dynamic'; + for (int i = 1; i < typeVariables.length; i++) { + typeArguments += ',dynamic'; + } + mirror = new JsTypeBoundClassMirror(classMirror, typeArguments); + } + } + + JsCache.update(classMirrors, mangledName, mirror); + return mirror; +} + +Map filterMethods(List methods) { + var result = new Map(); + for (JsMethodMirror method in methods) { + if (!method.isConstructor && !method.isGetter && !method.isSetter) { + result[method.simpleName] = method; + } + } + return result; +} + +Map filterConstructors(methods) { + var result = new Map(); + for (JsMethodMirror method in methods) { + if (method.isConstructor) { + result[method.simpleName] = method; + } + } + return result; +} + +Map filterGetters(List methods, + Map fields) { + var result = new Map(); + for (JsMethodMirror method in methods) { + if (method.isGetter) { + + // TODO(ahe): This is a hack to remove getters corresponding to a field. + if (fields[method.simpleName] != null) continue; + + result[method.simpleName] = method; + } + } + return result; +} + +Map filterSetters(List methods, + Map fields) { + var result = new Map(); + for (JsMethodMirror method in methods) { + if (method.isSetter) { + + // TODO(ahe): This is a hack to remove setters corresponding to a field. + String name = n(method.simpleName); + name = name.substring(0, name.length - 1); // Remove '='. + if (fields[s(name)] != null) continue; + + result[method.simpleName] = method; + } + } + return result; +} + +Map filterMembers(List methods, + Map variables) { + Map result = new Map.from(variables); + for (JsMethodMirror method in methods) { + if (method.isSetter) { + String name = n(method.simpleName); + name = name.substring(0, name.length - 1); + // Filter-out setters corresponding to variables. + if (result[s(name)] is VariableMirror) continue; + } + // Constructors aren't 'members'. + if (method.isConstructor) continue; + // Use putIfAbsent to filter-out getters corresponding to variables. + result.putIfAbsent(method.simpleName, () => method); + } + return result; +} + +int counter = 0; + +ClassMirror reflectMixinApplication(mixinNames, String mangledName) { + disableTreeShaking(); + var mixins = []; + for (String mangledName in mixinNames) { + mixins.add(reflectClassByMangledName(mangledName)); + } + var it = mixins.iterator; + it.moveNext(); + var superclass = it.current; + while (it.moveNext()) { + superclass = new JsMixinApplication(superclass, it.current, mangledName); + } + return superclass; +} + +class JsMixinApplication extends JsTypeMirror with JsObjectMirror + implements ClassMirror { + final ClassMirror superclass; + final ClassMirror mixin; + Symbol _cachedSimpleName; + Map _cachedInstanceMembers; + + JsMixinApplication(ClassMirror superclass, ClassMirror mixin, + String mangledName) + : this.superclass = superclass, + this.mixin = mixin, + super(s(mangledName)); + + String get _prettyName => 'ClassMirror'; + + Symbol get simpleName { + if (_cachedSimpleName != null) return _cachedSimpleName; + String superName = n(superclass.qualifiedName); + return _cachedSimpleName = (superName.contains(' with ')) + ? s('$superName, ${n(mixin.qualifiedName)}') + : s('$superName with ${n(mixin.qualifiedName)}'); + } + + Symbol get qualifiedName => simpleName; + + // TODO(ahe): Remove this method, only here to silence warning. + get _mixin => mixin; + + Map get __members => _mixin.__members; + + Map get __methods => _mixin.__methods; + + Map get __getters => _mixin.__getters; + + Map get __setters => _mixin.__setters; + + Map get __variables => _mixin.__variables; + + Map get declarations => mixin.declarations; + + Map get instanceMembers { + if (_cachedInstanceMembers == null) { + var result = new Map(); + if (superclass != null) { + result.addAll(superclass.instanceMembers); + } + result.addAll(mixin.instanceMembers); + _cachedInstanceMembers = result; + } + return _cachedInstanceMembers; + } + + Map get staticMembers => mixin.staticMembers; + + _asRuntimeType() => null; + + InstanceMirror invoke( + Symbol memberName, + List positionalArguments, + [Map namedArguments]) { + // TODO(ahe): What receiver to use? + throw new NoSuchMethodError(this, memberName, + positionalArguments, namedArguments); + } + + InstanceMirror getField(Symbol fieldName) { + // TODO(ahe): What receiver to use? + throw new NoSuchMethodError(this, fieldName, null, null); + } + + InstanceMirror setField(Symbol fieldName, Object arg) { + // TODO(ahe): What receiver to use? + throw new NoSuchMethodError(this, setterSymbol(fieldName), [arg], null); + } + + List get superinterfaces => [mixin]; + + Map get __constructors => _mixin.__constructors; + + InstanceMirror newInstance( + Symbol constructorName, + List positionalArguments, + [Map namedArguments]) { + throw new UnsupportedError( + "Can't instantiate mixin application '${n(qualifiedName)}'"); + } + + bool get isOriginalDeclaration => true; + + ClassMirror get originalDeclaration => this; + + // TODO(ahe): Implement this. + List get typeVariables { + throw new UnimplementedError(); + } + + List get typeArguments => const []; + + bool get isAbstract => throw new UnimplementedError(); + + bool isSubclassOf(ClassMirror other) { + superclass.isSubclassOf(other) || mixin.isSubclassOf(other); + } + + bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); + + bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); +} + +abstract class JsObjectMirror implements ObjectMirror { +} + +class JsInstanceMirror extends JsObjectMirror implements InstanceMirror { + final reflectee; + + JsInstanceMirror(this.reflectee); + + bool get hasReflectee => true; + + ClassMirror get type => reflectType(reflectee.runtimeType); + + InstanceMirror invoke(Symbol memberName, + List positionalArguments, + [Map namedArguments]) { + String name = n(memberName); + String reflectiveName; + if (namedArguments != null && !namedArguments.isEmpty) { + var interceptor = getInterceptor(reflectee); + + var jsFunction = JS('', '#[# + "*"]', interceptor, name); + if (jsFunction == null) { + // TODO(ahe): Invoke noSuchMethod. + throw new UnimplementedNoSuchMethodError( + 'Invoking noSuchMethod with named arguments not implemented'); + } + ReflectionInfo info = new ReflectionInfo(jsFunction); + if (jsFunction == null) { + // TODO(ahe): Invoke noSuchMethod. + throw new UnimplementedNoSuchMethodError( + 'Invoking noSuchMethod with named arguments not implemented'); + } + + positionalArguments = new List.from(positionalArguments); + // Check the number of positional arguments is valid. + if (info.requiredParameterCount != positionalArguments.length) { + // TODO(ahe): Invoke noSuchMethod. + throw new UnimplementedNoSuchMethodError( + 'Invoking noSuchMethod with named arguments not implemented'); + } + var defaultArguments = new Map(); + for (int i = 0; i < info.optionalParameterCount; i++) { + var parameterName = info.parameterName(i + info.requiredParameterCount); + var defaultValue = + getMetadata(info.defaultValue(i + info.requiredParameterCount)); + defaultArguments[parameterName] = defaultValue; + } + namedArguments.forEach((Symbol symbol, value) { + String parameter = n(symbol); + if (defaultArguments.containsKey(parameter)) { + defaultArguments[parameter] = value; + } else { + // Extraneous named argument. + // TODO(ahe): Invoke noSuchMethod. + throw new UnimplementedNoSuchMethodError( + 'Invoking noSuchMethod with named arguments not implemented'); + } + }); + positionalArguments.addAll(defaultArguments.values); + // TODO(ahe): Handle intercepted methods. + return reflect( + JS('', '#.apply(#, #)', jsFunction, reflectee, positionalArguments)); + } else { + reflectiveName = + JS('String', '# + ":" + # + ":0"', name, positionalArguments.length); + } + // We can safely pass positionalArguments to _invoke as it will wrap it in + // a JSArray if needed. + return _invoke(memberName, JSInvocationMirror.METHOD, reflectiveName, + positionalArguments); + } + + /// Grabs hold of the class-specific invocation cache for the reflectee. + /// All reflectees with the same class share the same cache. The cache + /// maps reflective names to cached invocation objects with enough decoded + /// reflective information to know how to to invoke a specific member. + get _classInvocationCache { + String cacheName = Primitives.mirrorInvokeCacheName; + var cacheHolder = (reflectee == null) ? getInterceptor(null) : reflectee; + var cache = JS('', r'#.constructor[#]', cacheHolder, cacheName); + if (cache == null) { + cache = JsCache.allocate(); + JS('void', r'#.constructor[#] = #', cacheHolder, cacheName, cache); + } + return cache; + } + + /// Invoke the member specified through name and type on the reflectee. + /// As a side-effect, this populates the class-specific invocation cache + /// for the reflectee. + InstanceMirror _invoke(Symbol name, + int type, + String reflectiveName, + List arguments) { + var cache = _classInvocationCache; + var cacheEntry = JsCache.fetch(cache, reflectiveName); + var result; + Invocation invocation; + if (cacheEntry == null) { + disableTreeShaking(); + String mangledName = reflectiveNames[reflectiveName]; + List argumentNames = const []; + if (type == JSInvocationMirror.METHOD) { + // Note: [argumentNames] are not what the user actually provided, it is + // always all the named parameters. + argumentNames = reflectiveName.split(':').skip(3).toList(); + } + + // TODO(ahe): We don't need to create an invocation mirror here. The + // logic from JSInvocationMirror.getCachedInvocation could easily be + // inlined here. + invocation = createUnmangledInvocationMirror( + name, mangledName, type, arguments, argumentNames); + + cacheEntry = + JSInvocationMirror.getCachedInvocation(invocation, reflectee); + JsCache.update(cache, reflectiveName, cacheEntry); + } + if (cacheEntry.isNoSuchMethod) { + if (invocation == null) { + String mangledName = reflectiveNames[reflectiveName]; + // TODO(ahe): Get the argument names. + List argumentNames = []; + invocation = createUnmangledInvocationMirror( + name, mangledName, type, arguments, argumentNames); + } + return reflect(cacheEntry.invokeOn(reflectee, invocation)); + } else { + return reflect(cacheEntry.invokeOn(reflectee, arguments)); + } + } + + InstanceMirror setField(Symbol fieldName, Object arg) { + String reflectiveName = '${n(fieldName)}='; + _invoke( + s(reflectiveName), JSInvocationMirror.SETTER, reflectiveName, [arg]); + return reflect(arg); + } + + // JS helpers for getField optimizations. + static bool isUndefined(x) + => JS('bool', 'typeof # == "undefined"', x); + static bool isMissingCache(x) + => JS('bool', 'typeof # == "number"', x); + static bool isMissingProbe(Symbol symbol) + => JS('bool', 'typeof #.\$p == "undefined"', symbol); + static bool isEvalAllowed() + => JS('bool', 'typeof dart_precompiled != "function"'); + + + /// The getter cache is lazily allocated after a couple + /// of invocations of [InstanceMirror.getField]. The delay is + /// used to avoid too aggressive caching and dynamic function + /// generation for rarely used mirrors. The cache is specific to + /// this [InstanceMirror] and maps reflective names to functions + /// that will invoke the corresponding getter on the reflectee. + /// The reflectee is passed to the function as the first argument + /// to avoid the overhead of fetching it from this mirror repeatedly. + /// The cache is lazily initialized to a JS object so we can + /// benefit from "map transitions" in the underlying JavaScript + /// engine to speed up cache probing. + var _getterCache = 4; + + InstanceMirror getField(Symbol fieldName) { + // BUG(16400): This should be a labelled block, but that makes + // dart2js crash when merging locals information in the type + // inferencing implementation. + do { + var cache = _getterCache; + if (isMissingCache(cache) || isMissingProbe(fieldName)) break; + // If the [fieldName] has an associated probe function, we can use + // it to read from the getter cache specific to this [InstanceMirror]. + var getter = JS('', '#.\$p(#)', fieldName, cache); + if (isUndefined(getter)) break; + // Call the getter passing the reflectee as the first argument. + var value = JS('', '#(#)', getter, reflectee); + // The getter has an associate cache of the last [InstanceMirror] + // returned to avoid repeated invocations of [reflect]. To validate + // the cache, we check that the value returned by the getter is the + // same value as last time. + if (JS('bool', '# === #.v', value, getter)) { + return JS('InstanceMirror', '#.m', getter); + } else { + var result = reflect(value); + JS('void', '#.v = #', getter, value); + JS('void', '#.m = #', getter, result); + return result; + } + } while (false); + return _getFieldSlow(fieldName); + } + + InstanceMirror _getFieldSlow(Symbol fieldName) { + // First do the slow-case getter invocation. As a side-effect of this, + // the invocation cache is filled in so we can query it afterwards. + String name = n(fieldName); + var result = _invoke(fieldName, JSInvocationMirror.GETTER, name, const []); + var cacheEntry = JsCache.fetch(_classInvocationCache, name); + if (cacheEntry.isNoSuchMethod) { + return result; + } + + // Make sure we have a getter cache in this [InstanceMirror]. + var cache = _getterCache; + if (isMissingCache(cache)) { + if ((_getterCache = --cache) != 0) return result; + cache = _getterCache = JS('=Object', '({})'); + } + + // Make sure that symbol [fieldName] has a cache probing function ($p). + bool useEval = isEvalAllowed(); + if (isMissingProbe(fieldName)) { + var probe = _newProbeFn(name, useEval); + JS('void', '#.\$p = #', fieldName, probe); + } + + // Create a new getter function and install it in the cache. + var mangledName = cacheEntry.mangledName; + var getter = (cacheEntry.isIntercepted) + ? _newInterceptedGetterFn(mangledName, useEval) + : _newGetterFn(mangledName, useEval); + JS('void', '#[#] = #', cache, name, getter); + + // Initialize the last value (v) and last mirror (m) on the + // newly generated getter to be a sentinel value that is hard + // to get hold of through user code. + JS('void', '#.v = #.m = #', getter, getter, cache); + + // Return the result of the slow-path getter invocation. + return result; + } + + _newProbeFn(String id, bool useEval) { + if (useEval) { + // We give the probe function a name to make it appear nicely in + // profiles and when debugging. The name also makes the source code + // for the function more "unique" so the underlying JavaScript + // engine is less likely to re-use an existing piece of generated + // code as the result of calling eval. In return, this leads to + // less polymorphic access in the probe function. + var body = "(function probe\$$id(c){return c.$id})"; + return JS('', '(function(b){return eval(b)})(#)', body); + } else { + return JS('', '(function(n){return(function(c){return c[n]})})(#)', id); + } + } + + _newGetterFn(String name, bool useEval) { + if (!useEval) return _newGetterNoEvalFn(name); + // We give the getter function a name that associates it with the + // class of the reflectee. This makes it easier to spot in profiles + // and when debugging, but it also means that the underlying JavaScript + // engine will only share the generated code for accessors on the + // same class (through caching of eval'ed code). This makes the + // generated call to the getter - e.g. o.get$foo() - much more likely + // to be monomorphic and inlineable. + String className = JS('String', '#.constructor.name', reflectee); + var body = "(function $className\$$name(o){return o.$name()})"; + return JS('', '(function(b){return eval(b)})(#)', body); + } + + _newGetterNoEvalFn(n) => JS('', + '(function(n){return(function(o){return o[n]()})})(#)', n); + + _newInterceptedGetterFn(String name, bool useEval) { + var object = reflectee; + // It is possible that the interceptor for a given object is the object + // itself, so it is important not to share the code that captures the + // interceptor between multiple different instances of [InstanceMirror]. + var interceptor = getInterceptor(object); + if (!useEval) return _newInterceptGetterNoEvalFn(name, interceptor); + String className = JS('String', '#.constructor.name', interceptor); + var body = "(function $className\$$name(o){return i.$name(o)})"; + return JS('', '(function(b,i){return eval(b)})(#,#)', body, interceptor); + } + + _newInterceptGetterNoEvalFn(n, i) => JS('', + '(function(n,i){return(function(o){return i[n](o)})})(#,#)', n, i); + + delegate(Invocation invocation) { + return JSInvocationMirror.invokeFromMirror(invocation, reflectee); + } + + operator ==(other) { + return other is JsInstanceMirror && + identical(reflectee, other.reflectee); + } + + int get hashCode { + // Avoid hash collisions with the reflectee. This constant is in Smi range + // and happens to be the inner padding from RFC 2104. + return identityHashCode(reflectee) ^ 0x36363636; + } + + String toString() => 'InstanceMirror on ${Error.safeToString(reflectee)}'; + + // TODO(ahe): Remove this method from the API. + MirrorSystem get mirrors => currentJsMirrorSystem; +} + +/** + * ClassMirror for generic classes where the type parameters are bound. + * + * [typeArguments] will return a list of the type arguments, in constrast + * to JsCLassMirror that returns an empty list since it represents original + * declarations and classes that are not generic. + */ +class JsTypeBoundClassMirror extends JsDeclarationMirror + implements ClassMirror { + final JsClassMirror _class; + + /** + * When instantiated this field will hold a string representing the list of + * type arguments for the class, i.e. what is inside the outermost angle + * brackets. Then, when get typeArguments is called the first time, the string + * is parsed into the actual list of TypeMirrors, and stored in + * [_cachedTypeArguments]. Due to type substitution of, for instance, + * superclasses the mangled name of the class and hence this string is needed + * after [_cachedTypeArguments] has been computed. + * + * If an integer is encountered as a type argument, it represents the type + * variable at the corresponding entry in [emitter.globalMetadata]. + */ + String _typeArguments; + + UnmodifiableListView _cachedTypeArguments; + UnmodifiableMapView _cachedDeclarations; + UnmodifiableMapView _cachedMembers; + UnmodifiableMapView _cachedConstructors; + Map _cachedVariables; + Map _cachedGetters; + Map _cachedSetters; + Map _cachedMethodsMap; + List _cachedMethods; + ClassMirror _superclass; + List _cachedSuperinterfaces; + Map _cachedInstanceMembers; + Map _cachedStaticMembers; + + JsTypeBoundClassMirror(JsClassMirror originalDeclaration, this._typeArguments) + : _class = originalDeclaration, + super(originalDeclaration.simpleName); + + String get _prettyName => 'ClassMirror'; + + String toString() { + String result = '$_prettyName on ${n(simpleName)}'; + if (typeArguments != null) { + result = "$result<${typeArguments.join(', ')}>"; + } + return result; + } + + String get _mangledName { + for (TypeMirror typeArgument in typeArguments) { + if (typeArgument != JsMirrorSystem._dynamicType) { + return '${_class._mangledName}<$_typeArguments>'; + } + } + // When all type arguments are dynamic, the canonical representation is to + // drop them. + return _class._mangledName; + } + + List get typeVariables => _class.typeVariables; + + List get typeArguments { + if (_cachedTypeArguments != null) return _cachedTypeArguments; + List result = new List(); + + addTypeArgument(String typeArgument) { + int parsedIndex = int.parse(typeArgument, onError: (_) => -1); + if (parsedIndex == -1) { + result.add(reflectClassByMangledName(typeArgument.trim())); + } else { + TypeVariable typeVariable = getMetadata(parsedIndex); + TypeMirror owner = reflectClass(typeVariable.owner); + TypeVariableMirror typeMirror = + new JsTypeVariableMirror(typeVariable, owner, parsedIndex); + result.add(typeMirror); + } + } + + if (_typeArguments.indexOf('<') == -1) { + _typeArguments.split(',').forEach((t) => addTypeArgument(t)); + } else { + int level = 0; + String currentTypeArgument = ''; + + for (int i = 0; i < _typeArguments.length; i++) { + var character = _typeArguments[i]; + if (character == ' ') { + continue; + } else if (character == '<') { + currentTypeArgument += character; + level++; + } else if (character == '>') { + currentTypeArgument += character; + level--; + } else if (character == ',') { + if (level > 0) { + currentTypeArgument += character; + } else { + addTypeArgument(currentTypeArgument); + currentTypeArgument = ''; + } + } else { + currentTypeArgument += character; + } + } + addTypeArgument(currentTypeArgument); + } + return _cachedTypeArguments = new UnmodifiableListView(result); + } + + List get _methods { + if (_cachedMethods != null) return _cachedMethods; + return _cachedMethods =_class._getMethodsWithOwner(this); + } + + Map get __methods { + if (_cachedMethodsMap != null) return _cachedMethodsMap; + return _cachedMethodsMap = new UnmodifiableMapView( + filterMethods(_methods)); + } + + Map get __constructors { + if (_cachedConstructors != null) return _cachedConstructors; + return _cachedConstructors = + new UnmodifiableMapView( + filterConstructors(_methods)); + } + + Map get __getters { + if (_cachedGetters != null) return _cachedGetters; + return _cachedGetters = new UnmodifiableMapView( + filterGetters(_methods, __variables)); + } + + Map get __setters { + if (_cachedSetters != null) return _cachedSetters; + return _cachedSetters = new UnmodifiableMapView( + filterSetters(_methods, __variables)); + } + + Map get __variables { + if (_cachedVariables != null) return _cachedVariables; + var result = new Map(); + for (JsVariableMirror mirror in _class._getFieldsWithOwner(this)) { + result[mirror.simpleName] = mirror; + } + return _cachedVariables = + new UnmodifiableMapView(result); + } + + Map get __members { + if (_cachedMembers != null) return _cachedMembers; + return _cachedMembers = new UnmodifiableMapView( + filterMembers(_methods, __variables)); + } + + Map get declarations { + if (_cachedDeclarations != null) return _cachedDeclarations; + Map result = + new Map(); + result.addAll(__members); + result.addAll(__constructors); + typeVariables.forEach((tv) => result[tv.simpleName] = tv); + return _cachedDeclarations = + new UnmodifiableMapView(result); + } + + Map get staticMembers { + if (_cachedStaticMembers == null) { + var result = new Map(); + declarations.values.forEach((decl) { + if (decl is MethodMirror && decl.isStatic && !decl.isConstructor) { + result[decl.simpleName] = decl; + } + if (decl is VariableMirror && decl.isStatic) { + var getterName = decl.simpleName; + result[getterName] = new JsSyntheticAccessor( + this, getterName, true, true, false, decl); + if (!decl.isFinal) { + var setterName = setterSymbol(decl.simpleName); + result[setterName] = new JsSyntheticAccessor( + this, setterName, false, true, false, decl); + } + } + }); + _cachedStaticMembers = result; + } + return _cachedStaticMembers; + } + + Map get instanceMembers { + if (_cachedInstanceMembers == null) { + var result = new Map(); + if (superclass != null) { + result.addAll(superclass.instanceMembers); + } + declarations.values.forEach((decl) { + if (decl is MethodMirror && !decl.isStatic && + !decl.isConstructor && !decl.isAbstract) { + result[decl.simpleName] = decl; + } + if (decl is VariableMirror && !decl.isStatic) { + var getterName = decl.simpleName; + result[getterName] = new JsSyntheticAccessor( + this, getterName, true, false, false, decl); + if (!decl.isFinal) { + var setterName = setterSymbol(decl.simpleName); + result[setterName] = new JsSyntheticAccessor( + this, setterName, false, false, false, decl); + } + } + }); + _cachedInstanceMembers = result; + } + return _cachedInstanceMembers; + } + + InstanceMirror setField(Symbol fieldName, Object arg) { + return _class.setField(fieldName, arg); + } + + InstanceMirror getField(Symbol fieldName) => _class.getField(fieldName); + + InstanceMirror newInstance(Symbol constructorName, + List positionalArguments, + [Map namedArguments]) { + var instance = _class._getInvokedInstance(constructorName, + positionalArguments, + namedArguments); + return reflect(setRuntimeTypeInfo( + instance, typeArguments.map((t) => t._asRuntimeType()).toList())); + } + + _asRuntimeType() { + return [_class._jsConstructor].addAll( + typeArguments.map((t) => t._asRuntimeType())); + } + + JsLibraryMirror get owner => _class.owner; + + List get metadata => _class.metadata; + + ClassMirror get superclass { + if (_superclass != null) return _superclass; + + List typeInformation = + JS('List|Null', 'init.typeInformation[#]', _class._mangledName); + assert(typeInformation != null); + var type = getMetadata(typeInformation[0]); + return _superclass = typeMirrorFromRuntimeTypeRepresentation(this, type); + } + + InstanceMirror invoke(Symbol memberName, + List positionalArguments, + [Map namedArguments]) { + return _class.invoke(memberName, positionalArguments, namedArguments); + } + + bool get isOriginalDeclaration => false; + + ClassMirror get originalDeclaration => _class; + + List get superinterfaces { + if (_cachedSuperinterfaces != null) return _cachedSuperinterfaces; + return _cachedSuperinterfaces = _class._getSuperinterfacesWithOwner(this); + } + + bool get isPrivate => _class.isPrivate; + + bool get isTopLevel => _class.isTopLevel; + + bool get isAbstract => _class.isAbstract; + + bool isSubclassOf(ClassMirror other) => _class.isSubclassOf(other); + + SourceLocation get location => _class.location; + + MirrorSystem get mirrors => _class.mirrors; + + Symbol get qualifiedName => _class.qualifiedName; + + bool get hasReflectedType => true; + + Type get reflectedType => createRuntimeType(_mangledName); + + Symbol get simpleName => _class.simpleName; + + // TODO(ahe): Implement this. + ClassMirror get mixin => throw new UnimplementedError(); + + bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); + + bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); +} + +class JsSyntheticAccessor implements MethodMirror { + final DeclarationMirror owner; + final Symbol simpleName; + final bool isGetter; + final bool isStatic; + final bool isTopLevel; + final _target; /// The field or type that introduces the synthetic accessor. + + JsSyntheticAccessor(this.owner, + this.simpleName, + this.isGetter, + this.isStatic, + this.isTopLevel, + this._target); + + bool get isSynthetic => true; + bool get isRegularMethod => false; + bool get isOperator => false; + bool get isConstructor => false; + bool get isConstConstructor => false; + bool get isGenerativeConstructor => false; + bool get isFactoryConstructor => false; + bool get isRedirectingConstructor => false; + bool get isAbstract => false; + + bool get isSetter => !isGetter; + bool get isPrivate => n(simpleName).startsWith('_'); + + Symbol get qualifiedName => computeQualifiedName(owner, simpleName); + Symbol get constructorName => const Symbol(''); + + TypeMirror get returnType => _target.type; + List get parameters { + if (isGetter) return const []; + return new UnmodifiableListView( + [new JsSyntheticSetterParameter(this, this._target)]); + } + + List get metadata => const []; + String get source => null; + SourceLocation get location => throw new UnimplementedError(); +} + +class JsSyntheticSetterParameter implements ParameterMirror { + final DeclarationMirror owner; + final VariableMirror _target; + + JsSyntheticSetterParameter(this.owner, this._target); + + Symbol get simpleName => _target.simpleName; + Symbol get qualifiedName => computeQualifiedName(owner, simpleName); + TypeMirror get type => _target.type; + + bool get isOptional => false; + bool get isNamed => false; + bool get isStatic => false; + bool get isTopLevel => false; + bool get isConst => false; + bool get isFinal => true; + bool get isPrivate => false; + bool get hasDefaultValue => false; + InstanceMirror get defaultValue => null; + List get metadata => const []; + SourceLocation get location => throw new UnimplementedError(); +} + +class JsClassMirror extends JsTypeMirror with JsObjectMirror + implements ClassMirror { + final String _mangledName; + final _jsConstructorOrInterceptor; + final String _fieldsDescriptor; + final List _fieldsMetadata; + final _jsConstructorCache = JsCache.allocate(); + List _metadata; + ClassMirror _superclass; + List _cachedMethods; + List _cachedFields; + UnmodifiableMapView _cachedConstructors; + UnmodifiableMapView _cachedMethodsMap; + UnmodifiableMapView _cachedGetters; + UnmodifiableMapView _cachedSetters; + UnmodifiableMapView _cachedVariables; + UnmodifiableMapView _cachedMembers; + UnmodifiableMapView _cachedDeclarations; + UnmodifiableListView _cachedMetadata; + UnmodifiableListView _cachedSuperinterfaces; + UnmodifiableListView _cachedTypeVariables; + Map _cachedInstanceMembers; + Map _cachedStaticMembers; + + // Set as side-effect of accessing JsLibraryMirror.classes. + JsLibraryMirror _owner; + + JsClassMirror(Symbol simpleName, + this._mangledName, + this._jsConstructorOrInterceptor, + this._fieldsDescriptor, + this._fieldsMetadata) + : super(simpleName); + + String get _prettyName => 'ClassMirror'; + + get _jsConstructor { + if (Primitives.isInterceptorToken(_jsConstructorOrInterceptor)) { + return JS('', '#.constructor', _jsConstructorOrInterceptor); + } else { + return _jsConstructorOrInterceptor; + } + } + + Map get __constructors { + if (_cachedConstructors != null) return _cachedConstructors; + return _cachedConstructors = + new UnmodifiableMapView( + filterConstructors(_methods)); + } + + _asRuntimeType() { + if (typeVariables.isEmpty) return _jsConstructor; + var type = [_jsConstructor]; + for (int i = 0; i < typeVariables.length; i ++) { + type.add(JsMirrorSystem._dynamicType._asRuntimeType); + } + return type; + } + + List _getMethodsWithOwner(DeclarationMirror methodOwner) { + var prototype = JS('', '#.prototype', _jsConstructor); + List keys = extractKeys(prototype); + var result = []; + for (String key in keys) { + if (isReflectiveDataInPrototype(key)) continue; + String simpleName = mangledNames[key]; + // [simpleName] can be null if [key] represents an implementation + // detail, for example, a bailout method, or runtime type support. + // It might also be null if the user has limited what is reified for + // reflection with metadata. + if (simpleName == null) continue; + var function = JS('', '#[#]', prototype, key); + if (isNoSuchMethodStub(function)) continue; + var mirror = + new JsMethodMirror.fromUnmangledName( + simpleName, function, false, false); + result.add(mirror); + mirror._owner = methodOwner; + } + + keys = extractKeys(JS('', 'init.statics[#]', _mangledName)); + for (String mangledName in keys) { + if (isReflectiveDataInPrototype(mangledName)) continue; + String unmangledName = mangledName; + var jsFunction = JS('', '#[#]', owner._globalObject, mangledName); + + bool isConstructor = false; + if (hasReflectableProperty(jsFunction)) { + String reflectionName = + JS('String|Null', r'#.$reflectionName', jsFunction); + if (reflectionName == null) continue; + isConstructor = reflectionName.startsWith('new '); + if (isConstructor) { + reflectionName = reflectionName.substring(4).replaceAll(r'$', '.'); + } + unmangledName = reflectionName; + } else { + continue; + } + bool isStatic = !isConstructor; // Constructors are not static. + JsMethodMirror mirror = + new JsMethodMirror.fromUnmangledName( + unmangledName, jsFunction, isStatic, isConstructor); + result.add(mirror); + mirror._owner = methodOwner; + } + + return result; + } + + List get _methods { + if (_cachedMethods != null) return _cachedMethods; + return _cachedMethods = _getMethodsWithOwner(this); + } + + List _getFieldsWithOwner(DeclarationMirror fieldOwner) { + var result = []; + + var instanceFieldSpecfication = _fieldsDescriptor.split(';')[1]; + if (_fieldsMetadata != null) { + instanceFieldSpecfication = + [instanceFieldSpecfication]..addAll(_fieldsMetadata); + } + parseCompactFieldSpecification( + fieldOwner, instanceFieldSpecfication, false, result); + + var staticDescriptor = JS('', 'init.statics[#]', _mangledName); + if (staticDescriptor != null) { + parseCompactFieldSpecification( + fieldOwner, + JS('', '#[#]', + staticDescriptor, JS_GET_NAME('CLASS_DESCRIPTOR_PROPERTY')), + true, result); + } + return result; + } + + List get _fields { + if (_cachedFields != null) return _cachedFields; + return _cachedFields = _getFieldsWithOwner(this); + } + + Map get __methods { + if (_cachedMethodsMap != null) return _cachedMethodsMap; + return _cachedMethodsMap = + new UnmodifiableMapView(filterMethods(_methods)); + } + + Map get __getters { + if (_cachedGetters != null) return _cachedGetters; + return _cachedGetters = new UnmodifiableMapView( + filterGetters(_methods, __variables)); + } + + Map get __setters { + if (_cachedSetters != null) return _cachedSetters; + return _cachedSetters = new UnmodifiableMapView( + filterSetters(_methods, __variables)); + } + + Map get __variables { + if (_cachedVariables != null) return _cachedVariables; + var result = new Map(); + for (JsVariableMirror mirror in _fields) { + result[mirror.simpleName] = mirror; + } + return _cachedVariables = + new UnmodifiableMapView(result); + } + + Map get __members { + if (_cachedMembers != null) return _cachedMembers; + return _cachedMembers = new UnmodifiableMapView( + filterMembers(_methods, __variables)); + } + + Map get declarations { + if (_cachedDeclarations != null) return _cachedDeclarations; + var result = new Map(); + addToResult(Symbol key, Mirror value) { + result[key] = value; + } + __members.forEach(addToResult); + __constructors.forEach(addToResult); + typeVariables.forEach((tv) => result[tv.simpleName] = tv); + return _cachedDeclarations = + new UnmodifiableMapView(result); + } + + Map get staticMembers { + if (_cachedStaticMembers == null) { + var result = new Map(); + declarations.values.forEach((decl) { + if (decl is MethodMirror && decl.isStatic && !decl.isConstructor) { + result[decl.simpleName] = decl; + } + if (decl is VariableMirror && decl.isStatic) { + var getterName = decl.simpleName; + result[getterName] = new JsSyntheticAccessor( + this, getterName, true, true, false, decl); + if (!decl.isFinal) { + var setterName = setterSymbol(decl.simpleName); + result[setterName] = new JsSyntheticAccessor( + this, setterName, false, true, false, decl); + } + } + }); + _cachedStaticMembers = result; + } + return _cachedStaticMembers; + } + + Map get instanceMembers { + if (_cachedInstanceMembers == null) { + var result = new Map(); + if (superclass != null) { + result.addAll(superclass.instanceMembers); + } + declarations.values.forEach((decl) { + if (decl is MethodMirror && !decl.isStatic && + !decl.isConstructor && !decl.isAbstract) { + result[decl.simpleName] = decl; + } + if (decl is VariableMirror && !decl.isStatic) { + var getterName = decl.simpleName; + result[getterName] = new JsSyntheticAccessor( + this, getterName, true, false, false, decl); + if (!decl.isFinal) { + var setterName = setterSymbol(decl.simpleName); + result[setterName] = new JsSyntheticAccessor( + this, setterName, false, false, false, decl); + } + } + }); + _cachedInstanceMembers = result; + } + return _cachedInstanceMembers; + } + + InstanceMirror setField(Symbol fieldName, Object arg) { + JsVariableMirror mirror = __variables[fieldName]; + if (mirror != null && mirror.isStatic && !mirror.isFinal) { + // '$' (JS_CURRENT_ISOLATE()) stores state which is stored directly, so + // we shouldn't use [JsLibraryMirror._globalObject] here. + String jsName = mirror._jsName; + if (!JS('bool', '# in #', jsName, JS_CURRENT_ISOLATE())) { + throw new RuntimeError('Cannot find "$jsName" in current isolate.'); + } + JS('void', '#[#] = #', JS_CURRENT_ISOLATE(), jsName, arg); + return reflect(arg); + } + // TODO(ahe): What receiver to use? + throw new NoSuchMethodError(this, setterSymbol(fieldName), [arg], null); + } + + InstanceMirror getField(Symbol fieldName) { + JsVariableMirror mirror = __variables[fieldName]; + if (mirror != null && mirror.isStatic) { + String jsName = mirror._jsName; + // '$' (JS_CURRENT_ISOLATE()) stores state which is read directly, so + // we shouldn't use [JsLibraryMirror._globalObject] here. + if (!JS('bool', '# in #', jsName, JS_CURRENT_ISOLATE())) { + throw new RuntimeError('Cannot find "$jsName" in current isolate.'); + } + if (JS('bool', '# in init.lazies', jsName)) { + String getterName = JS('String', 'init.lazies[#]', jsName); + return reflect(JS('', '#[#]()', JS_CURRENT_ISOLATE(), getterName)); + } else { + return reflect(JS('', '#[#]', JS_CURRENT_ISOLATE(), jsName)); + } + } + // TODO(ahe): What receiver to use? + throw new NoSuchMethodError(this, fieldName, null, null); + } + + _getInvokedInstance(Symbol constructorName, + List positionalArguments, + [Map namedArguments]) { + if (namedArguments != null && !namedArguments.isEmpty) { + throw new UnsupportedError('Named arguments are not implemented.'); + } + JsMethodMirror mirror = + JsCache.fetch(_jsConstructorCache, n(constructorName)); + if (mirror == null) { + mirror = __constructors.values.firstWhere( + (m) => m.constructorName == constructorName, + orElse: () { + // TODO(ahe): What receiver to use? + throw new NoSuchMethodError( + this, constructorName, positionalArguments, namedArguments); + }); + JsCache.update(_jsConstructorCache, n(constructorName), mirror); + } + return mirror._invoke(positionalArguments, namedArguments); + } + + InstanceMirror newInstance(Symbol constructorName, + List positionalArguments, + [Map namedArguments]) { + return reflect(_getInvokedInstance(constructorName, + positionalArguments, + namedArguments)); + } + + JsLibraryMirror get owner { + if (_owner == null) { + if (Primitives.isInterceptorToken(_jsConstructorOrInterceptor)) { + _owner = reflectType(Object).owner; + } else { + for (var list in JsMirrorSystem.librariesByName.values) { + for (JsLibraryMirror library in list) { + // This will set _owner field on all clasess as a side + // effect. This gives us a fast path to reflect on a + // class without parsing reflection data. + library.__classes; + } + } + } + if (_owner == null) { + throw new StateError('Class "${n(simpleName)}" has no owner'); + } + } + return _owner; + } + + List get metadata { + if (_cachedMetadata != null) return _cachedMetadata; + if (_metadata == null) { + _metadata = extractMetadata(JS('', '#.prototype', _jsConstructor)); + } + return _cachedMetadata = + new UnmodifiableListView(_metadata.map(reflect)); + } + + ClassMirror get superclass { + if (_superclass == null) { + List typeInformation = + JS('List|Null', 'init.typeInformation[#]', _mangledName); + if (typeInformation != null) { + var type = getMetadata(typeInformation[0]); + _superclass = typeMirrorFromRuntimeTypeRepresentation(this, type); + } else { + var superclassName = _fieldsDescriptor.split(';')[0]; + // TODO(zarah): Remove special handing of mixins. + var mixins = superclassName.split('+'); + if (mixins.length > 1) { + if (mixins.length != 2) { + throw new RuntimeError('Strange mixin: $_fieldsDescriptor'); + } + _superclass = reflectClassByMangledName(mixins[0]); + } else { + // Use _superclass == this to represent class with no superclass + // (Object). + _superclass = (superclassName == '') + ? this : reflectClassByMangledName(superclassName); + } + } + } + return _superclass == this ? null : _superclass; + } + + InstanceMirror invoke(Symbol memberName, + List positionalArguments, + [Map namedArguments]) { + // Mirror API gotcha: Calling [invoke] on a ClassMirror means invoke a + // static method. + + if (namedArguments != null && !namedArguments.isEmpty) { + throw new UnsupportedError('Named arguments are not implemented.'); + } + JsMethodMirror mirror = __methods[memberName]; + if (mirror == null || !mirror.isStatic) { + // TODO(ahe): What receiver to use? + throw new NoSuchMethodError( + this, memberName, positionalArguments, namedArguments); + } + if (!mirror.canInvokeReflectively()) { + throwInvalidReflectionError(n(memberName)); + } + return reflect(mirror._invoke(positionalArguments, namedArguments)); + } + + bool get isOriginalDeclaration => true; + + ClassMirror get originalDeclaration => this; + + List _getSuperinterfacesWithOwner(DeclarationMirror owner) { + List typeInformation = + JS('List|Null', 'init.typeInformation[#]', _mangledName); + List result = const []; + if (typeInformation != null) { + ClassMirror lookupType(int i) { + var type = getMetadata(i); + return typeMirrorFromRuntimeTypeRepresentation(owner, type); + } + + //We skip the first since it is the supertype. + result = typeInformation.skip(1).map(lookupType).toList(); + } + + return new UnmodifiableListView(result); + } + + List get superinterfaces { + if (_cachedSuperinterfaces != null) return _cachedSuperinterfaces; + return _cachedSuperinterfaces = _getSuperinterfacesWithOwner(this); + } + + List get typeVariables { + if (_cachedTypeVariables != null) return _cachedTypeVariables; + List result = new List(); + List typeVariables = + JS('JSExtendableArray|Null', '#.prototype["<>"]', _jsConstructor); + if (typeVariables == null) return result; + for (int i = 0; i < typeVariables.length; i++) { + TypeVariable typeVariable = getMetadata(typeVariables[i]); + result.add(new JsTypeVariableMirror(typeVariable, this, + typeVariables[i])); + } + return _cachedTypeVariables = new UnmodifiableListView(result); + } + + List get typeArguments => const []; + + bool get hasReflectedType => typeVariables.length == 0; + + Type get reflectedType { + if (!hasReflectedType) { + throw new UnsupportedError( + "Declarations of generics have no reflected type"); + } + return createRuntimeType(_mangledName); + } + + // TODO(ahe): Implement this. + ClassMirror get mixin => throw new UnimplementedError(); + + bool get isAbstract => throw new UnimplementedError(); + + bool isSubclassOf(ClassMirror other) { + if (other is! ClassMirror) { + throw new ArgumentError(other); + } + if (other is JsFunctionTypeMirror) { + return false; + } if (other is JsClassMirror && + JS('bool', '# == #', other._jsConstructor, _jsConstructor)) { + return true; + } else if (superclass == null) { + return false; + } else { + return superclass.isSubclassOf(other); + } + } +} + +class JsVariableMirror extends JsDeclarationMirror implements VariableMirror { + + // TODO(ahe): The values in these fields are virtually untested. + final String _jsName; + final bool isFinal; + final bool isStatic; + final _metadataFunction; + final DeclarationMirror _owner; + final int _type; + List _metadata; + + JsVariableMirror(Symbol simpleName, + this._jsName, + this._type, + this.isFinal, + this.isStatic, + this._metadataFunction, + this._owner) + : super(simpleName); + + factory JsVariableMirror.from(String descriptor, + metadataFunction, + JsDeclarationMirror owner, + bool isStatic) { + List fieldInformation = descriptor.split('-'); + if (fieldInformation.length == 1) { + // The field is not available for reflection. + // TODO(ahe): Should return an unreflectable field. + return null; + } + + String field = fieldInformation[0]; + int length = field.length; + var code = fieldCode(field.codeUnitAt(length - 1)); + bool isFinal = false; + if (code == 0) return null; // Inherited field. + bool hasGetter = (code & 3) != 0; + bool hasSetter = (code >> 2) != 0; + isFinal = !hasSetter; + length--; + String jsName; + String accessorName = jsName = field.substring(0, length); + int divider = field.indexOf(':'); + if (divider > 0) { + accessorName = accessorName.substring(0, divider); + jsName = field.substring(divider + 1); + } + var unmangledName; + if (isStatic) { + unmangledName = mangledGlobalNames[accessorName]; + } else { + String getterPrefix = JS_GET_NAME('GETTER_PREFIX'); + unmangledName = mangledNames['$getterPrefix$accessorName']; + } + if (unmangledName == null) unmangledName = accessorName; + if (!hasSetter) { + // TODO(ahe): This is a hack to handle checked setters in checked mode. + var setterName = s('$unmangledName='); + for (JsMethodMirror method in owner._methods) { + if (method.simpleName == setterName) { + isFinal = false; + break; + } + } + } + int type = int.parse(fieldInformation[1]); + return new JsVariableMirror(s(unmangledName), + jsName, + type, + isFinal, + isStatic, + metadataFunction, + owner); + } + + String get _prettyName => 'VariableMirror'; + + TypeMirror get type { + return typeMirrorFromRuntimeTypeRepresentation(owner, getMetadata(_type)); + } + + DeclarationMirror get owner => _owner; + + List get metadata { + preserveMetadata(); + if (_metadata == null) { + _metadata = (_metadataFunction == null) + ? const [] : JS('', '#()', _metadataFunction); + } + return _metadata.map(reflect).toList(); + } + + static int fieldCode(int code) { + if (code >= 60 && code <= 64) return code - 59; + if (code >= 123 && code <= 126) return code - 117; + if (code >= 37 && code <= 43) return code - 27; + return 0; + } + + _getField(JsMirror receiver) => receiver._loadField(_jsName); + + void _setField(JsMirror receiver, Object arg) { + if (isFinal) { + throw new NoSuchMethodError(this, setterSymbol(simpleName), [arg], null); + } + receiver._storeField(_jsName, arg); + } + + // TODO(ahe): Implement this method. + bool get isConst => throw new UnimplementedError(); +} + +class JsClosureMirror extends JsInstanceMirror implements ClosureMirror { + JsClosureMirror(reflectee) + : super(reflectee); + + MethodMirror get function { + String cacheName = Primitives.mirrorFunctionCacheName; + JsMethodMirror cachedFunction; + // TODO(ahe): Restore caching. + //= JS('JsMethodMirror|Null', r'#.constructor[#]', reflectee, cacheName); + if (cachedFunction != null) return cachedFunction; + disableTreeShaking(); + // TODO(ahe): What about optional parameters (named or not). + var extractCallName = JS('', r''' +function(reflectee) { + for (var property in reflectee) { + if ("call$" == property.substring(0, 5)) return property; + } + return null; +} +'''); + String callName = JS('String|Null', '#(#)', extractCallName, reflectee); + if (callName == null) { + throw new RuntimeError('Cannot find callName on "$reflectee"'); + } + int parameterCount = int.parse(callName.split(r'$')[1]); + if (reflectee is BoundClosure) { + var target = BoundClosure.targetOf(reflectee); + var self = BoundClosure.selfOf(reflectee); + var name = mangledNames[BoundClosure.nameOf(reflectee)]; + if (name == null) { + throwInvalidReflectionError(name); + } + cachedFunction = new JsMethodMirror.fromUnmangledName( + name, target, false, false); + } else { + bool isStatic = true; // TODO(ahe): Compute isStatic correctly. + var jsFunction = JS('', '#[#]', reflectee, callName); + cachedFunction = new JsMethodMirror( + s(callName), jsFunction, parameterCount, + false, false, isStatic, false, false); + } + JS('void', r'#.constructor[#] = #', reflectee, cacheName, cachedFunction); + return cachedFunction; + } + + InstanceMirror apply(List positionalArguments, + [Map namedArguments]) { + return reflect( + Function.apply(reflectee, positionalArguments, namedArguments)); + } + + String toString() => "ClosureMirror on '${Error.safeToString(reflectee)}'"; + + // TODO(ahe): Implement this method. + String get source => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + InstanceMirror findInContext(Symbol name, {ifAbsent: null}) { + throw new UnsupportedError("ClosureMirror.findInContext not yet supported"); + } +} + +class JsMethodMirror extends JsDeclarationMirror implements MethodMirror { + final _jsFunction; + final int _parameterCount; + final bool isGetter; + final bool isSetter; + final bool isStatic; + final bool isConstructor; + final bool isOperator; + DeclarationMirror _owner; + List _metadata; + TypeMirror _returnType; + UnmodifiableListView _parameters; + + JsMethodMirror(Symbol simpleName, + this._jsFunction, + this._parameterCount, + this.isGetter, + this.isSetter, + this.isStatic, + this.isConstructor, + this.isOperator) + : super(simpleName); + + factory JsMethodMirror.fromUnmangledName(String name, + jsFunction, + bool isStatic, + bool isConstructor) { + List info = name.split(':'); + name = info[0]; + bool isOperator = isOperatorName(name); + bool isSetter = !isOperator && name.endsWith('='); + int requiredParameterCount = 0; + int optionalParameterCount = 0; + bool isGetter = false; + if (info.length == 1) { + if (isSetter) { + requiredParameterCount = 1; + } else { + isGetter = true; + requiredParameterCount = 0; + } + } else { + requiredParameterCount = int.parse(info[1]); + optionalParameterCount = int.parse(info[2]); + } + return new JsMethodMirror( + s(name), jsFunction, requiredParameterCount + optionalParameterCount, + isGetter, isSetter, isStatic, isConstructor, isOperator); + } + + String get _prettyName => 'MethodMirror'; + + List get parameters { + if (_parameters != null) return _parameters; + metadata; // Compute _parameters as a side-effect of extracting metadata. + return _parameters; + } + + bool canInvokeReflectively() { + return hasReflectableProperty(_jsFunction); + } + + DeclarationMirror get owner => _owner; + + TypeMirror get returnType { + metadata; // Compute _returnType as a side-effect of extracting metadata. + return _returnType; + } + + List get metadata { + if (_metadata == null) { + var raw = extractMetadata(_jsFunction); + var formals = new List(_parameterCount); + ReflectionInfo info = new ReflectionInfo(_jsFunction); + if (info != null) { + assert(_parameterCount + == info.requiredParameterCount + info.optionalParameterCount); + var functionType = info.functionType; + var type; + if (functionType is int) { + type = new JsFunctionTypeMirror(info.computeFunctionRti(null), this); + assert(_parameterCount == type.parameters.length); + } else if (isTopLevel) { + type = new JsFunctionTypeMirror(info.computeFunctionRti(null), owner); + } else { + TypeMirror ownerType = owner; + JsClassMirror ownerClass = ownerType.originalDeclaration; + type = new JsFunctionTypeMirror( + info.computeFunctionRti(ownerClass._jsConstructorOrInterceptor), + owner); + } + // Constructors aren't reified with their return type. + if (isConstructor) { + _returnType = owner; + } else { + _returnType = type.returnType; + } + int i = 0; + bool isNamed = info.areOptionalParametersNamed; + for (JsParameterMirror parameter in type.parameters) { + var name = info.parameterName(i); + List annotations = info.parameterMetadataAnnotations(i); + var p; + if (i < info.requiredParameterCount) { + p = new JsParameterMirror(name, this, parameter._type, + metadataList: annotations); + } else { + var defaultValue = info.defaultValue(i); + p = new JsParameterMirror( + name, this, parameter._type, metadataList: annotations, + isOptional: true, isNamed: isNamed, defaultValue: defaultValue); + } + formals[i++] = p; + } + } + _parameters = new UnmodifiableListView(formals); + _metadata = new UnmodifiableListView(raw.map(reflect)); + } + return _metadata; + } + + Symbol get constructorName { + // TODO(ahe): I believe it is more appropriate to throw an exception or + // return null. + if (!isConstructor) return const Symbol(''); + String name = n(simpleName); + int index = name.indexOf('.'); + if (index == -1) return const Symbol(''); + return s(name.substring(index + 1)); + } + + _invoke(List positionalArguments, Map namedArguments) { + if (namedArguments != null && !namedArguments.isEmpty) { + throw new UnsupportedError('Named arguments are not implemented.'); + } + if (!isStatic && !isConstructor) { + throw new RuntimeError('Cannot invoke instance method without receiver.'); + } + if (_parameterCount != positionalArguments.length || _jsFunction == null) { + // TODO(ahe): What receiver to use? + throw new NoSuchMethodError( + owner, simpleName, positionalArguments, namedArguments); + } + // Using JS_CURRENT_ISOLATE() ('$') here is actually correct, although + // _jsFunction may not be a property of '$', most static functions do not + // care who their receiver is. But to lazy getters, it is important that + // 'this' is '$'. + return JS('', r'#.apply(#, #)', _jsFunction, JS_CURRENT_ISOLATE(), + new List.from(positionalArguments)); + } + + _getField(JsMirror receiver) { + if (isGetter) { + return _invoke([], null); + } else { + // TODO(ahe): Closurize method. + throw new UnimplementedError('getField on $receiver'); + } + } + + _setField(JsMirror receiver, Object arg) { + if (isSetter) { + return _invoke([arg], null); + } else { + throw new NoSuchMethodError(this, setterSymbol(simpleName), [], null); + } + } + + // Abstract methods are tree-shaken away. + bool get isAbstract => false; + + // TODO(ahe, 14633): This might not be true for all cases. + bool get isSynthetic => false; + + // TODO(ahe): Test this. + bool get isRegularMethod => !isGetter && !isSetter && !isConstructor; + + // TODO(ahe): Implement this method. + bool get isConstConstructor => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + bool get isGenerativeConstructor => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + bool get isRedirectingConstructor => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + bool get isFactoryConstructor => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + String get source => throw new UnimplementedError(); +} + +class JsParameterMirror extends JsDeclarationMirror implements ParameterMirror { + final DeclarationMirror owner; + // A JS object representing the type. + final _type; + + final bool isOptional; + + final bool isNamed; + + final int _defaultValue; + + final List metadataList; + + JsParameterMirror(String unmangledName, + this.owner, + this._type, + {this.metadataList: const [], + this.isOptional: false, + this.isNamed: false, + defaultValue}) + : _defaultValue = defaultValue, + super(s(unmangledName)); + + String get _prettyName => 'ParameterMirror'; + + TypeMirror get type { + return typeMirrorFromRuntimeTypeRepresentation(owner, _type); + } + + // Only true for static fields, never for a parameter. + bool get isStatic => false; + + // TODO(ahe): Implement this. + bool get isFinal => false; + + // TODO(ahe): Implement this. + bool get isConst => false; + + bool get hasDefaultValue => _defaultValue != null; + + get defaultValue { + return hasDefaultValue ? reflect(getMetadata(_defaultValue)) : null; + } + + List get metadata { + preserveMetadata(); + return metadataList.map((int i) => reflect(getMetadata(i))).toList(); + } +} + +class JsTypedefMirror extends JsDeclarationMirror implements TypedefMirror { + final String _mangledName; + JsFunctionTypeMirror referent; + + JsTypedefMirror(Symbol simpleName, this._mangledName, _typeData) + : super(simpleName) { + referent = new JsFunctionTypeMirror(_typeData, this); + } + + JsFunctionTypeMirror get value => referent; + + String get _prettyName => 'TypedefMirror'; + + bool get hasReflectedType => throw new UnimplementedError(); + + Type get reflectedType => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + List get typeVariables => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + List get typeArguments => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + bool get isOriginalDeclaration => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + TypeMirror get originalDeclaration => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + DeclarationMirror get owner => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + List get metadata => throw new UnimplementedError(); + + bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); + bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); +} + +// TODO(ahe): Remove this class when API is updated. +class BrokenClassMirror { + bool get hasReflectedType => throw new UnimplementedError(); + Type get reflectedType => throw new UnimplementedError(); + ClassMirror get superclass => throw new UnimplementedError(); + List get superinterfaces => throw new UnimplementedError(); + Map get declarations + => throw new UnimplementedError(); + Map get instanceMembers + => throw new UnimplementedError(); + Map get staticMembers => throw new UnimplementedError(); + ClassMirror get mixin => throw new UnimplementedError(); + InstanceMirror newInstance( + Symbol constructorName, + List positionalArguments, + [Map namedArguments]) => throw new UnimplementedError(); + InstanceMirror invoke(Symbol memberName, + List positionalArguments, + [Map namedArguments]) + => throw new UnimplementedError(); + InstanceMirror getField(Symbol fieldName) => throw new UnimplementedError(); + InstanceMirror setField(Symbol fieldName, Object value) + => throw new UnimplementedError(); + List get typeVariables => throw new UnimplementedError(); + List get typeArguments => throw new UnimplementedError(); + TypeMirror get originalDeclaration => throw new UnimplementedError(); + Symbol get simpleName => throw new UnimplementedError(); + Symbol get qualifiedName => throw new UnimplementedError(); + bool get isPrivate => throw new UnimplementedError(); + bool get isTopLevel => throw new UnimplementedError(); + SourceLocation get location => throw new UnimplementedError(); + List get metadata => throw new UnimplementedError(); +} + +class JsFunctionTypeMirror extends BrokenClassMirror + implements FunctionTypeMirror { + final _typeData; + String _cachedToString; + TypeMirror _cachedReturnType; + UnmodifiableListView _cachedParameters; + DeclarationMirror owner; + + JsFunctionTypeMirror(this._typeData, this.owner); + + bool get _hasReturnType => JS('bool', '"ret" in #', _typeData); + get _returnType => JS('', '#.ret', _typeData); + + bool get _isVoid => JS('bool', '!!#.void', _typeData); + + bool get _hasArguments => JS('bool', '"args" in #', _typeData); + List get _arguments => JS('JSExtendableArray', '#.args', _typeData); + + bool get _hasOptionalArguments => JS('bool', '"opt" in #', _typeData); + List get _optionalArguments => JS('JSExtendableArray', '#.opt', _typeData); + + bool get _hasNamedArguments => JS('bool', '"named" in #', _typeData); + get _namedArguments => JS('=Object', '#.named', _typeData); + bool get isOriginalDeclaration => true; + + bool get isAbstract => false; + + TypeMirror get returnType { + if (_cachedReturnType != null) return _cachedReturnType; + if (_isVoid) return _cachedReturnType = JsMirrorSystem._voidType; + if (!_hasReturnType) return _cachedReturnType = JsMirrorSystem._dynamicType; + return _cachedReturnType = + typeMirrorFromRuntimeTypeRepresentation(owner, _returnType); + } + + List get parameters { + if (_cachedParameters != null) return _cachedParameters; + List result = []; + int parameterCount = 0; + if (_hasArguments) { + for (var type in _arguments) { + result.add( + new JsParameterMirror('argument${parameterCount++}', this, type)); + } + } + if (_hasOptionalArguments) { + for (var type in _optionalArguments) { + result.add( + new JsParameterMirror('argument${parameterCount++}', this, type)); + } + } + if (_hasNamedArguments) { + for (var name in extractKeys(_namedArguments)) { + var type = JS('', '#[#]', _namedArguments, name); + result.add(new JsParameterMirror(name, this, type)); + } + } + return _cachedParameters = new UnmodifiableListView( + result); + } + + String toString() { + if (_cachedToString != null) return _cachedToString; + var s = "FunctionTypeMirror on '("; + var sep = ''; + if (_hasArguments) { + for (var argument in _arguments) { + s += sep; + s += runtimeTypeToString(argument); + sep = ', '; + } + } + if (_hasOptionalArguments) { + s += '$sep['; + sep = ''; + for (var argument in _optionalArguments) { + s += sep; + s += runtimeTypeToString(argument); + sep = ', '; + } + s += ']'; + } + if (_hasNamedArguments) { + s += '$sep{'; + sep = ''; + for (var name in extractKeys(_namedArguments)) { + s += sep; + s += '$name: '; + s += runtimeTypeToString(JS('', '#[#]', _namedArguments, name)); + sep = ', '; + } + s += '}'; + } + s += ') -> '; + if (_isVoid) { + s += 'void'; + } else if (_hasReturnType) { + s += runtimeTypeToString(_returnType); + } else { + s += 'dynamic'; + } + return _cachedToString = "$s'"; + } + + bool isSubclassOf(ClassMirror other) => false; + + bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); + + bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); + + // TODO(ahe): Implement this method. + MethodMirror get callMethod => throw new UnimplementedError(); +} + +int findTypeVariableIndex(List typeVariables, String name) { + for (int i = 0; i < typeVariables.length; i++) { + if (typeVariables[i].simpleName == s(name)) { + return i; + } + } + throw new ArgumentError('Type variable not present in list.'); +} + +TypeMirror typeMirrorFromRuntimeTypeRepresentation( + DeclarationMirror owner, + var /*int|List|JsFunction*/ type) { + // TODO(ahe): This method might benefit from using convertRtiToRuntimeType + // instead of working on strings. + ClassMirror ownerClass; + DeclarationMirror context = owner; + while (context != null) { + if (context is ClassMirror) { + ownerClass = context; + break; + } + // TODO(ahe): Get type parameters and arguments from typedefs. + if (context is TypedefMirror) break; + context = context.owner; + } + + String representation; + if (type == null) { + return JsMirrorSystem._dynamicType; + } else if (ownerClass == null) { + representation = runtimeTypeToString(type); + } else if (ownerClass.isOriginalDeclaration) { + if (type is num) { + // [type] represents a type variable so in the context of an original + // declaration the corresponding type variable should be returned. + TypeVariable typeVariable = getMetadata(type); + List typeVariables = ownerClass.typeVariables; + int index = findTypeVariableIndex(typeVariables, typeVariable.name); + return typeVariables[index]; + } else { + // Nested type variables will be retrieved lazily (the integer + // representation is kept in the string) so they are not processed here. + representation = runtimeTypeToString(type); + } + } else { + TypeMirror getTypeArgument(int index) { + TypeVariable typeVariable = getMetadata(index); + int variableIndex = + findTypeVariableIndex(ownerClass.typeVariables, typeVariable.name); + return ownerClass.typeArguments[variableIndex]; + } + + if (type is num) { + // [type] represents a type variable used as type argument for example + // the type argument of Bar: class Foo extends Bar {} + TypeMirror typeArgument = getTypeArgument(type); + if (typeArgument is JsTypeVariableMirror) + return typeArgument; + } + String substituteTypeVariable(int index) { + var typeArgument = getTypeArgument(index); + if (typeArgument is JsTypeVariableMirror) { + return '${typeArgument._metadataIndex}'; + } + if (typeArgument is! JsClassMirror && + typeArgument is! JsTypeBoundClassMirror) { + if (typeArgument == JsMirrorSystem._dynamicType) { + return 'dynamic'; + } else if (typeArgument == JsMirrorSystem._voidType) { + return 'void'; + } else { + // TODO(ahe): This case shouldn't happen. + return 'dynamic'; + } + } + return typeArgument._mangledName; + } + representation = + runtimeTypeToString(type, onTypeVariable: substituteTypeVariable); + } + if (representation != null) { + return reflectClassByMangledName( + getMangledTypeName(createRuntimeType(representation))); + } + return reflectClass(Function); +} + +Symbol computeQualifiedName(DeclarationMirror owner, Symbol simpleName) { + if (owner == null) return simpleName; + String ownerName = n(owner.qualifiedName); + return s('$ownerName.${n(simpleName)}'); +} + +List extractMetadata(victim) { + preserveMetadata(); + var metadataFunction = JS('', '#["@"]', victim); + if (metadataFunction != null) return JS('', '#()', metadataFunction); + if (JS('bool', 'typeof # != "function"', victim)) return const []; + if (JS('bool', '# in #', r'$metadataIndex', victim)) { + return JSArray.markFixedList( + JS('JSExtendableArray', + r'#.$reflectionInfo.splice(#.$metadataIndex)', victim, victim)) + .map((int i) => getMetadata(i)).toList(); + } + String source = JS('String', 'Function.prototype.toString.call(#)', victim); + int index = source.lastIndexOf(new RegExp('"[0-9,]*";?[ \n\r]*}')); + if (index == -1) return const []; + index++; + int endQuote = source.indexOf('"', index); + return source.substring(index, endQuote).split(',').map(int.parse).map( + (int i) => getMetadata(i)).toList(); +} + +void parseCompactFieldSpecification( + JsDeclarationMirror owner, + fieldSpecification, + bool isStatic, + List result) { + List fieldsMetadata = null; + List fields; + if (fieldSpecification is List) { + fields = splitFields(fieldSpecification[0], ','); + fieldsMetadata = fieldSpecification.sublist(1); + } else if (fieldSpecification is String) { + fields = splitFields(fieldSpecification, ','); + } else { + fields = []; + } + int fieldNumber = 0; + for (String field in fields) { + var metadata; + if (fieldsMetadata != null) { + metadata = fieldsMetadata[fieldNumber++]; + } + var mirror = new JsVariableMirror.from(field, metadata, owner, isStatic); + if (mirror != null) { + result.add(mirror); + } + } +} + +/// Similar to [String.split], but returns an empty list if [string] is empty. +List splitFields(String string, Pattern pattern) { + if (string.isEmpty) return []; + return string.split(pattern); +} + +bool isOperatorName(String name) { + switch (name) { + case '==': + case '[]': + case '*': + case '/': + case '%': + case '~/': + case '+': + case '<<': + case '>>': + case '>=': + case '>': + case '<=': + case '<': + case '&': + case '^': + case '|': + case '-': + case 'unary-': + case '[]=': + case '~': + return true; + default: + return false; + } +} + +/// Returns true if the key represent ancillary reflection data, that is, not a +/// method. +bool isReflectiveDataInPrototype(String key) { + if (key == JS_GET_NAME('CLASS_DESCRIPTOR_PROPERTY') || + key == METHODS_WITH_OPTIONAL_ARGUMENTS) { + return true; + } + String firstChar = key[0]; + return firstChar == '*' || firstChar == '+'; +} + +bool isNoSuchMethodStub(var jsFunction) { + return JS('bool', r'#.$reflectable == 2', jsFunction); +} + +class NoSuchStaticMethodError extends Error implements NoSuchMethodError { + static const int MISSING_CONSTRUCTOR = 0; + final ClassMirror _cls; + final Symbol _name; + final List _positionalArguments; + final Map _namedArguments; + final int _kind; + + NoSuchStaticMethodError.missingConstructor( + this._cls, + this._name, + this._positionalArguments, + this._namedArguments) + : _kind = MISSING_CONSTRUCTOR; + + String toString() { + switch(_kind) { + case MISSING_CONSTRUCTOR: + return + "NoSuchMethodError: No constructor named '${n(_name)}' in class" + " '${n(_cls.qualifiedName)}'."; + default: + return 'NoSuchMethodError'; + } + } +} + +// Copied from package "unmodifiable_collection". +// TODO(14314): Move to dart:collection. +class UnmodifiableMapView implements Map { + Map _source; + UnmodifiableMapView(Map source) : _source = source; + + static void _throw() { + throw new UnsupportedError("Cannot modify an unmodifiable Map"); + } + + int get length => _source.length; + + bool get isEmpty => _source.isEmpty; + + bool get isNotEmpty => _source.isNotEmpty; + + V operator [](K key) => _source[key]; + + bool containsKey(K key) => _source.containsKey(key); + + bool containsValue(V value) => _source.containsValue(value); + + void forEach(void f(K key, V value)) => _source.forEach(f); + + Iterable get keys => _source.keys; + + Iterable get values => _source.values; + + + void operator []=(K key, V value) => _throw(); + + V putIfAbsent(K key, V ifAbsent()) { _throw(); } + + void addAll(Map other) => _throw(); + + V remove(K key) { _throw(); } + + void clear() => _throw(); +} + +Symbol getSymbol(String name, LibraryMirror library) { + if (_isPublicSymbol(name)) { + return new _symbol_dev.Symbol.validated(name); + } + if (library == null) { + throw new ArgumentError( + "Library required for private symbol name: $name"); + } + if (!_symbol_dev.Symbol.isValidSymbol(name)) { + throw new ArgumentError("Not a valid symbol name: $name"); + } + throw new UnimplementedError( + "MirrorSystem.getSymbol not implemented for private names"); +} + +bool _isPublicSymbol(String name) { + // A symbol is public if it doesn't start with '_' and it doesn't + // have a part (following a '.') that starts with '_'. + const int UNDERSCORE = 0x5f; + if (name.isEmpty) return true; + int index = -1; + do { + if (name.codeUnitAt(index + 1) == UNDERSCORE) return false; + index = name.indexOf('.', index + 1); + } while (index >= 0 && index + 1 < name.length); + return true; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_names.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_names.dart new file mode 100644 index 0000000..6aa3632 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_names.dart @@ -0,0 +1,99 @@ +// Copyright (c) 2013, 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. + +library dart._js_names; + +import 'dart:_foreign_helper' show + JS, + JS_GET_NAME; + +import 'dart:_js_helper' show + JsCache, + NoInline; + +import 'dart:_interceptors' show JSArray; + +/// No-op method that is called to inform the compiler that unmangled named +/// must be preserved. +preserveNames() {} + +/// A map from mangled names to "reflective" names, that is, unmangled names +/// with some additional information, such as, number of required arguments. +/// This map is for mangled names used as instance members. +final Map mangledNames = + computeMangledNames(JS('=Object', 'init.mangledNames'), false); + +/// A map from "reflective" names to mangled names (the reverse of +/// [mangledNames]). +final Map reflectiveNames = + computeReflectiveNames(mangledNames); + +/// A map from mangled names to "reflective" names (see [mangledNames]). This +/// map is for globals, that is, static and top-level members. +final Map mangledGlobalNames = + computeMangledNames(JS('=Object', 'init.mangledGlobalNames'), true); + +/// A map from "reflective" names to mangled names (the reverse of +/// [mangledGlobalNames]). +final Map reflectiveGlobalNames = + computeReflectiveNames(mangledGlobalNames); + +/// [jsMangledNames] is a JavaScript object literal. The keys are the mangled +/// names, and the values are the "reflective" names. +Map computeMangledNames(jsMangledNames, bool isGlobal) { + preserveNames(); + var keys = extractKeys(jsMangledNames); + var result = {}; + String getterPrefix = JS_GET_NAME('GETTER_PREFIX'); + int getterPrefixLength = getterPrefix.length; + String setterPrefix = JS_GET_NAME('SETTER_PREFIX'); + for (String key in keys) { + String value = JS('String', '#[#]', jsMangledNames, key); + result[key] = value; + if (!isGlobal) { + if (key.startsWith(getterPrefix)) { + result['$setterPrefix${key.substring(getterPrefixLength)}'] = '$value='; + } + } + } + return result; +} + +Map computeReflectiveNames(Map map) { + preserveNames(); + var result = {}; + map.forEach((String mangledName, String reflectiveName) { + result[reflectiveName] = mangledName; + }); + return result; +} + +@NoInline() +List extractKeys(victim) { + var result = JS('', ''' +(function(victim, hasOwnProperty) { + var result = []; + for (var key in victim) { + if (hasOwnProperty.call(victim, key)) result.push(key); + } + return result; +})(#, Object.prototype.hasOwnProperty)''', victim); + return new JSArray.markFixed(result); +} + +/** + * Returns the (global) unmangled version of [name]. + * + * Normally, you should use [mangledGlobalNames] directly, but this method + * doesn't tell the compiler to preserve names. So this method only returns a + * non-null value if some other component has made the compiler preserve names. + * + * This is used, for example, to return unmangled names from TypeImpl.toString + * *if* names are being preserved for other reasons (use of dart:mirrors, for + * example). + */ +String unmangleGlobalNameIfPreservedAnyways(String name) { + var names = JS('=Object', 'init.mangledGlobalNames'); + return JsCache.fetch(names, name); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_number.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_number.dart new file mode 100644 index 0000000..7aaeb93 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_number.dart @@ -0,0 +1,399 @@ +// Copyright (c) 2012, 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. + +part of _interceptors; + +/** + * The super interceptor class for [JSInt] and [JSDouble]. The compiler + * recognizes this class as an interceptor, and changes references to + * [:this:] to actually use the receiver of the method, which is + * generated as an extra argument added to each member. + * + * Note that none of the methods here delegate to a method defined on JSInt or + * JSDouble. This is exploited in [tryComputeConstantInterceptor]. + */ +class JSNumber extends Interceptor implements num { + const JSNumber(); + + int compareTo(num b) { + if (b is! num) throw new ArgumentError(b); + if (this < b) { + return -1; + } else if (this > b) { + return 1; + } else if (this == b) { + if (this == 0) { + bool bIsNegative = b.isNegative; + if (isNegative == bIsNegative) return 0; + if (isNegative) return -1; + return 1; + } + return 0; + } else if (isNaN) { + if (b.isNaN) { + return 0; + } + return 1; + } else { + return -1; + } + } + + bool get isNegative => (this == 0) ? (1 / this) < 0 : this < 0; + + bool get isNaN => JS('bool', r'isNaN(#)', this); + + bool get isInfinite { + return JS('bool', r'# == Infinity', this) + || JS('bool', r'# == -Infinity', this); + } + + bool get isFinite => JS('bool', r'isFinite(#)', this); + + num remainder(num b) { + checkNull(b); // TODO(ngeoffray): This is not specified but co19 tests it. + if (b is! num) throw new ArgumentError(b); + return JS('num', r'# % #', this, b); + } + + num abs() => JS('num', r'Math.abs(#)', this); + + num get sign => this > 0 ? 1 : this < 0 ? -1 : this; + + static const int _MIN_INT32 = -0x80000000; + static const int _MAX_INT32 = 0x7FFFFFFF; + + int toInt() { + if (this >= _MIN_INT32 && this <= _MAX_INT32) { + return JS('int', '# | 0', this); + } + if (JS('bool', r'isFinite(#)', this)) { + return JS('int', r'# + 0', truncateToDouble()); // Converts -0.0 to +0.0. + } + // This is either NaN, Infinity or -Infinity. + throw new UnsupportedError(JS("String", "''+#", this)); + } + + int truncate() => toInt(); + int ceil() => ceilToDouble().toInt(); + int floor() => floorToDouble().toInt(); + int round() => roundToDouble().toInt(); + + double ceilToDouble() => JS('num', r'Math.ceil(#)', this); + + double floorToDouble() => JS('num', r'Math.floor(#)', this); + + double roundToDouble() { + if (this < 0) { + return JS('num', r'-Math.round(-#)', this); + } else { + return JS('num', r'Math.round(#)', this); + } + } + + double truncateToDouble() => this < 0 ? ceilToDouble() : floorToDouble(); + + num clamp(lowerLimit, upperLimit) { + if (lowerLimit is! num) throw new ArgumentError(lowerLimit); + if (upperLimit is! num) throw new ArgumentError(upperLimit); + if (lowerLimit.compareTo(upperLimit) > 0) { + throw new ArgumentError(lowerLimit); + } + if (this.compareTo(lowerLimit) < 0) return lowerLimit; + if (this.compareTo(upperLimit) > 0) return upperLimit; + return this; + } + + // The return type is intentionally omitted to avoid type checker warnings + // from assigning JSNumber to double. + toDouble() => this; + + String toStringAsFixed(int fractionDigits) { + checkNum(fractionDigits); + // TODO(floitsch): fractionDigits must be an integer. + if (fractionDigits < 0 || fractionDigits > 20) { + throw new RangeError(fractionDigits); + } + String result = JS('String', r'#.toFixed(#)', this, fractionDigits); + if (this == 0 && isNegative) return "-$result"; + return result; + } + + String toStringAsExponential([int fractionDigits]) { + String result; + if (fractionDigits != null) { + // TODO(floitsch): fractionDigits must be an integer. + checkNum(fractionDigits); + if (fractionDigits < 0 || fractionDigits > 20) { + throw new RangeError(fractionDigits); + } + result = JS('String', r'#.toExponential(#)', this, fractionDigits); + } else { + result = JS('String', r'#.toExponential()', this); + } + if (this == 0 && isNegative) return "-$result"; + return result; + } + + String toStringAsPrecision(int precision) { + // TODO(floitsch): precision must be an integer. + checkNum(precision); + if (precision < 1 || precision > 21) { + throw new RangeError(precision); + } + String result = JS('String', r'#.toPrecision(#)', + this, precision); + if (this == 0 && isNegative) return "-$result"; + return result; + } + + String toRadixString(int radix) { + checkNum(radix); + if (radix < 2 || radix > 36) throw new RangeError(radix); + return JS('String', r'#.toString(#)', this, radix); + } + + // Note: if you change this, also change the function [S]. + String toString() { + if (this == 0 && JS('bool', '(1 / #) < 0', this)) { + return '-0.0'; + } else { + return JS('String', r'"" + (#)', this); + } + } + + int get hashCode => JS('int', '# & 0x1FFFFFFF', this); + + num operator -() => JS('num', r'-#', this); + + num operator +(num other) { + if (other is !num) throw new ArgumentError(other); + return JS('num', '# + #', this, other); + } + + num operator -(num other) { + if (other is !num) throw new ArgumentError(other); + return JS('num', '# - #', this, other); + } + + num operator /(num other) { + if (other is !num) throw new ArgumentError(other); + return JS('num', '# / #', this, other); + } + + num operator *(num other) { + if (other is !num) throw new ArgumentError(other); + return JS('num', '# * #', this, other); + } + + num operator %(num other) { + if (other is !num) throw new ArgumentError(other); + // Euclidean Modulo. + num result = JS('num', r'# % #', this, other); + if (result == 0) return 0; // Make sure we don't return -0.0. + if (result > 0) return result; + if (JS('num', '#', other) < 0) { + return result - JS('num', '#', other); + } else { + return result + JS('num', '#', other); + } + } + + bool _isInt32(value) => JS('bool', '(# | 0) === #', value, value); + + num operator ~/(num other) { + if (false) _tdivFast(other); // Ensure resolution. + if (_isInt32(this) && _isInt32(other) && 0 != other && -1 != other) { + return JS('num', r'(# / #) | 0', this, other); + } else { + return _tdivSlow(other); + } + } + + num _tdivFast(num other) { + return _isInt32(this) + ? JS('num', r'(# / #) | 0', this, other) + : (JS('num', r'# / #', this, other)).toInt(); + } + + num _tdivSlow(num other) { + if (other is !num) throw new ArgumentError(other); + return (JS('num', r'# / #', this, other)).toInt(); + } + + // TODO(ngeoffray): Move the bit operations below to [JSInt] and + // make them take an int. Because this will make operations slower, + // we define these methods on number for now but we need to decide + // the grain at which we do the type checks. + + num operator <<(num other) { + if (other is !num) throw new ArgumentError(other); + if (JS('num', '#', other) < 0) throw new ArgumentError(other); + return _shlPositive(other); + } + + num _shlPositive(num other) { + // JavaScript only looks at the last 5 bits of the shift-amount. Shifting + // by 33 is hence equivalent to a shift by 1. + return JS('bool', r'# > 31', other) + ? 0 + : JS('JSUInt32', r'(# << #) >>> 0', this, other); + } + + num operator >>(num other) { + if (false) _shrReceiverPositive(other); + if (other is !num) throw new ArgumentError(other); + if (JS('num', '#', other) < 0) throw new ArgumentError(other); + return _shrOtherPositive(other); + } + + num _shrOtherPositive(num other) { + return JS('num', '#', this) > 0 + ? _shrBothPositive(other) + // For negative numbers we just clamp the shift-by amount. + // `this` could be negative but not have its 31st bit set. + // The ">>" would then shift in 0s instead of 1s. Therefore + // we cannot simply return 0xFFFFFFFF. + : JS('JSUInt32', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); + } + + num _shrReceiverPositive(num other) { + if (JS('num', '#', other) < 0) throw new ArgumentError(other); + return _shrBothPositive(other); + } + + num _shrBothPositive(num other) { + return JS('bool', r'# > 31', other) + // JavaScript only looks at the last 5 bits of the shift-amount. In JS + // shifting by 33 is hence equivalent to a shift by 1. Shortcut the + // computation when that happens. + ? 0 + // Given that `this` is positive we must not use '>>'. Otherwise a + // number that has the 31st bit set would be treated as negative and + // shift in ones. + : JS('JSUInt32', r'# >>> #', this, other); + } + + num operator &(num other) { + if (other is !num) throw new ArgumentError(other); + return JS('JSUInt32', r'(# & #) >>> 0', this, other); + } + + num operator |(num other) { + if (other is !num) throw new ArgumentError(other); + return JS('JSUInt32', r'(# | #) >>> 0', this, other); + } + + num operator ^(num other) { + if (other is !num) throw new ArgumentError(other); + return JS('JSUInt32', r'(# ^ #) >>> 0', this, other); + } + + bool operator <(num other) { + if (other is !num) throw new ArgumentError(other); + return JS('bool', '# < #', this, other); + } + + bool operator >(num other) { + if (other is !num) throw new ArgumentError(other); + return JS('bool', '# > #', this, other); + } + + bool operator <=(num other) { + if (other is !num) throw new ArgumentError(other); + return JS('bool', '# <= #', this, other); + } + + bool operator >=(num other) { + if (other is !num) throw new ArgumentError(other); + return JS('bool', '# >= #', this, other); + } +} + +/** + * The interceptor class for [int]s. + * + * This class implements double since in JavaScript all numbers are doubles, so + * while we want to treat `2.0` as an integer for some operations, its + * interceptor should answer `true` to `is double`. + */ +class JSInt extends JSNumber implements int, double { + const JSInt(); + + bool get isEven => (this & 1) == 0; + + bool get isOdd => (this & 1) == 1; + + int toUnsigned(int width) { + return this & ((1 << width) - 1); + } + + int toSigned(int width) { + int signMask = 1 << (width - 1); + return (this & (signMask - 1)) - (this & signMask); + } + + int get bitLength { + int nonneg = this < 0 ? -this - 1 : this; + if (nonneg >= 0x100000000) { + nonneg = nonneg ~/ 0x100000000; + return _bitCount(_spread(nonneg)) + 32; + } + return _bitCount(_spread(nonneg)); + } + + // Assumes i is <= 32-bit and unsigned. + static int _bitCount(int i) { + // See "Hacker's Delight", section 5-1, "Counting 1-Bits". + + // The basic strategy is to use "divide and conquer" to + // add pairs (then quads, etc.) of bits together to obtain + // sub-counts. + // + // A straightforward approach would look like: + // + // i = (i & 0x55555555) + ((i >> 1) & 0x55555555); + // i = (i & 0x33333333) + ((i >> 2) & 0x33333333); + // i = (i & 0x0F0F0F0F) + ((i >> 4) & 0x0F0F0F0F); + // i = (i & 0x00FF00FF) + ((i >> 8) & 0x00FF00FF); + // i = (i & 0x0000FFFF) + ((i >> 16) & 0x0000FFFF); + // + // The code below removes unnecessary &'s and uses a + // trick to remove one instruction in the first line. + + i = _shru(i, 0) - (_shru(i, 1) & 0x55555555); + i = (i & 0x33333333) + (_shru(i, 2) & 0x33333333); + i = 0x0F0F0F0F & (i + _shru(i, 4)); + i += _shru(i, 8); + i += _shru(i, 16); + return (i & 0x0000003F); + } + + static _shru(int value, int shift) => JS('int', '# >>> #', value, shift); + static _shrs(int value, int shift) => JS('int', '# >> #', value, shift); + static _ors(int a, int b) => JS('int', '# | #', a, b); + + // Assumes i is <= 32-bit + static int _spread(int i) { + i = _ors(i, _shrs(i, 1)); + i = _ors(i, _shrs(i, 2)); + i = _ors(i, _shrs(i, 4)); + i = _ors(i, _shrs(i, 8)); + i = _shru(_ors(i, _shrs(i, 16)), 0); + return i; + } + + Type get runtimeType => int; + + int operator ~() => JS('JSUInt32', r'(~#) >>> 0', this); +} + +class JSDouble extends JSNumber implements double { + const JSDouble(); + Type get runtimeType => double; +} + +class JSPositiveInt extends JSInt {} +class JSUInt32 extends JSPositiveInt {} +class JSUInt31 extends JSUInt32 {} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_primitives.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_primitives.dart new file mode 100644 index 0000000..3315040 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_primitives.dart @@ -0,0 +1,50 @@ +// Copyright (c) 2013, 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. + +/// dart2js "primitives", that is, features that cannot be implemented without +/// access to JavaScript features. +library dart2js._js_primitives; + +import 'dart:_foreign_helper' show + JS; + +/** + * This is the low-level method that is used to implement [print]. It is + * possible to override this function from JavaScript by defining a function in + * JavaScript called "dartPrint". + * + * Notice that it is also possible to intercept calls to [print] from within a + * Dart program using zones. This means that there is no guarantee that a call + * to print ends in this method. + */ +void printString(String string) { + if (JS('bool', r'typeof dartPrint == "function"')) { + // Support overriding print from JavaScript. + JS('void', r'dartPrint(#)', string); + return; + } + + // Inside browser or nodejs. + if (JS('bool', r'typeof console == "object"') && + JS('bool', r'typeof console.log == "function"')) { + JS('void', r'console.log(#)', string); + return; + } + + // Don't throw inside IE, the console is only defined if dev tools is open. + if (JS('bool', r'typeof window == "object"')) { + return; + } + + // Running in d8, the V8 developer shell, or in Firefox' js-shell. + if (JS('bool', r'typeof print == "function"')) { + JS('void', r'print(#)', string); + return; + } + + // This is somewhat nasty, but we don't want to drag in a bunch of + // dependencies to handle a situation that cannot happen. So we + // avoid using Dart [:throw:] and Dart [toString]. + JS('void', 'throw "Unable to print message: " + String(#)', string); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_rti.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_rti.dart new file mode 100644 index 0000000..f389abb --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_rti.dart @@ -0,0 +1,702 @@ +// Copyright (c) 2013, 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. + +/** + * This part contains helpers for supporting runtime type information. + * + * The helper use a mixture of Dart and JavaScript objects. To indicate which is + * used where we adopt the scheme of using explicit type annotation for Dart + * objects and 'var' or omitted return type for JavaScript objects. + * + * Since bool, int, and String values are represented by the same JavaScript + * primitives, type annotations are used for these types in all cases. + * + * Several methods use a common JavaScript encoding of runtime type information. + * This encoding is referred to as the type representation which is one of + * these: + * 1) a JavaScript constructor for a class C: the represented type is the raw + * type C. + * 2) a Dart object: this is the interceptor instance for a native type. + * 3) a JavaScript object: this represents a class for which there is no + * JavaScript constructor, because it is only used in type arguments or it + * is native. The represented type is the raw type of this class. + * 4) a JavaScript array: the first entry is of type 1, 2 or 3 and contains the + * subtyping flags and the substitution of the type and the rest of the + * array are the type arguments. + * 5) `null`: the dynamic type. + * + * + * To check subtype relations between generic classes we use a JavaScript + * expression that describes the necessary substitution for type arguments. + * Such a substitution expresssion can be: + * 1) `null`, if no substituted check is necessary, because the + * type variables are the same or there are no type variables in the class + * that is checked for. + * 2) A list expression describing the type arguments to be used in the + * subtype check, if the type arguments to be used in the check do not + * depend on the type arguments of the object. + * 3) A function mapping the type variables of the object to be checked to + * a list expression. + */ + +part of _js_helper; + +Type createRuntimeType(String name) => new TypeImpl(name); + +class TypeImpl implements Type { + final String _typeName; + String _unmangledName; + + TypeImpl(this._typeName); + + String toString() { + if (_unmangledName != null) return _unmangledName; + String unmangledName = unmangleGlobalNameIfPreservedAnyways(_typeName); + // TODO(ahe): Handle type arguments. + if (unmangledName == null) unmangledName = _typeName; + return _unmangledName = unmangledName; + } + + // TODO(ahe): This is a poor hashCode as it collides with its name. + int get hashCode => _typeName.hashCode; + + bool operator ==(other) { + return (other is TypeImpl) && _typeName == other._typeName; + } +} + +/** + * Represents a type variable. + * + * This class holds the information needed when reflecting on generic classes + * and their members. + */ +class TypeVariable { + final Type owner; + final String name; + final int bound; + + const TypeVariable(this.owner, this.name, this.bound); +} + +getMangledTypeName(TypeImpl type) => type._typeName; + +/** + * Sets the runtime type information on [target]. [typeInfo] is a type + * representation of type 4 or 5, that is, either a JavaScript array or + * [:null:]. + */ +Object setRuntimeTypeInfo(Object target, var typeInfo) { + assert(isNull(typeInfo) || isJsArray(typeInfo)); + // We have to check for null because factories may return null. + if (target != null) JS('var', r'#.$builtinTypeInfo = #', target, typeInfo); + return target; +} + +/** + * Returns the runtime type information of [target]. The returned value is a + * list of type representations for the type arguments. + */ +getRuntimeTypeInfo(Object target) { + if (target == null) return null; + return JS('var', r'#.$builtinTypeInfo', target); +} + +/** + * Returns the type arguments of [target] as an instance of [substitutionName]. + */ +getRuntimeTypeArguments(target, substitutionName) { + var substitution = + getField(target, '${JS_OPERATOR_AS_PREFIX()}$substitutionName'); + return substitute(substitution, getRuntimeTypeInfo(target)); +} + +/** + * Returns the [index]th type argument of [target] as an instance of + * [substitutionName]. + */ +@NoThrows() @NoSideEffects() @NoInline() +getRuntimeTypeArgument(Object target, String substitutionName, int index) { + var arguments = getRuntimeTypeArguments(target, substitutionName); + return isNull(arguments) ? null : getIndex(arguments, index); +} + +@NoThrows() @NoSideEffects() @NoInline() +getTypeArgumentByIndex(Object target, int index) { + var rti = getRuntimeTypeInfo(target); + return isNull(rti) ? null : getIndex(rti, index); +} + +void copyTypeArguments(Object source, Object target) { + JS('var', r'#.$builtinTypeInfo = #.$builtinTypeInfo', target, source); +} + +/** + * Retrieves the class name from type information stored on the constructor + * of [object]. + */ +String getClassName(var object) { + return JS('String', r'#.constructor.builtin$cls', getInterceptor(object)); +} + +/** + * Creates the string representation for the type representation [runtimeType] + * of type 4, the JavaScript array, where the first element represents the class + * and the remaining elements represent the type arguments. + */ +String getRuntimeTypeAsString(var runtimeType, {String onTypeVariable(int i)}) { + assert(isJsArray(runtimeType)); + String className = getConstructorName(getIndex(runtimeType, 0)); + return '$className' + '${joinArguments(runtimeType, 1, onTypeVariable: onTypeVariable)}'; +} + +/** + * Retrieves the class name from type information stored on the constructor + * [type]. + */ +String getConstructorName(var type) => JS('String', r'#.builtin$cls', type); + +/** + * Returns a human-readable representation of the type representation [type]. + */ +String runtimeTypeToString(var type, {String onTypeVariable(int i)}) { + if (isNull(type)) { + return 'dynamic'; + } else if (isJsArray(type)) { + // A list representing a type with arguments. + return getRuntimeTypeAsString(type, onTypeVariable: onTypeVariable); + } else if (isJsFunction(type)) { + // A reference to the constructor. + return getConstructorName(type); + } else if (type is int) { + if (onTypeVariable == null) { + return type.toString(); + } else { + return onTypeVariable(type); + } + } else { + // TODO(ahe): Handle function types, and be sure to always return a string. + return null; + } +} + +/** + * Creates a comma-separated string of human-readable representations of the + * type representations in the JavaScript array [types] starting at index + * [startIndex]. + */ +String joinArguments(var types, int startIndex, + {String onTypeVariable(int i)}) { + if (isNull(types)) return ''; + assert(isJsArray(types)); + bool firstArgument = true; + bool allDynamic = true; + StringBuffer buffer = new StringBuffer(); + for (int index = startIndex; index < getLength(types); index++) { + if (firstArgument) { + firstArgument = false; + } else { + buffer.write(', '); + } + var argument = getIndex(types, index); + if (argument != null) { + allDynamic = false; + } + buffer.write(runtimeTypeToString(argument, onTypeVariable: onTypeVariable)); + } + return allDynamic ? '' : '<$buffer>'; +} + +/** + * Returns a human-readable representation of the type of [object]. + */ +String getRuntimeTypeString(var object) { + String className = isJsArray(object) ? 'List' : getClassName(object); + var typeInfo = JS('var', r'#.$builtinTypeInfo', object); + return "$className${joinArguments(typeInfo, 0)}"; +} + +Type getRuntimeType(var object) { + String type = getRuntimeTypeString(object); + return new TypeImpl(type); +} + +/** + * Applies the [substitution] on the [arguments]. + * + * See the comment in the beginning of this file for a description of the + * possible values for [substitution]. + */ +substitute(var substitution, var arguments) { + assert(isNull(substitution) || + isJsArray(substitution) || + isJsFunction(substitution)); + assert(isNull(arguments) || isJsArray(arguments)); + if (isJsArray(substitution)) { + arguments = substitution; + } else if (isJsFunction(substitution)) { + substitution = invoke(substitution, arguments); + if (isJsArray(substitution)) { + arguments = substitution; + } else if (isJsFunction(substitution)) { + // TODO(johnniwinther): Check if this is still needed. + arguments = invoke(substitution, arguments); + } + } + return arguments; +} + +/** + * Perform a type check with arguments on the Dart object [object]. + * + * Parameters: + * - [isField]: the name of the flag/function to check if the object + * is of the correct class. + * - [checks]: the (JavaScript) list of type representations for the + * arguments to check against. + * - [asField]: the name of the function that transforms the type + * arguments of [objects] to an instance of the class that we check + * against. + */ +bool checkSubtype(Object object, String isField, List checks, String asField) { + if (object == null) return false; + var arguments = getRuntimeTypeInfo(object); + // Interceptor is needed for JSArray and native classes. + // TODO(sra): It could be a more specialized interceptor since [object] is not + // `null` or a primitive. + // TODO(9586): Move type info for static functions onto an interceptor. + var interceptor = getInterceptor(object); + var isSubclass = getField(interceptor, isField); + // When we read the field and it is not there, [isSubclass] will be [:null:]. + if (isNull(isSubclass)) return false; + // Should the asField function be passed the receiver? + var substitution = getField(interceptor, asField); + return checkArguments(substitution, arguments, checks); +} + +String computeTypeName(String isField, List arguments) { + // Shorten the field name to the class name and append the textual + // representation of the type arguments. + int prefixLength = JS_OPERATOR_IS_PREFIX().length; + return Primitives.formatType(isField.substring(prefixLength, isField.length), + arguments); +} + +Object subtypeCast(Object object, String isField, List checks, String asField) { + if (object != null && !checkSubtype(object, isField, checks, asField)) { + String actualType = Primitives.objectTypeName(object); + String typeName = computeTypeName(isField, checks); + // TODO(johnniwinther): Move type lookup to [CastErrorImplementation] to + // align with [TypeErrorImplementation]. + throw new CastErrorImplementation(actualType, typeName); + } + return object; +} + +Object assertSubtype(Object object, String isField, List checks, + String asField) { + if (object != null && !checkSubtype(object, isField, checks, asField)) { + String typeName = computeTypeName(isField, checks); + throw new TypeErrorImplementation(object, typeName); + } + return object; +} + +/// Checks that the type represented by [subtype] is a subtype of [supertype]. +/// If not a type error with [message] is thrown. +assertIsSubtype(var subtype, var supertype, String message) { + if (!isSubtype(subtype, supertype)) { + throwTypeError(message); + } +} + +throwTypeError(message) { + throw new TypeErrorImplementation.fromMessage(message); +} + +/** + * Check that the types in the list [arguments] are subtypes of the types in + * list [checks] (at the respective positions), possibly applying [substitution] + * to the arguments before the check. + * + * See the comment in the beginning of this file for a description of the + * possible values for [substitution]. + */ +bool checkArguments(var substitution, var arguments, var checks) { + return areSubtypes(substitute(substitution, arguments), checks); +} + +/** + * Checks whether the types of [s] are all subtypes of the types of [t]. + * + * [s] and [t] are either [:null:] or JavaScript arrays of type representations, + * A [:null:] argument is interpreted as the arguments of a raw type, that is a + * list of [:dynamic:]. If [s] and [t] are JavaScript arrays they must be of the + * same length. + * + * See the comment in the beginning of this file for a description of type + * representations. + */ +bool areSubtypes(var s, var t) { + // [:null:] means a raw type. + if (isNull(s) || isNull(t)) return true; + + assert(isJsArray(s)); + assert(isJsArray(t)); + assert(getLength(s) == getLength(t)); + + int len = getLength(s); + for (int i = 0; i < len; i++) { + if (!isSubtype(getIndex(s, i), getIndex(t, i))) { + return false; + } + } + return true; +} + +/** + * Computes the signature by applying the type arguments of [context] as an + * instance of [contextName] to the signature function [signature]. + */ +computeSignature(var signature, var context, var contextName) { + var typeArguments = getRuntimeTypeArguments(context, contextName); + return invokeOn(signature, context, typeArguments); +} + +/** + * Returns [:true:] if the runtime type representation [type] is a supertype of + * [:Null:]. + */ +bool isSupertypeOfNull(var type) { + // `null` means `dynamic`. + return isNull(type) || getConstructorName(type) == JS_OBJECT_CLASS_NAME() + || getConstructorName(type) == JS_NULL_CLASS_NAME(); +} + +/** + * Tests whether the Dart object [o] is a subtype of the runtime type + * representation [t]. + * + * See the comment in the beginning of this file for a description of type + * representations. + */ +bool checkSubtypeOfRuntimeType(o, t) { + if (isNull(o)) return isSupertypeOfNull(t); + if (isNull(t)) return true; + // Get the runtime type information from the object here, because we may + // overwrite o with the interceptor below. + var rti = getRuntimeTypeInfo(o); + o = getInterceptor(o); + // We can use the object as its own type representation because we install + // the subtype flags and the substitution on the prototype, so they are + // properties of the object in JS. + var type; + if (isNotNull(rti)) { + // If the type has type variables (that is, [:rti != null:]), make a copy of + // the type arguments and insert [o] in the first position to create a + // compound type representation. + type = JS('JSExtendableArray', '#.slice()', rti); + JS('', '#.splice(0, 0, #)', type, o); + } else { + // Use the object as representation of the raw type. + type = o; + } + return isSubtype(type, t); +} + +Object subtypeOfRuntimeTypeCast(Object object, var type) { + if (object != null && !checkSubtypeOfRuntimeType(object, type)) { + String actualType = Primitives.objectTypeName(object); + throw new CastErrorImplementation(actualType, runtimeTypeToString(type)); + } + return object; +} + +Object assertSubtypeOfRuntimeType(Object object, var type) { + if (object != null && !checkSubtypeOfRuntimeType(object, type)) { + throw new TypeErrorImplementation(object, runtimeTypeToString(type)); + } + return object; +} + +/** + * Extracts the type arguments from a type representation. The result is a + * JavaScript array or [:null:]. + */ +getArguments(var type) { + return isJsArray(type) ? JS('var', r'#.slice(1)', type) : null; +} + +/** + * Checks whether the type represented by the type representation [s] is a + * subtype of the type represented by the type representation [t]. + * + * See the comment in the beginning of this file for a description of type + * representations. + */ +bool isSubtype(var s, var t) { + // Subtyping is reflexive. + if (isIdentical(s, t)) return true; + // If either type is dynamic, [s] is a subtype of [t]. + if (isNull(s) || isNull(t)) return true; + if (hasField(t, '${JS_FUNCTION_TYPE_TAG()}')) { + if (hasNoField(s, '${JS_FUNCTION_TYPE_TAG()}')) { + var signatureName = + '${JS_OPERATOR_IS_PREFIX()}_${getField(t, JS_FUNCTION_TYPE_TAG())}'; + if (hasField(s, signatureName)) return true; + var targetSignatureFunction = getField(s, '${JS_SIGNATURE_NAME()}'); + if (isNull(targetSignatureFunction)) return false; + s = invokeOn(targetSignatureFunction, s, null); + } + return isFunctionSubtype(s, t); + } + // Check function types against the Function class. + if (getConstructorName(t) == JS_FUNCTION_CLASS_NAME() && + hasField(s, '${JS_FUNCTION_TYPE_TAG()}')) { + return true; + } + // Get the object describing the class and check for the subtyping flag + // constructed from the type of [t]. + var typeOfS = isJsArray(s) ? getIndex(s, 0) : s; + var typeOfT = isJsArray(t) ? getIndex(t, 0) : t; + // Check for a subtyping flag. + var name = runtimeTypeToString(typeOfT); + // Get the necessary substitution of the type arguments, if there is one. + var substitution; + if (isNotIdentical(typeOfT, typeOfS)) { + var test = '${JS_OPERATOR_IS_PREFIX()}${name}'; + if (hasNoField(typeOfS, test)) return false; + var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}'; + substitution = getField(typeOfS, field); + } + // The class of [s] is a subclass of the class of [t]. If [s] has no type + // arguments and no substitution, it is used as raw type. If [t] has no + // type arguments, it used as a raw type. In both cases, [s] is a subtype + // of [t]. + if ((!isJsArray(s) && isNull(substitution)) || !isJsArray(t)) { + return true; + } + // Recursively check the type arguments. + return checkArguments(substitution, getArguments(s), getArguments(t)); +} + +bool isAssignable(var s, var t) { + return isSubtype(s, t) || isSubtype(t, s); +} + +/** + * If [allowShorter] is [:true:], [t] is allowed to be shorter than [s]. + */ +bool areAssignable(List s, List t, bool allowShorter) { + // Both lists are empty and thus equal. + if (isNull(t) && isNull(s)) return true; + // [t] is empty (and [s] is not) => only OK if [allowShorter]. + if (isNull(t)) return allowShorter; + // [s] is empty (and [t] is not) => [s] is not longer or equal to [t]. + if (isNull(s)) return false; + + assert(isJsArray(s)); + assert(isJsArray(t)); + + int sLength = getLength(s); + int tLength = getLength(t); + if (allowShorter) { + if (sLength < tLength) return false; + } else { + if (sLength != tLength) return false; + } + + for (int i = 0; i < tLength; i++) { + if (!isAssignable(getIndex(s, i), getIndex(t, i))) { + return false; + } + } + return true; +} + +bool areAssignableMaps(var s, var t) { + if (isNull(t)) return true; + if (isNull(s)) return false; + + assert(isJsObject(s)); + assert(isJsObject(t)); + + List names = + JSArray.markFixedList(JS('', 'Object.getOwnPropertyNames(#)', t)); + for (int i = 0; i < names.length; i++) { + var name = names[i]; + if (JS('bool', '!Object.hasOwnProperty.call(#, #)', s, name)) { + return false; + } + var tType = JS('', '#[#]', t, name); + var sType = JS('', '#[#]', s, name); + if (!isAssignable(tType, sType)) return false; + } + return true; +} + +bool isFunctionSubtype(var s, var t) { + assert(hasField(t, '${JS_FUNCTION_TYPE_TAG()}')); + if (hasNoField(s, '${JS_FUNCTION_TYPE_TAG()}')) return false; + if (hasField(s, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}')) { + if (hasNoField(t, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}') && + hasField(t, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}')) { + return false; + } + } else if (hasNoField(t, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}')) { + var sReturnType = getField(s, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}'); + var tReturnType = getField(t, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}'); + if (!isAssignable(sReturnType, tReturnType)) return false; + } + var sParameterTypes = + getField(s, '${JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG()}'); + var tParameterTypes = + getField(t, '${JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG()}'); + + var sOptionalParameterTypes = + getField(s, '${JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG()}'); + var tOptionalParameterTypes = + getField(t, '${JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG()}'); + + int sParametersLen = + isNotNull(sParameterTypes) ? getLength(sParameterTypes) : 0; + int tParametersLen = + isNotNull(tParameterTypes) ? getLength(tParameterTypes) : 0; + + int sOptionalParametersLen = isNotNull(sOptionalParameterTypes) + ? getLength(sOptionalParameterTypes) : 0; + int tOptionalParametersLen = isNotNull(tOptionalParameterTypes) + ? getLength(tOptionalParameterTypes) : 0; + + if (sParametersLen > tParametersLen) { + // Too many required parameters in [s]. + return false; + } + if (sParametersLen + sOptionalParametersLen < + tParametersLen + tOptionalParametersLen) { + // Too few required and optional parameters in [s]. + return false; + } + if (sParametersLen == tParametersLen) { + // Simple case: Same number of required parameters. + if (!areAssignable(sParameterTypes, tParameterTypes, false)) return false; + if (!areAssignable(sOptionalParameterTypes, + tOptionalParameterTypes, true)) { + return false; + } + } else { + // Complex case: Optional parameters of [s] for required parameters of [t]. + int pos = 0; + // Check all required parameters of [s]. + for (; pos < sParametersLen; pos++) { + if (!isAssignable(getIndex(sParameterTypes, pos), + getIndex(tParameterTypes, pos))) { + return false; + } + } + int sPos = 0; + int tPos = pos; + // Check the remaining parameters of [t] with the first optional parameters + // of [s]. + for (; tPos < tParametersLen ; sPos++, tPos++) { + if (!isAssignable(getIndex(sOptionalParameterTypes, sPos), + getIndex(tParameterTypes, tPos))) { + return false; + } + } + tPos = 0; + // Check the optional parameters of [t] with the remaining optional + // parameters of [s]: + for (; tPos < tOptionalParametersLen ; sPos++, tPos++) { + if (!isAssignable(getIndex(sOptionalParameterTypes, sPos), + getIndex(tOptionalParameterTypes, tPos))) { + return false; + } + } + } + + var sNamedParameters = + getField(s, '${JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG()}'); + var tNamedParameters = + getField(t, '${JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG()}'); + return areAssignableMaps(sNamedParameters, tNamedParameters); +} + +/** + * Calls the JavaScript [function] with the [arguments] with the global scope + * as the [:this:] context. + */ +invoke(var function, var arguments) => invokeOn(function, null, arguments); + +/** + * Calls the JavaScript [function] with the [arguments] with [receiver] as the + * [:this:] context. + */ +Object invokeOn(function, receiver, arguments) { + assert(isJsFunction(function)); + assert(isNull(arguments) || isJsArray(arguments)); + return JS('var', r'#.apply(#, #)', function, receiver, arguments); +} + +/// Calls the property [name] on the JavaScript [object]. +call(var object, String name) => JS('var', r'#[#]()', object, name); + +/// Returns the property [name] of the JavaScript object [object]. +getField(var object, String name) => JS('var', r'#[#]', object, name); + +/// Returns the property [index] of the JavaScript array [array]. +getIndex(var array, int index) { + assert(isJsArray(array)); + return JS('var', r'#[#]', array, index); +} + +/// Returns the length of the JavaScript array [array]. +int getLength(var array) { + assert(isJsArray(array)); + return JS('int', r'#.length', array); +} + +/// Returns whether [value] is a JavaScript array. +bool isJsArray(var value) { + return value is JSArray; +} + +hasField(var object, var name) => JS('bool', r'# in #', name, object); + +hasNoField(var object, var name) => !hasField(object, name); + +/// Returns [:true:] if [o] is a JavaScript function. +bool isJsFunction(var o) => JS('bool', r'typeof # == "function"', o); + +/// Returns [:true:] if [o] is a JavaScript object. +bool isJsObject(var o) => JS('bool', r"typeof # == 'object'", o); + +/** + * Returns [:true:] if [o] is equal to [:null:], that is either [:null:] or + * [:undefined:]. We use this helper to avoid generating code under the invalid + * assumption that [o] is a Dart value. + */ +bool isNull(var o) => JS('bool', '# == null', o); + +/** + * Returns [:true:] if [o] is not equal to [:null:], that is neither [:null:] + * nor [:undefined:]. We use this helper to avoid generating code under the + * invalid assumption that [o] is a Dart value. + */ +bool isNotNull(var o) => JS('bool', '# != null', o); + +/** + * Returns [:true:] if the JavaScript values [s] and [t] are identical. We use + * this helper to avoid generating code under the invalid assumption that [s] + * and [t] are Dart values. + */ +bool isIdentical(var s, var t) => JS('bool', '# === #', s, t); + +/** + * Returns [:true:] if the JavaScript values [s] and [t] are not identical. We + * use this helper to avoid generating code under the invalid assumption that + * [s] and [t] are Dart values. + */ +bool isNotIdentical(var s, var t) => JS('bool', '# !== #', s, t); diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_string.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_string.dart new file mode 100644 index 0000000..36372f4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/js_string.dart @@ -0,0 +1,435 @@ +// Copyright (c) 2012, 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. + +part of _interceptors; + +/** + * The interceptor class for [String]. The compiler recognizes this + * class as an interceptor, and changes references to [:this:] to + * actually use the receiver of the method, which is generated as an extra + * argument added to each member. + */ +class JSString extends Interceptor implements String, JSIndexable { + const JSString(); + + int codeUnitAt(int index) { + if (index is !int) throw new ArgumentError(index); + if (index < 0) throw new RangeError.value(index); + if (index >= length) throw new RangeError.value(index); + return JS('JSUInt31', r'#.charCodeAt(#)', this, index); + } + + Iterable allMatches(String str) { + checkString(str); + return allMatchesInStringUnchecked(this, str); + } + + Match matchAsPrefix(String string, [int start = 0]) { + if (start < 0 || start > string.length) { + throw new RangeError.range(start, 0, string.length); + } + if (start + this.length > string.length) return null; + // TODO(lrn): See if this can be optimized. + for (int i = 0; i < this.length; i++) { + if (string.codeUnitAt(start + i) != this.codeUnitAt(i)) { + return null; + } + } + return new StringMatch(start, string, this); + } + + String operator +(String other) { + if (other is !String) throw new ArgumentError(other); + return JS('String', r'# + #', this, other); + } + + bool endsWith(String other) { + checkString(other); + int otherLength = other.length; + if (otherLength > length) return false; + return other == substring(length - otherLength); + } + + String replaceAll(Pattern from, String to) { + checkString(to); + return stringReplaceAllUnchecked(this, from, to); + } + + String replaceAllMapped(Pattern from, String convert(Match match)) { + return this.splitMapJoin(from, onMatch: convert); + } + + String splitMapJoin(Pattern from, + {String onMatch(Match match), + String onNonMatch(String nonMatch)}) { + return stringReplaceAllFuncUnchecked(this, from, onMatch, onNonMatch); + } + + String replaceFirst(Pattern from, String to) { + checkString(to); + return stringReplaceFirstUnchecked(this, from, to); + } + + List split(Pattern pattern) { + checkNull(pattern); + if (pattern is String) { + return JS('JSExtendableArray', r'#.split(#)', this, pattern); + } else if (pattern is JSSyntaxRegExp) { + var re = regExpGetNative(pattern); + return JS('JSExtendableArray', r'#.split(#)', this, re); + } else { + throw "String.split(Pattern) UNIMPLEMENTED"; + } + } + + bool startsWith(Pattern pattern, [int index = 0]) { + checkInt(index); + if (index < 0 || index > this.length) { + throw new RangeError.range(index, 0, this.length); + } + if (pattern is String) { + String other = pattern; + int otherLength = other.length; + int endIndex = index + otherLength; + if (endIndex > length) return false; + return other == JS('String', r'#.substring(#, #)', this, index, endIndex); + } + return pattern.matchAsPrefix(this, index) != null; + } + + String substring(int startIndex, [int endIndex]) { + checkInt(startIndex); + if (endIndex == null) endIndex = length; + checkInt(endIndex); + if (startIndex < 0 ) throw new RangeError.value(startIndex); + if (startIndex > endIndex) throw new RangeError.value(startIndex); + if (endIndex > length) throw new RangeError.value(endIndex); + return JS('String', r'#.substring(#, #)', this, startIndex, endIndex); + } + + String toLowerCase() { + return JS('String', r'#.toLowerCase()', this); + } + + String toUpperCase() { + return JS('String', r'#.toUpperCase()', this); + } + + // Characters with Whitespace property (Unicode 6.2). + // 0009..000D ; White_Space # Cc .. + // 0020 ; White_Space # Zs SPACE + // 0085 ; White_Space # Cc + // 00A0 ; White_Space # Zs NO-BREAK SPACE + // 1680 ; White_Space # Zs OGHAM SPACE MARK + // 180E ; White_Space # Zs MONGOLIAN VOWEL SEPARATOR + // 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE + // 2028 ; White_Space # Zl LINE SEPARATOR + // 2029 ; White_Space # Zp PARAGRAPH SEPARATOR + // 202F ; White_Space # Zs NARROW NO-BREAK SPACE + // 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE + // 3000 ; White_Space # Zs IDEOGRAPHIC SPACE + // + // BOM: 0xFEFF + static bool _isWhitespace(int codeUnit) { + // Most codeUnits should be less than 256. Special case with a smaller + // switch. + if (codeUnit < 256) { + switch (codeUnit) { + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x20: + case 0x85: + case 0xA0: + return true; + default: + return false; + } + } + switch (codeUnit) { + case 0x1680: + case 0x180E: + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + case 0x2004: + case 0x2005: + case 0x2006: + case 0x2007: + case 0x2008: + case 0x2009: + case 0x200A: + case 0x2028: + case 0x2029: + case 0x202F: + case 0x205F: + case 0x3000: + case 0xFEFF: + return true; + default: + return false; + } + } + + /// Finds the index of the first non-whitespace character, or the + /// end of the string. Start looking at position [index]. + static int _skipLeadingWhitespace(String string, int index) { + const int SPACE = 0x20; + const int CARRIAGE_RETURN = 0x0D; + while (index < string.length) { + int codeUnit = string.codeUnitAt(index); + if (codeUnit != SPACE && + codeUnit != CARRIAGE_RETURN && + !_isWhitespace(codeUnit)) { + break; + } + index++; + } + return index; + } + + /// Finds the index after the the last non-whitespace character, or 0. + /// Start looking at position [index - 1]. + static int _skipTrailingWhitespace(String string, int index) { + const int SPACE = 0x20; + const int CARRIAGE_RETURN = 0x0D; + while (index > 0) { + int codeUnit = string.codeUnitAt(index - 1); + if (codeUnit != SPACE && + codeUnit != CARRIAGE_RETURN && + !_isWhitespace(codeUnit)) { + break; + } + index--; + } + return index; + } + + // Dart2js can't use JavaScript trim directly, + // because JavaScript does not trim + // the NEXT LINE (NEL) character (0x85). + String trim() { + const int NEL = 0x85; + + // Start by doing JS trim. Then check if it leaves a NEL at + // either end of the string. + String result = JS('String', '#.trim()', this); + if (result.length == 0) return result; + int firstCode = result.codeUnitAt(0); + int startIndex = 0; + if (firstCode == NEL) { + startIndex = _skipLeadingWhitespace(result, 1); + if (startIndex == result.length) return ""; + } + + int endIndex = result.length; + // We know that there is at least one character that is non-whitespace. + // Therefore we don't need to verify that endIndex > startIndex. + int lastCode = result.codeUnitAt(endIndex - 1); + if (lastCode == NEL) { + endIndex = _skipTrailingWhitespace(result, endIndex - 1); + } + if (startIndex == 0 && endIndex == result.length) return result; + return JS('String', r'#.substring(#, #)', result, startIndex, endIndex); + } + + // Dart2js can't use JavaScript trimLeft directly, + // because it is not in ES5, so not every browser implements it, + // and because those that do will not trim the NEXT LINE character (0x85). + String trimLeft() { + const int NEL = 0x85; + + // Start by doing JS trim. Then check if it leaves a NEL at + // the beginning of the string. + String result; + int startIndex = 0; + if (JS('bool', 'typeof #.trimLeft != "undefined"', this)) { + result = JS('String', '#.trimLeft()', this); + if (result.length == 0) return result; + int firstCode = result.codeUnitAt(0); + if (firstCode == NEL) { + startIndex = _skipLeadingWhitespace(result, 1); + } + } else { + result = this; + startIndex = _skipLeadingWhitespace(this, 0); + } + if (startIndex == 0) return result; + if (startIndex == result.length) return ""; + return JS('String', r'#.substring(#)', result, startIndex); + } + + // Dart2js can't use JavaScript trimRight directly, + // because it is not in ES5 and because JavaScript does not trim + // the NEXT LINE character (0x85). + String trimRight() { + const int NEL = 0x85; + + // Start by doing JS trim. Then check if it leaves a NEL or BOM at + // the end of the string. + String result; + int endIndex; + // trimRight is implemented by Firefox and Chrome/Blink, + // so use it if it is there. + if (JS('bool', 'typeof #.trimRight != "undefined"', this)) { + result = JS('String', '#.trimRight()', this); + endIndex = result.length; + if (endIndex == 0) return result; + int lastCode = result.codeUnitAt(endIndex - 1); + if (lastCode == NEL) { + endIndex = _skipTrailingWhitespace(result, endIndex - 1); + } + } else { + result = this; + endIndex = _skipTrailingWhitespace(this, this.length); + } + + if (endIndex == result.length) return result; + if (endIndex == 0) return ""; + return JS('String', r'#.substring(#, #)', result, 0, endIndex); + } + + String operator*(int times) { + if (0 >= times) return ''; // Unnecessary but hoists argument type check. + if (times == 1 || this.length == 0) return this; + if (times != JS('JSUInt32', '# >>> 0', times)) { + // times >= 2^32. We can't create a string that big. + throw const OutOfMemoryError(); + } + var result = ''; + var s = this; + while (true) { + if (times & 1 == 1) result = s + result; + times = JS('JSUInt31', '# >>> 1', times); + if (times == 0) break; + s += s; + } + return result; + } + + String padLeft(int width, [String padding = ' ']) { + int delta = width - this.length; + if (delta <= 0) return this; + return padding * delta + this; + } + + String padRight(int width, [String padding = ' ']) { + int delta = width - this.length; + if (delta <= 0) return this; + return this + padding * delta; + } + + List get codeUnits => new _CodeUnits(this); + + Runes get runes => new Runes(this); + + int indexOf(Pattern pattern, [int start = 0]) { + checkNull(pattern); + if (start is! int) throw new ArgumentError(start); + if (start < 0 || start > this.length) { + throw new RangeError.range(start, 0, this.length); + } + if (pattern is String) { + return JS('int', r'#.indexOf(#, #)', this, pattern, start); + } + if (pattern is JSSyntaxRegExp) { + JSSyntaxRegExp re = pattern; + Match match = firstMatchAfter(re, this, start); + return (match == null) ? -1 : match.start; + } + for (int i = start; i <= this.length; i++) { + if (pattern.matchAsPrefix(this, i) != null) return i; + } + return -1; + } + + int lastIndexOf(Pattern pattern, [int start]) { + checkNull(pattern); + if (start == null) { + start = length; + } else if (start is! int) { + throw new ArgumentError(start); + } else if (start < 0 || start > this.length) { + throw new RangeError.range(start, 0, this.length); + } + if (pattern is String) { + String other = pattern; + if (start + other.length > this.length) { + start = this.length - other.length; + } + return stringLastIndexOfUnchecked(this, other, start); + } + for (int i = start; i >= 0; i--) { + if (pattern.matchAsPrefix(this, i) != null) return i; + } + return -1; + } + + bool contains(Pattern other, [int startIndex = 0]) { + checkNull(other); + if (startIndex < 0 || startIndex > this.length) { + throw new RangeError.range(startIndex, 0, this.length); + } + return stringContainsUnchecked(this, other, startIndex); + } + + bool get isEmpty => length == 0; + + bool get isNotEmpty => !isEmpty; + + int compareTo(String other) { + if (other is !String) throw new ArgumentError(other); + return this == other ? 0 + : JS('bool', r'# < #', this, other) ? -1 : 1; + } + + // Note: if you change this, also change the function [S]. + String toString() => this; + + /** + * This is the [Jenkins hash function][1] but using masking to keep + * values in SMI range. + * + * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function + */ + int get hashCode { + // TODO(ahe): This method shouldn't have to use JS. Update when our + // optimizations are smarter. + int hash = 0; + for (int i = 0; i < length; i++) { + hash = 0x1fffffff & (hash + JS('int', r'#.charCodeAt(#)', this, i)); + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + hash = JS('int', '# ^ (# >> 6)', hash, hash); + } + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + hash = JS('int', '# ^ (# >> 11)', hash, hash); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } + + Type get runtimeType => String; + + int get length => JS('int', r'#.length', this); + + String operator [](int index) { + if (index is !int) throw new ArgumentError(index); + if (index >= length || index < 0) throw new RangeError.value(index); + return JS('String', '#[#]', this, index); + } +} + +/** + * An [Iterable] of the UTF-16 code units of a [String] in index order. + */ +class _CodeUnits extends UnmodifiableListBase { + /** The string that this is the code units of. */ + String _string; + + _CodeUnits(this._string); + + int get length => _string.length; + int operator[](int i) => _string.codeUnitAt(i); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/math_patch.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/math_patch.dart new file mode 100644 index 0000000..2ba47aa --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/math_patch.dart @@ -0,0 +1,225 @@ +// Copyright (c) 2012, 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. + +// Patch file for dart:math library. +import 'dart:_foreign_helper' show JS; +import 'dart:_js_helper' show checkNum; + +patch double sqrt(num x) + => JS('double', r'Math.sqrt(#)', checkNum(x)); + +patch double sin(num x) + => JS('double', r'Math.sin(#)', checkNum(x)); + +patch double cos(num x) + => JS('double', r'Math.cos(#)', checkNum(x)); + +patch double tan(num x) + => JS('double', r'Math.tan(#)', checkNum(x)); + +patch double acos(num x) + => JS('double', r'Math.acos(#)', checkNum(x)); + +patch double asin(num x) + => JS('double', r'Math.asin(#)', checkNum(x)); + +patch double atan(num x) + => JS('double', r'Math.atan(#)', checkNum(x)); + +patch double atan2(num a, num b) + => JS('double', r'Math.atan2(#, #)', checkNum(a), checkNum(b)); + +patch double exp(num x) + => JS('double', r'Math.exp(#)', checkNum(x)); + +patch double log(num x) + => JS('double', r'Math.log(#)', checkNum(x)); + +patch num pow(num x, num exponent) { + checkNum(x); + checkNum(exponent); + return JS('num', r'Math.pow(#, #)', x, exponent); +} + +const int _POW2_32 = 0x100000000; + +patch class Random { + patch factory Random([int seed]) => + (seed == null) ? const _JSRandom() : new _Random(seed); +} + +class _JSRandom implements Random { + // The Dart2JS implementation of Random doesn't use a seed. + const _JSRandom(); + + int nextInt(int max) { + if (max <= 0 || max > _POW2_32) { + throw new RangeError("max must be in range 0 < max ≤ 2^32, was $max"); + } + return JS("int", "(Math.random() * #) >>> 0", max); + } + + /** + * Generates a positive random floating point value uniformly distributed on + * the range from 0.0, inclusive, to 1.0, exclusive. + */ + double nextDouble() => JS("double", "Math.random()"); + + /** + * Generates a random boolean value. + */ + bool nextBool() => JS("bool", "Math.random() < 0.5"); +} + + +class _Random implements Random { + // Constants used by the algorithm or masking. + static const double _POW2_53_D = 1.0 * (0x20000000000000); + static const double _POW2_27_D = 1.0 * (1 << 27); + static const int _MASK32 = 0xFFFFFFFF; + + // State comprised of two unsigned 32 bit integers. + int _lo = 0; + int _hi = 0; + + // Implements: + // uint64_t hash = 0; + // do { + // hash = hash * 1037 ^ mix64((uint64_t)seed); + // seed >>= 64; + // } while (seed != 0 && seed != -1); // Limits for pos/neg seed. + // if (hash == 0) { + // hash = 0x5A17; + // } + // _lo = hash & _MASK_32; + // _hi = hash >> 32; + // and then does four _nextState calls to shuffle bits around. + _Random(int seed) { + int empty_seed = 0; + if (seed < 0) { + empty_seed = -1; + } + do { + int low = seed & _MASK32; + seed = (seed - low) ~/ _POW2_32; + int high = seed & _MASK32; + seed = (seed - high) ~/ _POW2_32; + + // Thomas Wang's 64-bit mix function. + // http://www.concentric.net/~Ttwang/tech/inthash.htm + // via. http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm + + // key = ~key + (key << 21); + int tmplow = low << 21; + int tmphigh = (high << 21) | (low >> 11); + tmplow = (~low & _MASK32) + tmplow; + low = tmplow & _MASK32; + high = (~high + tmphigh + ((tmplow - low) ~/ 0x100000000)) & _MASK32; + // key = key ^ (key >> 24). + tmphigh = high >> 24; + tmplow = (low >> 24) | (high << 8); + low ^= tmplow; + high ^= tmphigh; + // key = key * 265 + tmplow = low * 265; + low = tmplow & _MASK32; + high = (high * 265 + (tmplow - low) ~/ 0x100000000) & _MASK32; + // key = key ^ (key >> 14); + tmphigh = high >> 14; + tmplow = (low >> 14) | (high << 18); + low ^= tmplow; + high ^= tmphigh; + // key = key * 21 + tmplow = low * 21; + low = tmplow & _MASK32; + high = (high * 21 + (tmplow - low) ~/ 0x100000000) & _MASK32; + // key = key ^ (key >> 28). + tmphigh = high >> 28; + tmplow = (low >> 28) | (high << 4); + low ^= tmplow; + high ^= tmphigh; + // key = key + (key << 31); + tmplow = low << 31; + tmphigh = (high << 31) | (low >> 1); + tmplow += low; + low = tmplow & _MASK32; + high = (high + tmphigh + (tmplow - low) ~/ 0x100000000) & _MASK32; + // Mix end. + + // seed = seed * 1037 ^ key; + tmplow = _lo * 1037; + _lo = tmplow & _MASK32; + _hi = (_hi * 1037 + (tmplow - _lo) ~/ 0x100000000) & _MASK32; + _lo ^= low; + _hi ^= high; + } while (seed != empty_seed); + + if (_hi == 0 && _lo == 0) { + _lo = 0x5A17; + } + _nextState(); + _nextState(); + _nextState(); + _nextState(); + } + + // The algorithm used here is Multiply with Carry (MWC) with a Base b = 2^32. + // http://en.wikipedia.org/wiki/Multiply-with-carry + // The constant A (0xFFFFDA61) is selected from "Numerical Recipes 3rd + // Edition" p.348 B1. + + // Implements: + // var state = (A * _lo + _hi) & _MASK_64; + // _lo = state & _MASK_32; + // _hi = state >> 32; + void _nextState() { + // Simulate (0xFFFFDA61 * lo + hi) without overflowing 53 bits. + int tmpHi = 0xFFFF0000 * _lo; // At most 48 bits of significant result. + int tmpHiLo = tmpHi & _MASK32; // Get the lower 32 bits. + int tmpHiHi = tmpHi - tmpHiLo; // And just the upper 32 bits. + int tmpLo = 0xDA61 * _lo; + int tmpLoLo = tmpLo & _MASK32; + int tmpLoHi = tmpLo - tmpLoLo; + + int newLo = tmpLoLo + tmpHiLo + _hi; + _lo = newLo & _MASK32; + int newLoHi = newLo - _lo; + _hi = ((tmpLoHi + tmpHiHi + newLoHi) ~/ _POW2_32) & _MASK32; + assert(_lo < _POW2_32); + assert(_hi < _POW2_32); + } + + int nextInt(int max) { + if (max <= 0 || max > _POW2_32) { + throw new RangeError("max must be in range 0 < max ≤ 2^32, was $max"); + } + if ((max & (max - 1)) == 0) { + // Fast case for powers of two. + _nextState(); + return _lo & (max - 1); + } + + int rnd32; + int result; + do { + _nextState(); + rnd32 = _lo; + result = rnd32.remainder(max); // % max; + } while ((rnd32 - result + max) >= _POW2_32); + return result; + } + + double nextDouble() { + _nextState(); + int bits26 = _lo & ((1 << 26) - 1); + _nextState(); + int bits27 = _lo & ((1 << 27) - 1); + return (bits26 * _POW2_27_D + bits27) / _POW2_53_D; + } + + bool nextBool() { + _nextState(); + return (_lo & 1) == 0; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/mirror_helper.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/mirror_helper.dart new file mode 100644 index 0000000..9c3024f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/mirror_helper.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2013, 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. +/** + * Helps dealing with reflection in the case that the source code has been + * changed as a result of compiling with dart2dart. + */ +library _mirror_helper; + +import 'dart:mirrors'; + +/// The compiler will replace this variable with a map containing all the +/// renames made in dart2dart. +const Map _SYMBOLS = null; + +/// This method is a wrapper for MirrorSystem.getName() and will be inlined and +/// called in the generated output Dart code. +String helperGetName(Symbol sym) { + var name = MirrorSystem.getName(sym); + if (_SYMBOLS.containsKey(name)) { + return _SYMBOLS[name]; + } else { + return name; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/mirrors_patch.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/mirrors_patch.dart new file mode 100644 index 0000000..5d4a5fa --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/mirrors_patch.dart @@ -0,0 +1,37 @@ +// Copyright (c) 2012, 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. + +// Patch library for dart:mirrors. + +import 'dart:_js_mirrors' as js; + +patch class MirrorSystem { + patch static String getName(Symbol symbol) => js.getName(symbol); + + patch static Symbol getSymbol(String name, [LibraryMirror library]) { + return js.getSymbol(name, library); + } +} + +patch MirrorSystem currentMirrorSystem() => js.currentJsMirrorSystem; + +patch InstanceMirror reflect(Object reflectee) => js.reflect(reflectee); + +patch ClassMirror reflectClass(Type key) { + if (key is! Type || key == dynamic) { + throw new ArgumentError('$key does not denote a class'); + } + TypeMirror tm = reflectType(key); + if (tm is! ClassMirror) { + throw new ArgumentError("$key does not denote a class"); + } + return (tm as ClassMirror).originalDeclaration; +} + +patch TypeMirror reflectType(Type key) { + if (key == dynamic) { + return currentMirrorSystem().dynamicType; + } + return js.reflectType(key); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/native_helper.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/native_helper.dart new file mode 100644 index 0000000..6608481 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/native_helper.dart @@ -0,0 +1,637 @@ +// Copyright (c) 2012, 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. + +part of _js_helper; + + +// TODO(ngeoffray): stop using this method once our optimizers can +// change str1.contains(str2) into str1.indexOf(str2) != -1. +bool contains(String userAgent, String name) { + return JS('int', '#.indexOf(#)', userAgent, name) != -1; +} + +int arrayLength(List array) { + return JS('int', '#.length', array); +} + +arrayGet(List array, int index) { + return JS('var', '#[#]', array, index); +} + +void arraySet(List array, int index, var value) { + JS('var', '#[#] = #', array, index, value); +} + +propertyGet(var object, String property) { + return JS('var', '#[#]', object, property); +} + +bool callHasOwnProperty(var function, var object, String property) { + return JS('bool', '#.call(#, #)', function, object, property); +} + +void propertySet(var object, String property, var value) { + JS('var', '#[#] = #', object, property, value); +} + +getPropertyFromPrototype(var object, String name) { + return JS('var', 'Object.getPrototypeOf(#)[#]', object, name); +} + +newJsObject() { + return JS('var', '{}'); +} + +/** + * Returns a String tag identifying the type of the native object, or `null`. + * The tag is not the name of the type, but usually the name of the JavaScript + * constructor function. Initialized by [initHooks]. + */ +Function getTagFunction; + +/** + * If a lookup via [getTagFunction] on an object [object] that has [tag] fails, + * this function is called to provide an alternate tag. This allows us to fail + * gracefully if we can make a good guess, for example, when browsers add novel + * kinds of HTMLElement that we have never heard of. Initialized by + * [initHooks]. + */ +Function alternateTagFunction; + +/** + * Returns the prototype for the JavaScript constructor named by an input tag. + * Returns `null` if there is no such constructor, or if pre-patching of the + * constructor is to be avoided. Initialized by [initHooks]. + */ +Function prototypeForTagFunction; + + +String toStringForNativeObject(var obj) { + // TODO(sra): Is this code dead? + // [getTagFunction] might be uninitialized, but in usual usage, toString has + // been called via an interceptor and initialized it. + String name = getTagFunction == null + ? '' + : JS('String', '#', getTagFunction(obj)); + return 'Instance of $name'; +} + +int hashCodeForNativeObject(object) => Primitives.objectHashCode(object); + +/** + * Sets a JavaScript property on an object. + */ +void defineProperty(var obj, String property, var value) { + JS('void', + 'Object.defineProperty(#, #, ' + '{value: #, enumerable: false, writable: true, configurable: true})', + obj, + property, + value); +} + + +// Is [obj] an instance of a Dart-defined class? +bool isDartObject(obj) { + // Some of the extra parens here are necessary. + return JS('bool', '((#) instanceof (#))', obj, JS_DART_OBJECT_CONSTRUCTOR()); +} + +/** + * A JavaScript object mapping tags to the constructors of interceptors. + * This is a JavaScript object with no prototype. + * + * Example: 'HTMLImageElement' maps to the ImageElement class constructor. + */ +get interceptorsByTag => JS('=Object', 'init.interceptorsByTag'); + +/** + * A JavaScript object mapping tags to `true` or `false`. + * + * Example: 'HTMLImageElement' maps to `true` since, as there are no subclasses + * of ImageElement, it is a leaf class in the native class hierarchy. + */ +get leafTags => JS('=Object', 'init.leafTags'); + +String findDispatchTagForInterceptorClass(interceptorClassConstructor) { + return JS('', r'#.$nativeSuperclassTag', interceptorClassConstructor); +} + +/** + * Cache of dispatch records for instances. This is a JavaScript object used as + * a map. Keys are instance tags, e.g. "!SomeThing". The cache permits the + * sharing of one dispatch record between multiple instances. + */ +var dispatchRecordsForInstanceTags; + +/** + * Cache of interceptors indexed by uncacheable tags, e.g. "~SomeThing". + * This is a JavaScript object used as a map. + */ +var interceptorsForUncacheableTags; + + +lookupInterceptor(String tag) { + return propertyGet(interceptorsByTag, tag); +} + + +// Dispatch tag marks are optional prefixes for a dispatch tag that direct how +// the interceptor for the tag may be cached. + +/// No caching permitted. +const UNCACHED_MARK = '~'; + +/// Dispatch record must be cached per instance +const INSTANCE_CACHED_MARK = '!'; + +/// Dispatch record is cached on immediate prototype. +const LEAF_MARK = '-'; + +/// Dispatch record is cached on immediate prototype with a prototype +/// verification to prevent the interceptor being associated with a subclass +/// before a dispatch record is cached on the subclass. +const INTERIOR_MARK = '+'; + +/// A 'discriminator' function is to be used. TBD. +const DISCRIMINATED_MARK = '*'; + + +/** + * Returns the interceptor for a native object, or returns `null` if not found. + * + * A dispatch record is cached according to the specification of the dispatch + * tag for [obj]. + */ +lookupAndCacheInterceptor(obj) { + assert(!isDartObject(obj)); + String tag = getTagFunction(obj); + + // Fast path for instance (and uncached) tags because the lookup is repeated + // for each instance (or getInterceptor call). + var record = propertyGet(dispatchRecordsForInstanceTags, tag); + if (record != null) return patchInstance(obj, record); + var interceptor = propertyGet(interceptorsForUncacheableTags, tag); + if (interceptor != null) return interceptor; + + // This lookup works for derived dispatch tags because we add them all in + // [initNativeDispatch]. + var interceptorClass = lookupInterceptor(tag); + if (interceptorClass == null) { + tag = alternateTagFunction(obj, tag); + if (tag != null) { + // Fast path for instance and uncached tags again. + record = propertyGet(dispatchRecordsForInstanceTags, tag); + if (record != null) return patchInstance(obj, record); + interceptor = propertyGet(interceptorsForUncacheableTags, tag); + if (interceptor != null) return interceptor; + + interceptorClass = lookupInterceptor(tag); + } + } + + if (interceptorClass == null) { + // This object is not known to Dart. There could be several reasons for + // that, including (but not limited to): + // + // * A bug in native code (hopefully this is caught during development). + // * An unknown DOM object encountered. + // * JavaScript code running in an unexpected context. For example, on + // node.js. + return null; + } + + interceptor = JS('', '#.prototype', interceptorClass); + + var mark = JS('String|Null', '#[0]', tag); + + if (mark == INSTANCE_CACHED_MARK) { + record = makeLeafDispatchRecord(interceptor); + propertySet(dispatchRecordsForInstanceTags, tag, record); + return patchInstance(obj, record); + } + + if (mark == UNCACHED_MARK) { + propertySet(interceptorsForUncacheableTags, tag, interceptor); + return interceptor; + } + + if (mark == LEAF_MARK) { + return patchProto(obj, makeLeafDispatchRecord(interceptor)); + } + + if (mark == INTERIOR_MARK) { + return patchInteriorProto(obj, interceptor); + } + + if (mark == DISCRIMINATED_MARK) { + // TODO(sra): Use discriminator of tag. + throw new UnimplementedError(tag); + } + + // [tag] was not explicitly an interior or leaf tag, so + var isLeaf = JS('bool', '(#[#]) === true', leafTags, tag); + if (isLeaf) { + return patchProto(obj, makeLeafDispatchRecord(interceptor)); + } else { + return patchInteriorProto(obj, interceptor); + } +} + +patchInstance(obj, record) { + setDispatchProperty(obj, record); + return dispatchRecordInterceptor(record); +} + +patchProto(obj, record) { + setDispatchProperty(JS('', 'Object.getPrototypeOf(#)', obj), record); + return dispatchRecordInterceptor(record); +} + +patchInteriorProto(obj, interceptor) { + var proto = JS('', 'Object.getPrototypeOf(#)', obj); + var record = makeDispatchRecord(interceptor, proto, null, null); + setDispatchProperty(proto, record); + return interceptor; +} + + +makeLeafDispatchRecord(interceptor) { + var fieldName = JS_IS_INDEXABLE_FIELD_NAME(); + bool indexability = JS('bool', r'!!#[#]', interceptor, fieldName); + return makeDispatchRecord(interceptor, false, null, indexability); +} + +makeDefaultDispatchRecord(tag, interceptorClass, proto) { + var interceptor = JS('', '#.prototype', interceptorClass); + var isLeaf = JS('bool', '(#[#]) === true', leafTags, tag); + if (isLeaf) { + return makeLeafDispatchRecord(interceptor); + } else { + return makeDispatchRecord(interceptor, proto, null, null); + } +} + +/** + * [proto] should have no shadowing prototypes that are not also assigned a + * dispatch rescord. + */ +setNativeSubclassDispatchRecord(proto, interceptor) { + setDispatchProperty(proto, makeLeafDispatchRecord(interceptor)); +} + +String constructorNameFallback(object) { + return JS('String', '#(#)', _constructorNameFallback, object); +} + + +var initNativeDispatchFlag; // null or true + +void initNativeDispatch() { + if (true == initNativeDispatchFlag) return; + initNativeDispatchFlag = true; + initNativeDispatchContinue(); +} + +void initNativeDispatchContinue() { + + dispatchRecordsForInstanceTags = JS('', 'Object.create(null)'); + interceptorsForUncacheableTags = JS('', 'Object.create(null)'); + + initHooks(); + + // Try to pro-actively patch prototypes of DOM objects. For each of our known + // tags `TAG`, if `window.TAG` is a (constructor) function, set the dispatch + // property if the function's prototype to a dispatch record. + var map = interceptorsByTag; + var tags = JS('JSMutableArray', 'Object.getOwnPropertyNames(#)', map); + + if (JS('bool', 'typeof window != "undefined"')) { + var context = JS('=Object', 'window'); + for (int i = 0; i < tags.length; i++) { + var tag = tags[i]; + var proto = prototypeForTagFunction(tag); + if (proto != null) { + var interceptorClass = JS('', '#[#]', map, tag); + var record = makeDefaultDispatchRecord(tag, interceptorClass, proto); + if (record != null) { + setDispatchProperty(proto, record); + } + } + } + } + + // [interceptorsByTag] maps 'plain' dispatch tags. Add all the derived + // dispatch tags to simplify lookup of derived tags. + for (int i = 0; i < tags.length; i++) { + var tag = JS('String', '#[#]', tags, i); + if (JS('bool', '/^[A-Za-z_]/.test(#)', tag)) { + var interceptorClass = propertyGet(map, tag); + propertySet(map, INSTANCE_CACHED_MARK + tag, interceptorClass); + propertySet(map, UNCACHED_MARK + tag, interceptorClass); + propertySet(map, LEAF_MARK + tag, interceptorClass); + propertySet(map, INTERIOR_MARK + tag, interceptorClass); + propertySet(map, DISCRIMINATED_MARK + tag, interceptorClass); + } + } +} + + +/** + * Initializes [getTagFunction] and [alternateTagFunction]. + * + * These functions are 'hook functions', collectively 'hooks'. They initialized + * by applying a series of hooks transformers. Built-in hooks transformers deal + * with various known browser behaviours. + * + * Each hook tranformer takes a 'hooks' input which is a JavaScript object + * containing the hook functions, and returns the same or a new object with + * replacements. The replacements can wrap the originals to provide alternate + * or modified behaviour. + * + * { getTag: function(obj) {...}, + * getUnknownTag: function(obj, tag) {...}, + * prototypeForTag: function(tag) {...}, + * discriminator: function(tag) {...}, + * } + * + * * getTag(obj) returns the dispatch tag, or `null`. + * * getUnknownTag(obj, tag) returns a tag when [getTag] fails. + * * prototypeForTag(tag) returns the prototype of the constructor for tag, + * or `null` if not available or prepatching is undesirable. + * * discriminator(tag) returns a function TBD. + * + * The web site can adapt a dart2js application by loading code ahead of the + * dart2js application that defines hook transformers to be after the built in + * ones. Code defining a transformer HT should use the following pattern to + * ensure multiple transformers can be composed: + * + * (dartNativeDispatchHooksTransformer = + * window.dartNativeDispatchHooksTransformer || []).push(HT); + * + * + * TODO: Implement and describe dispatch tags and their caching methods. + */ +void initHooks() { + // The initial simple hooks: + var hooks = JS('', '#()', _baseHooks); + + // Customize for browsers where `object.constructor.name` fails: + var _fallbackConstructorHooksTransformer = + JS('', '#(#)', _fallbackConstructorHooksTransformerGenerator, + _constructorNameFallback); + hooks = applyHooksTransformer(_fallbackConstructorHooksTransformer, hooks); + + // Customize for browsers: + hooks = applyHooksTransformer(_firefoxHooksTransformer, hooks); + hooks = applyHooksTransformer(_ieHooksTransformer, hooks); + hooks = applyHooksTransformer(_operaHooksTransformer, hooks); + hooks = applyHooksTransformer(_safariHooksTransformer, hooks); + + hooks = applyHooksTransformer(_fixDocumentHooksTransformer, hooks); + + // TODO(sra): Update ShadowDOM polyfil to use + // [dartNativeDispatchHooksTransformer] and remove this hook. + hooks = applyHooksTransformer(_dartExperimentalFixupGetTagHooksTransformer, + hooks); + + // Apply global hooks. + // + // If defined, dartNativeDispatchHookdTransformer should be a single function + // of a JavaScript Array of functions. + + if (JS('bool', 'typeof dartNativeDispatchHooksTransformer != "undefined"')) { + var transformers = JS('', 'dartNativeDispatchHooksTransformer'); + if (JS('bool', 'typeof # == "function"', transformers)) { + transformers = [transformers]; + } + if (JS('bool', '#.constructor == Array', transformers)) { + for (int i = 0; i < JS('int', '#.length', transformers); i++) { + var transformer = JS('', '#[#]', transformers, i); + if (JS('bool', 'typeof # == "function"', transformer)) { + hooks = applyHooksTransformer(transformer, hooks); + } + } + } + } + + var getTag = JS('', '#.getTag', hooks); + var getUnknownTag = JS('', '#.getUnknownTag', hooks); + var prototypeForTag = JS('', '#.prototypeForTag', hooks); + + getTagFunction = (o) => JS('String|Null', '#(#)', getTag, o); + alternateTagFunction = + (o, String tag) => JS('String|Null', '#(#, #)', getUnknownTag, o, tag); + prototypeForTagFunction = + (String tag) => JS('', '#(#)', prototypeForTag, tag); +} + +applyHooksTransformer(transformer, hooks) { + var newHooks = JS('=Object|Null', '#(#)', transformer, hooks); + return JS('', '# || #', newHooks, hooks); +} + +// JavaScript code fragments. +// +// This is a temporary place for the JavaScript code. +// +// TODO(sra): These code fragments are not minified. They could be generated by +// the code emitter, or JS_CONST could be improved to parse entire functions and +// take care of the minification. + +const _baseHooks = const JS_CONST(r''' +function() { + function typeNameInChrome(o) { + var name = o.constructor.name; + if (name) return name; + var s = Object.prototype.toString.call(o); + return s.substring(8, s.length - 1); + } + function getUnknownTag(object, tag) { + // This code really belongs in [getUnknownTagGenericBrowser] but having it + // here allows [getUnknownTag] to be tested on d8. + if (/^HTML[A-Z].*Element$/.test(tag)) { + // Check that it is not a simple JavaScript object. + var name = Object.prototype.toString.call(object); + if (name == "[object Object]") return null; + return "HTMLElement"; + } + } + function getUnknownTagGenericBrowser(object, tag) { + if (object instanceof HTMLElement) return "HTMLElement"; + return getUnknownTag(object, tag); + } + function prototypeForTag(tag) { + if (typeof window == "undefined") return null; + if (typeof window[tag] == "undefined") return null; + var constructor = window[tag]; + if (typeof constructor != "function") return null; + return constructor.prototype; + } + function discriminator(tag) { return null; } + + var isBrowser = typeof navigator == "object"; + + return { + getTag: typeNameInChrome, + getUnknownTag: isBrowser ? getUnknownTagGenericBrowser : getUnknownTag, + prototypeForTag: prototypeForTag, + discriminator: discriminator }; +}'''); + + +/** + * Returns the name of the constructor function for browsers where + * `object.constructor.name` is not reliable. + * + * This function is split out of [_fallbackConstructorHooksTransformerGenerator] + * as it is called from both the dispatch hooks and via + * [constructorNameFallback] from objectToString. + */ +const _constructorNameFallback = const JS_CONST(r''' +function getTagFallback(o) { + var constructor = o.constructor; + if (typeof constructor == "function") { + var name = constructor.name; + // If the name is a non-empty string, we use that as the type name of this + // object. On Firefox, we often get "Object" as the constructor name even + // for more specialized objects so we have to fall through to the toString() + // based implementation below in that case. + if (typeof name == "string" + && name !== "" + && name !== "Object" + && name !== "Function.prototype") { // Can happen in Opera. + return name; + } + } + var s = Object.prototype.toString.call(o); + return s.substring(8, s.length - 1); +}'''); + + +const _fallbackConstructorHooksTransformerGenerator = const JS_CONST(r''' +function(getTagFallback) { + return function(hooks) { + // If we are not in a browser, assume we are in d8. + // TODO(sra): Recognize jsshell. + if (typeof navigator != "object") return hooks; + + var ua = navigator.userAgent; + // TODO(antonm): remove a reference to DumpRenderTree. + if (ua.indexOf("DumpRenderTree") >= 0) return hooks; + if (ua.indexOf("Chrome") >= 0) { + // Confirm constructor name is usable for dispatch. + function confirm(p) { + return typeof window == "object" && window[p] && window[p].name == p; + } + if (confirm("Window") && confirm("HTMLElement")) return hooks; + } + + hooks.getTag = getTagFallback; + }; +}'''); + + +const _ieHooksTransformer = const JS_CONST(r''' +function(hooks) { + var userAgent = typeof navigator == "object" ? navigator.userAgent : ""; + if (userAgent.indexOf("Trident/") == -1) return hooks; + + var getTag = hooks.getTag; + + var quickMap = { + "BeforeUnloadEvent": "Event", + "DataTransfer": "Clipboard", + "HTMLDDElement": "HTMLElement", + "HTMLDTElement": "HTMLElement", + "HTMLPhraseElement": "HTMLElement", + "Position": "Geoposition" + }; + + function getTagIE(o) { + var tag = getTag(o); + var newTag = quickMap[tag]; + if (newTag) return newTag; + // Patches for types which report themselves as Objects. + if (tag == "Object") { + if (window.DataView && (o instanceof window.DataView)) return "DataView"; + } + return tag; + } + + function prototypeForTagIE(tag) { + var constructor = window[tag]; + if (constructor == null) return null; + return constructor.prototype; + } + + hooks.getTag = getTagIE; + hooks.prototypeForTag = prototypeForTagIE; +}'''); + +const _fixDocumentHooksTransformer = const JS_CONST(r''' +function(hooks) { + var getTag = hooks.getTag; + var prototypeForTag = hooks.prototypeForTag; + function getTagFixed(o) { + var tag = getTag(o); + if (tag == "Document") { + // Some browsers and the polymer polyfill call both HTML and XML documents + // "Document", so we check for the xmlVersion property, which is the empty + // string on HTML documents. Since both dart:html classes Document and + // HtmlDocument share the same type, we must patch the instances and not + // the prototype. + if (!!o.xmlVersion) return "!Document"; + return "!HTMLDocument"; + } + return tag; + } + + function prototypeForTagFixed(tag) { + if (tag == "Document") return null; // Do not pre-patch Document. + return prototypeForTag(tag); + } + + hooks.getTag = getTagFixed; + hooks.prototypeForTag = prototypeForTagFixed; +}'''); + +const _firefoxHooksTransformer = const JS_CONST(r''' +function(hooks) { + var userAgent = typeof navigator == "object" ? navigator.userAgent : ""; + if (userAgent.indexOf("Firefox") == -1) return hooks; + + var getTag = hooks.getTag; + + var quickMap = { + "BeforeUnloadEvent": "Event", + "DataTransfer": "Clipboard", + "GeoGeolocation": "Geolocation", + "WorkerMessageEvent": "MessageEvent", + "XMLDocument": "!Document"}; + + function getTagFirefox(o) { + var tag = getTag(o); + return quickMap[tag] || tag; + } + + hooks.getTag = getTagFirefox; +}'''); + + +const _operaHooksTransformer = const JS_CONST(r''' +function(hooks) { return hooks; } +'''); + + +const _safariHooksTransformer = const JS_CONST(r''' +function(hooks) { return hooks; } +'''); + + +const _dartExperimentalFixupGetTagHooksTransformer = const JS_CONST(r''' +function(hooks) { + if (typeof dartExperimentalFixupGetTag != "function") return hooks; + hooks.getTag = dartExperimentalFixupGetTag(hooks.getTag); +}'''); diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/regexp_helper.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/regexp_helper.dart new file mode 100644 index 0000000..1409c9b --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/regexp_helper.dart @@ -0,0 +1,209 @@ +// Copyright (c) 2012, 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. + +part of _js_helper; + +// Helper method used by internal libraries. +regExpGetNative(JSSyntaxRegExp regexp) => regexp._nativeRegExp; + +/** + * Returns a native version of the RegExp with the global flag set. + * + * The RegExp's `lastIndex` property is zero when it is returned. + * + * The returned regexp is shared, and its `lastIndex` property may be + * modified by other uses, so the returned regexp must be used immediately + * when it's returned, with no user-provided code run in between. + */ +regExpGetGlobalNative(JSSyntaxRegExp regexp) { + var nativeRegexp = regexp._nativeGlobalVersion; + JS("void", "#.lastIndex = 0", nativeRegexp); + return nativeRegexp; +} + +class JSSyntaxRegExp implements RegExp { + final _nativeRegExp; + var _nativeGlobalRegExp; + var _nativeAnchoredRegExp; + + JSSyntaxRegExp(String pattern, + { bool multiLine: false, + bool caseSensitive: true }) + : this._nativeRegExp = + makeNative(pattern, multiLine, caseSensitive, false); + + get _nativeGlobalVersion { + if (_nativeGlobalRegExp != null) return _nativeGlobalRegExp; + return _nativeGlobalRegExp = makeNative(_pattern, + _isMultiLine, + _isCaseSensitive, + true); + } + + get _nativeAnchoredVersion { + if (_nativeAnchoredRegExp != null) return _nativeAnchoredRegExp; + // An "anchored version" of a regexp is created by adding "|()" to the + // source. This means that the regexp always matches at the first position + // that it tries, and you can see if the original regexp matched, or it + // was the added zero-width match that matched, by looking at the last + // capture. If it is a String, the match participated, otherwise it didn't. + return _nativeAnchoredRegExp = makeNative("$_pattern|()", + _isMultiLine, + _isCaseSensitive, + true); + } + + String get _pattern => JS("String", "#.source", _nativeRegExp); + bool get _isMultiLine => JS("bool", "#.multiline", _nativeRegExp); + bool get _isCaseSensitive => JS("bool", "!#.ignoreCase", _nativeRegExp); + + static makeNative( + String pattern, bool multiLine, bool caseSensitive, bool global) { + checkString(pattern); + String m = multiLine ? 'm' : ''; + String i = caseSensitive ? '' : 'i'; + String g = global ? 'g' : ''; + // We're using the JavaScript's try catch instead of the Dart one + // to avoid dragging in Dart runtime support just because of using + // RegExp. + var regexp = JS('', + '(function() {' + 'try {' + 'return new RegExp(#, # + # + #);' + '} catch (e) {' + 'return e;' + '}' + '})()', pattern, m, i, g); + if (JS('bool', '# instanceof RegExp', regexp)) return regexp; + // The returned value is the JavaScript exception. Turn it into a + // Dart exception. + String errorMessage = JS('String', r'String(#)', regexp); + throw new FormatException( + "Illegal RegExp pattern: $pattern, $errorMessage"); + } + + Match firstMatch(String str) { + List m = JS('JSExtendableArray|Null', + r'#.exec(#)', + _nativeRegExp, + checkString(str)); + if (m == null) return null; + return new _MatchImplementation(this, m); + } + + bool hasMatch(String str) { + return JS('bool', r'#.test(#)', _nativeRegExp, checkString(str)); + } + + String stringMatch(String str) { + var match = firstMatch(str); + if (match != null) return match.group(0); + return null; + } + + Iterable allMatches(String str) { + checkString(str); + return new _AllMatchesIterable(this, str); + } + + Match _execGlobal(String string, int start) { + Object regexp = _nativeGlobalVersion; + JS("void", "#.lastIndex = #", regexp, start); + List match = JS("JSExtendableArray|Null", "#.exec(#)", regexp, string); + if (match == null) return null; + return new _MatchImplementation(this, match); + } + + Match _execAnchored(String string, int start) { + Object regexp = _nativeAnchoredVersion; + JS("void", "#.lastIndex = #", regexp, start); + List match = JS("JSExtendableArray|Null", "#.exec(#)", regexp, string); + if (match == null) return null; + // If the last capture group participated, the original regexp did not + // match at the start position. + if (match[match.length - 1] != null) return null; + match.length -= 1; + return new _MatchImplementation(this, match); + } + + Match matchAsPrefix(String string, [int start = 0]) { + if (start < 0 || start > string.length) { + throw new RangeError.range(start, 0, string.length); + } + return _execAnchored(string, start); + } + + String get pattern => _pattern; + bool get isMultiLine => _isMultiLine; + bool get isCaseSensitive => _isCaseSensitive; +} + +class _MatchImplementation implements Match { + final Pattern pattern; + // Contains a JS RegExp match object. + // It is an Array of String values with extra "index" and "input" properties. + final List _match; + + _MatchImplementation(this.pattern, this._match) { + assert(JS("var", "#.input", _match) is String); + assert(JS("var", "#.index", _match) is int); + } + + String get input => JS("String", "#.input", _match); + int get start => JS("int", "#.index", _match); + int get end => start + _match[0].length; + + String group(int index) => _match[index]; + String operator [](int index) => group(index); + int get groupCount => _match.length - 1; + + List groups(List groups) { + List out = []; + for (int i in groups) { + out.add(group(i)); + } + return out; + } +} + +class _AllMatchesIterable extends IterableBase { + final JSSyntaxRegExp _re; + final String _string; + + const _AllMatchesIterable(this._re, this._string); + + Iterator get iterator => new _AllMatchesIterator(_re, _string); +} + +class _AllMatchesIterator implements Iterator { + final JSSyntaxRegExp _regExp; + String _string; + Match _current; + + _AllMatchesIterator(this._regExp, this._string); + + Match get current => _current; + + bool moveNext() { + if (_string == null) return false; + int index = 0; + if (_current != null) { + index = _current.end; + if (_current.start == index) { + index++; + } + } + _current = _regExp._execGlobal(_string, index); + if (_current == null) { + _string = null; // Marks iteration as ended. + return false; + } + return true; + } +} + +/** Find the first match of [regExp] in [string] at or after [start]. */ +Match firstMatchAfter(JSSyntaxRegExp regExp, String string, int start) { + return regExp._execGlobal(string, start); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/string_helper.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/string_helper.dart new file mode 100644 index 0000000..328cf29 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/lib/string_helper.dart @@ -0,0 +1,203 @@ +// Copyright (c) 2012, 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. + +part of _js_helper; + +class StringMatch implements Match { + const StringMatch(int this.start, + String this.input, + String this.pattern); + + int get end => start + pattern.length; + String operator[](int g) => group(g); + int get groupCount => 0; + + String group(int group_) { + if (group_ != 0) { + throw new RangeError.value(group_); + } + return pattern; + } + + List groups(List groups_) { + List result = new List(); + for (int g in groups_) { + result.add(group(g)); + } + return result; + } + + final int start; + final String input; + final String pattern; +} + +List allMatchesInStringUnchecked(String needle, String haystack) { + // Copied from StringBase.allMatches in + // /runtime/lib/string_base.dart + List result = new List(); + int length = haystack.length; + int patternLength = needle.length; + int startIndex = 0; + while (true) { + int position = haystack.indexOf(needle, startIndex); + if (position == -1) { + break; + } + result.add(new StringMatch(position, haystack, needle)); + int endIndex = position + patternLength; + if (endIndex == length) { + break; + } else if (position == endIndex) { + ++startIndex; // empty match, advance and restart + } else { + startIndex = endIndex; + } + } + return result; +} + +stringContainsUnchecked(receiver, other, startIndex) { + if (other is String) { + return receiver.indexOf(other, startIndex) != -1; + } else if (other is JSSyntaxRegExp) { + return other.hasMatch(receiver.substring(startIndex)); + } else { + var substr = receiver.substring(startIndex); + return other.allMatches(substr).isNotEmpty; + } +} + +stringReplaceJS(receiver, replacer, to) { + // The JavaScript String.replace method recognizes replacement + // patterns in the replacement string. Dart does not have that + // behavior. + to = JS('String', r'#.replace("$", "$$$$")', to); + return JS('String', r'#.replace(#, #)', receiver, replacer, to); +} + +const String ESCAPE_REGEXP = r'[[\]{}()*+?.\\^$|]'; + +stringReplaceAllUnchecked(receiver, from, to) { + checkString(to); + if (from is String) { + if (from == "") { + if (receiver == "") { + return to; + } else { + StringBuffer result = new StringBuffer(); + int length = receiver.length; + result.write(to); + for (int i = 0; i < length; i++) { + result.write(receiver[i]); + result.write(to); + } + return result.toString(); + } + } else { + var quoter = JS('', "new RegExp(#, 'g')", ESCAPE_REGEXP); + var quoted = JS('String', r'#.replace(#, "\\$&")', from, quoter); + var replacer = JS('', "new RegExp(#, 'g')", quoted); + return stringReplaceJS(receiver, replacer, to); + } + } else if (from is JSSyntaxRegExp) { + var re = regExpGetGlobalNative(from); + return stringReplaceJS(receiver, re, to); + } else { + checkNull(from); + // TODO(floitsch): implement generic String.replace (with patterns). + throw "String.replaceAll(Pattern) UNIMPLEMENTED"; + } +} + +String _matchString(Match match) => match[0]; +String _stringIdentity(String string) => string; + +stringReplaceAllFuncUnchecked(receiver, pattern, onMatch, onNonMatch) { + if (pattern is! Pattern) { + throw new ArgumentError("${pattern} is not a Pattern"); + } + if (onMatch == null) onMatch = _matchString; + if (onNonMatch == null) onNonMatch = _stringIdentity; + if (pattern is String) { + return stringReplaceAllStringFuncUnchecked(receiver, pattern, + onMatch, onNonMatch); + } + StringBuffer buffer = new StringBuffer(); + int startIndex = 0; + for (Match match in pattern.allMatches(receiver)) { + buffer.write(onNonMatch(receiver.substring(startIndex, match.start))); + buffer.write(onMatch(match)); + startIndex = match.end; + } + buffer.write(onNonMatch(receiver.substring(startIndex))); + return buffer.toString(); +} + +stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch) { + // Pattern is the empty string. + StringBuffer buffer = new StringBuffer(); + int length = receiver.length; + int i = 0; + buffer.write(onNonMatch("")); + while (i < length) { + buffer.write(onMatch(new StringMatch(i, receiver, ""))); + // Special case to avoid splitting a surrogate pair. + int code = receiver.codeUnitAt(i); + if ((code & ~0x3FF) == 0xD800 && length > i + 1) { + // Leading surrogate; + code = receiver.codeUnitAt(i + 1); + if ((code & ~0x3FF) == 0xDC00) { + // Matching trailing surrogate. + buffer.write(onNonMatch(receiver.substring(i, i + 2))); + i += 2; + continue; + } + } + buffer.write(onNonMatch(receiver[i])); + i++; + } + buffer.write(onMatch(new StringMatch(i, receiver, ""))); + buffer.write(onNonMatch("")); + return buffer.toString(); +} + +stringReplaceAllStringFuncUnchecked(receiver, pattern, onMatch, onNonMatch) { + int patternLength = pattern.length; + if (patternLength == 0) { + return stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch); + } + int length = receiver.length; + StringBuffer buffer = new StringBuffer(); + int startIndex = 0; + while (startIndex < length) { + int position = receiver.indexOf(pattern, startIndex); + if (position == -1) { + break; + } + buffer.write(onNonMatch(receiver.substring(startIndex, position))); + buffer.write(onMatch(new StringMatch(position, receiver, pattern))); + startIndex = position + patternLength; + } + buffer.write(onNonMatch(receiver.substring(startIndex))); + return buffer.toString(); +} + + +stringReplaceFirstUnchecked(receiver, from, to) { + if (from is String) { + return stringReplaceJS(receiver, from, to); + } else if (from is JSSyntaxRegExp) { + var re = regExpGetNative(from); + return stringReplaceJS(receiver, re, to); + } else { + checkNull(from); + // TODO(floitsch): implement generic String.replace (with patterns). + throw "String.replace(Pattern) UNIMPLEMENTED"; + } +} + +stringJoinUnchecked(array, separator) { + return JS('String', r'#.join(#)', array, separator); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/libraries.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/libraries.dart new file mode 100644 index 0000000..cb6e7b2 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/libraries.dart @@ -0,0 +1,306 @@ +// Copyright (c) 2012, 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. + +library libraries; + +/** + * A bit flag used by [LibraryInfo] indicating that a library is used by dart2js + */ +const int DART2JS_PLATFORM = 1; + +/** + * A bit flag used by [LibraryInfo] indicating that a library is used by the VM + */ +const int VM_PLATFORM = 2; + +/** + * Mapping of "dart:" library name (e.g. "core") to information about that library. + * This information is structured such that Dart Editor can parse this file + * and extract the necessary information without executing it + * while other tools can access via execution. + */ +const Map LIBRARIES = const { + + "async": const LibraryInfo( + "async/async.dart", + maturity: Maturity.STABLE, + dart2jsPatchPath: "_internal/lib/async_patch.dart"), + + "_chrome": const LibraryInfo( + "_chrome/dart2js/chrome_dart2js.dart", + documented: false, + category: "Client"), + + "collection": const LibraryInfo( + "collection/collection.dart", + maturity: Maturity.STABLE, + dart2jsPatchPath: "_internal/lib/collection_patch.dart"), + + "convert": const LibraryInfo( + "convert/convert.dart", + maturity: Maturity.STABLE, + dart2jsPatchPath: "_internal/lib/convert_patch.dart"), + + "core": const LibraryInfo( + "core/core.dart", + maturity: Maturity.STABLE, + dart2jsPatchPath: "_internal/lib/core_patch.dart"), + + "html": const LibraryInfo( + "html/dartium/html_dartium.dart", + category: "Client", + maturity: Maturity.WEB_STABLE, + dart2jsPath: "html/dart2js/html_dart2js.dart"), + + "html_common": const LibraryInfo( + "html/html_common/html_common.dart", + category: "Client", + maturity: Maturity.WEB_STABLE, + dart2jsPath: "html/html_common/html_common_dart2js.dart", + documented: false, + implementation: true), + + "indexed_db": const LibraryInfo( + "indexed_db/dartium/indexed_db_dartium.dart", + category: "Client", + maturity: Maturity.WEB_STABLE, + dart2jsPath: "indexed_db/dart2js/indexed_db_dart2js.dart"), + + "io": const LibraryInfo( + "io/io.dart", + category: "Server", + maturity: Maturity.STABLE, + dart2jsPatchPath: "_internal/lib/io_patch.dart"), + + "isolate": const LibraryInfo( + "isolate/isolate.dart", + maturity: Maturity.STABLE, + dart2jsPatchPath: "_internal/lib/isolate_patch.dart"), + + "js": const LibraryInfo( + "js/dartium/js_dartium.dart", + category: "Client", + maturity: Maturity.STABLE, + dart2jsPath: "js/dart2js/js_dart2js.dart"), + + "math": const LibraryInfo( + "math/math.dart", + maturity: Maturity.STABLE, + dart2jsPatchPath: "_internal/lib/math_patch.dart"), + + "mirrors": const LibraryInfo( + "mirrors/mirrors.dart", + maturity: Maturity.UNSTABLE, + dart2jsPatchPath: "_internal/lib/mirrors_patch.dart"), + + "nativewrappers": const LibraryInfo( + "html/dartium/nativewrappers.dart", + category: "Client", + implementation: true, + documented: false, + platforms: VM_PLATFORM), + + "typed_data": const LibraryInfo( + "typed_data/typed_data.dart", + maturity: Maturity.STABLE, + dart2jsPath: "typed_data/dart2js/typed_data_dart2js.dart"), + + "_native_typed_data": const LibraryInfo( + "typed_data/dart2js/native_typed_data_dart2js.dart", + category: "Internal", + implementation: true, + documented: false, + platforms: DART2JS_PLATFORM), + + "svg": const LibraryInfo( + "svg/dartium/svg_dartium.dart", + category: "Client", + maturity: Maturity.WEB_STABLE, + dart2jsPath: "svg/dart2js/svg_dart2js.dart"), + + "web_audio": const LibraryInfo( + "web_audio/dartium/web_audio_dartium.dart", + category: "Client", + maturity: Maturity.WEB_STABLE, + dart2jsPath: "web_audio/dart2js/web_audio_dart2js.dart"), + + "web_gl": const LibraryInfo( + "web_gl/dartium/web_gl_dartium.dart", + category: "Client", + maturity: Maturity.WEB_STABLE, + dart2jsPath: "web_gl/dart2js/web_gl_dart2js.dart"), + + "web_sql": const LibraryInfo( + "web_sql/dartium/web_sql_dartium.dart", + category: "Client", + maturity: Maturity.WEB_STABLE, + dart2jsPath: "web_sql/dart2js/web_sql_dart2js.dart"), + + "_internal": const LibraryInfo( + "internal/internal.dart", + category: "Internal", + documented: false, + dart2jsPatchPath: + "_internal/lib/internal_patch.dart"), + + "_js_helper": const LibraryInfo( + "_internal/lib/js_helper.dart", + category: "Internal", + documented: false, + platforms: DART2JS_PLATFORM), + + "_interceptors": const LibraryInfo( + "_internal/lib/interceptors.dart", + category: "Internal", + documented: false, + platforms: DART2JS_PLATFORM), + + "_foreign_helper": const LibraryInfo( + "_internal/lib/foreign_helper.dart", + category: "Internal", + documented: false, + platforms: DART2JS_PLATFORM), + + "_isolate_helper": const LibraryInfo( + "_internal/lib/isolate_helper.dart", + category: "Internal", + documented: false, + platforms: DART2JS_PLATFORM), + + "_js_mirrors": const LibraryInfo( + "_internal/lib/js_mirrors.dart", + category: "Internal", + documented: false, + platforms: DART2JS_PLATFORM), + + "_js_names": const LibraryInfo( + "_internal/lib/js_names.dart", + category: "Internal", + documented: false, + platforms: DART2JS_PLATFORM), + + "_js_primitives": const LibraryInfo( + "_internal/lib/js_primitives.dart", + category: "Internal", + documented: false, + platforms: DART2JS_PLATFORM), + + // TODO(ahe): This library is only for dart2dart, perhaps it should use a + // different platform. + "_mirror_helper": const LibraryInfo( + "_internal/lib/mirror_helper.dart", + category: "Internal", + documented: false, + platforms: DART2JS_PLATFORM) +}; + +/** + * Information about a "dart:" library. + */ +class LibraryInfo { + + /** + * Path to the library's *.dart file relative to this file. + */ + final String path; + + /** + * The category in which the library should appear in the editor + * (e.g. "Shared", "Client", "Server", ...). + * If a category is not specified it defaults to "Shared". + */ + final String category; + + /** + * Path to the dart2js library's *.dart file relative to this file + * or null if dart2js uses the common library path defined above. + * Access using the [#getDart2JsPath()] method. + */ + final String dart2jsPath; + + /** + * Path to the dart2js library's patch file relative to this file + * or null if no dart2js patch file associated with this library. + * Access using the [#getDart2JsPatchPath()] method. + */ + final String dart2jsPatchPath; + + /** + * True if this library is documented and should be shown to the user. + */ + final bool documented; + + /** + * Bit flags indicating which platforms consume this library. + * See [DART2JS_LIBRARY] and [VM_LIBRARY]. + */ + final int platforms; + + /** + * True if the library contains implementation details for another library. + * The implication is that these libraries are less commonly used + * and that tools like Dart Editor should not show these libraries + * in a list of all libraries unless the user specifically asks the tool to + * do so. + */ + final bool implementation; + + /** + * States the current maturity of this library. + */ + final Maturity maturity; + + const LibraryInfo(this.path, { + this.category: "Shared", + this.dart2jsPath, + this.dart2jsPatchPath, + this.implementation: false, + this.documented: true, + this.maturity: Maturity.UNSPECIFIED, + this.platforms: DART2JS_PLATFORM | VM_PLATFORM}); + + bool get isDart2jsLibrary => (platforms & DART2JS_PLATFORM) != 0; + bool get isVmLibrary => (platforms & VM_PLATFORM) != 0; +} + + + +/** + * Abstraction to capture the maturity of a library. + */ +class Maturity { + final int level; + final String name; + final String description; + + const Maturity(this.level, this.name, this.description); + + String toString() => "$name: $level\n$description\n"; + + static const Maturity DEPRECATED = const Maturity(0, "Deprecated", + "This library will be remove before next major release."); + + static const Maturity EXPERIMENTAL = const Maturity(1, "Experimental", + "This library is experimental and will likely change or be removed\n" + "in future versions."); + + static const Maturity UNSTABLE = const Maturity(2, "Unstable", + "This library is in still changing and have not yet endured\n" + "sufficient real-world testing.\n" + "Backwards-compatibility is NOT guaranteed."); + + static const Maturity WEB_STABLE = const Maturity(3, "Web Stable", + "This library is tracking the DOM evolution as defined by WC3.\n" + "Backwards-compatibility is NOT guaranteed."); + + static const Maturity STABLE = const Maturity(4, "Stable", + "The library is stable. API backwards-compatibility is guaranteed.\n" + "However implementation details might change."); + + static const Maturity LOCKED = const Maturity(5, "Locked", + "This library will not change except when serious bugs are encountered."); + + static const Maturity UNSPECIFIED = const Maturity(-1, "Unspecified", + "The maturity for this library has not been specified."); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize.dart new file mode 100644 index 0000000..43328e1 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize.dart @@ -0,0 +1,100 @@ +// Copyright (c) 2014, 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. + +library pub.asset.serialize; + +import 'dart:async'; +import 'dart:isolate'; + +import 'package:barback/barback.dart'; +import 'package:source_maps/span.dart'; + +import 'serialize/exception.dart'; + +export 'serialize/exception.dart'; +export 'serialize/transform.dart'; +export 'serialize/transformer.dart'; + +/// Converts [id] into a serializable map. +Map serializeId(AssetId id) => {'package': id.package, 'path': id.path}; + +/// Converts a serializable map into an [AssetId]. +AssetId deserializeId(Map id) => new AssetId(id['package'], id['path']); + +/// Converts [span] into a serializable map. +Map serializeSpan(Span span) { + // TODO(nweiz): convert FileSpans to FileSpans. + return { + 'type': 'fixed', + 'sourceUrl': span.sourceUrl, + 'start': serializeLocation(span.start), + 'text': span.text, + 'isIdentifier': span.isIdentifier + }; +} + +/// Converts a serializable map into a [Span]. +Span deserializeSpan(Map span) { + assert(span['type'] == 'fixed'); + var location = deserializeLocation(span['start']); + return new FixedSpan(span['sourceUrl'], location.offset, location.line, + location.column, text: span['text'], isIdentifier: span['isIdentifier']); +} + +/// Converts [location] into a serializable map. +Map serializeLocation(Location location) { + // TODO(nweiz): convert FileLocations to FileLocations. + return { + 'type': 'fixed', + 'sourceUrl': location.sourceUrl, + 'offset': location.offset, + 'line': location.line, + 'column': location.column + }; +} + +/// Converts a serializable map into a [Location]. +Location deserializeLocation(Map location) { + assert(location['type'] == 'fixed'); + return new FixedLocation(location['offset'], location['sourceUrl'], + location['line'], location['column']); +} + +/// Wraps [message] and sends it across [port], then waits for a response which +/// should be sent using [respond]. +/// +/// The returned Future will complete to the value or error returned by +/// [respond]. +Future call(SendPort port, message) { + var receivePort = new ReceivePort(); + port.send({ + 'message': message, + 'replyTo': receivePort.sendPort + }); + + return receivePort.first.then((response) { + if (response['type'] == 'success') return response['value']; + assert(response['type'] == 'error'); + var exception = deserializeException(response['error']); + return new Future.error(exception, exception.stackTrace); + }); +} + +/// Responds to a message sent by [call]. +/// +/// [wrappedMessage] is the raw message sent by [call]. This unwraps it and +/// passes the contents of the message to [callback], then sends the return +/// value of [callback] back to [call]. If [callback] returns a Future or +/// throws an error, that will also be sent. +void respond(wrappedMessage, callback(message)) { + var replyTo = wrappedMessage['replyTo']; + new Future.sync(() => callback(wrappedMessage['message'])) + .then((result) => replyTo.send({'type': 'success', 'value': result})) + .catchError((error, stackTrace) { + replyTo.send({ + 'type': 'error', + 'error': serializeException(error, stackTrace) + }); + }); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize/exception.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize/exception.dart new file mode 100644 index 0000000..26040a9 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize/exception.dart @@ -0,0 +1,102 @@ +// Copyright (c) 2014, 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. + +library pub.asset.serialize.exception; + +import 'package:barback/barback.dart'; +import 'package:stack_trace/stack_trace.dart'; + +import '../utils.dart'; + +/// An exception that was originally raised in another isolate. +/// +/// Exception objects can't cross isolate boundaries in general, so this class +/// wraps as much information as can be consistently serialized. +class CrossIsolateException implements Exception { + /// The name of the type of exception thrown. + /// + /// This is the return value of [error.runtimeType.toString()]. Keep in mind + /// that objects in different libraries may have the same type name. + final String type; + + /// The exception's message, or its [toString] if it didn't expose a `message` + /// property. + final String message; + + /// The exception's stack chain, or `null` if no stack chain was available. + final Chain stackTrace; + + /// Loads a [CrossIsolateException] from a serialized representation. + /// + /// [error] should be the result of [CrossIsolateException.serialize]. + CrossIsolateException.deserialize(Map error) + : type = error['type'], + message = error['message'], + stackTrace = error['stack'] == null ? null : + new Chain.parse(error['stack']); + + /// Serializes [error] to an object that can safely be passed across isolate + /// boundaries. + static Map serialize(error, [StackTrace stack]) { + if (stack == null && error is Error) stack = error.stackTrace; + return { + 'type': error.runtimeType.toString(), + 'message': getErrorMessage(error), + 'stack': stack == null ? null : new Chain.forTrace(stack).toString() + }; + } + + String toString() => "$message\n$stackTrace"; +} + +/// An [AssetNotFoundException] that was originally raised in another isolate. +class _CrossIsolateAssetNotFoundException extends CrossIsolateException + implements AssetNotFoundException { + final AssetId id; + + String get message => "Could not find asset $id."; + + /// Loads a [_CrossIsolateAssetNotFoundException] from a serialized + /// representation. + /// + /// [error] should be the result of + /// [_CrossIsolateAssetNotFoundException.serialize]. + _CrossIsolateAssetNotFoundException.deserialize(Map error) + : id = new AssetId(error['package'], error['path']), + super.deserialize(error); + + /// Serializes [error] to an object that can safely be passed across isolate + /// boundaries. + static Map serialize(AssetNotFoundException error, [StackTrace stack]) { + var map = CrossIsolateException.serialize(error); + map['package'] = error.id.package; + map['path'] = error.id.path; + return map; + } +} + +/// Serializes [error] to an object that can safely be passed across isolate +/// boundaries. +/// +/// This handles [AssetNotFoundException]s specially, ensuring that their +/// metadata is preserved. +Map serializeException(error, [StackTrace stack]) { + if (error is AssetNotFoundException) { + return _CrossIsolateAssetNotFoundException.serialize(error, stack); + } else { + return CrossIsolateException.serialize(error, stack); + } +} + +/// Loads an exception from a serialized representation. +/// +/// This handles [AssetNotFoundException]s specially, ensuring that their +/// metadata is preserved. +CrossIsolateException deserializeException(Map error) { + if (error['type'] == 'AssetNotFoundException') { + return new _CrossIsolateAssetNotFoundException.deserialize(error); + } else { + return new CrossIsolateException.deserialize(error); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize/transform.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize/transform.dart new file mode 100644 index 0000000..cc5e052 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize/transform.dart @@ -0,0 +1,126 @@ +// Copyright (c) 2014, 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. + +library pub.asset.serialize.transform; + +import 'dart:async'; +import 'dart:isolate'; +import 'dart:convert'; + +import 'package:barback/barback.dart'; +// TODO(nweiz): don't import from "src" once issue 14966 is fixed. +import 'package:barback/src/internal_asset.dart'; + +import '../serialize.dart'; +import '../utils.dart'; + +/// Converts [transform] into a serializable map. +Map serializeTransform(Transform transform) { + var receivePort = new ReceivePort(); + receivePort.listen((wrappedMessage) { + respond(wrappedMessage, (message) { + if (message['type'] == 'getInput') { + return transform.getInput(deserializeId(message['id'])) + .then((asset) => serializeAsset(asset)); + } + + if (message['type'] == 'addOutput') { + transform.addOutput(deserializeAsset(message['output'])); + return null; + } + + if (message['type'] == 'consumePrimary') { + transform.consumePrimary(); + return null; + } + + assert(message['type'] == 'log'); + var method; + if (message['level'] == 'Info') { + method = transform.logger.info; + } else if (message['level'] == 'Fine') { + method = transform.logger.fine; + } else if (message['level'] == 'Warning') { + method = transform.logger.warning; + } else { + assert(message['level'] == 'Error'); + method = transform.logger.error; + } + + var assetId = message['assetId'] == null ? null : + deserializeId(message['assetId']); + var span = message['span'] == null ? null : + deserializeSpan(message['span']); + method(message['message'], asset: assetId, span: span); + }); + }); + + return { + 'port': receivePort.sendPort, + 'primaryInput': serializeAsset(transform.primaryInput) + }; +} + +/// A wrapper for a [Transform] that's in the host isolate. +/// +/// This retrieves inputs from and sends outputs and logs to the host isolate. +class ForeignTransform implements Transform { + /// The port with which we communicate with the host isolate. + /// + /// This port and all messages sent across it are specific to this transform. + final SendPort _port; + + final Asset primaryInput; + + TransformLogger get logger => _logger; + TransformLogger _logger; + + /// Creates a transform from a serializable map sent from the host isolate. + ForeignTransform(Map transform) + : _port = transform['port'], + primaryInput = deserializeAsset(transform['primaryInput']) { + _logger = new TransformLogger((assetId, level, message, span) { + call(_port, { + 'type': 'log', + 'level': level.name, + 'message': message, + 'assetId': assetId == null ? null : serializeId(assetId), + 'span': span == null ? null : serializeSpan(span) + }); + }); + } + + Future getInput(AssetId id) { + return call(_port, { + 'type': 'getInput', + 'id': serializeId(id) + }).then(deserializeAsset); + } + + Future readInputAsString(AssetId id, {Encoding encoding}) { + if (encoding == null) encoding = UTF8; + return getInput(id).then((input) => input.readAsString(encoding: encoding)); + } + + Stream> readInput(AssetId id) => + futureStream(getInput(id).then((input) => input.read())); + + Future hasInput(AssetId id) { + return getInput(id).then((_) => true).catchError((error) { + if (error is AssetNotFoundException && error.id == id) return false; + throw error; + }); + } + + void addOutput(Asset output) { + call(_port, { + 'type': 'addOutput', + 'output': serializeAsset(output) + }); + } + + void consumePrimary() { + call(_port, {'type': 'consumePrimary'}); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart new file mode 100644 index 0000000..608eaa7 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart @@ -0,0 +1,60 @@ +// Copyright (c) 2014, 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. + +library pub.asset.serialize.transformer; + +import 'dart:isolate'; + +import 'package:barback/barback.dart'; +// TODO(nweiz): don't import from "src" once issue 14966 is fixed. +import 'package:barback/src/internal_asset.dart'; + +import '../serialize.dart'; +import 'transform.dart'; + +/// Converts [transformer] into a serializable map. +Map serializeTransformer(Transformer transformer) { + var port = new ReceivePort(); + port.listen((wrappedMessage) { + respond(wrappedMessage, (message) { + if (message['type'] == 'isPrimary') { + return transformer.isPrimary(deserializeAsset(message['asset'])); + } else { + assert(message['type'] == 'apply'); + + // Make sure we return null so that if the transformer's [apply] returns + // a non-serializable value it doesn't cause problems. + return transformer.apply( + new ForeignTransform(message['transform'])).then((_) => null); + } + }); + }); + + return { + 'type': 'Transformer', + 'toString': transformer.toString(), + 'port': port.sendPort + }; +} + +// Converts [group] into a serializable map. +Map serializeTransformerGroup(TransformerGroup group) { + return { + 'type': 'TransformerGroup', + 'toString': group.toString(), + 'phases': group.phases.map((phase) { + return phase.map(serializeTransformerOrGroup).toList(); + }).toList() + }; +} + +/// Converts [transformerOrGroup] into a serializable map. +Map serializeTransformerOrGroup(transformerOrGroup) { + if (transformerOrGroup is Transformer) { + return serializeTransformer(transformerOrGroup); + } else { + assert(transformerOrGroup is TransformerGroup); + return serializeTransformerGroup(transformerOrGroup); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart new file mode 100644 index 0000000..5b6dfa7 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart @@ -0,0 +1,71 @@ +// Copyright (c) 2014, 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. + +library pub.asset.transformer_isolate; + +import 'dart:convert'; +import 'dart:isolate'; +import 'dart:mirrors'; + +import 'package:barback/barback.dart'; + +import 'serialize.dart'; + +/// Sets up the initial communication with the host isolate. +void loadTransformers(SendPort replyTo) { + var port = new ReceivePort(); + replyTo.send(port.sendPort); + port.first.then((wrappedMessage) { + respond(wrappedMessage, (message) { + var library = Uri.parse(message['library']); + var configuration = JSON.decode(message['configuration']); + var mode = new BarbackMode(message['mode']); + return _initialize(library, configuration, mode). + map(serializeTransformerOrGroup).toList(); + }); + }); +} + +/// Loads all the transformers and groups defined in [uri]. +/// +/// Loads the library, finds any Transformer or TransformerGroup subclasses in +/// it, instantiates them with [configuration] and [mode], and returns them. +Iterable _initialize(Uri uri, Map configuration, BarbackMode mode) { + var mirrors = currentMirrorSystem(); + var transformerClass = reflectClass(Transformer); + var groupClass = reflectClass(TransformerGroup); + + // TODO(nweiz): if no valid transformers are found, throw an error message + // describing candidates and why they were rejected. + return mirrors.libraries[uri].declarations.values.map((declaration) { + if (declaration is! ClassMirror) return null; + var classMirror = declaration; + if (classMirror.isPrivate) return null; + if (classMirror.isAbstract) return null; + if (!classMirror.isSubtypeOf(transformerClass) && + !classMirror.isSubtypeOf(groupClass)) { + return null; + } + + var constructor = _getConstructor(classMirror, 'asPlugin'); + if (constructor == null) return null; + if (constructor.parameters.isEmpty) { + if (configuration.isNotEmpty) return null; + return classMirror.newInstance(const Symbol('asPlugin'), []).reflectee; + } + if (constructor.parameters.length != 1) return null; + + return classMirror.newInstance(const Symbol('asPlugin'), + [new BarbackSettings(configuration, mode)]).reflectee; + }).where((classMirror) => classMirror != null); +} + +// TODO(nweiz): clean this up when issue 13248 is fixed. +MethodMirror _getConstructor(ClassMirror classMirror, String constructor) { + var name = new Symbol("${MirrorSystem.getName(classMirror.simpleName)}" + ".$constructor"); + var candidate = classMirror.declarations[name]; + if (candidate is MethodMirror && candidate.isConstructor) return candidate; + return null; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/utils.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/utils.dart new file mode 100644 index 0000000..72150ed --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/_internal/pub/asset/dart/utils.dart @@ -0,0 +1,86 @@ +// Copyright (c) 2014, 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. + +/// Functions go in this file as opposed to lib/src/utils.dart if they need to +/// be accessible to the transformer-loading isolate. +library pub.asset.utils; + +import 'dart:async'; + +/// A regular expression to match the exception prefix that some exceptions' +/// [Object.toString] values contain. +final _exceptionPrefix = new RegExp(r'^([A-Z][a-zA-Z]*)?(Exception|Error): '); + +/// Get a string description of an exception. +/// +/// Many exceptions include the exception class name at the beginning of their +/// [toString], so we remove that if it exists. +String getErrorMessage(error) => + error.toString().replaceFirst(_exceptionPrefix, ''); + +/// Returns a buffered stream that will emit the same values as the stream +/// returned by [future] once [future] completes. +/// +/// If [future] completes to an error, the return value will emit that error and +/// then close. +/// +/// If [broadcast] is true, a broadcast stream is returned. This assumes that +/// the stream returned by [future] will be a broadcast stream as well. +/// [broadcast] defaults to false. +Stream futureStream(Future future, {bool broadcast: false}) { + var subscription; + var controller; + + future = future.catchError((e, stackTrace) { + // Since [controller] is synchronous, it's likely that emitting an error + // will cause it to be cancelled before we call close. + if (controller != null) controller.addError(e, stackTrace); + if (controller != null) controller.close(); + controller = null; + }); + + onListen() { + future.then((stream) { + if (controller == null) return; + subscription = stream.listen( + controller.add, + onError: controller.addError, + onDone: controller.close); + }); + } + + onCancel() { + if (subscription != null) subscription.cancel(); + subscription = null; + controller = null; + } + + if (broadcast) { + controller = new StreamController.broadcast( + sync: true, onListen: onListen, onCancel: onCancel); + } else { + controller = new StreamController( + sync: true, onListen: onListen, onCancel: onCancel); + } + return controller.stream; +} + +/// Returns a [Stream] that will emit the same values as the stream returned by +/// [callback]. +/// +/// [callback] will only be called when the returned [Stream] gets a subscriber. +Stream callbackStream(Stream callback()) { + var subscription; + var controller; + controller = new StreamController(onListen: () { + subscription = callback().listen(controller.add, + onError: controller.addError, + onDone: controller.close); + }, + onCancel: () => subscription.cancel(), + onPause: () => subscription.pause(), + onResume: () => subscription.resume(), + sync: true); + return controller.stream; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/async.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/async.dart new file mode 100644 index 0000000..adf54f1 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/async.dart @@ -0,0 +1,108 @@ +// Copyright (c) 2012, 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. + +/** + * Support for asynchronous programming, + * with classes such as Future and Stream. + * + * Understanding [Future]s and [Stream]s is a prerequisite for + * writing just about any Dart program. + * + * To use this library in your code: + * + * import 'dart:async'; + * + * ## Future + * + * A Future object represents a computation whose return value + * might not yet be available. + * The Future returns the value of the computation + * when it completes at some time in the future. + * Futures are often used for potentially lengthy computations + * such as I/O and interaction with users. + * + * Many methods in the Dart libraries return Futures when + * performing tasks. For example, when binding an HttpServer + * to a host and port, the `bind()` method returns a Future. + * + * HttpServer.bind('127.0.0.1', 4444) + * .then((server) => print('${server.isBroadcast}')) + * .catchError(print); + * + * [Future.then] registers a callback function that runs + * when the Future's operation, in this case the `bind()` method, + * completes successfully. + * The value returned by the operation + * is passed into the callback function. + * In this example, the `bind()` method returns the HttpServer + * object. The callback function prints one of its properties. + * [Future.catchError] registers a callback function that + * runs if an error occurs within the Future. + * + * ## Stream + * + * A Stream provides an asynchronous sequence of data. + * Examples of data sequences include individual events, like mouse clicks, + * or sequential chunks of larger data, like multiple byte lists with the + * contents of a file + * such as mouse clicks, and a stream of byte lists read from a file. + * The following example opens a file for reading. + * [Stream.listen] registers a callback function that runs + * each time more data is available. + * + * Stream> stream = new File('quotes.txt').openRead(); + * stream.transform(UTF8.decoder).listen(print); + * + * The stream emits a sequence of a list of bytes. + * The program must interpret the bytes or handle the raw byte data. + * Here, the code uses a UTF8 decoder (provided in the `dart:convert` library) + * to convert the sequence of bytes into a sequence + * of Dart strings. + * + * Another common use of streams is for user-generated events + * in a web app: The following code listens for mouse clicks on a button. + * + * querySelector('#myButton').onClick.listen((_) => print('Click.')); + * + * ## Other resources + * + * * The [dart:async section of the library tour] + * (https://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html#ch03-asynchronous-programming): + * A brief overview of asynchronous programming. + * + * * [Use Future-Based APIs] + * (https://www.dartlang.org/docs/tutorials/futures/): A closer look at + * Futures and how to use them to write asynchronous Dart code. + * + * * [Futures and Error Handling] + * (https://www.dartlang.org/articles/futures-and-error-handling/): Everything + * you wanted to know about handling errors and exceptions when working with + * Futures (but were afraid to ask). + * + * * [The Event Loop and Dart](https://www.dartlang.org/articles/event-loop/): + * Learn how Dart handles the event queue and microtask queue, so you can write + * better asynchronous code with fewer surprises. + * + * * [Asynchronous Unit Testing with Dart] + * (https://www.dartlang.org/articles/dart-unit-tests/#asynchronous-tests): How + * to test asynchronous code. + */ +library dart.async; + +import "dart:collection"; +import "dart:_internal" show deprecated, printToZone, printToConsole; + +part 'async_error.dart'; +part 'broadcast_stream_controller.dart'; +part 'deferred_load.dart'; +part 'future.dart'; +part 'future_impl.dart'; +part 'schedule_microtask.dart'; +part 'stream.dart'; +part 'stream_controller.dart'; +part 'stream_impl.dart'; +part 'stream_pipe.dart'; +part 'stream_transformers.dart'; +part 'timer.dart'; +part 'zone.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/async_error.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/async_error.dart new file mode 100644 index 0000000..cac1e4d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/async_error.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2012, 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. + +part of dart.async; + +_invokeErrorHandler(Function errorHandler, + Object error, StackTrace stackTrace) { + if (errorHandler is ZoneBinaryCallback) { + return errorHandler(error, stackTrace); + } else { + return errorHandler(error); + } +} + +Function _registerErrorHandler(Function errorHandler, Zone zone) { + if (errorHandler is ZoneBinaryCallback) { + return zone.registerBinaryCallback(errorHandler); + } else { + return zone.registerUnaryCallback(errorHandler); + } +} + +class _AsyncError implements Error { + final error; + final StackTrace stackTrace; + + _AsyncError(this.error, this.stackTrace); +} + +class _UncaughtAsyncError extends _AsyncError { + _UncaughtAsyncError(error, StackTrace stackTrace) + : super(error, _getBestStackTrace(error, stackTrace)); + + static StackTrace _getBestStackTrace(error, StackTrace stackTrace) { + if (stackTrace != null) return stackTrace; + if (error is Error) { + return error.stackTrace; + } + return null; + } + + String toString() { + String result = "Uncaught Error: ${error}"; + + if (stackTrace != null) { + result += "\nStack Trace:\n$stackTrace"; + } + return result; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/broadcast_stream_controller.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/broadcast_stream_controller.dart new file mode 100644 index 0000000..18ff654 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/broadcast_stream_controller.dart @@ -0,0 +1,497 @@ +// Copyright (c) 2012, 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. + +part of dart.async; + +class _BroadcastStream extends _ControllerStream { + _BroadcastStream(_StreamControllerLifecycle controller) : super(controller); + + bool get isBroadcast => true; +} + +abstract class _BroadcastSubscriptionLink { + _BroadcastSubscriptionLink _next; + _BroadcastSubscriptionLink _previous; +} + +class _BroadcastSubscription extends _ControllerSubscription + implements _BroadcastSubscriptionLink { + static const int _STATE_EVENT_ID = 1; + static const int _STATE_FIRING = 2; + static const int _STATE_REMOVE_AFTER_FIRING = 4; + // TODO(lrn): Use the _state field on _ControllerSubscription to + // also store this state. Requires that the subscription implementation + // does not assume that it's use of the state integer is the only use. + int _eventState; + + _BroadcastSubscriptionLink _next; + _BroadcastSubscriptionLink _previous; + + _BroadcastSubscription(_StreamControllerLifecycle controller, + bool cancelOnError) + : super(controller, cancelOnError) { + _next = _previous = this; + } + + _BroadcastStreamController get _controller => super._controller; + + bool _expectsEvent(int eventId) => + (_eventState & _STATE_EVENT_ID) == eventId; + + + void _toggleEventId() { + _eventState ^= _STATE_EVENT_ID; + } + + bool get _isFiring => (_eventState & _STATE_FIRING) != 0; + + bool _setRemoveAfterFiring() { + assert(_isFiring); + _eventState |= _STATE_REMOVE_AFTER_FIRING; + } + + bool get _removeAfterFiring => + (_eventState & _STATE_REMOVE_AFTER_FIRING) != 0; + + // The controller._recordPause doesn't do anything for a broadcast controller, + // so we don't bother calling it. + void _onPause() { } + + // The controller._recordResume doesn't do anything for a broadcast + // controller, so we don't bother calling it. + void _onResume() { } + + // _onCancel is inherited. +} + + +abstract class _BroadcastStreamController + implements StreamController, + _StreamControllerLifecycle, + _BroadcastSubscriptionLink, + _EventSink, + _EventDispatch { + static const int _STATE_INITIAL = 0; + static const int _STATE_EVENT_ID = 1; + static const int _STATE_FIRING = 2; + static const int _STATE_CLOSED = 4; + static const int _STATE_ADDSTREAM = 8; + + final _NotificationHandler _onListen; + final _NotificationHandler _onCancel; + + // State of the controller. + int _state; + + // Double-linked list of active listeners. + _BroadcastSubscriptionLink _next; + _BroadcastSubscriptionLink _previous; + + // Extra state used during an [addStream] call. + _AddStreamState _addStreamState; + + /** + * Future returned by [close] and [done]. + * + * The future is completed whenever the done event has been sent to all + * relevant listeners. + * The relevant listeners are the ones that were listening when [close] was + * called. When all of these have been canceled (sending the done event makes + * them cancel, but they can also be canceled before sending the event), + * this future completes. + * + * Any attempt to listen after calling [close] will throw, so there won't + * be any further listeners. + */ + _Future _doneFuture; + + _BroadcastStreamController(this._onListen, this._onCancel) + : _state = _STATE_INITIAL { + _next = _previous = this; + } + + // StreamController interface. + + Stream get stream => new _BroadcastStream(this); + + StreamSink get sink => new _StreamSinkWrapper(this); + + bool get isClosed => (_state & _STATE_CLOSED) != 0; + + /** + * A broadcast controller is never paused. + * + * Each receiving stream may be paused individually, and they handle their + * own buffering. + */ + bool get isPaused => false; + + /** Whether there are currently one or more subscribers. */ + bool get hasListener => !_isEmpty; + + /** + * Test whether the stream has exactly one listener. + * + * Assumes that the stream has a listener (not [_isEmpty]). + */ + bool get _hasOneListener { + assert(!_isEmpty); + return identical(_next._next, this); + } + + /** Whether an event is being fired (sent to some, but not all, listeners). */ + bool get _isFiring => (_state & _STATE_FIRING) != 0; + + bool get _isAddingStream => (_state & _STATE_ADDSTREAM) != 0; + + bool get _mayAddEvent => (_state < _STATE_CLOSED); + + _Future _ensureDoneFuture() { + if (_doneFuture != null) return _doneFuture; + return _doneFuture = new _Future(); + } + + // Linked list helpers + + bool get _isEmpty => identical(_next, this); + + /** Adds subscription to linked list of active listeners. */ + void _addListener(_BroadcastSubscription subscription) { + assert(identical(subscription._next, subscription)); + // Insert in linked list just before `this`. + subscription._previous = _previous; + subscription._next = this; + this._previous._next = subscription; + this._previous = subscription; + subscription._eventState = (_state & _STATE_EVENT_ID); + } + + void _removeListener(_BroadcastSubscription subscription) { + assert(identical(subscription._controller, this)); + assert(!identical(subscription._next, subscription)); + _BroadcastSubscriptionLink previous = subscription._previous; + _BroadcastSubscriptionLink next = subscription._next; + previous._next = next; + next._previous = previous; + subscription._next = subscription._previous = subscription; + } + + // _StreamControllerLifecycle interface. + + StreamSubscription _subscribe(bool cancelOnError) { + if (isClosed) { + throw new StateError("Subscribing to closed stream"); + } + StreamSubscription subscription = + new _BroadcastSubscription(this, cancelOnError); + _addListener(subscription); + if (identical(_next, _previous)) { + // Only one listener, so it must be the first listener. + _runGuarded(_onListen); + } + return subscription; + } + + Future _recordCancel(_BroadcastSubscription subscription) { + // If already removed by the stream, don't remove it again. + if (identical(subscription._next, subscription)) return null; + assert(!identical(subscription._next, subscription)); + if (subscription._isFiring) { + subscription._setRemoveAfterFiring(); + } else { + assert(!identical(subscription._next, subscription)); + _removeListener(subscription); + // If we are currently firing an event, the empty-check is performed at + // the end of the listener loop instead of here. + if (!_isFiring && _isEmpty) { + _callOnCancel(); + } + } + } + + void _recordPause(StreamSubscription subscription) {} + void _recordResume(StreamSubscription subscription) {} + + // EventSink interface. + + Error _addEventError() { + if (isClosed) { + return new StateError("Cannot add new events after calling close"); + } + assert(_isAddingStream); + return new StateError("Cannot add new events while doing an addStream"); + } + + void add(T data) { + if (!_mayAddEvent) throw _addEventError(); + _sendData(data); + } + + void addError(Object error, [StackTrace stackTrace]) { + if (!_mayAddEvent) throw _addEventError(); + _sendError(error, stackTrace); + } + + Future close() { + if (isClosed) { + assert(_doneFuture != null); + return _doneFuture; + } + if (!_mayAddEvent) throw _addEventError(); + _state |= _STATE_CLOSED; + Future doneFuture = _ensureDoneFuture(); + _sendDone(); + return doneFuture; + } + + Future get done => _ensureDoneFuture(); + + Future addStream(Stream stream, {bool cancelOnError: true}) { + if (!_mayAddEvent) throw _addEventError(); + _state |= _STATE_ADDSTREAM; + _addStreamState = new _AddStreamState(this, stream, cancelOnError); + return _addStreamState.addStreamFuture; + } + + // _EventSink interface, called from AddStreamState. + void _add(T data) { + _sendData(data); + } + + void _addError(Object error, StackTrace stackTrace) { + assert(_isAddingStream); + _sendError(error, stackTrace); + } + + void _close() { + assert(_isAddingStream); + _AddStreamState addState = _addStreamState; + _addStreamState = null; + _state &= ~_STATE_ADDSTREAM; + addState.complete(); + } + + // Event handling. + void _forEachListener( + void action(_BufferingStreamSubscription subscription)) { + if (_isFiring) { + throw new StateError( + "Cannot fire new event. Controller is already firing an event"); + } + if (_isEmpty) return; + + // Get event id of this event. + int id = (_state & _STATE_EVENT_ID); + // Start firing (set the _STATE_FIRING bit). We don't do [_onCancel] + // callbacks while firing, and we prevent reentrancy of this function. + // + // Set [_state]'s event id to the next event's id. + // Any listeners added while firing this event will expect the next event, + // not this one, and won't get notified. + _state ^= _STATE_EVENT_ID | _STATE_FIRING; + _BroadcastSubscriptionLink link = _next; + while (!identical(link, this)) { + _BroadcastSubscription subscription = link; + if (subscription._expectsEvent(id)) { + subscription._eventState |= _BroadcastSubscription._STATE_FIRING; + action(subscription); + subscription._toggleEventId(); + link = subscription._next; + if (subscription._removeAfterFiring) { + _removeListener(subscription); + } + subscription._eventState &= ~_BroadcastSubscription._STATE_FIRING; + } else { + link = subscription._next; + } + } + _state &= ~_STATE_FIRING; + + if (_isEmpty) { + _callOnCancel(); + } + } + + void _callOnCancel() { + assert(_isEmpty); + if (isClosed && _doneFuture._mayComplete) { + // When closed, _doneFuture is not null. + _doneFuture._asyncComplete(null); + } + _runGuarded(_onCancel); + } +} + +class _SyncBroadcastStreamController extends _BroadcastStreamController { + _SyncBroadcastStreamController(void onListen(), void onCancel()) + : super(onListen, onCancel); + + // EventDispatch interface. + + void _sendData(T data) { + if (_isEmpty) return; + if (_hasOneListener) { + _state |= _BroadcastStreamController._STATE_FIRING; + _BroadcastSubscription subscription = _next; + subscription._add(data); + _state &= ~_BroadcastStreamController._STATE_FIRING; + if (_isEmpty) { + _callOnCancel(); + } + return; + } + _forEachListener((_BufferingStreamSubscription subscription) { + subscription._add(data); + }); + } + + void _sendError(Object error, StackTrace stackTrace) { + if (_isEmpty) return; + _forEachListener((_BufferingStreamSubscription subscription) { + subscription._addError(error, stackTrace); + }); + } + + void _sendDone() { + if (!_isEmpty) { + _forEachListener((_BroadcastSubscription subscription) { + subscription._close(); + }); + } else { + assert(_doneFuture != null); + assert(_doneFuture._mayComplete); + _doneFuture._asyncComplete(null); + } + } +} + +class _AsyncBroadcastStreamController extends _BroadcastStreamController { + _AsyncBroadcastStreamController(void onListen(), void onCancel()) + : super(onListen, onCancel); + + // EventDispatch interface. + + void _sendData(T data) { + for (_BroadcastSubscriptionLink link = _next; + !identical(link, this); + link = link._next) { + _BroadcastSubscription subscription = link; + subscription._addPending(new _DelayedData(data)); + } + } + + void _sendError(Object error, StackTrace stackTrace) { + for (_BroadcastSubscriptionLink link = _next; + !identical(link, this); + link = link._next) { + _BroadcastSubscription subscription = link; + subscription._addPending(new _DelayedError(error, stackTrace)); + } + } + + void _sendDone() { + if (!_isEmpty) { + for (_BroadcastSubscriptionLink link = _next; + !identical(link, this); + link = link._next) { + _BroadcastSubscription subscription = link; + subscription._addPending(const _DelayedDone()); + } + } else { + assert(_doneFuture != null); + assert(_doneFuture._mayComplete); + _doneFuture._asyncComplete(null); + } + } +} + +/** + * Stream controller that is used by [Stream.asBroadcastStream]. + * + * This stream controller allows incoming events while it is firing + * other events. This is handled by delaying the events until the + * current event is done firing, and then fire the pending events. + * + * This class extends [_SyncBroadcastStreamController]. Events of + * an "asBroadcastStream" stream are always initiated by events + * on another stream, and it is fine to forward them synchronously. + */ +class _AsBroadcastStreamController + extends _SyncBroadcastStreamController + implements _EventDispatch { + _StreamImplEvents _pending; + + _AsBroadcastStreamController(void onListen(), void onCancel()) + : super(onListen, onCancel); + + bool get _hasPending => _pending != null && ! _pending.isEmpty; + + void _addPendingEvent(_DelayedEvent event) { + if (_pending == null) { + _pending = new _StreamImplEvents(); + } + _pending.add(event); + } + + void add(T data) { + if (!isClosed && _isFiring) { + _addPendingEvent(new _DelayedData(data)); + return; + } + super.add(data); + while (_hasPending) { + _pending.handleNext(this); + } + } + + void addError(Object error, [StackTrace stackTrace]) { + if (!isClosed && _isFiring) { + _addPendingEvent(new _DelayedError(error, stackTrace)); + return; + } + super.addError(error, stackTrace); + while (_hasPending) { + _pending.handleNext(this); + } + } + + Future close() { + if (!isClosed && _isFiring) { + _addPendingEvent(const _DelayedDone()); + _state |= _BroadcastStreamController._STATE_CLOSED; + return super.done; + } + Future result = super.close(); + assert(!_hasPending); + return result; + } + + void _callOnCancel() { + if (_hasPending) { + _pending.clear(); + _pending = null; + } + super._callOnCancel(); + } +} + +// A subscription that never receives any events. +// It can simulate pauses, but otherwise does nothing. +class _DoneSubscription implements StreamSubscription { + int _pauseCount = 0; + void onData(void handleData(T data)) {} + void onError(Function handleError) {} + void onDone(void handleDone()) {} + void pause([Future resumeSignal]) { + if (resumeSignal != null) resumeSignal.then(_resume); + _pauseCount++; + } + void resume() { _resume(null); } + void _resume(_) { + if (_pauseCount > 0) _pauseCount--; + } + Future cancel() { return new _Future.immediate(null); } + bool get isPaused => _pauseCount > 0; + Future asFuture([Object value]) => new _Future(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/deferred_load.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/deferred_load.dart new file mode 100644 index 0000000..43982ce --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/deferred_load.dart @@ -0,0 +1,53 @@ +// Copyright (c) 2013, 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. + +part of dart.async; + +/** + * Indicates that loading of [libraryName] is deferred. + * + * Applies to library imports, when used as metadata. + * + * Example usage: + * + * @lazy + * import 'foo.dart' as foo; + * + * const lazy = const DeferredLibrary('com.example.foo'); + * + * void main() { + * foo.method(); // Throws a NoSuchMethodError, foo is not loaded yet. + * lazy.load().then(onFooLoaded); + * } + * + * void onFooLoaded(_) { + * foo.method(); + * } + */ +class DeferredLibrary { + final String libraryName; + final String uri; + + const DeferredLibrary(this.libraryName, {this.uri}); + + /** + * Ensure that [libraryName] has been loaded. + * + * The value of the returned future is true if this invocation of + * [load] caused the library to be loaded. + * + * If the library fails to load, the Future will complete with a + * DeferredLoadException. + */ + external Future load(); +} + +/** + * Thrown when a deferred library fails to load. + */ +class DeferredLoadException implements Exception { + DeferredLoadException(String this._s); + String toString() => "DeferredLoadException: '$_s'"; + final String _s; +} \ No newline at end of file diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/future.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/future.dart new file mode 100644 index 0000000..5e02069 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/future.dart @@ -0,0 +1,616 @@ +// Copyright (c) 2012, 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. + +part of dart.async; + +/** + * An object representing a delayed computation. + * + * A [Future] is used to represent a potential value, or error, + * that will be available at some time in the future. + * Receivers of a [Future] can register callbacks + * that handle the value or error once it is available. + * For example: + * + * Future future = getFuture(); + * future.then((value) => handleValue(value)) + * .catchError((error) => handleError(error)); + * + * A [Future] can complete in two ways: + * with a value ("the future succeeds") + * or with an error ("the future fails"). + * Users can install callbacks for each case. + * The result of registering a pair of callbacks is a new Future (the + * "successor") which in turn is completed with the result of invoking the + * corresponding callback. + * The successor is completed with an error if the invoked callback throws. + * For example: + * + * Future successor = future.then((int value) { + * // Invoked when the future is completed with a value. + * return 42; // The successor is completed with the value 42. + * }, + * onError: (e) { + * // Invoked when the future is completed with an error. + * if (canHandle(e)) { + * return 499; // The successor is completed with the value 499. + * } else { + * throw e; // The successor is completed with the error e. + * } + * }); + * + * If a future does not have a successor when it completes with an error, + * it forwards the error message to the global error-handler. + * This behavior makes sure that no error is silently dropped. + * However, it also means that error handlers should be installed early, + * so that they are present as soon as a future is completed with an error. + * The following example demonstrates this potential bug: + * + * var future = getFuture(); + * new Timer(new Duration(milliseconds: 5), () { + * // The error-handler is not attached until 5 ms after the future has + * // been received. If the future fails before that, the error is + * // forwarded to the global error-handler, even though there is code + * // (just below) to eventually handle the error. + * future.then((value) { useValue(value); }, + * onError: (e) { handleError(e); }); + * }); + * + * When registering callbacks, it's often more readable to register the two + * callbacks separately, by first using [then] with one argument + * (the value handler) and using a second [catchError] for handling errors. + * Each of these will forward the result that they don't handle + * to their successors, and together they handle both value and error result. + * It also has the additional benefit of the [catchError] handling errors in the + * [then] value callback too. + * Using sequential handlers instead of parallel ones often leads to code that + * is easier to reason about. + * It also makes asynchronous code very similar to synchronous code: + * + * // Synchronous code. + * try { + * int value = foo(); + * return bar(value); + * } catch (e) { + * return 499; + * } + * + * Equivalent asynchronous code, based on futures: + * + * Future future = new Future(foo); // Result of foo() as a future. + * future.then((int value) => bar(value)) + * .catchError((e) => 499); + * + * Similar to the synchronous code, the error handler (registered with + * [catchError]) is handling any errors thrown by either `foo` or `bar`. + * If the error-handler had been registered as the `onError` parameter of + * the `then` call, it would not catch errors from the `bar` call. + * + * Futures can have more than one callback-pair registered. Each successor is + * treated independently and is handled as if it was the only successor. + * + * A future may also fail to ever complete. In that case, no callbacks are + * called. + */ +abstract class Future { + // The `_nullFuture` is a completed Future with the value `null`. + static final _Future _nullFuture = new Future.value(null); + + /** + * Creates a future containing the result of calling [computation] + * asynchronously with [Timer.run]. + * + * If the result of executing [computation] throws, the returned future is + * completed with the error. + * + * If the returned value is itself a [Future], completion of + * the created future will wait until the returned future completes, + * and will then complete with the same result. + * + * If a non-future value is returned, the returned future is completed + * with that value. + */ + factory Future(computation()) { + _Future result = new _Future(); + Timer.run(() { + try { + result._complete(computation()); + } catch (e, s) { + result._completeError(e, s); + } + }); + return result; + } + + /** + * Creates a future containing the result of calling [computation] + * asynchronously with [scheduleMicrotask]. + * + * If executing [computation] throws, + * the returned future is completed with the thrown error. + * + * If calling [computation] returns a [Future], completion of + * the created future will wait until the returned future completes, + * and will then complete with the same result. + * + * If calling [computation] returns a non-future value, + * the returned future is completed with that value. + */ + factory Future.microtask(computation()) { + _Future result = new _Future(); + scheduleMicrotask(() { + try { + result._complete(computation()); + } catch (e, s) { + result._completeError(e, s); + } + }); + return result; + } + + /** + * Creates a future containing the result of immediately calling + * [computation]. + * + * If calling [computation] throws, the returned future is completed with the + * error. + * + * If calling [computation] returns a [Future], completion of + * the created future will wait until the returned future completes, + * and will then complete with the same result. + * + * If calling [computation] returns a non-future value, + * the returned future is completed with that value. + */ + factory Future.sync(computation()) { + try { + var result = computation(); + return new Future.value(result); + } catch (error, stackTrace) { + return new Future.error(error, stackTrace); + } + } + + /** + * A future whose value is available in the next event-loop iteration. + * + * If [value] is not a [Future], using this constructor is equivalent + * to [:new Future.sync(() => value):]. + * + * Use [Completer] to create a Future and complete it later. + */ + factory Future.value([value]) { + return new _Future.immediate(value); + } + + /** + * A future that completes with an error in the next event-loop iteration. + * + * Use [Completer] to create a Future and complete it later. + */ + factory Future.error(Object error, [StackTrace stackTrace]) { + return new _Future.immediateError(error, stackTrace); + } + + /** + * Creates a future that completes after a delay. + * + * The future will be completed after the given [duration] has passed with + * the result of calling [computation]. If the duration is 0 or less, it + * completes no sooner than in the next event-loop iteration. + * + * If [computation] is omitted, + * it will be treated as if [computation] was set to `() => null`, + * and the future will eventually complete with the `null` value. + * + * If calling [computation] throws, the created future will complete with the + * error. + * + * See also [Completer] for a way to complete a future at a later + * time that isn't a known fixed duration. + */ + factory Future.delayed(Duration duration, [T computation()]) { + Completer completer = new Completer.sync(); + Future result = completer.future; + if (computation != null) { + result = result.then((ignored) => computation()); + } + new Timer(duration, () { completer.complete(null); }); + return result; + } + + /** + * Wait for all the given futures to complete and collect their values. + * + * Returns a future which will complete once all the futures in a list are + * complete. If any of the futures in the list completes with an error, + * the resulting future also completes with an error. Otherwise the value + * of the returned future will be a list of all the values that were produced. + * + * If `eagerError` is true, the future completes with an error immediately on + * the first error from one of the futures. Otherwise all futures must + * complete before the returned future is completed (still with the first + * error to occur, the remaining errors are silently dropped). + */ + static Future wait(Iterable futures, {bool eagerError: false}) { + Completer completer; // Completer for the returned future. + List values; // Collects the values. Set to null on error. + int remaining = 0; // How many futures are we waiting for. + var error; // The first error from a future. + StackTrace stackTrace; // The stackTrace that came with the error. + + // Handle an error from any of the futures. + handleError(theError, theStackTrace) { + bool isFirstError = values != null; + values = null; + remaining--; + if (isFirstError) { + if (remaining == 0 || eagerError) { + completer.completeError(theError, theStackTrace); + } else { + error = theError; + stackTrace = theStackTrace; + } + } else if (remaining == 0 && !eagerError) { + completer.completeError(error, stackTrace); + } + } + + // As each future completes, put its value into the corresponding + // position in the list of values. + for (Future future in futures) { + int pos = remaining++; + future.then((Object value) { + remaining--; + if (values != null) { + values[pos] = value; + if (remaining == 0) { + completer.complete(values); + } + } else if (remaining == 0 && !eagerError) { + completer.completeError(error, stackTrace); + } + }, onError: handleError); + } + if (remaining == 0) { + return new Future.value(const []); + } + values = new List(remaining); + completer = new Completer(); + return completer.future; + } + + /** + * Perform an async operation for each element of the iterable, in turn. + * + * Runs [f] for each element in [input] in order, moving to the next element + * only when the [Future] returned by [f] completes. Returns a [Future] that + * completes when all elements have been processed. + * + * The return values of all [Future]s are discarded. Any errors will cause the + * iteration to stop and will be piped through the returned [Future]. + */ + static Future forEach(Iterable input, Future f(element)) { + _Future doneSignal = new _Future(); + Iterator iterator = input.iterator; + void nextElement(_) { + if (iterator.moveNext()) { + new Future.sync(() => f(iterator.current)) + .then(nextElement, onError: doneSignal._completeError); + } else { + doneSignal._complete(null); + } + } + nextElement(null); + return doneSignal; + } + + /** + * Register callbacks to be called when this future completes. + * + * When this future completes with a value, + * the [onValue] callback will be called with that value. + * If this future is already completed, the callback will not be called + * immediately, but will be scheduled in a later microtask. + * + * If [onError] is provided, and this future completes with an error, + * the `onError` callback is called with that error its stack trace. + * The `onError` callback must accept either one argument or two arguments. + * If `onError` accepts two arguments, + * it is called with both the error and the stack trace, + * otherwise it is called with just the error object. + * + * Returns a new [Future] + * which is completed with the result of the call to `onValue` + * (if this future completes with a value) + * or to `onError` (if this future completes with an error). + * + * If the invoked callback throws, + * the returned future is completed with the thrown error + * and a stack trace for the error. + * In the case of `onError`, + * if the exception thrown is `identical` to the error argument to `onError`, + * the throw is considered a rethrow, + * and the original stack trace is used instead. + * + * If the callback returns a [Future], + * the future returned by `then` will be completed with + * the same result of the future returned by the callback. + * + * If [onError] is not given, and this future completes with an error, + * the error is forwarded directly to the returned future. + * + * In most cases, it is more readable to use [catchError] separately, possibly + * with a `test` parameter, instead of handling both value and error in a + * single [then] call. + */ + Future then(onValue(T value), { Function onError }); + + /** + * Handles errors emitted by this [Future]. + * + * This is the asynchronous equivalent of a "catch" block. + * + * Returns a new [Future] that will be completed with either the result of + * this future or the result of calling the `onError` callback. + * + * If this future completes with a value, + * the returned future completes with the same value. + * + * If this future completes with an error, + * then [test] is first called with the error value. + * + * If `test` returns false, the exception is not handled by this `catchError`, + * and the returned future completes with the same error and stack trace + * as this future. + * + * If `test` returns `true`, + * [onError] is called with the error and possibly stack trace, + * and the returned future is completed with the result of this call + * in exactly the same way as for [then]'s `onError`. + * + * If `test` is omitted, it defaults to a function that always returns true. + * The `test` function should not throw, but if it does, it is handled as + * if the the `onError` function had thrown. + * + * Example: + * + * foo + * .catchError(..., test: (e) => e is ArgumentError) + * .catchError(..., test: (e) => e is NoSuchMethodError) + * .then((v) { ... }); + * + * This method is equivalent to: + * + * Future catchError(onError(error), + * {bool test(error)}) { + * this.then((v) => v, // Forward the value. + * // But handle errors, if the [test] succeeds. + * onError: (e, stackTrace) { + * if (test == null || test(e)) { + * if (onError is ZoneBinaryCallback) { + * return onError(e, stackTrace); + * } + * return onError(e); + * } + * throw e; + * }); + * } + * + */ + Future catchError(Function onError, + {bool test(Object error)}); + + /** + * Register a function to be called when this future completes. + * + * The [action] function is called when this future completes, whether it + * does so with a value or with an error. + * + * This is the asynchronous equivalent of a "finally" block. + * + * The future returned by this call, `f`, will complete the same way + * as this future unless an error occurs in the [action] call, or in + * a [Future] returned by the [action] call. If the call to [action] + * does not return a future, its return value is ignored. + * + * If the call to [action] throws, then `f` is completed with the + * thrown error. + * + * If the call to [action] returns a [Future], `f2`, then completion of + * `f` is delayed until `f2` completes. If `f2` completes with + * an error, that will be the result of `f` too. The value of `f2` is always + * ignored. + * + * This method is equivalent to: + * + * Future whenComplete(action()) { + * this.then((v) { + * var f2 = action(); + * if (f2 is Future) return f2.then((_) => v); + * return v + * }, + * onError: (e) { + * var f2 = action(); + * if (f2 is Future) return f2.then((_) { throw e; }); + * throw e; + * }); + * } + */ + Future whenComplete(action()); + + /** + * Creates a [Stream] that sends [this]' completion value, data or error, to + * its subscribers. The stream closes after the completion value. + */ + Stream asStream(); + + /** + * Time-out the future computation after [timeLimit] has passed. + * + * Returns a new future that completes with the same value as this future, + * if this future completes in time. + * + * If this future does not complete before `timeLimit` has passed, + * the [onTimeout] action is executed instead, and its result (whether it + * returns or throws) is used as the result of the returned future. + * + * If `onTimeout` is omitted, a timeout will cause the returned future to + * complete with a [TimeoutException]. + */ + Future timeout(Duration timeLimit, {onTimeout()}); +} + +/** + * Thrown when a scheduled timeout happens while waiting for an async result. + */ +class TimeoutException implements Exception { + /** Description of the cause of the timeout. */ + final String message; + /** The duration that was exceeded. */ + final Duration duration; + + TimeoutException(this.message, [this.duration]); + + String toString() { + if (message != null) { + if (duration != null) return "TimeoutException after $duration: $message"; + return "TimeoutException: $message"; + } + if (duration != null) return "TimeoutException after $duration"; + return "TimeoutException"; + } +} + +/** + * A way to produce Future objects and to complete them later + * with a value or error. + * + * If you already have a Future, you probably don't need a Completer. + * Instead, you can usually use [Future.then], which returns a Future: + * + * Future doStuff(){ + * return someAsyncOperation().then((result) { + * // Do something. + * }); + * } + * + * If you do need to create a Future from scratch—for example, + * when you're converting a callback-based API into a Future-based + * one—you can use a Completer as follows: + * + * Class AsyncOperation { + * Completer _completer = new Completer(); + * + * Future doOperation() { + * _startOperation(); + * return _completer.future; // Send future object back to client. + * } + * + * // Something calls this when the value is ready. + * _finishOperation(T result) { + * _completer.complete(result); + * } + * + * // If something goes wrong, call this. + * _errorHappened(error) { + * _completer.completeError(error); + * } + * } + */ +abstract class Completer { + + /** + * Creates a completer whose future is completed asynchronously, sometime + * after [complete] is called on it. This allows a call to [complete] to + * be in the middle of other code, without running an unknown amount of + * future completion and [then] callbacks synchronously at the point that + * [complete] is called. + * + * Example: + * + * var completer = new Completer.sync(); + * completer.future.then((_) { bar(); }); + * // The completion is the result of the asynchronous onDone event. + * // However, there is code executed after the call to complete, + * // but before completer.future runs its completion callback. + * stream.listen(print, onDone: () { + * completer.complete("done"); + * foo(); // In this case, foo() runs before bar(). + * }); + */ + factory Completer() => new _AsyncCompleter(); + + /** + * Completes the future synchronously. + * + * This constructor should be avoided unless the completion of the future is + * known to be the final result of another asynchronous operation. If in doubt + * use the default [Completer] constructor. + * + * Example: + * + * var completer = new Completer.sync(); + * // The completion is the result of the asynchronous onDone event. + * // No other operation is performed after the completion. It is safe + * // to use the Completer.sync constructor. + * stream.listen(print, onDone: () { completer.complete("done"); }); + * + * Bad example. Do not use this code. Only for illustrative purposes: + * + * var completer = new Completer.sync(); + * completer.future.then((_) { bar(); }); + * // The completion is the result of the asynchronous onDone event. + * // However, there is still code executed after the completion. This + * // operation is *not* safe. + * stream.listen(print, onDone: () { + * completer.complete("done"); + * foo(); // In this case, foo() runs after bar(). + * }); + */ + factory Completer.sync() => new _SyncCompleter(); + + /** The future that will contain the result provided to this completer. */ + Future get future; + + /** + * Completes [future] with the supplied values. + * + * If the value is itself a future, the completer will wait for that future + * to complete, and complete with the same result, whether it is a success + * or an error. + * + * Calling `complete` or [completeError] must not be done more than once. + * + * All listeners on the future are informed about the value. + */ + void complete([value]); + + /** + * Complete [future] with an error. + * + * Calling [complete] or `completeError` must not be done more than once. + * + * Completing a future with an error indicates that an exception was thrown + * while trying to produce a value. + * + * The argument [error] must not be `null`. + * + * If `error` is a `Future`, the future itself is used as the error value. + * If you want to complete with the result of the future, you can use: + * + * thisCompleter.complete(theFuture) + * + * or if you only want to handle an error from the future: + * + * theFuture.catchError(thisCompleter.completeError); + * + */ + void completeError(Object error, [StackTrace stackTrace]); + + /** + * Whether the future has been completed. + */ + bool get isCompleted; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/future_impl.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/future_impl.dart new file mode 100644 index 0000000..5fcbe3d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/future_impl.dart @@ -0,0 +1,654 @@ +// Copyright (c) 2012, 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. + +part of dart.async; + +/** The onValue and onError handlers return either a value or a future */ +typedef dynamic _FutureOnValue(T value); +/** Test used by [Future.catchError] to handle skip some errors. */ +typedef bool _FutureErrorTest(var error); +/** Used by [WhenFuture]. */ +typedef _FutureAction(); + +abstract class _Completer implements Completer { + final _Future future = new _Future(); + + void complete([value]); + + void completeError(Object error, [StackTrace stackTrace]); + + // The future's _isComplete doesn't take into account pending completions. + // We therefore use _mayComplete. + bool get isCompleted => !future._mayComplete; +} + +class _AsyncCompleter extends _Completer { + + void complete([value]) { + if (!future._mayComplete) throw new StateError("Future already completed"); + future._asyncComplete(value); + } + + void completeError(Object error, [StackTrace stackTrace]) { + if (error == null) throw new ArgumentError("Error must not be null"); + if (!future._mayComplete) throw new StateError("Future already completed"); + future._asyncCompleteError(error, stackTrace); + } +} + +class _SyncCompleter extends _Completer { + + void complete([value]) { + if (!future._mayComplete) throw new StateError("Future already completed"); + future._complete(value); + } + + void completeError(Object error, [StackTrace stackTrace]) { + if (!future._mayComplete) throw new StateError("Future already completed"); + future._completeError(error, stackTrace); + } +} + +class _Future implements Future { + // State of the future. The state determines the interpretation of the + // [resultOrListeners] field. + // TODO(lrn): rename field since it can also contain a chained future. + + /// Initial state, waiting for a result. In this state, the + /// [resultOrListeners] field holds a single-linked list of + /// [FutureListener] listeners. + static const int _INCOMPLETE = 0; + /// Pending completion. Set when completed using [_asyncComplete] or + /// [_asyncCompleteError]. It is an error to try to complete it again. + static const int _PENDING_COMPLETE = 1; + /// The future has been chained to another future. The result of that + /// other future becomes the result of this future as well. + /// In this state, no callback should be executed anymore. + // TODO(floitsch): we don't really need a special "_CHAINED" state. We could + // just use the PENDING_COMPLETE state instead. + static const int _CHAINED = 2; + /// The future has been completed with a value result. + static const int _VALUE = 4; + /// The future has been completed with an error result. + static const int _ERROR = 8; + + /** Whether the future is complete, and as what. */ + int _state = _INCOMPLETE; + + final Zone _zone; + + bool get _mayComplete => _state == _INCOMPLETE; + bool get _isChained => _state == _CHAINED; + bool get _isComplete => _state >= _VALUE; + bool get _hasValue => _state == _VALUE; + bool get _hasError => _state == _ERROR; + + set _isChained(bool value) { + if (value) { + assert(!_isComplete); + _state = _CHAINED; + } else { + assert(_isChained); + _state = _INCOMPLETE; + } + } + + /** + * Either the result, a list of listeners or another future. + * + * The result of the future is either a value or an error. + * A result is only stored when the future has completed. + * + * The listeners is an internally linked list of [_FutureListener]s. + * Listeners are only remembered while the future is not yet complete, + * and it is not chained to another future. + * + * The future is another future that his future is chained to. This future + * is waiting for the other future to complete, and when it does, this future + * will complete with the same result. + * All listeners are forwarded to the other future. + * + * The cases are disjoint (incomplete and unchained, incomplete and + * chained, or completed with value or error), so the field only needs to hold + * one value at a time. + */ + var _resultOrListeners; + + /** + * A [_Future] implements a linked list. If a future has more than one + * listener the [_nextListener] field of the first listener points to the + * remaining listeners. + */ + // TODO(floitsch): since single listeners are the common case we should + // use a bit to indicate that the _resultOrListeners contains a container. + _Future _nextListener; + + // TODO(floitsch): we only need two closure fields to store the callbacks. + // If we store the type of a closure in the state field (where there are + // still bits left), we can just store two closures instead of using 4 + // fields of which 2 are always null. + final _FutureOnValue _onValueCallback; + final _FutureErrorTest _errorTestCallback; + final Function _onErrorCallback; + final _FutureAction _whenCompleteActionCallback; + + _FutureOnValue get _onValue => _isChained ? null : _onValueCallback; + _FutureErrorTest get _errorTest => _isChained ? null : _errorTestCallback; + Function get _onError => _isChained ? null : _onErrorCallback; + _FutureAction get _whenCompleteAction + => _isChained ? null : _whenCompleteActionCallback; + + _Future() + : _zone = Zone.current, + _onValueCallback = null, _errorTestCallback = null, + _onErrorCallback = null, _whenCompleteActionCallback = null; + + /// Valid types for value: `T` or `Future`. + _Future.immediate(value) + : _zone = Zone.current, + _onValueCallback = null, _errorTestCallback = null, + _onErrorCallback = null, _whenCompleteActionCallback = null { + _asyncComplete(value); + } + + _Future.immediateError(var error, [StackTrace stackTrace]) + : _zone = Zone.current, + _onValueCallback = null, _errorTestCallback = null, + _onErrorCallback = null, _whenCompleteActionCallback = null { + _asyncCompleteError(error, stackTrace); + } + + _Future._then(onValueCallback(value), Function onErrorCallback) + : _zone = Zone.current, + _onValueCallback = Zone.current.registerUnaryCallback(onValueCallback), + _onErrorCallback = _registerErrorHandler(onErrorCallback, Zone.current), + _errorTestCallback = null, + _whenCompleteActionCallback = null; + + _Future._catchError(Function onErrorCallback, bool errorTestCallback(e)) + : _zone = Zone.current, + _onErrorCallback = _registerErrorHandler(onErrorCallback, Zone.current), + _errorTestCallback = + Zone.current.registerUnaryCallback(errorTestCallback), + _onValueCallback = null, + _whenCompleteActionCallback = null; + + _Future._whenComplete(whenCompleteActionCallback()) + : _zone = Zone.current, + _whenCompleteActionCallback = + Zone.current.registerCallback(whenCompleteActionCallback), + _onValueCallback = null, + _errorTestCallback = null, + _onErrorCallback = null; + + Future then(f(T value), { Function onError }) { + _Future result; + result = new _Future._then(f, onError); + _addListener(result); + return result; + } + + Future catchError(Function onError, { bool test(error) }) { + _Future result = new _Future._catchError(onError, test); + _addListener(result); + return result; + } + + Future whenComplete(action()) { + _Future result = new _Future._whenComplete(action); + _addListener(result); + return result; + } + + Stream asStream() => new Stream.fromFuture(this); + + void _markPendingCompletion() { + if (!_mayComplete) throw new StateError("Future already completed"); + _state = _PENDING_COMPLETE; + } + + T get _value { + assert(_isComplete && _hasValue); + return _resultOrListeners; + } + + _AsyncError get _error { + assert(_isComplete && _hasError); + return _resultOrListeners; + } + + void _setValue(T value) { + assert(!_isComplete); // But may have a completion pending. + _state = _VALUE; + _resultOrListeners = value; + } + + void _setError(Object error, StackTrace stackTrace) { + assert(!_isComplete); // But may have a completion pending. + _state = _ERROR; + _resultOrListeners = new _AsyncError(error, stackTrace); + } + + void _addListener(_Future listener) { + assert(listener._nextListener == null); + if (_isComplete) { + // Handle late listeners asynchronously. + _zone.scheduleMicrotask(() { + _propagateToListeners(this, listener); + }); + } else { + listener._nextListener = _resultOrListeners; + _resultOrListeners = listener; + } + } + + _Future _removeListeners() { + // Reverse listeners before returning them, so the resulting list is in + // subscription order. + assert(!_isComplete); + _Future current = _resultOrListeners; + _resultOrListeners = null; + _Future prev = null; + while (current != null) { + _Future next = current._nextListener; + current._nextListener = prev; + prev = current; + current = next; + } + return prev; + } + + // Take the value (when completed) of source and complete target with that + // value (or error). This function can chain all Futures, but is slower + // for _Future than _chainCoreFuture - Use _chainCoreFuture in that case. + static void _chainForeignFuture(Future source, _Future target) { + assert(!target._isComplete); + assert(source is! _Future); + + // Mark the target as chained (and as such half-completed). + target._isChained = true; + source.then((value) { + assert(target._isChained); + target._completeWithValue(value); + }, + // TODO(floitsch): eventually we would like to make this non-optional + // and dependent on the listeners of the target future. If none of + // the target future's listeners want to have the stack trace we don't + // need a trace. + onError: (error, [stackTrace]) { + assert(target._isChained); + target._completeError(error, stackTrace); + }); + } + + // Take the value (when completed) of source and complete target with that + // value (or error). This function expects that source is a _Future. + static void _chainCoreFuture(_Future source, _Future target) { + assert(!target._isComplete); + assert(source is _Future); + + // Mark the target as chained (and as such half-completed). + target._isChained = true; + _Future internalFuture = source; + if (internalFuture._isComplete) { + _propagateToListeners(internalFuture, target); + } else { + internalFuture._addListener(target); + } + } + + void _complete(value) { + assert(!_isComplete); + assert(_onValue == null); + assert(_onError == null); + assert(_whenCompleteAction == null); + assert(_errorTest == null); + + if (value is Future) { + if (value is _Future) { + _chainCoreFuture(value, this); + } else { + _chainForeignFuture(value, this); + } + } else { + _Future listeners = _removeListeners(); + _setValue(value); + _propagateToListeners(this, listeners); + } + } + + void _completeWithValue(value) { + assert(!_isComplete); + assert(_onValue == null); + assert(_onError == null); + assert(_whenCompleteAction == null); + assert(_errorTest == null); + assert(value is! Future); + + _Future listeners = _removeListeners(); + _setValue(value); + _propagateToListeners(this, listeners); + } + + void _completeError(error, [StackTrace stackTrace]) { + assert(!_isComplete); + assert(_onValue == null); + assert(_onError == null); + assert(_whenCompleteAction == null); + assert(_errorTest == null); + + _Future listeners = _removeListeners(); + _setError(error, stackTrace); + _propagateToListeners(this, listeners); + } + + void _asyncComplete(value) { + assert(!_isComplete); + assert(_onValue == null); + assert(_onError == null); + assert(_whenCompleteAction == null); + assert(_errorTest == null); + // Two corner cases if the value is a future: + // 1. the future is already completed and an error. + // 2. the future is not yet completed but might become an error. + // The first case means that we must not immediately complete the Future, + // as our code would immediately start propagating the error without + // giving the time to install error-handlers. + // However the second case requires us to deal with the value immediately. + // Otherwise the value could complete with an error and report an + // unhandled error, even though we know we are already going to listen to + // it. + + if (value == null) { + // No checks for `null`. + } else if (value is Future) { + // Assign to typed variables so we get earlier checks in checked mode. + Future typedFuture = value; + if (typedFuture is _Future) { + _Future coreFuture = typedFuture; + if (coreFuture._isComplete && coreFuture._hasError) { + // Case 1 from above. Delay completion to enable the user to register + // callbacks. + _markPendingCompletion(); + _zone.scheduleMicrotask(() { + _chainCoreFuture(coreFuture, this); + }); + } else { + _chainCoreFuture(coreFuture, this); + } + } else { + // Case 2 from above. Chain the future immidiately. + // Note that we are still completing asynchronously (through + // _chainForeignFuture).. + _chainForeignFuture(typedFuture, this); + } + return; + } else { + T typedValue = value; + } + + _markPendingCompletion(); + _zone.scheduleMicrotask(() { + _completeWithValue(value); + }); + } + + void _asyncCompleteError(error, StackTrace stackTrace) { + assert(!_isComplete); + assert(_onValue == null); + assert(_onError == null); + assert(_whenCompleteAction == null); + assert(_errorTest == null); + + _markPendingCompletion(); + _zone.scheduleMicrotask(() { + _completeError(error, stackTrace); + }); + } + + /** + * Propagates the value/error of [source] to its [listeners]. + * + * Unlinks all listeners and propagates the source to each listener + * separately. + */ + static void _propagateMultipleListeners(_Future source, _Future listeners) { + assert(listeners != null); + assert(listeners._nextListener != null); + do { + _Future listener = listeners; + listeners = listener._nextListener; + listener._nextListener = null; + _propagateToListeners(source, listener); + } while (listeners != null); + } + + /** + * Propagates the value/error of [source] to its [listeners], executing the + * listeners' callbacks. + * + * If [runCallback] is true (which should be the default) it executes + * the registered action of listeners. If it is `false` then the callback is + * skipped. This is used to complete futures with chained futures. + */ + static void _propagateToListeners(_Future source, _Future listeners) { + while (true) { + if (!source._isComplete) return; // Chained future. + bool hasError = source._hasError; + if (hasError && listeners == null) { + _AsyncError asyncError = source._error; + source._zone.handleUncaughtError( + asyncError.error, asyncError.stackTrace); + return; + } + if (listeners == null) return; + _Future listener = listeners; + if (listener._nextListener != null) { + // Usually futures only have one listener. If they have several, we + // handle them specially. + _propagateMultipleListeners(source, listeners); + return; + } + // Do the actual propagation. + // Set initial state of listenerHasValue and listenerValueOrError. These + // variables are updated, with the outcome of potential callbacks. + bool listenerHasValue = true; + final sourceValue = source._hasValue ? source._value : null; + var listenerValueOrError = sourceValue; + // Set to true if a whenComplete needs to wait for a future. + // The whenComplete action will resume the propagation by itself. + bool isPropagationAborted = false; + // TODO(floitsch): mark the listener as pending completion. Currently + // we can't do this, since the markPendingCompletion verifies that + // the future is not already marked (or chained). + // Only if we either have an error or callbacks, go into this, somewhat + // expensive, branch. Here we'll enter/leave the zone. Many futures + // doesn't have callbacks, so this is a significant optimization. + if (hasError || + listener._onValue != null || + listener._whenCompleteAction != null) { + Zone zone = listener._zone; + if (hasError && !source._zone.inSameErrorZone(zone)) { + // Don't cross zone boundaries with errors. + _AsyncError asyncError = source._error; + source._zone.handleUncaughtError( + asyncError.error, asyncError.stackTrace); + return; + } + + Zone oldZone; + if (!identical(Zone.current, zone)) { + // Change zone if it's not current. + oldZone = Zone._enter(zone); + } + + bool handleValueCallback() { + try { + listenerValueOrError = zone.runUnary(listener._onValue, + sourceValue); + return true; + } catch (e, s) { + listenerValueOrError = new _AsyncError(e, s); + return false; + } + } + + void handleError() { + _AsyncError asyncError = source._error; + _FutureErrorTest test = listener._errorTest; + bool matchesTest = true; + if (test != null) { + try { + matchesTest = zone.runUnary(test, asyncError.error); + } catch (e, s) { + // TODO(ajohnsen): Should we suport rethrow for test throws? + listenerValueOrError = identical(asyncError.error, e) ? + asyncError : new _AsyncError(e, s); + listenerHasValue = false; + return; + } + } + Function errorCallback = listener._onError; + if (matchesTest && errorCallback != null) { + try { + if (errorCallback is ZoneBinaryCallback) { + listenerValueOrError = zone.runBinary(errorCallback, + asyncError.error, + asyncError.stackTrace); + } else { + listenerValueOrError = zone.runUnary(errorCallback, + asyncError.error); + } + } catch (e, s) { + listenerValueOrError = identical(asyncError.error, e) ? + asyncError : new _AsyncError(e, s); + listenerHasValue = false; + return; + } + listenerHasValue = true; + } else { + // Copy over the error from the source. + listenerValueOrError = asyncError; + listenerHasValue = false; + } + } + + void handleWhenCompleteCallback() { + var completeResult; + try { + completeResult = zone.run(listener._whenCompleteAction); + } catch (e, s) { + if (hasError && identical(source._error.error, e)) { + listenerValueOrError = source._error; + } else { + listenerValueOrError = new _AsyncError(e, s); + } + listenerHasValue = false; + } + if (completeResult is Future) { + listener._isChained = true; + isPropagationAborted = true; + completeResult.then((ignored) { + // Try again. Since the future is marked as chained it won't run + // the whenComplete again. + _propagateToListeners(source, listener); + }, onError: (error, [stackTrace]) { + // When there is an error, we have to make the error the new + // result of the current listener. + if (completeResult is! _Future) { + // This should be a rare case. + completeResult = new _Future(); + completeResult._setError(error, stackTrace); + } + _propagateToListeners(completeResult, listener); + }); + } + } + + if (!hasError) { + if (listener._onValue != null) { + listenerHasValue = handleValueCallback(); + } + } else { + handleError(); + } + if (listener._whenCompleteAction != null) { + handleWhenCompleteCallback(); + } + // If we changed zone, oldZone will not be null. + if (oldZone != null) Zone._leave(oldZone); + + if (isPropagationAborted) return; + // If the listener's value is a future we need to chain it. Note that + // this can only happen if there is a callback. Since 'is' checks + // can be expensive, we're trying to avoid it. + if (listenerHasValue && + !identical(sourceValue, listenerValueOrError) && + listenerValueOrError is Future) { + Future chainSource = listenerValueOrError; + // Shortcut if the chain-source is already completed. Just continue + // the loop. + if (chainSource is _Future) { + if (chainSource._isComplete) { + // propagate the value (simulating a tail call). + listener._isChained = true; + source = chainSource; + listeners = listener; + continue; + } else { + _chainCoreFuture(chainSource, listener); + } + } else { + _chainForeignFuture(chainSource, listener); + } + return; + } + } + if (listenerHasValue) { + listeners = listener._removeListeners(); + listener._setValue(listenerValueOrError); + } else { + listeners = listener._removeListeners(); + _AsyncError asyncError = listenerValueOrError; + listener._setError(asyncError.error, asyncError.stackTrace); + } + // Prepare for next round. + source = listener; + } + } + + Future timeout(Duration timeLimit, {onTimeout()}) { + if (_isComplete) return new _Future.immediate(this); + _Future result = new _Future(); + Timer timer; + if (onTimeout == null) { + timer = new Timer(timeLimit, () { + result._completeError(new TimeoutException("Future not completed", + timeLimit)); + }); + } else { + Zone zone = Zone.current; + onTimeout = zone.registerCallback(onTimeout); + timer = new Timer(timeLimit, () { + try { + result._complete(zone.run(onTimeout)); + } catch (e, s) { + result._completeError(e, s); + } + }); + } + this.then((T v) { + if (timer.isActive) { + timer.cancel(); + result._completeWithValue(v); + } + }, onError: (e, s) { + if (timer.isActive) { + timer.cancel(); + result._completeError(e, s); + } + }); + return result; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/schedule_microtask.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/schedule_microtask.dart new file mode 100644 index 0000000..bd5312a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/schedule_microtask.dart @@ -0,0 +1,90 @@ +// Copyright (c) 2013, 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. + +part of dart.async; + +typedef void _AsyncCallback(); + +class _AsyncCallbackEntry { + final _AsyncCallback callback; + _AsyncCallbackEntry next; + _AsyncCallbackEntry(this.callback); +} + +_AsyncCallbackEntry _nextCallback; +_AsyncCallbackEntry _lastCallback; + +void _asyncRunCallbackLoop() { + _AsyncCallbackEntry entry = _nextCallback; + // As long as we are iterating over the registered callbacks we don't + // set the [_lastCallback] entry. + while (entry != null) { + entry.callback(); + entry = _nextCallback = entry.next; + } + // Any new callback must register a callback function now. + _lastCallback = null; +} + +void _asyncRunCallback() { + try { + _asyncRunCallbackLoop(); + } catch (e) { + _AsyncRun._scheduleImmediate(_asyncRunCallback); + _nextCallback = _nextCallback.next; + rethrow; + } +} + +void _scheduleAsyncCallback(callback) { + // Optimizing a group of Timer.run callbacks to be executed in the + // same Timer callback. + if (_lastCallback == null) { + _nextCallback = _lastCallback = new _AsyncCallbackEntry(callback); + _AsyncRun._scheduleImmediate(_asyncRunCallback); + } else { + _lastCallback = _lastCallback.next = new _AsyncCallbackEntry(callback); + } +} + +/** + * Runs a function asynchronously. + * + * Callbacks registered through this function are always executed in order and + * are guaranteed to run before other asynchronous events (like [Timer] events, + * or DOM events). + * + * **Warning:** it is possible to starve the DOM by registering asynchronous + * callbacks through this method. For example the following program runs + * the callbacks without ever giving the Timer callback a chance to execute: + * + * Timer.run(() { print("executed"); }); // Will never be executed. + * foo() { + * scheduleMicrotask(foo); // Schedules [foo] in front of other events. + * } + * main() { + * foo(); + * } + * + * ## Other resources + * + * * [The Event Loop and Dart](https://www.dartlang.org/articles/event-loop/): + * Learn how Dart handles the event queue and microtask queue, so you can write + * better asynchronous code with fewer surprises. + */ +void scheduleMicrotask(void callback()) { + if (Zone.current == Zone.ROOT) { + // No need to bind the callback. We know that the root's scheduleMicrotask + // will be invoked in the root zone. + Zone.current.scheduleMicrotask(callback); + return; + } + Zone.current.scheduleMicrotask( + Zone.current.bindCallback(callback, runGuarded: true)); +} + +class _AsyncRun { + /** Schedule the given callback before any other event in the event-loop. */ + external static void _scheduleImmediate(void callback()); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream.dart new file mode 100644 index 0000000..4b29de8 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream.dart @@ -0,0 +1,1491 @@ +// Copyright (c) 2013, 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. + +part of dart.async; + +// ------------------------------------------------------------------- +// Core Stream types +// ------------------------------------------------------------------- + +/** + * A source of asynchronous data events. + * + * A Stream provides a sequence of events. Each event is either a data event or + * an error event, representing the result of a single computation. When the + * Stream is exhausted, it may send a single "done" event. + * + * You can [listen] on a stream to receive the events it sends. When you listen, + * you receive a [StreamSubscription] object that can be used to stop listening, + * or to temporarily pause events from the stream. + * + * When an event is fired, the listeners at that time are informed. + * If a listener is added while an event is being fired, the change + * will only take effect after the event is completely fired. If a listener + * is canceled, it immediately stops receiving events. + * + * When the "done" event is fired, subscribers are unsubscribed before + * receiving the event. After the event has been sent, the stream has no + * subscribers. Adding new subscribers after this point is allowed, but + * they will just receive a new "done" event as soon as possible. + * + * Streams always respect "pause" requests. If necessary they need to buffer + * their input, but often, and preferably, they can simply request their input + * to pause too. + * + * There are two kinds of streams: The normal "single-subscription" streams and + * "broadcast" streams. + * + * A single-subscription stream allows only a single listener during the whole + * lifetime of the stream. + * It holds back events until it gets a listener, and it may exhaust + * itself when the listener is unsubscribed, even if the stream wasn't done. + * + * Single-subscription streams are generally used for streaming parts of + * contiguous data like file I/O. + * + * A broadcast stream allows any number of listeners, and it fires + * its events when they are ready, whether there are listeners or not. + * + * Broadcast streams are used for independent events/observers. + * + * Stream transformations, such as [where] and [skip], always return + * non-broadcast streams. If several listeners want to listen to the returned + * stream, use [asBroadcastStream] to create a broadcast stream on top of the + * non-broadcast stream. + * + * The default implementation of [isBroadcast] returns false. + * A broadcast stream inheriting from [Stream] must override [isBroadcast] + * to return [:true:]. + */ +abstract class Stream { + Stream(); + + /** + * Creates a new single-subscription stream from the future. + * + * When the future completes, the stream will fire one event, either + * data or error, and then close with a done-event. + */ + factory Stream.fromFuture(Future future) { + StreamController controller = new StreamController(sync: true); + future.then((value) { + controller.add(value); + controller.close(); + }, + onError: (error, stackTrace) { + controller.addError(error, stackTrace); + controller.close(); + }); + return controller.stream; + } + + /** + * Creates a single-subscription stream that gets its data from [data]. + * + * If iterating [data] throws an error, the stream ends immediately with + * that error. No done event will be sent (iteration is not complete), but no + * further data events will be generated either, since iteration cannot + * continue. + */ + factory Stream.fromIterable(Iterable data) { + return new _GeneratedStreamImpl( + () => new _IterablePendingEvents(data)); + } + + /** + * Creates a stream that repeatedly emits events at [period] intervals. + * + * The event values are computed by invoking [computation]. The argument to + * this callback is an integer that starts with 0 and is incremented for + * every event. + * + * If [computation] is omitted the event values will all be `null`. + */ + factory Stream.periodic(Duration period, + [T computation(int computationCount)]) { + if (computation == null) computation = ((i) => null); + + Timer timer; + int computationCount = 0; + StreamController controller; + // Counts the time that the Stream was running (and not paused). + Stopwatch watch = new Stopwatch(); + + void sendEvent() { + watch.reset(); + T data = computation(computationCount++); + controller.add(data); + } + + void startPeriodicTimer() { + assert(timer == null); + timer = new Timer.periodic(period, (Timer timer) { + sendEvent(); + }); + } + + controller = new StreamController(sync: true, + onListen: () { + watch.start(); + startPeriodicTimer(); + }, + onPause: () { + timer.cancel(); + timer = null; + watch.stop(); + }, + onResume: () { + assert(timer == null); + Duration elapsed = watch.elapsed; + watch.start(); + timer = new Timer(period - elapsed, () { + timer = null; + startPeriodicTimer(); + sendEvent(); + }); + }, + onCancel: () { + if (timer != null) timer.cancel(); + timer = null; + }); + return controller.stream; + } + + /** + * Creates a stream where all events of an existing stream are piped through + * a sink-transformation. + * + * The given [mapSink] closure is invoked when the returned stream is + * listened to. All events from the [source] are added into the event sink + * that is returned from the invocation. The transformation puts all + * transformed events into the sink the [mapSink] closure received during + * its invocation. Conceptually the [mapSink] creates a transformation pipe + * with the input sink being the returned [EventSink] and the output sink + * being the sink it received. + * + * This constructor is frequently used to build transformers. + * + * Example use for a duplicating transformer: + * + * class DuplicationSink implements EventSink { + * final EventSink _outputSink; + * DuplicationSink(this._outputSink); + * + * void add(String data) { + * _outputSink.add(data); + * _outputSink.add(data); + * } + * + * void addError(e, [st]) => _outputSink(e, st); + * void close() => _outputSink.close(); + * } + * + * class DuplicationTransformer implements StreamTransformer { + * // Some generic types ommitted for brevety. + * Stream bind(Stream stream) => new Stream.eventTransform( + * stream, + * (EventSink sink) => new DuplicationSink(sink)); + * } + * + * stringStream.transform(new DuplicationTransformer()); + */ + factory Stream.eventTransformed(Stream source, + EventSink mapSink(EventSink sink)) { + return new _BoundSinkStream(source, mapSink); + } + + /** + * Reports whether this stream is a broadcast stream. + */ + bool get isBroadcast => false; + + /** + * Returns a multi-subscription stream that produces the same events as this. + * + * If this stream is already a broadcast stream, it is returned unmodified. + * + * If this stream is single-subscription, return a new stream that allows + * multiple subscribers. It will subscribe to this stream when its first + * subscriber is added, and will stay subscribed until this stream ends, + * or a callback cancels the subscription. + * + * If [onListen] is provided, it is called with a subscription-like object + * that represents the underlying subscription to this stream. It is + * possible to pause, resume or cancel the subscription during the call + * to [onListen]. It is not possible to change the event handlers, including + * using [StreamSubscription.asFuture]. + * + * If [onCancel] is provided, it is called in a similar way to [onListen] + * when the returned stream stops having listener. If it later gets + * a new listener, the [onListen] function is called again. + * + * Use the callbacks, for example, for pausing the underlying subscription + * while having no subscribers to prevent losing events, or canceling the + * subscription when there are no listeners. + */ + Stream asBroadcastStream({ + void onListen(StreamSubscription subscription), + void onCancel(StreamSubscription subscription) }) { + if (isBroadcast) return this; + return new _AsBroadcastStream(this, onListen, onCancel); + } + + /** + * Adds a subscription to this stream. + * + * On each data event from this stream, the subscriber's [onData] handler + * is called. If [onData] is null, nothing happens. + * + * On errors from this stream, the [onError] handler is given a + * object describing the error. + * + * The [onError] callback must be of type `void onError(error)` or + * `void onError(error, StackTrace stackTrace)`. If [onError] accepts + * two arguments it is called with the stack trace (which could be `null` if + * the stream itself received an error without stack trace). + * Otherwise it is called with just the error object. + * + * If this stream closes, the [onDone] handler is called. + * + * If [cancelOnError] is true, the subscription is ended when + * the first error is reported. The default is false. + */ + StreamSubscription listen(void onData(T event), + { Function onError, + void onDone(), + bool cancelOnError}); + + /** + * Creates a new stream from this stream that discards some data events. + * + * The new stream sends the same error and done events as this stream, + * but it only sends the data events that satisfy the [test]. + * + * The returned stream is not a broadcast stream, even if this stream is. + */ + Stream where(bool test(T event)) { + return new _WhereStream(this, test); + } + + /** + * Creates a new stream that converts each element of this stream + * to a new value using the [convert] function. + * + * The returned stream is not a broadcast stream, even if this stream is. + */ + Stream map(convert(T event)) { + return new _MapStream(this, convert); + } + + /** + * Creates a new stream with each data event of this stream asynchronously + * mapped to a new event. + * + * This acts like [map], except that [convert] may return a [Future], + * and in that case, the stream waits for that future to complete before + * continuing with its result. + */ + Stream asyncMap(convert(T event)) { + StreamController controller; + StreamSubscription subscription; + controller = new StreamController( + onListen: () { + var add = controller.add; + var addError = controller.addError; + subscription = this.listen( + (T event) { + var newValue; + try { + newValue = convert(event); + } catch (e, s) { + controller.addError(e, s); + return; + } + if (newValue is Future) { + subscription.pause(); + newValue.then(add, onError: addError) + .whenComplete(subscription.resume); + } else { + controller.add(newValue); + } + }, + onError: addError, + onDone: controller.close + ); + }, + onPause: () { subscription.pause(); }, + onResume: () { subscription.resume(); }, + onCancel: () { subscription.cancel(); }, + sync: true + ); + return controller.stream; + } + + /** + * Creates a new stream with the events of a stream per original event. + * + * This acts like [expand], except that [convert] returns a [Stream] + * instead of an [Iterable]. + * The events of the returned stream becomes the events of the returned + * stream, in the order they are produced. + * + * If [convert] returns `null`, no value is put on the output stream, + * just as if it returned an empty stream. + */ + Stream asyncExpand(Stream convert(T event)) { + StreamController controller; + StreamSubscription subscription; + controller = new StreamController( + onListen: () { + subscription = this.listen( + (T event) { + Stream newStream; + try { + newStream = convert(event); + } catch (e, s) { + controller.addError(e, s); + return; + } + if (newStream != null) { + subscription.pause(); + controller.addStream(newStream) + .whenComplete(subscription.resume); + } + }, + onError: controller.addError, + onDone: controller.close + ); + }, + onPause: () { subscription.pause(); }, + onResume: () { subscription.resume(); }, + onCancel: () { subscription.cancel(); }, + sync: true + ); + return controller.stream; + } + + /** + * Creates a wrapper Stream that intercepts some errors from this stream. + * + * If this stream sends an error that matches [test], then it is intercepted + * by the [handle] function. + * + * The [onError] callback must be of type `void onError(error)` or + * `void onError(error, StackTrace stackTrace)`. Depending on the function + * type the the stream either invokes [onError] with or without a stack + * trace. The stack trace argument might be `null` if the stream itself + * received an error without stack trace. + * + * An asynchronous error [:e:] is matched by a test function if [:test(e):] + * returns true. If [test] is omitted, every error is considered matching. + * + * If the error is intercepted, the [handle] function can decide what to do + * with it. It can throw if it wants to raise a new (or the same) error, + * or simply return to make the stream forget the error. + * + * If you need to transform an error into a data event, use the more generic + * [Stream.transform] to handle the event by writing a data event to + * the output sink + * + * The returned stream is not a broadcast stream, even if this stream is. + */ + Stream handleError(Function onError, { bool test(error) }) { + return new _HandleErrorStream(this, onError, test); + } + + /** + * Creates a new stream from this stream that converts each element + * into zero or more events. + * + * Each incoming event is converted to an [Iterable] of new events, + * and each of these new events are then sent by the returned stream + * in order. + * + * The returned stream is not a broadcast stream, even if this stream is. + */ + Stream expand(Iterable convert(T value)) { + return new _ExpandStream(this, convert); + } + + /** + * Binds this stream as the input of the provided [StreamConsumer]. + */ + Future pipe(StreamConsumer streamConsumer) { + return streamConsumer.addStream(this).then((_) => streamConsumer.close()); + } + + /** + * Chains this stream as the input of the provided [StreamTransformer]. + * + * Returns the result of [:streamTransformer.bind:] itself. + */ + Stream transform(StreamTransformer streamTransformer) { + return streamTransformer.bind(this); + } + + /** + * Reduces a sequence of values by repeatedly applying [combine]. + */ + Future reduce(T combine(T previous, T element)) { + _Future result = new _Future(); + bool seenFirst = false; + T value; + StreamSubscription subscription; + subscription = this.listen( + (T element) { + if (seenFirst) { + _runUserCode(() => combine(value, element), + (T newValue) { value = newValue; }, + _cancelAndErrorClosure(subscription, result)); + } else { + value = element; + seenFirst = true; + } + }, + onError: result._completeError, + onDone: () { + if (!seenFirst) { + result._completeError(new StateError("No elements")); + } else { + result._complete(value); + } + }, + cancelOnError: true + ); + return result; + } + + /** Reduces a sequence of values by repeatedly applying [combine]. */ + Future fold(var initialValue, combine(var previous, T element)) { + _Future result = new _Future(); + var value = initialValue; + StreamSubscription subscription; + subscription = this.listen( + (T element) { + _runUserCode( + () => combine(value, element), + (newValue) { value = newValue; }, + _cancelAndErrorClosure(subscription, result) + ); + }, + onError: (e, st) { + result._completeError(e, st); + }, + onDone: () { + result._complete(value); + }, + cancelOnError: true); + return result; + } + + /** + * Collects string of data events' string representations. + * + * If [separator] is provided, it is inserted between any two + * elements. + * + * Any error in the stream causes the future to complete with that + * error. Otherwise it completes with the collected string when + * the "done" event arrives. + */ + Future join([String separator = ""]) { + _Future result = new _Future(); + StringBuffer buffer = new StringBuffer(); + StreamSubscription subscription; + bool first = true; + subscription = this.listen( + (T element) { + if (!first) { + buffer.write(separator); + } + first = false; + try { + buffer.write(element); + } catch (e, s) { + _cancelAndError(subscription, result, e, s); + } + }, + onError: (e) { + result._completeError(e); + }, + onDone: () { + result._complete(buffer.toString()); + }, + cancelOnError: true); + return result; + } + + /** + * Checks whether [needle] occurs in the elements provided by this stream. + * + * Completes the [Future] when the answer is known. + * If this stream reports an error, the [Future] will report that error. + */ + Future contains(Object needle) { + _Future future = new _Future(); + StreamSubscription subscription; + subscription = this.listen( + (T element) { + _runUserCode( + () => (element == needle), + (bool isMatch) { + if (isMatch) { + _cancelAndValue(subscription, future, true); + } + }, + _cancelAndErrorClosure(subscription, future) + ); + }, + onError: future._completeError, + onDone: () { + future._complete(false); + }, + cancelOnError: true); + return future; + } + + /** + * Executes [action] on each data event of the stream. + * + * Completes the returned [Future] when all events of the stream + * have been processed. Completes the future with an error if the + * stream has an error event, or if [action] throws. + */ + Future forEach(void action(T element)) { + _Future future = new _Future(); + StreamSubscription subscription; + subscription = this.listen( + (T element) { + _runUserCode( + () => action(element), + (_) {}, + _cancelAndErrorClosure(subscription, future) + ); + }, + onError: future._completeError, + onDone: () { + future._complete(null); + }, + cancelOnError: true); + return future; + } + + /** + * Checks whether [test] accepts all elements provided by this stream. + * + * Completes the [Future] when the answer is known. + * If this stream reports an error, the [Future] will report that error. + */ + Future every(bool test(T element)) { + _Future future = new _Future(); + StreamSubscription subscription; + subscription = this.listen( + (T element) { + _runUserCode( + () => test(element), + (bool isMatch) { + if (!isMatch) { + _cancelAndValue(subscription, future, false); + } + }, + _cancelAndErrorClosure(subscription, future) + ); + }, + onError: future._completeError, + onDone: () { + future._complete(true); + }, + cancelOnError: true); + return future; + } + + /** + * Checks whether [test] accepts any element provided by this stream. + * + * Completes the [Future] when the answer is known. + * + * If this stream reports an error, the [Future] reports that error. + * + * Stops listening to the stream after the first matching element has been + * found. + * + * Internally the method cancels its subscription after this element. This + * means that single-subscription (non-broadcast) streams are closed and + * cannot be reused after a call to this method. + */ + Future any(bool test(T element)) { + _Future future = new _Future(); + StreamSubscription subscription; + subscription = this.listen( + (T element) { + _runUserCode( + () => test(element), + (bool isMatch) { + if (isMatch) { + _cancelAndValue(subscription, future, true); + } + }, + _cancelAndErrorClosure(subscription, future) + ); + }, + onError: future._completeError, + onDone: () { + future._complete(false); + }, + cancelOnError: true); + return future; + } + + + /** Counts the elements in the stream. */ + Future get length { + _Future future = new _Future(); + int count = 0; + this.listen( + (_) { count++; }, + onError: future._completeError, + onDone: () { + future._complete(count); + }, + cancelOnError: true); + return future; + } + + /** + * Reports whether this stream contains any elements. + * + * Stops listening to the stream after the first element has been received. + * + * Internally the method cancels its subscription after the first element. + * This means that single-subscription (non-broadcast) streams are closed and + * cannot be reused after a call to this getter. + */ + Future get isEmpty { + _Future future = new _Future(); + StreamSubscription subscription; + subscription = this.listen( + (_) { + _cancelAndValue(subscription, future, false); + }, + onError: future._completeError, + onDone: () { + future._complete(true); + }, + cancelOnError: true); + return future; + } + + /** Collects the data of this stream in a [List]. */ + Future> toList() { + List result = []; + _Future> future = new _Future>(); + this.listen( + (T data) { + result.add(data); + }, + onError: future._completeError, + onDone: () { + future._complete(result); + }, + cancelOnError: true); + return future; + } + + /** Collects the data of this stream in a [Set]. */ + Future> toSet() { + Set result = new Set(); + _Future> future = new _Future>(); + this.listen( + (T data) { + result.add(data); + }, + onError: future._completeError, + onDone: () { + future._complete(result); + }, + cancelOnError: true); + return future; + } + + /** + * Discards all data on the stream, but signals when it's done or an error + * occured. + * + * When subscribing using [drain], cancelOnError will be true. This means + * that the future will complete with the first error on the stream and then + * cancel the subscription. + * + * In case of a `done` event the future completes with the given + * [futureValue]. + */ + Future drain([var futureValue]) => listen(null, cancelOnError: true) + .asFuture(futureValue); + + /** + * Provides at most the first [n] values of this stream. + * + * Forwards the first [n] data events of this stream, and all error + * events, to the returned stream, and ends with a done event. + * + * If this stream produces fewer than [count] values before it's done, + * so will the returned stream. + * + * Stops listening to the stream after the first [n] elements have been + * received. + * + * Internally the method cancels its subscription after these elements. This + * means that single-subscription (non-broadcast) streams are closed and + * cannot be reused after a call to this method. + * + * The returned stream is not a broadcast stream, even if this stream is. + */ + Stream take(int count) { + return new _TakeStream(this, count); + } + + /** + * Forwards data events while [test] is successful. + * + * The returned stream provides the same events as this stream as long + * as [test] returns [:true:] for the event data. The stream is done + * when either this stream is done, or when this stream first provides + * a value that [test] doesn't accept. + * + * Stops listening to the stream after the accepted elements. + * + * Internally the method cancels its subscription after these elements. This + * means that single-subscription (non-broadcast) streams are closed and + * cannot be reused after a call to this method. + * + * The returned stream is not a broadcast stream, even if this stream is. + */ + Stream takeWhile(bool test(T element)) { + return new _TakeWhileStream(this, test); + } + + /** + * Skips the first [count] data events from this stream. + * + * The returned stream is not a broadcast stream, even if this stream is. + */ + Stream skip(int count) { + return new _SkipStream(this, count); + } + + /** + * Skip data events from this stream while they are matched by [test]. + * + * Error and done events are provided by the returned stream unmodified. + * + * Starting with the first data event where [test] returns false for the + * event data, the returned stream will have the same events as this stream. + * + * The returned stream is not a broadcast stream, even if this stream is. + */ + Stream skipWhile(bool test(T element)) { + return new _SkipWhileStream(this, test); + } + + /** + * Skips data events if they are equal to the previous data event. + * + * The returned stream provides the same events as this stream, except + * that it never provides two consequtive data events that are equal. + * + * Equality is determined by the provided [equals] method. If that is + * omitted, the '==' operator on the last provided data element is used. + * + * The returned stream is not a broadcast stream, even if this stream is. + */ + Stream distinct([bool equals(T previous, T next)]) { + return new _DistinctStream(this, equals); + } + + /** + * Returns the first element of the stream. + * + * Stops listening to the stream after the first element has been received. + * + * Internally the method cancels its subscription after the first element. + * This means that single-subscription (non-broadcast) streams are closed + * and cannot be reused after a call to this getter. + * + * If an error event occurs before the first data event, the resulting future + * is completed with that error. + * + * If this stream is empty (a done event occurs before the first data event), + * the resulting future completes with a [StateError]. + * + * Except for the type of the error, this method is equivalent to + * [:this.elementAt(0):]. + */ + Future get first { + _Future future = new _Future(); + StreamSubscription subscription; + subscription = this.listen( + (T value) { + _cancelAndValue(subscription, future, value); + }, + onError: future._completeError, + onDone: () { + future._completeError(new StateError("No elements")); + }, + cancelOnError: true); + return future; + } + + /** + * Returns the last element of the stream. + * + * If an error event occurs before the first data event, the resulting future + * is completed with that error. + * + * If this stream is empty (a done event occurs before the first data event), + * the resulting future completes with a [StateError]. + */ + Future get last { + _Future future = new _Future(); + T result = null; + bool foundResult = false; + StreamSubscription subscription; + subscription = this.listen( + (T value) { + foundResult = true; + result = value; + }, + onError: future._completeError, + onDone: () { + if (foundResult) { + future._complete(result); + return; + } + future._completeError(new StateError("No elements")); + }, + cancelOnError: true); + return future; + } + + /** + * Returns the single element. + * + * If an error event occurs before or after the first data event, the + * resulting future is completed with that error. + * + * If [this] is empty or has more than one element throws a [StateError]. + */ + Future get single { + _Future future = new _Future(); + T result = null; + bool foundResult = false; + StreamSubscription subscription; + subscription = this.listen( + (T value) { + if (foundResult) { + // This is the second element we get. + Error error = new StateError("More than one element"); + _cancelAndError(subscription, future, error, null); + return; + } + foundResult = true; + result = value; + }, + onError: future._completeError, + onDone: () { + if (foundResult) { + future._complete(result); + return; + } + future._completeError(new StateError("No elements")); + }, + cancelOnError: true); + return future; + } + + /** + * Finds the first element of this stream matching [test]. + * + * Returns a future that is filled with the first element of this stream + * that [test] returns true for. + * + * If no such element is found before this stream is done, and a + * [defaultValue] function is provided, the result of calling [defaultValue] + * becomes the value of the future. + * + * Stops listening to the stream after the first matching element has been + * received. + * + * Internally the method cancels its subscription after the first element that + * matches the predicate. This means that single-subscription (non-broadcast) + * streams are closed and cannot be reused after a call to this method. + * + * If an error occurs, or if this stream ends without finding a match and + * with no [defaultValue] function provided, the future will receive an + * error. + */ + Future firstWhere(bool test(T element), {Object defaultValue()}) { + _Future future = new _Future(); + StreamSubscription subscription; + subscription = this.listen( + (T value) { + _runUserCode( + () => test(value), + (bool isMatch) { + if (isMatch) { + _cancelAndValue(subscription, future, value); + } + }, + _cancelAndErrorClosure(subscription, future) + ); + }, + onError: future._completeError, + onDone: () { + if (defaultValue != null) { + _runUserCode(defaultValue, future._complete, future._completeError); + return; + } + future._completeError(new StateError("firstMatch ended without match")); + }, + cancelOnError: true); + return future; + } + + /** + * Finds the last element in this stream matching [test]. + * + * As [firstWhere], except that the last matching element is found. + * That means that the result cannot be provided before this stream + * is done. + */ + Future lastWhere(bool test(T element), {Object defaultValue()}) { + _Future future = new _Future(); + T result = null; + bool foundResult = false; + StreamSubscription subscription; + subscription = this.listen( + (T value) { + _runUserCode( + () => true == test(value), + (bool isMatch) { + if (isMatch) { + foundResult = true; + result = value; + } + }, + _cancelAndErrorClosure(subscription, future) + ); + }, + onError: future._completeError, + onDone: () { + if (foundResult) { + future._complete(result); + return; + } + if (defaultValue != null) { + _runUserCode(defaultValue, future._complete, future._completeError); + return; + } + future._completeError(new StateError("lastMatch ended without match")); + }, + cancelOnError: true); + return future; + } + + /** + * Finds the single element in this stream matching [test]. + * + * Like [lastMatch], except that it is an error if more than one + * matching element occurs in the stream. + */ + Future singleWhere(bool test(T element)) { + _Future future = new _Future(); + T result = null; + bool foundResult = false; + StreamSubscription subscription; + subscription = this.listen( + (T value) { + _runUserCode( + () => true == test(value), + (bool isMatch) { + if (isMatch) { + if (foundResult) { + _cancelAndError( + subscription, + future, + new StateError('Multiple matches for "single"'), + null); + return; + } + foundResult = true; + result = value; + } + }, + _cancelAndErrorClosure(subscription, future) + ); + }, + onError: future._completeError, + onDone: () { + if (foundResult) { + future._complete(result); + return; + } + future._completeError(new StateError("single ended without match")); + }, + cancelOnError: true); + return future; + } + + /** + * Returns the value of the [index]th data event of this stream. + * + * Stops listening to the stream after the [index]th data event has been + * received. + * + * Internally the method cancels its subscription after these elements. This + * means that single-subscription (non-broadcast) streams are closed and + * cannot be reused after a call to this method. + * + * If an error event occurs before the value is found, the future completes + * with this error. + * + * If a done event occurs before the value is found, the future completes + * with a [RangeError]. + */ + Future elementAt(int index) { + if (index is! int || index < 0) throw new ArgumentError(index); + _Future future = new _Future(); + StreamSubscription subscription; + subscription = this.listen( + (T value) { + if (index == 0) { + _cancelAndValue(subscription, future, value); + return; + } + index -= 1; + }, + onError: future._completeError, + onDone: () { + future._completeError(new RangeError.value(index)); + }, + cancelOnError: true); + return future; + } + + /** + * Creates a new stream with the same events as this stream. + * + * Whenever more than [timeLimit] passes between two events from this stream, + * the [onTimeout] function is called. + * + * The countdown doesn't start until the returned stream is listened to. + * The countdown is reset every time an event is forwarded from this stream, + * or when the stream is paused and resumed. + * + * The [onTimeout] function is called with one argument: an + * [EventSink] that allows putting events into the returned stream. + * This `EventSink` is only valid during the call to `onTimeout`. + * + * If `onTimeout` is omitted, a timeout will just put a [TimeoutException] + * into the error channel of the returned stream. + * + * The returned stream is not a broadcast stream, even if this stream is. + */ + Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) { + StreamSubscription subscription; + _StreamController controller; + // The following variables are set on listen. + Timer timer; + Zone zone; + Function timeout; + + void onData(T event) { + timer.cancel(); + controller.add(event); + timer = zone.createTimer(timeLimit, timeout); + } + void onError(error, StackTrace stackTrace) { + timer.cancel(); + controller.addError(error, stackTrace); + timer = zone.createTimer(timeLimit, timeout); + } + void onDone() { + timer.cancel(); + controller.close(); + } + controller = new _SyncStreamController( + () { + // This is the onListen callback for of controller. + // It runs in the same zone that the subscription was created in. + // Use that zone for creating timers and running the onTimeout + // callback. + zone = Zone.current; + if (onTimeout == null) { + timeout = () { + controller.addError(new TimeoutException("No stream event", + timeLimit)); + }; + } else { + onTimeout = zone.registerUnaryCallback(onTimeout); + _ControllerEventSinkWrapper wrapper = + new _ControllerEventSinkWrapper(null); + timeout = () { + wrapper._sink = controller; // Only valid during call. + zone.runUnaryGuarded(onTimeout, wrapper); + wrapper._sink = null; + }; + } + + subscription = this.listen(onData, onError: onError, onDone: onDone); + timer = zone.createTimer(timeLimit, timeout); + }, + () { + timer.cancel(); + subscription.pause(); + }, + () { + subscription.resume(); + timer = zone.createTimer(timeLimit, timeout); + }, + () { + timer.cancel(); + Future result = subscription.cancel(); + subscription = null; + return result; + }); + return controller.stream; + } +} + +/** + * A control object for the subscription on a [Stream]. + * + * When you subscribe on a [Stream] using [Stream.listen], + * a [StreamSubscription] object is returned. This object + * is used to later unsubscribe again, or to temporarily pause + * the stream's events. + */ +abstract class StreamSubscription { + /** + * Cancels this subscription. It will no longer receive events. + * + * If an event is currently firing, this unsubscription will only + * take effect after all subscribers have received the current event. + * + * Returns a future if the cancel-operation is not completed synchronously. + * Otherwise returns `null`. + */ + Future cancel(); + + /** Set or override the data event handler of this subscription. */ + void onData(void handleData(T data)); + + /** + * Set or override the error event handler of this subscription. + * + * This method overrides the handler that has been set at the invocation of + * [Stream.listen]. + */ + void onError(Function handleError); + + /** Set or override the done event handler of this subscription. */ + void onDone(void handleDone()); + + /** + * Request that the stream pauses events until further notice. + * + * If [resumeSignal] is provided, the stream will undo the pause + * when the future completes. If the future completes with an error, + * it will not be handled! + * + * A call to [resume] will also undo a pause. + * + * If the subscription is paused more than once, an equal number + * of resumes must be performed to resume the stream. + * + * Currently DOM streams silently drop events when the stream is paused. This + * is a bug and will be fixed. + */ + void pause([Future resumeSignal]); + + /** + * Resume after a pause. + */ + void resume(); + + /** + * Returns true if the [StreamSubscription] is paused. + */ + bool get isPaused; + + /** + * Returns a future that handles the [onDone] and [onError] callbacks. + * + * This method *overwrites* the existing [onDone] and [onError] callbacks + * with new ones that complete the returned future. + * + * In case of an error the subscription will automatically cancel (even + * when it was listening with `cancelOnError` set to `false`). + * + * In case of a `done` event the future completes with the given + * [futureValue]. + */ + Future asFuture([var futureValue]); +} + + +/** + * An interface that abstracts creation or handling of [Stream] events. + */ +abstract class EventSink implements Sink { + /** Create a data event */ + void add(T event); + /** Create an async error. */ + void addError(errorEvent, [StackTrace stackTrace]); + /** Request a stream to close. */ + void close(); +} + + +/** [Stream] wrapper that only exposes the [Stream] interface. */ +class StreamView extends Stream { + Stream _stream; + + StreamView(this._stream); + + bool get isBroadcast => _stream.isBroadcast; + + Stream asBroadcastStream({void onListen(StreamSubscription subscription), + void onCancel(StreamSubscription subscription)}) + => _stream.asBroadcastStream(onListen: onListen, onCancel: onCancel); + + StreamSubscription listen(void onData(T value), + { Function onError, + void onDone(), + bool cancelOnError }) { + return _stream.listen(onData, onError: onError, onDone: onDone, + cancelOnError: cancelOnError); + } +} + + +/** + * The target of a [Stream.pipe] call. + * + * The [Stream.pipe] call will pass itself to this object, and then return + * the resulting [Future]. The pipe should complete the future when it's + * done. + */ +abstract class StreamConsumer { + /** + * Consumes the elements of [stream]. + * + * Listens on [stream] and does something for each event. + * + * The consumer may stop listening after an error, or it may consume + * all the errors and only stop at a done event. + */ + Future addStream(Stream stream); + + Future close(); +} + + +/** + * A [StreamSink] unifies the asynchronous methods from [StreamConsumer] and + * the synchronous methods from [EventSink]. + * + * The [EventSink] methods can't be used while the [addStream] is called. + * As soon as the [addStream]'s [Future] completes with a value, the + * [EventSink] methods can be used again. + * + * If [addStream] is called after any of the [EventSink] methods, it'll + * be delayed until the underlying system has consumed the data added by the + * [EventSink] methods. + * + * When [EventSink] methods are used, the [done] [Future] can be used to + * catch any errors. + * + * When [close] is called, it will return the [done] [Future]. + */ +abstract class StreamSink implements StreamConsumer, EventSink { + /** + * Close the [StreamSink]. It'll return the [done] Future. + */ + Future close(); + + /** + * The [done] Future completes with the same values as [close], except + * for the following case: + * + * * The synchronous methods of [EventSink] were called, resulting in an + * error. If there is no active future (like from an addStream call), the + * [done] future will complete with that error + */ + Future get done; +} + + +/** + * The target of a [Stream.transform] call. + * + * The [Stream.transform] call will pass itself to this object and then return + * the resulting stream. + * + * It is good practice to write transformers that can be used multiple times. + */ +abstract class StreamTransformer { + + /** + * Creates a [StreamTransformer]. + * + * The returned instance takes responsibility of implementing ([bind]). + * When the user invokes `bind` it returns a new "bound" stream. Only when + * the user starts listening to the bound stream, the `listen` method + * invokes the given closure [transformer]. + * + * The [transformer] closure receives the stream, that was bound, as argument + * and returns a [StreamSubscription]. In almost all cases the closure + * listens itself to the stream that is given as argument. + * + * The result of invoking the [transformer] closure is a [StreamSubscription]. + * The bound stream-transformer (created by the `bind` method above) then sets + * the handlers it received as part of the `listen` call. + * + * Conceptually this can be summarized as follows: + * + * 1. `var transformer = new StreamTransformer(transformerClosure);` + * creates a `StreamTransformer` that supports the `bind` method. + * 2. `var boundStream = stream.transform(transformer);` binds the `stream` + * and returns a bound stream that has a pointer to `stream`. + * 3. `boundStream.listen(f1, onError: f2, onDone: f3, cancelOnError: b)` + * starts the listening and transformation. This is accomplished + * in 2 steps: first the `boundStream` invokes the `transformerClosure` with + * the `stream` it captured: `transformerClosure(stream, b)`. + * The result `subscription`, a [StreamSubscription], is then + * updated to receive its handlers: `subscription.onData(f1)`, + * `subscription.onError(f2)`, `subscription(f3)`. Finally the subscription + * is returned as result of the `listen` call. + * + * There are two common ways to create a StreamSubscription: + * + * 1. by creating a new class that implements [StreamSubscription]. + * Note that the subscription should run callbacks in the [Zone] the + * stream was listened to. + * 2. by allocating a [StreamController] and to return the result of + * listening to its stream. + * + * Example use of a duplicating transformer: + * + * stringStream.transform(new StreamTransformer( + * (Stream input, bool cancelOnError) { + * StreamController controller; + * StreamSubscription subscription; + * controller = new StreamController( + * onListen: () { + * subscription = input.listen((data) { + * // Duplicate the data. + * controller.add(data); + * controller.add(data); + * }, + * onError: controller.addError, + * onDone: controller.close, + * cancelOnError: cancelOnError); + * }, + * onPause: subscription.pause, + * onResume: subscription.resume, + * onCancel: subscription.cancel, + * sync: true); + * return controller.stream.listen(null); + * }); + */ + const factory StreamTransformer( + StreamSubscription transformer(Stream stream, bool cancelOnError)) + = _StreamSubscriptionTransformer; + + /** + * Creates a [StreamTransformer] that delegates events to the given functions. + * + * Example use of a duplicating transformer: + * + * stringStream.transform(new StreamTransformer.fromHandlers( + * handleData: (String value, EventSink sink) { + * sink.add(value); + * sink.add(value); // Duplicate the incoming events. + * })); + */ + factory StreamTransformer.fromHandlers({ + void handleData(S data, EventSink sink), + void handleError(Object error, StackTrace stackTrace, EventSink sink), + void handleDone(EventSink sink)}) + = _StreamHandlerTransformer; + + Stream bind(Stream stream); +} + +/** + * An [Iterable] like interface for the values of a [Stream]. + * + * This wraps a [Stream] and a subscription on the stream. It listens + * on the stream, and completes the future returned by [moveNext] when the + * next value becomes available. + */ +abstract class StreamIterator { + + /** Create a [StreamIterator] on [stream]. */ + factory StreamIterator(Stream stream) + // TODO(lrn): use redirecting factory constructor when type + // arguments are supported. + => new _StreamIteratorImpl(stream); + + /** + * Wait for the next stream value to be available. + * + * It is not allowed to call this function again until the future has + * completed. If the returned future completes with anything except `true`, + * the iterator is done, and no new value will ever be available. + * + * The future may complete with an error, if the stream produces an error. + */ + Future moveNext(); + + /** + * The current value of the stream. + * + * Only valid when the future returned by [moveNext] completes with `true` + * as value, and only until the next call to [moveNext]. + */ + T get current; + + /** + * Cancels the stream iterator (and the underlying stream subscription) early. + * + * The stream iterator is automatically canceled if the [moveNext] future + * completes with either `false` or an error. + * + * If a [moveNext] call has been made, it will complete with `false` as value, + * as will all further calls to [moveNext]. + * + * If you need to stop listening for values before the stream iterator is + * automatically closed, you must call [cancel] to ensure that the stream + * is properly closed. + * + * Returns a future if the cancel-operation is not completed synchronously. + * Otherwise returns `null`. + */ + Future cancel(); +} + + +/** + * Wraps an [_EventSink] so it exposes only the [EventSink] interface. + */ +class _ControllerEventSinkWrapper implements EventSink { + EventSink _sink; + _ControllerEventSinkWrapper(this._sink); + + void add(T data) { _sink.add(data); } + void addError(error, [StackTrace stackTrace]) { + _sink.addError(error, stackTrace); + } + void close() { _sink.close(); } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_controller.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_controller.dart new file mode 100644 index 0000000..d800825 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_controller.dart @@ -0,0 +1,731 @@ +// Copyright (c) 2012, 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. + +part of dart.async; + +// ------------------------------------------------------------------- +// Controller for creating and adding events to a stream. +// ------------------------------------------------------------------- + +/** + * A controller with the stream it controls. + * + * This controller allows sending data, error and done events on + * its [stream]. + * This class can be used to create a simple stream that others + * can listen on, and to push events to that stream. + * + * It's possible to check whether the stream is paused or not, and whether + * it has subscribers or not, as well as getting a callback when either of + * these change. + * + * If the stream starts or stops having listeners (first listener subscribing, + * last listener unsubscribing), the `onSubscriptionStateChange` callback + * is notified as soon as possible. If the subscription stat changes during + * an event firing or a callback being executed, the change will not be reported + * until the current event or callback has finished. + * If the pause state has also changed during an event or callback, only the + * subscription state callback is notified. + * + * If the subscriber state has not changed, but the pause state has, the + * `onPauseStateChange` callback is notified as soon as possible, after firing + * a current event or completing another callback. This happens if the stream + * is not paused, and a listener pauses it, or if the stream has been resumed + * from pause and has no pending events. If the listeners resume a paused stream + * while it still has queued events, the controller will still consider the + * stream paused until all queued events have been dispatched. + * + * Whether to invoke a callback depends only on the state before and after + * a stream action, for example firing an event. If the state changes multiple + * times during the action, and then ends up in the same state as before, no + * callback is performed. + * + * If listeners are added after the stream has completed (sent a "done" event), + * the listeners will be sent a "done" event eventually, but they won't affect + * the stream at all, and won't trigger callbacks. From the controller's point + * of view, the stream is completely inert when has completed. + */ +abstract class StreamController implements StreamSink { + /** The stream that this controller is controlling. */ + Stream get stream; + + /** + * A controller with a [stream] that supports only one single subscriber. + * + * If [sync] is true, events may be passed directly to the stream's listener + * during an [add], [addError] or [close] call. If [sync] is false, the event + * will be passed to the listener at a later time, after the code creating + * the event has returned. + * + * The controller will buffer all incoming events until the subscriber is + * registered. + * + * The [onPause] function is called when the stream becomes + * paused. [onResume] is called when the stream resumed. + * + * The [onListen] callback is called when the stream + * receives its listener and [onCancel] when the listener ends + * its subscription. If [onCancel] needs to perform an asynchronous operation, + * [onCancel] should return a future that completes when the cancel operation + * is done. + * + * If the stream is canceled before the controller needs new data the + * [onResume] call might not be executed. + */ + factory StreamController({void onListen(), + void onPause(), + void onResume(), + onCancel(), + bool sync: false}) { + if (onListen == null && onPause == null && + onResume == null && onCancel == null) { + return sync + ? new _NoCallbackSyncStreamController/**/() + : new _NoCallbackAsyncStreamController/**/(); + } + return sync + ? new _SyncStreamController(onListen, onPause, onResume, onCancel) + : new _AsyncStreamController(onListen, onPause, onResume, onCancel); + } + + /** + * A controller where [stream] can be listened to more than once. + * + * The [Stream] returned by [stream] is a broadcast stream. + * It can be listened to more than once. + * + * The controller distributes any events to all currently subscribed + * listeners at the time when [add], [addError] or [close] is called. + * It is not allowed to call `add`, `addError`, or `close` before a previous + * call has returned. The controller does not have any internal queue of + * events, and if there are no listeners at the time the event is added, + * it will just be dropped, or, if it is an error, be reported as uncaught. + * + * Each listener subscription is handled independently, + * and if one pauses, only the pausing listener is affected. + * A paused listener will buffer events internally until unpaused or canceled. + * + * If [sync] is true, events may be fired directly by the stream's + * subscriptions during an [add], [addError] or [close] call. + * If [sync] is false, the event will be fired at a later time, + * after the code adding the event has completed. + * + * When [sync] is false, no guarantees are given with regard to when + * multiple listeners get the events, except that each listener will get + * all events in the correct order. Each subscription handles the events + * individually. + * If two events are sent on an async controller with two listeners, + * one of the listeners may get both events + * before the other listener gets any. + * A listener must be subscribed both when the event is initiated + * (that is, when [add] is called) + * and when the event is later delivered, + * in order to receive the event. + * + * The [onListen] callback is called when the first listener is subscribed, + * and the [onCancel] is called when there are no longer any active listeners. + * If a listener is added again later, after the [onCancel] was called, + * the [onListen] will be called again. + */ + factory StreamController.broadcast({void onListen(), + void onCancel(), + bool sync: false}) { + return sync + ? new _SyncBroadcastStreamController(onListen, onCancel) + : new _AsyncBroadcastStreamController(onListen, onCancel); + } + + /** + * Returns a view of this object that only exposes the [StreamSink] interface. + */ + StreamSink get sink; + + /** + * Whether the stream is closed for adding more events. + * + * If true, the "done" event might not have fired yet, but it has been + * scheduled, and it is too late to add more events. + */ + bool get isClosed; + + /** + * Whether the subscription would need to buffer events. + * + * This is the case if the controller's stream has a listener and it is + * paused, or if it has not received a listener yet. In that case, the + * controller is considered paused as well. + * + * A broadcast stream controller is never considered paused. It always + * forwards its events to all uncanceled listeners, if any, and let them + * handle their own pausing. + */ + bool get isPaused; + + /** Whether there is a subscriber on the [Stream]. */ + bool get hasListener; + + /** + * Send or enqueue an error event. + * + * Also allows an objection stack trace object, on top of what [EventSink] + * allows. + */ + void addError(Object error, [StackTrace stackTrace]); + + /** + * Receives events from [source] and puts them into this controller's stream. + * + * Returns a future which completes when the source stream is done. + * + * Events must not be added directly to this controller using [add], + * [addError], [close] or [addStream], until the returned future + * is complete. + * + * Data and error events are forwarded to this controller's stream. A done + * event on the source will end the `addStream` operation and complete the + * returned future. + * + * If [cancelOnError] is true, only the first error on [source] is + * forwarded to the controller's stream, and the `addStream` ends + * after this. If [cancelOnError] is false, all errors are forwarded + * and only a done event will end the `addStream`. + */ + Future addStream(Stream source, {bool cancelOnError: true}); +} + + +abstract class _StreamControllerLifecycle { + StreamSubscription _subscribe(bool cancelOnError); + void _recordPause(StreamSubscription subscription) {} + void _recordResume(StreamSubscription subscription) {} + Future _recordCancel(StreamSubscription subscription) => null; +} + +/** + * Default implementation of [StreamController]. + * + * Controls a stream that only supports a single controller. + */ +abstract class _StreamController implements StreamController, + _StreamControllerLifecycle, + _EventSink, + _EventDispatch { + // The states are bit-flags. More than one can be set at a time. + // + // The "subscription state" goes through the states: + // initial -> subscribed -> canceled. + // These are mutually exclusive. + // The "closed" state records whether the [close] method has been called + // on the controller. This can be done at any time. If done before + // subscription, the done event is queued. If done after cancel, the done + // event is ignored (just as any other event after a cancel). + + /** The controller is in its initial state with no subscription. */ + static const int _STATE_INITIAL = 0; + /** The controller has a subscription, but hasn't been closed or canceled. */ + static const int _STATE_SUBSCRIBED = 1; + /** The subscription is canceled. */ + static const int _STATE_CANCELED = 2; + /** Mask for the subscription state. */ + static const int _STATE_SUBSCRIPTION_MASK = 3; + + // The following state relate to the controller, not the subscription. + // If closed, adding more events is not allowed. + // If executing an [addStream], new events are not allowed either, but will + // be added by the stream. + + /** + * The controller is closed due to calling [close]. + * + * When the stream is closed, you can neither add new events nor add new + * listeners. + */ + static const int _STATE_CLOSED = 4; + /** + * The controller is in the middle of an [addStream] operation. + * + * While adding events from a stream, no new events can be added directly + * on the controller. + */ + static const int _STATE_ADDSTREAM = 8; + + /** + * Field containing different data depending on the current subscription + * state. + * + * If [_state] is [_STATE_INITIAL], the field may contain a [_PendingEvents] + * for events added to the controller before a subscription. + * + * While [_state] is [_STATE_SUBSCRIBED], the field contains the subscription. + * + * When [_state] is [_STATE_CANCELED] the field is currently not used. + */ + var _varData; + + /** Current state of the controller. */ + int _state = _STATE_INITIAL; + + /** + * Future completed when the stream sends its last event. + * + * This is also the future returned by [close]. + */ + // TODO(lrn): Could this be stored in the varData field too, if it's not + // accessed until the call to "close"? Then we need to special case if it's + // accessed earlier, or if close is called before subscribing. + _Future _doneFuture; + + _StreamController(); + + _NotificationHandler get _onListen; + _NotificationHandler get _onPause; + _NotificationHandler get _onResume; + _NotificationHandler get _onCancel; + + // Return a new stream every time. The streams are equal, but not identical. + Stream get stream => new _ControllerStream(this); + + /** + * Returns a view of this object that only exposes the [StreamSink] interface. + */ + StreamSink get sink => new _StreamSinkWrapper(this); + + /** + * Whether a listener has existed and been canceled. + * + * After this, adding more events will be ignored. + */ + bool get _isCanceled => (_state & _STATE_CANCELED) != 0; + + /** Whether there is an active listener. */ + bool get hasListener => (_state & _STATE_SUBSCRIBED) != 0; + + /** Whether there has not been a listener yet. */ + bool get _isInitialState => + (_state & _STATE_SUBSCRIPTION_MASK) == _STATE_INITIAL; + + bool get isClosed => (_state & _STATE_CLOSED) != 0; + + bool get isPaused => hasListener ? _subscription._isInputPaused + : !_isCanceled; + + bool get _isAddingStream => (_state & _STATE_ADDSTREAM) != 0; + + /** New events may not be added after close, or during addStream. */ + bool get _mayAddEvent => (_state < _STATE_CLOSED); + + // Returns the pending events. + // Pending events are events added before a subscription exists. + // They are added to the subscription when it is created. + // Pending events, if any, are kept in the _varData field until the + // stream is listened to. + // While adding a stream, pending events are moved into the + // state object to allow the state object to use the _varData field. + _PendingEvents get _pendingEvents { + assert(_isInitialState); + if (!_isAddingStream) { + return _varData; + } + _StreamControllerAddStreamState state = _varData; + return state.varData; + } + + // Returns the pending events, and creates the object if necessary. + _StreamImplEvents _ensurePendingEvents() { + assert(_isInitialState); + if (!_isAddingStream) { + if (_varData == null) _varData = new _StreamImplEvents(); + return _varData; + } + _StreamControllerAddStreamState state = _varData; + if (state.varData == null) state.varData = new _StreamImplEvents(); + return state.varData; + } + + // Get the current subscription. + // If we are adding a stream, the subscription is moved into the state + // object to allow the state object to use the _varData field. + _ControllerSubscription get _subscription { + assert(hasListener); + if (_isAddingStream) { + _StreamControllerAddStreamState addState = _varData; + return addState.varData; + } + return _varData; + } + + /** + * Creates an error describing why an event cannot be added. + * + * The reason, and therefore the error message, depends on the current state. + */ + Error _badEventState() { + if (isClosed) { + return new StateError("Cannot add event after closing"); + } + assert(_isAddingStream); + return new StateError("Cannot add event while adding a stream"); + } + + // StreamSink interface. + Future addStream(Stream source, {bool cancelOnError: true}) { + if (!_mayAddEvent) throw _badEventState(); + if (_isCanceled) return new _Future.immediate(null); + _StreamControllerAddStreamState addState = + new _StreamControllerAddStreamState(this, + _varData, + source, + cancelOnError); + _varData = addState; + _state |= _STATE_ADDSTREAM; + return addState.addStreamFuture; + } + + Future get done => _ensureDoneFuture(); + + Future _ensureDoneFuture() { + if (_doneFuture == null) { + _doneFuture = _isCanceled ? Future._nullFuture : new _Future(); + } + return _doneFuture; + } + + /** + * Send or enqueue a data event. + */ + void add(T value) { + if (!_mayAddEvent) throw _badEventState(); + _add(value); + } + + /** + * Send or enqueue an error event. + */ + void addError(Object error, [StackTrace stackTrace]) { + if (!_mayAddEvent) throw _badEventState(); + _addError(error, stackTrace); + } + + /** + * Closes this controller. + * + * After closing, no further events may be added using [add] or [addError]. + * + * You are allowed to close the controller more than once, but only the first + * call has any effect. + * + * The first time a controller is closed, a "done" event is sent to its + * stream. + */ + Future close() { + if (isClosed) { + _ensureDoneFuture(); + return _doneFuture; + } + if (!_mayAddEvent) throw _badEventState(); + _state |= _STATE_CLOSED; + if (hasListener) { + _sendDone(); + } else if (_isInitialState) { + _ensurePendingEvents().add(const _DelayedDone()); + } + _ensureDoneFuture(); + return _doneFuture; + } + + // EventSink interface. Used by the [addStream] events. + + // Add data event, used both by the [addStream] events and by [add]. + void _add(T value) { + if (hasListener) { + _sendData(value); + } else if (_isInitialState) { + _ensurePendingEvents().add(new _DelayedData(value)); + } + } + + void _addError(Object error, StackTrace stackTrace) { + if (hasListener) { + _sendError(error, stackTrace); + } else if (_isInitialState) { + _ensurePendingEvents().add(new _DelayedError(error, stackTrace)); + } + } + + void _close() { + // End of addStream stream. + assert(_isAddingStream); + _StreamControllerAddStreamState addState = _varData; + _varData = addState.varData; + _state &= ~_STATE_ADDSTREAM; + addState.complete(); + } + + // _StreamControllerLifeCycle interface + + StreamSubscription _subscribe(bool cancelOnError) { + if (!_isInitialState) { + throw new StateError("Stream has already been listened to."); + } + _ControllerSubscription subscription = + new _ControllerSubscription(this, cancelOnError); + + _PendingEvents pendingEvents = _pendingEvents; + _state |= _STATE_SUBSCRIBED; + if (_isAddingStream) { + _StreamControllerAddStreamState addState = _varData; + addState.varData = subscription; + addState.resume(); + } else { + _varData = subscription; + } + subscription._setPendingEvents(pendingEvents); + subscription._guardCallback(() { + _runGuarded(_onListen); + }); + + return subscription; + } + + Future _recordCancel(StreamSubscription subscription) { + if (_isAddingStream) { + _StreamControllerAddStreamState addState = _varData; + addState.cancel(); + } + _varData = null; + _state = + (_state & ~(_STATE_SUBSCRIBED | _STATE_ADDSTREAM)) | _STATE_CANCELED; + + void complete() { + if (_doneFuture != null && _doneFuture._mayComplete) { + _doneFuture._asyncComplete(null); + } + } + + Future future = _runGuarded(_onCancel); + if (future != null) { + future = future.whenComplete(complete); + } else { + complete(); + } + return future; + } + + void _recordPause(StreamSubscription subscription) { + if (_isAddingStream) { + _StreamControllerAddStreamState addState = _varData; + addState.pause(); + } + _runGuarded(_onPause); + } + + void _recordResume(StreamSubscription subscription) { + if (_isAddingStream) { + _StreamControllerAddStreamState addState = _varData; + addState.resume(); + } + _runGuarded(_onResume); + } +} + +abstract class _SyncStreamControllerDispatch + implements _StreamController { + void _sendData(T data) { + _subscription._add(data); + } + + void _sendError(Object error, StackTrace stackTrace) { + _subscription._addError(error, stackTrace); + } + + void _sendDone() { + _subscription._close(); + } +} + +abstract class _AsyncStreamControllerDispatch + implements _StreamController { + void _sendData(T data) { + _subscription._addPending(new _DelayedData(data)); + } + + void _sendError(Object error, StackTrace stackTrace) { + _subscription._addPending(new _DelayedError(error, stackTrace)); + } + + void _sendDone() { + _subscription._addPending(const _DelayedDone()); + } +} + +// TODO(lrn): Use common superclass for callback-controllers when VM supports +// constructors in mixin superclasses. + +class _AsyncStreamController extends _StreamController + with _AsyncStreamControllerDispatch { + final _NotificationHandler _onListen; + final _NotificationHandler _onPause; + final _NotificationHandler _onResume; + final _NotificationHandler _onCancel; + + _AsyncStreamController(void this._onListen(), + void this._onPause(), + void this._onResume(), + this._onCancel()); +} + +class _SyncStreamController extends _StreamController + with _SyncStreamControllerDispatch { + final _NotificationHandler _onListen; + final _NotificationHandler _onPause; + final _NotificationHandler _onResume; + final _NotificationHandler _onCancel; + + _SyncStreamController(void this._onListen(), + void this._onPause(), + void this._onResume(), + this._onCancel()); +} + +abstract class _NoCallbacks { + _NotificationHandler get _onListen => null; + _NotificationHandler get _onPause => null; + _NotificationHandler get _onResume => null; + _NotificationHandler get _onCancel => null; +} + +class _NoCallbackAsyncStreamController/**/ = _StreamController/**/ + with _AsyncStreamControllerDispatch/**/, _NoCallbacks; + +class _NoCallbackSyncStreamController/**/ = _StreamController/**/ + with _SyncStreamControllerDispatch/**/, _NoCallbacks; + +typedef _NotificationHandler(); + +Future _runGuarded(_NotificationHandler notificationHandler) { + if (notificationHandler == null) return null; + try { + var result = notificationHandler(); + if (result is Future) return result; + return null; + } catch (e, s) { + Zone.current.handleUncaughtError(e, s); + } +} + +class _ControllerStream extends _StreamImpl { + _StreamControllerLifecycle _controller; + + _ControllerStream(this._controller); + + StreamSubscription _createSubscription(bool cancelOnError) => + _controller._subscribe(cancelOnError); + + // Override == and hashCode so that new streams returned by the same + // controller are considered equal. The controller returns a new stream + // each time it's queried, but doesn't have to cache the result. + + int get hashCode => _controller.hashCode ^ 0x35323532; + + bool operator==(Object other) { + if (identical(this, other)) return true; + if (other is! _ControllerStream) return false; + _ControllerStream otherStream = other; + return identical(otherStream._controller, this._controller); + } +} + +class _ControllerSubscription extends _BufferingStreamSubscription { + final _StreamControllerLifecycle _controller; + + _ControllerSubscription(this._controller, bool cancelOnError) + : super(cancelOnError); + + Future _onCancel() { + return _controller._recordCancel(this); + } + + void _onPause() { + _controller._recordPause(this); + } + + void _onResume() { + _controller._recordResume(this); + } +} + + +/** A class that exposes only the [StreamSink] interface of an object. */ +class _StreamSinkWrapper implements StreamSink { + final StreamController _target; + _StreamSinkWrapper(this._target); + void add(T data) { _target.add(data); } + void addError(Object error, [StackTrace stackTrace]) { + _target.addError(error, stackTrace); + } + Future close() => _target.close(); + Future addStream(Stream source, {bool cancelOnError: true}) => + _target.addStream(source, cancelOnError: cancelOnError); + Future get done => _target.done; +} + +/** + * Object containing the state used to handle [StreamController.addStream]. + */ +class _AddStreamState { + // [_Future] returned by call to addStream. + final _Future addStreamFuture; + + // Subscription on stream argument to addStream. + final StreamSubscription addSubscription; + + _AddStreamState(_EventSink controller, Stream source, bool cancelOnError) + : addStreamFuture = new _Future(), + addSubscription = source.listen(controller._add, + onError: cancelOnError + ? makeErrorHandler(controller) + : controller._addError, + onDone: controller._close, + cancelOnError: cancelOnError); + + static makeErrorHandler(_EventSink controller) => + (e, StackTrace s) { + controller._addError(e, s); + controller._close(); + }; + + void pause() { + addSubscription.pause(); + } + + void resume() { + addSubscription.resume(); + } + + void cancel() { + addSubscription.cancel(); + complete(); + } + + void complete() { + addStreamFuture._asyncComplete(null); + } +} + +class _StreamControllerAddStreamState extends _AddStreamState { + // The subscription or pending data of a _StreamController. + // Stored here because we reuse the `_varData` field in the _StreamController + // to store this state object. + var varData; + + _StreamControllerAddStreamState(_StreamController controller, + this.varData, + Stream source, + bool cancelOnError) + : super(controller, source, cancelOnError) { + if (controller.isPaused) { + addSubscription.pause(); + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_impl.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_impl.dart new file mode 100644 index 0000000..78c4087 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_impl.dart @@ -0,0 +1,1038 @@ +// Copyright (c) 2012, 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. + +part of dart.async; + +/** Abstract and private interface for a place to put events. */ +abstract class _EventSink { + void _add(T data); + void _addError(Object error, StackTrace stackTrace); + void _close(); +} + +/** + * Abstract and private interface for a place to send events. + * + * Used by event buffering to finally dispatch the pending event, where + * [_EventSink] is where the event first enters the stream subscription, + * and may yet be buffered. + */ +abstract class _EventDispatch { + void _sendData(T data); + void _sendError(Object error, StackTrace stackTrace); + void _sendDone(); +} + +/** + * Default implementation of stream subscription of buffering events. + * + * The only public methods are those of [StreamSubscription], so instances of + * [_BufferingStreamSubscription] can be returned directly as a + * [StreamSubscription] without exposing internal functionality. + * + * The [StreamController] is a public facing version of [Stream] and this class, + * with some methods made public. + * + * The user interface of [_BufferingStreamSubscription] are the following + * methods: + * + * * [_add]: Add a data event to the stream. + * * [_addError]: Add an error event to the stream. + * * [_close]: Request to close the stream. + * * [_onCancel]: Called when the subscription will provide no more events, + * either due to being actively canceled, or after sending a done event. + * * [_onPause]: Called when the subscription wants the event source to pause. + * * [_onResume]: Called when allowing new events after a pause. + * + * The user should not add new events when the subscription requests a paused, + * but if it happens anyway, the subscription will enqueue the events just as + * when new events arrive while still firing an old event. + */ +class _BufferingStreamSubscription implements StreamSubscription, + _EventSink, + _EventDispatch { + /** The `cancelOnError` flag from the `listen` call. */ + static const int _STATE_CANCEL_ON_ERROR = 1; + /** + * Whether the "done" event has been received. + * No further events are accepted after this. + */ + static const int _STATE_CLOSED = 2; + /** + * Set if the input has been asked not to send events. + * + * This is not the same as being paused, since the input will remain paused + * after a call to [resume] if there are pending events. + */ + static const int _STATE_INPUT_PAUSED = 4; + /** + * Whether the subscription has been canceled. + * + * Set by calling [cancel], or by handling a "done" event, or an "error" event + * when `cancelOnError` is true. + */ + static const int _STATE_CANCELED = 8; + /** + * Set when either: + * + * * an error is sent, and [cancelOnError] is true, or + * * a done event is sent. + * + * If the subscription is canceled while _STATE_WAIT_FOR_CANCEL is set, the + * state is unset, and no furher events must be delivered. + */ + static const int _STATE_WAIT_FOR_CANCEL = 16; + static const int _STATE_IN_CALLBACK = 32; + static const int _STATE_HAS_PENDING = 64; + static const int _STATE_PAUSE_COUNT = 128; + static const int _STATE_PAUSE_COUNT_SHIFT = 7; + + /* Event handlers provided in constructor. */ + _DataHandler _onData; + Function _onError; + _DoneHandler _onDone; + final Zone _zone = Zone.current; + + /** Bit vector based on state-constants above. */ + int _state; + + // TODO(floitsch): reuse another field + /** The future [_onCancel] may return. */ + Future _cancelFuture; + + /** + * Queue of pending events. + * + * Is created when necessary, or set in constructor for preconfigured events. + */ + _PendingEvents _pending; + + _BufferingStreamSubscription(bool cancelOnError) + : _state = (cancelOnError ? _STATE_CANCEL_ON_ERROR : 0); + + /** + * Sets the subscription's pending events object. + * + * This can only be done once. The pending events object is used for the + * rest of the subscription's life cycle. + */ + void _setPendingEvents(_PendingEvents pendingEvents) { + assert(_pending == null); + if (pendingEvents == null) return; + _pending = pendingEvents; + if (!pendingEvents.isEmpty) { + _state |= _STATE_HAS_PENDING; + _pending.schedule(this); + } + } + + /** + * Extracts the pending events from a canceled stream. + * + * This can only be done during the [_onCancel] method call. After that, + * any remaining pending events will be cleared. + */ + _PendingEvents _extractPending() { + assert(_isCanceled); + _PendingEvents events = _pending; + _pending = null; + return events; + } + + // StreamSubscription interface. + + void onData(void handleData(T event)) { + if (handleData == null) handleData = _nullDataHandler; + _onData = _zone.registerUnaryCallback(handleData); + } + + void onError(Function handleError) { + if (handleError == null) handleError = _nullErrorHandler; + _onError = _registerErrorHandler(handleError, _zone); + } + + void onDone(void handleDone()) { + if (handleDone == null) handleDone = _nullDoneHandler; + _onDone = _zone.registerCallback(handleDone); + } + + void pause([Future resumeSignal]) { + if (_isCanceled) return; + bool wasPaused = _isPaused; + bool wasInputPaused = _isInputPaused; + // Increment pause count and mark input paused (if it isn't already). + _state = (_state + _STATE_PAUSE_COUNT) | _STATE_INPUT_PAUSED; + if (resumeSignal != null) resumeSignal.whenComplete(resume); + if (!wasPaused && _pending != null) _pending.cancelSchedule(); + if (!wasInputPaused && !_inCallback) _guardCallback(_onPause); + } + + void resume() { + if (_isCanceled) return; + if (_isPaused) { + _decrementPauseCount(); + if (!_isPaused) { + if (_hasPending && !_pending.isEmpty) { + // Input is still paused. + _pending.schedule(this); + } else { + assert(_mayResumeInput); + _state &= ~_STATE_INPUT_PAUSED; + if (!_inCallback) _guardCallback(_onResume); + } + } + } + } + + Future cancel() { + // The user doesn't want to receive any further events. If there is an + // error or done event pending (waiting for the cancel to be done) discard + // that event. + _state &= ~_STATE_WAIT_FOR_CANCEL; + if (_isCanceled) return _cancelFuture; + _cancel(); + return _cancelFuture; + } + + Future asFuture([var futureValue]) { + _Future result = new _Future(); + + // Overwrite the onDone and onError handlers. + _onDone = () { result._complete(futureValue); }; + _onError = (error, stackTrace) { + cancel(); + result._completeError(error, stackTrace); + }; + + return result; + } + + // State management. + + bool get _isInputPaused => (_state & _STATE_INPUT_PAUSED) != 0; + bool get _isClosed => (_state & _STATE_CLOSED) != 0; + bool get _isCanceled => (_state & _STATE_CANCELED) != 0; + bool get _waitsForCancel => (_state & _STATE_WAIT_FOR_CANCEL) != 0; + bool get _inCallback => (_state & _STATE_IN_CALLBACK) != 0; + bool get _hasPending => (_state & _STATE_HAS_PENDING) != 0; + bool get _isPaused => _state >= _STATE_PAUSE_COUNT; + bool get _canFire => _state < _STATE_IN_CALLBACK; + bool get _mayResumeInput => + !_isPaused && (_pending == null || _pending.isEmpty); + bool get _cancelOnError => (_state & _STATE_CANCEL_ON_ERROR) != 0; + + bool get isPaused => _isPaused; + + void _cancel() { + _state |= _STATE_CANCELED; + if (_hasPending) { + _pending.cancelSchedule(); + } + if (!_inCallback) _pending = null; + _cancelFuture = _onCancel(); + } + + /** + * Increment the pause count. + * + * Also marks input as paused. + */ + void _incrementPauseCount() { + _state = (_state + _STATE_PAUSE_COUNT) | _STATE_INPUT_PAUSED; + } + + /** + * Decrements the pause count. + * + * Does not automatically unpause the input (call [_onResume]) when + * the pause count reaches zero. This is handled elsewhere, and only + * if there are no pending events buffered. + */ + void _decrementPauseCount() { + assert(_isPaused); + _state -= _STATE_PAUSE_COUNT; + } + + // _EventSink interface. + + void _add(T data) { + assert(!_isClosed); + if (_isCanceled) return; + if (_canFire) { + _sendData(data); + } else { + _addPending(new _DelayedData(data)); + } + } + + void _addError(Object error, StackTrace stackTrace) { + if (_isCanceled) return; + if (_canFire) { + _sendError(error, stackTrace); // Reports cancel after sending. + } else { + _addPending(new _DelayedError(error, stackTrace)); + } + } + + void _close() { + assert(!_isClosed); + if (_isCanceled) return; + _state |= _STATE_CLOSED; + if (_canFire) { + _sendDone(); + } else { + _addPending(const _DelayedDone()); + } + } + + // Hooks called when the input is paused, unpaused or canceled. + // These must not throw. If overwritten to call user code, include suitable + // try/catch wrapping and send any errors to + // [_Zone.current.handleUncaughtError]. + void _onPause() { + assert(_isInputPaused); + } + + void _onResume() { + assert(!_isInputPaused); + } + + Future _onCancel() { + assert(_isCanceled); + } + + // Handle pending events. + + /** + * Add a pending event. + * + * If the subscription is not paused, this also schedules a firing + * of pending events later (if necessary). + */ + void _addPending(_DelayedEvent event) { + _StreamImplEvents pending = _pending; + if (_pending == null) pending = _pending = new _StreamImplEvents(); + pending.add(event); + if (!_hasPending) { + _state |= _STATE_HAS_PENDING; + if (!_isPaused) { + _pending.schedule(this); + } + } + } + + /* _EventDispatch interface. */ + + void _sendData(T data) { + assert(!_isCanceled); + assert(!_isPaused); + assert(!_inCallback); + bool wasInputPaused = _isInputPaused; + _state |= _STATE_IN_CALLBACK; + _zone.runUnaryGuarded(_onData, data); + _state &= ~_STATE_IN_CALLBACK; + _checkState(wasInputPaused); + } + + void _sendError(var error, StackTrace stackTrace) { + assert(!_isCanceled); + assert(!_isPaused); + assert(!_inCallback); + bool wasInputPaused = _isInputPaused; + + void sendError() { + // If the subscription has been canceled while waiting for the cancel + // future to finish we must not report the error. + if (_isCanceled && !_waitsForCancel) return; + _state |= _STATE_IN_CALLBACK; + if (!_zone.inSameErrorZone(Zone.current)) { + // Errors are not allowed to traverse zone boundaries. + Zone.current.handleUncaughtError(error, stackTrace); + } else if (_onError is ZoneBinaryCallback) { + _zone.runBinaryGuarded(_onError, error, stackTrace); + } else { + _zone.runUnaryGuarded(_onError, error); + } + _state &= ~_STATE_IN_CALLBACK; + } + + if (_cancelOnError) { + _state |= _STATE_WAIT_FOR_CANCEL; + _cancel(); + if (_cancelFuture is Future) { + _cancelFuture.whenComplete(sendError); + } else { + sendError(); + } + } else { + sendError(); + // Only check state if not cancelOnError. + _checkState(wasInputPaused); + } + } + + void _sendDone() { + assert(!_isCanceled); + assert(!_isPaused); + assert(!_inCallback); + + void sendDone() { + // If the subscription has been canceled while waiting for the cancel + // future to finish we must not report the done event. + if (!_waitsForCancel) return; + _state |= (_STATE_CANCELED | _STATE_CLOSED | _STATE_IN_CALLBACK); + _zone.runGuarded(_onDone); + _state &= ~_STATE_IN_CALLBACK; + } + + _cancel(); + _state |= _STATE_WAIT_FOR_CANCEL; + if (_cancelFuture is Future) { + _cancelFuture.whenComplete(sendDone); + } else { + sendDone(); + } + } + + /** + * Call a hook function. + * + * The call is properly wrapped in code to avoid other callbacks + * during the call, and it checks for state changes after the call + * that should cause further callbacks. + */ + void _guardCallback(callback) { + assert(!_inCallback); + bool wasInputPaused = _isInputPaused; + _state |= _STATE_IN_CALLBACK; + callback(); + _state &= ~_STATE_IN_CALLBACK; + _checkState(wasInputPaused); + } + + /** + * Check if the input needs to be informed of state changes. + * + * State changes are pausing, resuming and canceling. + * + * After canceling, no further callbacks will happen. + * + * The cancel callback is called after a user cancel, or after + * the final done event is sent. + */ + void _checkState(bool wasInputPaused) { + assert(!_inCallback); + if (_hasPending && _pending.isEmpty) { + _state &= ~_STATE_HAS_PENDING; + if (_isInputPaused && _mayResumeInput) { + _state &= ~_STATE_INPUT_PAUSED; + } + } + // If the state changes during a callback, we immediately + // make a new state-change callback. Loop until the state didn't change. + while (true) { + if (_isCanceled) { + _pending = null; + return; + } + bool isInputPaused = _isInputPaused; + if (wasInputPaused == isInputPaused) break; + _state ^= _STATE_IN_CALLBACK; + if (isInputPaused) { + _onPause(); + } else { + _onResume(); + } + _state &= ~_STATE_IN_CALLBACK; + wasInputPaused = isInputPaused; + } + if (_hasPending && !_isPaused) { + _pending.schedule(this); + } + } +} + +// ------------------------------------------------------------------- +// Common base class for single and multi-subscription streams. +// ------------------------------------------------------------------- +abstract class _StreamImpl extends Stream { + // ------------------------------------------------------------------ + // Stream interface. + + StreamSubscription listen(void onData(T data), + { Function onError, + void onDone(), + bool cancelOnError }) { + cancelOnError = identical(true, cancelOnError); + StreamSubscription subscription = _createSubscription(cancelOnError); + subscription.onData(onData); + subscription.onError(onError); + subscription.onDone(onDone); + _onListen(subscription); + return subscription; + } + + // ------------------------------------------------------------------- + /** Create a subscription object. Called by [subcribe]. */ + _BufferingStreamSubscription _createSubscription(bool cancelOnError) { + return new _BufferingStreamSubscription(cancelOnError); + } + + /** Hook called when the subscription has been created. */ + void _onListen(StreamSubscription subscription) {} +} + +typedef _PendingEvents _EventGenerator(); + +/** Stream that generates its own events. */ +class _GeneratedStreamImpl extends _StreamImpl { + final _EventGenerator _pending; + bool _isUsed = false; + /** + * Initializes the stream to have only the events provided by a + * [_PendingEvents]. + * + * A new [_PendingEvents] must be generated for each listen. + */ + _GeneratedStreamImpl(this._pending); + + StreamSubscription _createSubscription(bool cancelOnError) { + if (_isUsed) throw new StateError("Stream has already been listened to."); + _isUsed = true; + _BufferingStreamSubscription subscription = + new _BufferingStreamSubscription(cancelOnError); + subscription._setPendingEvents(_pending()); + return subscription; + } +} + + +/** Pending events object that gets its events from an [Iterable]. */ +class _IterablePendingEvents extends _PendingEvents { + // The iterator providing data for data events. + // Set to null when iteration has completed. + Iterator _iterator; + + _IterablePendingEvents(Iterable data) : _iterator = data.iterator; + + bool get isEmpty => _iterator == null; + + void handleNext(_EventDispatch dispatch) { + if (_iterator == null) { + throw new StateError("No events pending."); + } + // Send one event per call to moveNext. + // If moveNext returns true, send the current element as data. + // If moveNext returns false, send a done event and clear the _iterator. + // If moveNext throws an error, send an error and clear the _iterator. + // After an error, no further events will be sent. + bool isDone; + try { + isDone = !_iterator.moveNext(); + } catch (e, s) { + _iterator = null; + dispatch._sendError(e, s); + return; + } + if (!isDone) { + dispatch._sendData(_iterator.current); + } else { + _iterator = null; + dispatch._sendDone(); + } + } + + void clear() { + if (isScheduled) cancelSchedule(); + _iterator = null; + } +} + + +// Internal helpers. + +// Types of the different handlers on a stream. Types used to type fields. +typedef void _DataHandler(T value); +typedef void _DoneHandler(); + + +/** Default data handler, does nothing. */ +void _nullDataHandler(var value) {} + +/** Default error handler, reports the error to the current zone's handler. */ +void _nullErrorHandler(error, [StackTrace stackTrace]) { + Zone.current.handleUncaughtError(error, stackTrace); +} + +/** Default done handler, does nothing. */ +void _nullDoneHandler() {} + + +/** A delayed event on a buffering stream subscription. */ +abstract class _DelayedEvent { + /** Added as a linked list on the [StreamController]. */ + _DelayedEvent next; + /** Execute the delayed event on the [StreamController]. */ + void perform(_EventDispatch dispatch); +} + +/** A delayed data event. */ +class _DelayedData extends _DelayedEvent { + final T value; + _DelayedData(this.value); + void perform(_EventDispatch dispatch) { + dispatch._sendData(value); + } +} + +/** A delayed error event. */ +class _DelayedError extends _DelayedEvent { + final error; + final StackTrace stackTrace; + + _DelayedError(this.error, this.stackTrace); + void perform(_EventDispatch dispatch) { + dispatch._sendError(error, stackTrace); + } +} + +/** A delayed done event. */ +class _DelayedDone implements _DelayedEvent { + const _DelayedDone(); + void perform(_EventDispatch dispatch) { + dispatch._sendDone(); + } + + _DelayedEvent get next => null; + + void set next(_DelayedEvent _) { + throw new StateError("No events after a done."); + } +} + +/** Superclass for provider of pending events. */ +abstract class _PendingEvents { + // No async event has been scheduled. + static const int _STATE_UNSCHEDULED = 0; + // An async event has been scheduled to run a function. + static const int _STATE_SCHEDULED = 1; + // An async event has been scheduled, but it will do nothing when it runs. + // Async events can't be preempted. + static const int _STATE_CANCELED = 3; + + /** + * State of being scheduled. + * + * Set to [_STATE_SCHEDULED] when pending events are scheduled for + * async dispatch. Since we can't cancel a [scheduleMicrotask] call, if + * scheduling is "canceled", the _state is simply set to [_STATE_CANCELED] + * which will make the async code do nothing except resetting [_state]. + * + * If events are scheduled while the state is [_STATE_CANCELED], it is + * merely switched back to [_STATE_SCHEDULED], but no new call to + * [scheduleMicrotask] is performed. + */ + int _state = _STATE_UNSCHEDULED; + + bool get isEmpty; + + bool get isScheduled => _state == _STATE_SCHEDULED; + bool get _eventScheduled => _state >= _STATE_SCHEDULED; + + /** + * Schedule an event to run later. + * + * If called more than once, it should be called with the same dispatch as + * argument each time. It may reuse an earlier argument in some cases. + */ + void schedule(_EventDispatch dispatch) { + if (isScheduled) return; + assert(!isEmpty); + if (_eventScheduled) { + assert(_state == _STATE_CANCELED); + _state = _STATE_SCHEDULED; + return; + } + scheduleMicrotask(() { + int oldState = _state; + _state = _STATE_UNSCHEDULED; + if (oldState == _STATE_CANCELED) return; + handleNext(dispatch); + }); + _state = _STATE_SCHEDULED; + } + + void cancelSchedule() { + if (isScheduled) _state = _STATE_CANCELED; + } + + void handleNext(_EventDispatch dispatch); + + /** Throw away any pending events and cancel scheduled events. */ + void clear(); +} + + +/** Class holding pending events for a [_StreamImpl]. */ +class _StreamImplEvents extends _PendingEvents { + /// Single linked list of [_DelayedEvent] objects. + _DelayedEvent firstPendingEvent = null; + /// Last element in the list of pending events. New events are added after it. + _DelayedEvent lastPendingEvent = null; + + bool get isEmpty => lastPendingEvent == null; + + void add(_DelayedEvent event) { + if (lastPendingEvent == null) { + firstPendingEvent = lastPendingEvent = event; + } else { + lastPendingEvent = lastPendingEvent.next = event; + } + } + + void handleNext(_EventDispatch dispatch) { + assert(!isScheduled); + _DelayedEvent event = firstPendingEvent; + firstPendingEvent = event.next; + if (firstPendingEvent == null) { + lastPendingEvent = null; + } + event.perform(dispatch); + } + + void clear() { + if (isScheduled) cancelSchedule(); + firstPendingEvent = lastPendingEvent = null; + } +} + +class _BroadcastLinkedList { + _BroadcastLinkedList _next; + _BroadcastLinkedList _previous; + + void _unlink() { + _previous._next = _next; + _next._previous = _previous; + _next = _previous = this; + } + + void _insertBefore(_BroadcastLinkedList newNext) { + _BroadcastLinkedList newPrevious = newNext._previous; + newPrevious._next = this; + newNext._previous = _previous; + _previous._next = newNext; + _previous = newPrevious; + } +} + +typedef void _broadcastCallback(StreamSubscription subscription); + +/** + * Dummy subscription that will never receive any events. + */ +class _DummyStreamSubscription implements StreamSubscription { + int _pauseCounter = 0; + + void onData(void handleData(T data)) {} + void onError(Function handleError) {} + void onDone(void handleDone()) {} + + void pause([Future resumeSignal]) { + _pauseCounter++; + if (resumeSignal != null) resumeSignal.then((_) { resume(); }); + } + void resume() { + if (_pauseCounter > 0) _pauseCounter--; + } + Future cancel() => null; + bool get isPaused => _pauseCounter > 0; + + Future asFuture([futureValue]) => new _Future(); +} + +class _AsBroadcastStream extends Stream { + final Stream _source; + final _broadcastCallback _onListenHandler; + final _broadcastCallback _onCancelHandler; + final Zone _zone; + + _AsBroadcastStreamController _controller; + StreamSubscription _subscription; + + _AsBroadcastStream(this._source, + void onListenHandler(StreamSubscription subscription), + void onCancelHandler(StreamSubscription subscription)) + : _onListenHandler = Zone.current.registerUnaryCallback(onListenHandler), + _onCancelHandler = Zone.current.registerUnaryCallback(onCancelHandler), + _zone = Zone.current { + _controller = new _AsBroadcastStreamController(_onListen, _onCancel); + } + + bool get isBroadcast => true; + + StreamSubscription listen(void onData(T data), + { Function onError, + void onDone(), + bool cancelOnError}) { + if (_controller == null) { + // Return a dummy subscription backed by nothing, since + // it won't ever receive any events. + return new _DummyStreamSubscription(); + } + if (_subscription == null) { + _subscription = _source.listen(_controller.add, + onError: _controller.addError, + onDone: _controller.close); + } + cancelOnError = identical(true, cancelOnError); + StreamSubscription result = _controller._subscribe(cancelOnError); + result.onData(onData); + result.onError(onError); + result.onDone(onDone); + return result; + } + + void _onCancel() { + bool shutdown = (_controller == null) || _controller.isClosed; + if (_onCancelHandler != null) { + _zone.runUnary(_onCancelHandler, new _BroadcastSubscriptionWrapper(this)); + } + if (shutdown) { + if (_subscription != null) { + _subscription.cancel(); + _subscription = null; + } + } + } + + void _onListen() { + if (_onListenHandler != null) { + _zone.runUnary(_onListenHandler, new _BroadcastSubscriptionWrapper(this)); + } + } + + // Methods called from _BroadcastSubscriptionWrapper. + void _cancelSubscription() { + if (_subscription == null) return; + // Called by [_controller] when it has no subscribers left. + StreamSubscription subscription = _subscription; + _subscription = null; + _controller = null; // Marks the stream as no longer listenable. + subscription.cancel(); + } + + void _pauseSubscription(Future resumeSignal) { + if (_subscription == null) return; + _subscription.pause(resumeSignal); + } + + void _resumeSubscription() { + if (_subscription == null) return; + _subscription.resume(); + } + + bool get _isSubscriptionPaused { + if (_subscription == null) return false; + return _subscription.isPaused; + } +} + +/** + * Wrapper for subscription that disallows changing handlers. + */ +class _BroadcastSubscriptionWrapper implements StreamSubscription { + final _AsBroadcastStream _stream; + + _BroadcastSubscriptionWrapper(this._stream); + + void onData(void handleData(T data)) { + throw new UnsupportedError( + "Cannot change handlers of asBroadcastStream source subscription."); + } + + void onError(void handleError(Object data)) { + throw new UnsupportedError( + "Cannot change handlers of asBroadcastStream source subscription."); + } + + void onDone(void handleDone()) { + throw new UnsupportedError( + "Cannot change handlers of asBroadcastStream source subscription."); + } + + void pause([Future resumeSignal]) { + _stream._pauseSubscription(resumeSignal); + } + + void resume() { + _stream._resumeSubscription(); + } + + Future cancel() { + _stream._cancelSubscription(); + return null; + } + + bool get isPaused { + return _stream._isSubscriptionPaused; + } + + Future asFuture([var futureValue]) { + throw new UnsupportedError( + "Cannot change handlers of asBroadcastStream source subscription."); + } +} + + +/** + * Simple implementation of [StreamIterator]. + */ +class _StreamIteratorImpl implements StreamIterator { + // Internal state of the stream iterator. + // At any time, it is in one of these states. + // The interpretation of the [_futureOrPrefecth] field depends on the state. + // In _STATE_MOVING, the _data field holds the most recently returned + // future. + // When in one of the _STATE_EXTRA_* states, the it may hold the + // next data/error object, and the subscription is paused. + + /// The simple state where [_data] holds the data to return, and [moveNext] + /// is allowed. The subscription is actively listening. + static const int _STATE_FOUND = 0; + /// State set after [moveNext] has returned false or an error, + /// or after calling [cancel]. The subscription is always canceled. + static const int _STATE_DONE = 1; + /// State set after calling [moveNext], but before its returned future has + /// completed. Calling [moveNext] again is not allowed in this state. + /// The subscription is actively listening. + static const int _STATE_MOVING = 2; + /// States set when another event occurs while in _STATE_FOUND. + /// This extra overflow event is cached until the next call to [moveNext], + /// which will complete as if it received the event normally. + /// The subscription is paused in these states, so we only ever get one + /// event too many. + static const int _STATE_EXTRA_DATA = 3; + static const int _STATE_EXTRA_ERROR = 4; + static const int _STATE_EXTRA_DONE = 5; + + /// Subscription being listened to. + StreamSubscription _subscription; + + /// The current element represented by the most recent call to moveNext. + /// + /// Is null between the time moveNext is called and its future completes. + T _current = null; + + /// The future returned by the most recent call to [moveNext]. + /// + /// Also used to store the next value/error in case the stream provides an + /// event before [moveNext] is called again. In that case, the stream will + /// be paused to prevent further events. + var _futureOrPrefetch = null; + + /// The current state. + int _state = _STATE_FOUND; + + _StreamIteratorImpl(final Stream stream) { + _subscription = stream.listen(_onData, + onError: _onError, + onDone: _onDone, + cancelOnError: true); + } + + T get current => _current; + + Future moveNext() { + if (_state == _STATE_DONE) { + return new _Future.immediate(false); + } + if (_state == _STATE_MOVING) { + throw new StateError("Already waiting for next."); + } + if (_state == _STATE_FOUND) { + _state = _STATE_MOVING; + _current = null; + _futureOrPrefetch = new _Future(); + return _futureOrPrefetch; + } else { + assert(_state >= _STATE_EXTRA_DATA); + switch (_state) { + case _STATE_EXTRA_DATA: + _state = _STATE_FOUND; + _current = _futureOrPrefetch; + _futureOrPrefetch = null; + _subscription.resume(); + return new _Future.immediate(true); + case _STATE_EXTRA_ERROR: + Object prefetch = _futureOrPrefetch; + _clear(); + return new _Future.immediateError(prefetch); + case _STATE_EXTRA_DONE: + _clear(); + return new _Future.immediate(false); + } + } + } + + /** Clears up the internal state when the iterator ends. */ + void _clear() { + _subscription = null; + _futureOrPrefetch = null; + _current = null; + _state = _STATE_DONE; + } + + Future cancel() { + StreamSubscription subscription = _subscription; + if (_state == _STATE_MOVING) { + _Future hasNext = _futureOrPrefetch; + _clear(); + hasNext._complete(false); + } else { + _clear(); + } + return subscription.cancel(); + } + + void _onData(T data) { + if (_state == _STATE_MOVING) { + _current = data; + _Future hasNext = _futureOrPrefetch; + _futureOrPrefetch = null; + _state = _STATE_FOUND; + hasNext._complete(true); + return; + } + _subscription.pause(); + assert(_futureOrPrefetch == null); + _futureOrPrefetch = data; + _state = _STATE_EXTRA_DATA; + } + + void _onError(Object error, [StackTrace stackTrace]) { + if (_state == _STATE_MOVING) { + _Future hasNext = _futureOrPrefetch; + // We have cancelOnError: true, so the subscription is canceled. + _clear(); + hasNext._completeError(error, stackTrace); + return; + } + _subscription.pause(); + assert(_futureOrPrefetch == null); + _futureOrPrefetch = error; + _state = _STATE_EXTRA_ERROR; + } + + void _onDone() { + if (_state == _STATE_MOVING) { + _Future hasNext = _futureOrPrefetch; + _clear(); + hasNext._complete(false); + return; + } + _subscription.pause(); + _futureOrPrefetch = null; + _state = _STATE_EXTRA_DONE; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_pipe.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_pipe.dart new file mode 100644 index 0000000..2bb6ca4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_pipe.dart @@ -0,0 +1,407 @@ +// Copyright (c) 2012, 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. + +part of dart.async; + +/** Runs user code and takes actions depending on success or failure. */ +_runUserCode(userCode(), + onSuccess(value), + onError(error, StackTrace stackTrace)) { + try { + onSuccess(userCode()); + } catch (e, s) { + onError(e, s); + } +} + +/** Helper function to cancel a subscription and wait for the potential future, + before completing with an error. */ +void _cancelAndError(StreamSubscription subscription, + _Future future, + error, + StackTrace stackTrace) { + var cancelFuture = subscription.cancel(); + if (cancelFuture is Future) { + cancelFuture.whenComplete(() => future._completeError(error, stackTrace)); + } else { + future._completeError(error, stackTrace); + } +} + +/** Helper function to make an onError argument to [_runUserCode]. */ +_cancelAndErrorClosure(StreamSubscription subscription, _Future future) => + ((error, StackTrace stackTrace) => _cancelAndError( + subscription, future, error, stackTrace)); + +/** Helper function to cancel a subscription and wait for the potential future, + before completing with a value. */ +void _cancelAndValue(StreamSubscription subscription, _Future future, value) { + var cancelFuture = subscription.cancel(); + if (cancelFuture is Future) { + cancelFuture.whenComplete(() => future._complete(value)); + } else { + future._complete(value); + } +} + + +/** + * A [Stream] that forwards subscriptions to another stream. + * + * This stream implements [Stream], but forwards all subscriptions + * to an underlying stream, and wraps the returned subscription to + * modify the events on the way. + * + * This class is intended for internal use only. + */ +abstract class _ForwardingStream extends Stream { + final Stream _source; + + _ForwardingStream(this._source); + + bool get isBroadcast => _source.isBroadcast; + + StreamSubscription listen(void onData(T value), + { Function onError, + void onDone(), + bool cancelOnError }) { + cancelOnError = identical(true, cancelOnError); + StreamSubscription result = _createSubscription(cancelOnError); + result.onData(onData); + result.onError(onError); + result.onDone(onDone); + return result; + } + + StreamSubscription _createSubscription(bool cancelOnError) { + return new _ForwardingStreamSubscription(this, cancelOnError); + } + + // Override the following methods in subclasses to change the behavior. + + void _handleData(S data, _EventSink sink) { + var outputData = data; + sink._add(outputData); + } + + void _handleError(error, StackTrace stackTrace, _EventSink sink) { + sink._addError(error, stackTrace); + } + + void _handleDone(_EventSink sink) { + sink._close(); + } +} + +/** + * Abstract superclass for subscriptions that forward to other subscriptions. + */ +class _ForwardingStreamSubscription + extends _BufferingStreamSubscription { + final _ForwardingStream _stream; + + StreamSubscription _subscription; + + _ForwardingStreamSubscription(this._stream, bool cancelOnError) + : super(cancelOnError) { + _subscription = _stream._source.listen(_handleData, + onError: _handleError, + onDone: _handleDone); + } + + // _StreamSink interface. + // Transformers sending more than one event have no way to know if the stream + // is canceled or closed after the first, so we just ignore remaining events. + + void _add(T data) { + if (_isClosed) return; + super._add(data); + } + + void _addError(Object error, StackTrace stackTrace) { + if (_isClosed) return; + super._addError(error, stackTrace); + } + + // StreamSubscription callbacks. + + void _onPause() { + if (_subscription == null) return; + _subscription.pause(); + } + + void _onResume() { + if (_subscription == null) return; + _subscription.resume(); + } + + Future _onCancel() { + if (_subscription != null) { + StreamSubscription subscription = _subscription; + _subscription = null; + subscription.cancel(); + } + return null; + } + + // Methods used as listener on source subscription. + + void _handleData(S data) { + _stream._handleData(data, this); + } + + void _handleError(error, StackTrace stackTrace) { + _stream._handleError(error, stackTrace, this); + } + + void _handleDone() { + _stream._handleDone(this); + } +} + +// ------------------------------------------------------------------- +// Stream transformers used by the default Stream implementation. +// ------------------------------------------------------------------- + +typedef bool _Predicate(T value); + +class _WhereStream extends _ForwardingStream { + final _Predicate _test; + + _WhereStream(Stream source, bool test(T value)) + : _test = test, super(source); + + void _handleData(T inputEvent, _EventSink sink) { + bool satisfies; + try { + satisfies = _test(inputEvent); + } catch (e, s) { + sink._addError(e, s); + return; + } + if (satisfies) { + sink._add(inputEvent); + } + } +} + + +typedef T _Transformation(S value); + +/** + * A stream pipe that converts data events before passing them on. + */ +class _MapStream extends _ForwardingStream { + final _Transformation _transform; + + _MapStream(Stream source, T transform(S event)) + : this._transform = transform, super(source); + + void _handleData(S inputEvent, _EventSink sink) { + T outputEvent; + try { + outputEvent = _transform(inputEvent); + } catch (e, s) { + sink._addError(e, s); + return; + } + sink._add(outputEvent); + } +} + +/** + * A stream pipe that converts data events before passing them on. + */ +class _ExpandStream extends _ForwardingStream { + final _Transformation> _expand; + + _ExpandStream(Stream source, Iterable expand(S event)) + : this._expand = expand, super(source); + + void _handleData(S inputEvent, _EventSink sink) { + try { + for (T value in _expand(inputEvent)) { + sink._add(value); + } + } catch (e, s) { + // If either _expand or iterating the generated iterator throws, + // we abort the iteration. + sink._addError(e, s); + } + } +} + + +typedef bool _ErrorTest(error); + +/** + * A stream pipe that converts or disposes error events + * before passing them on. + */ +class _HandleErrorStream extends _ForwardingStream { + final Function _transform; + final _ErrorTest _test; + + _HandleErrorStream(Stream source, + Function onError, + bool test(error)) + : this._transform = onError, this._test = test, super(source); + + void _handleError(Object error, StackTrace stackTrace, _EventSink sink) { + bool matches = true; + if (_test != null) { + try { + matches = _test(error); + } catch (e, s) { + sink._addError(e, s); + return; + } + } + if (matches) { + try { + _invokeErrorHandler(_transform, error, stackTrace); + } catch (e, s) { + if (identical(e, error)) { + sink._addError(error, stackTrace); + } else { + sink._addError(e, s); + } + return; + } + } else { + sink._addError(error, stackTrace); + } + } +} + + +class _TakeStream extends _ForwardingStream { + int _remaining; + + _TakeStream(Stream source, int count) + : this._remaining = count, super(source) { + // This test is done early to avoid handling an async error + // in the _handleData method. + if (count is! int) throw new ArgumentError(count); + } + + void _handleData(T inputEvent, _EventSink sink) { + if (_remaining > 0) { + sink._add(inputEvent); + _remaining -= 1; + if (_remaining == 0) { + // Closing also unsubscribes all subscribers, which unsubscribes + // this from source. + sink._close(); + } + } + } +} + + +class _TakeWhileStream extends _ForwardingStream { + final _Predicate _test; + + _TakeWhileStream(Stream source, bool test(T value)) + : this._test = test, super(source); + + void _handleData(T inputEvent, _EventSink sink) { + bool satisfies; + try { + satisfies = _test(inputEvent); + } catch (e, s) { + sink._addError(e, s); + // The test didn't say true. Didn't say false either, but we stop anyway. + sink._close(); + return; + } + if (satisfies) { + sink._add(inputEvent); + } else { + sink._close(); + } + } +} + +class _SkipStream extends _ForwardingStream { + int _remaining; + + _SkipStream(Stream source, int count) + : this._remaining = count, super(source) { + // This test is done early to avoid handling an async error + // in the _handleData method. + if (count is! int || count < 0) throw new ArgumentError(count); + } + + void _handleData(T inputEvent, _EventSink sink) { + if (_remaining > 0) { + _remaining--; + return; + } + sink._add(inputEvent); + } +} + +class _SkipWhileStream extends _ForwardingStream { + final _Predicate _test; + bool _hasFailed = false; + + _SkipWhileStream(Stream source, bool test(T value)) + : this._test = test, super(source); + + void _handleData(T inputEvent, _EventSink sink) { + if (_hasFailed) { + sink._add(inputEvent); + return; + } + bool satisfies; + try { + satisfies = _test(inputEvent); + } catch (e, s) { + sink._addError(e, s); + // A failure to return a boolean is considered "not matching". + _hasFailed = true; + return; + } + if (!satisfies) { + _hasFailed = true; + sink._add(inputEvent); + } + } +} + +typedef bool _Equality(T a, T b); + +class _DistinctStream extends _ForwardingStream { + static var _SENTINEL = new Object(); + + _Equality _equals; + var _previous = _SENTINEL; + + _DistinctStream(Stream source, bool equals(T a, T b)) + : _equals = equals, super(source); + + void _handleData(T inputEvent, _EventSink sink) { + if (identical(_previous, _SENTINEL)) { + _previous = inputEvent; + return sink._add(inputEvent); + } else { + bool isEqual; + try { + if (_equals == null) { + isEqual = (_previous == inputEvent); + } else { + isEqual = _equals(_previous, inputEvent); + } + } catch (e, s) { + sink._addError(e, s); + return null; + } + if (!isEqual) { + sink._add(inputEvent); + _previous = inputEvent; + } + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_transformers.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_transformers.dart new file mode 100644 index 0000000..832c395 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/stream_transformers.dart @@ -0,0 +1,309 @@ +// Copyright (c) 2013, 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. + +part of dart.async; + +/** + * Wraps an [_EventSink] so it exposes only the [EventSink] interface. + */ +class _EventSinkWrapper implements EventSink { + _EventSink _sink; + _EventSinkWrapper(this._sink); + + void add(T data) { _sink._add(data); } + void addError(error, [StackTrace stackTrace]) { + _sink._addError(error, stackTrace); + } + void close() { _sink._close(); } +} + +/** + * A StreamSubscription that pipes data through a sink. + * + * The constructor of this class takes a [_SinkMapper] which maps from + * [EventSink] to [EventSink]. The input to the mapper is the output of + * the transformation. The returned sink is the transformation's input. + */ +class _SinkTransformerStreamSubscription + extends _BufferingStreamSubscription { + /// The transformer's input sink. + EventSink _transformerSink; + + /// The subscription to the input stream. + StreamSubscription _subscription; + + _SinkTransformerStreamSubscription(Stream source, + _SinkMapper mapper, + bool cancelOnError) + // We set the adapter's target only when the user is allowed to send data. + : super(cancelOnError) { + _EventSinkWrapper eventSink = new _EventSinkWrapper(this); + _transformerSink = mapper(eventSink); + _subscription = source.listen(_handleData, + onError: _handleError, + onDone: _handleDone); + } + + /** Whether this subscription is still subscribed to its source. */ + bool get _isSubscribed => _subscription != null; + + // _EventSink interface. + + /** + * Adds an event to this subscriptions. + * + * Contrary to normal [_BufferingStreamSubscription]s we may receive + * events when the stream is already closed. Report them as state + * error. + */ + void _add(T data) { + if (_isClosed) { + throw new StateError("Stream is already closed"); + } + super._add(data); + } + + /** + * Adds an error event to this subscriptions. + * + * Contrary to normal [_BufferingStreamSubscription]s we may receive + * events when the stream is already closed. Report them as state + * error. + */ + void _addError(Object error, StackTrace stackTrace) { + if (_isClosed) { + throw new StateError("Stream is already closed"); + } + super._addError(error, stackTrace); + } + + /** + * Adds a close event to this subscriptions. + * + * Contrary to normal [_BufferingStreamSubscription]s we may receive + * events when the stream is already closed. Report them as state + * error. + */ + void _close() { + if (_isClosed) { + throw new StateError("Stream is already closed"); + } + super._close(); + } + + // _BufferingStreamSubscription hooks. + + void _onPause() { + if (_isSubscribed) _subscription.pause(); + } + + void _onResume() { + if (_isSubscribed) _subscription.resume(); + } + + Future _onCancel() { + if (_isSubscribed) { + StreamSubscription subscription = _subscription; + _subscription = null; + subscription.cancel(); + } + return null; + } + + void _handleData(S data) { + try { + _transformerSink.add(data); + } catch (e, s) { + _addError(e, s); + } + } + + void _handleError(error, [stackTrace]) { + try { + _transformerSink.addError(error, stackTrace); + } catch (e, s) { + if (identical(e, error)) { + _addError(error, stackTrace); + } else { + _addError(e, s); + } + } + } + + void _handleDone() { + try { + _subscription = null; + _transformerSink.close(); + } catch (e, s) { + _addError(e, s); + } + } +} + + +typedef EventSink _SinkMapper(EventSink output); + +/** + * A StreamTransformer for Sink-mappers. + * + * A Sink-mapper takes an [EventSink] (its output) and returns another + * EventSink (its input). + * + * Note that this class can be `const`. + */ +class _StreamSinkTransformer implements StreamTransformer { + final _SinkMapper _sinkMapper; + const _StreamSinkTransformer(this._sinkMapper); + + Stream bind(Stream stream) + => new _BoundSinkStream(stream, _sinkMapper); +} + +/** + * The result of binding a StreamTransformer for Sink-mappers. + * + * It contains the bound Stream and the sink-mapper. Only when the user starts + * listening to this stream is the sink-mapper invoked. The result is used + * to create a StreamSubscription that transforms events. + */ +class _BoundSinkStream extends Stream { + final _SinkMapper _sinkMapper; + final Stream _stream; + + _BoundSinkStream(this._stream, this._sinkMapper); + + StreamSubscription listen(void onData(T event), + { Function onError, + void onDone(), + bool cancelOnError }) { + cancelOnError = identical(true, cancelOnError); + StreamSubscription subscription = new _SinkTransformerStreamSubscription( + _stream, _sinkMapper, cancelOnError); + subscription.onData(onData); + subscription.onError(onError); + subscription.onDone(onDone); + return subscription; + } +} + +/// Data-handler coming from [StreamTransformer.fromHandlers]. +typedef void _TransformDataHandler(S data, EventSink sink); +/// Error-handler coming from [StreamTransformer.fromHandlers]. +typedef void _TransformErrorHandler( + Object error, StackTrace stackTrace, EventSink sink); +/// Done-handler coming from [StreamTransformer.fromHandlers]. +typedef void _TransformDoneHandler(EventSink sink); + +/** + * Wraps handlers (from [StreamTransformer.fromHandlers]) into an `EventSink`. + * + * This way we can reuse the code from [_StreamSinkTransformer]. + */ +class _HandlerEventSink implements EventSink { + final _TransformDataHandler _handleData; + final _TransformErrorHandler _handleError; + final _TransformDoneHandler _handleDone; + + /// The output sink where the handlers should send their data into. + final EventSink _sink; + + _HandlerEventSink(this._handleData, this._handleError, this._handleDone, + this._sink); + + void add(S data) => _handleData(data, _sink); + void addError(Object error, [StackTrace stackTrace]) + => _handleError(error, stackTrace, _sink); + void close() => _handleDone(_sink); +} + +/** + * A StreamTransformer that transformers events with the given handlers. + * + * Note that this transformer can only be used once. + */ +class _StreamHandlerTransformer extends _StreamSinkTransformer { + + _StreamHandlerTransformer({ + void handleData(S data, EventSink sink), + void handleError(Object error, StackTrace stackTrace, EventSink sink), + void handleDone(EventSink sink)}) + : super((EventSink outputSink) { + if (handleData == null) handleData = _defaultHandleData; + if (handleError == null) handleError = _defaultHandleError; + if (handleDone == null) handleDone = _defaultHandleDone; + return new _HandlerEventSink( + handleData, handleError, handleDone, outputSink); + }); + + Stream bind(Stream stream) { + return super.bind(stream); + } + + /** Default data handler forwards all data. */ + static void _defaultHandleData(var data, EventSink sink) { + sink.add(data); + } + + /** Default error handler forwards all errors. */ + static void _defaultHandleError(error, StackTrace stackTrace, + EventSink sink) { + sink.addError(error); + } + + /** Default done handler forwards done. */ + static void _defaultHandleDone(EventSink sink) { + sink.close(); + } +} + +/// A closure mapping a stream and cancelOnError to a StreamSubscription. +typedef StreamSubscription _SubscriptionTransformer( + Stream stream, bool cancelOnError); + +/** + * A [StreamTransformer] that minimizes the number of additional classes. + * + * Instead of implementing three classes: a [StreamTransformer], a [Stream] + * (as the result of a `bind` call) and a [StreamSubscription] (which does the + * actual work), this class only requires a fincution that is invoked when the + * last bit (the subscription) of the transformer-workflow is needed. + * + * The given transformer function maps from Stream and cancelOnError to a + * `StreamSubscription`. As such it can also act on `cancel` events, making it + * fully general. + */ +class _StreamSubscriptionTransformer implements StreamTransformer { + final _SubscriptionTransformer _transformer; + + const _StreamSubscriptionTransformer(this._transformer); + + Stream bind(Stream stream) => + new _BoundSubscriptionStream(stream, _transformer); +} + +/** + * A stream transformed by a [_StreamSubscriptionTransformer]. + * + * When this stream is listened to it invokes the [_transformer] function with + * the stored [_stream]. Usually the transformer starts listening at this + * moment. + */ +class _BoundSubscriptionStream extends Stream { + final _SubscriptionTransformer _transformer; + final Stream _stream; + + _BoundSubscriptionStream(this._stream, this._transformer); + + StreamSubscription listen(void onData(T event), + { Function onError, + void onDone(), + bool cancelOnError }) { + cancelOnError = identical(true, cancelOnError); + StreamSubscription result = _transformer(_stream, cancelOnError); + result.onData(onData); + result.onError(onError); + result.onDone(onDone); + return result; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/timer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/timer.dart new file mode 100644 index 0000000..8b2b115 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/timer.dart @@ -0,0 +1,100 @@ +// Copyright (c) 2012, 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. + +part of dart.async; + +/** + * A count-down timer that can be configured to fire once or repeatedly. + * + * The timer counts down from the specified duration to 0. + * When the timer reaches 0, the timer invokes the specified callback function. + * Use a periodic timer to repeatedly count down the same interval. + * + * A negative duration is treated the same as a duration of 0. + * If the duration is statically known to be 0, consider using [run]. + * + * Frequently the duration is either a constant or computed as in the + * following example (taking advantage of the multiplication operator of + * the [Duration] class): + * + * const TIMEOUT = const Duration(seconds: 3); + * const ms = const Duration(milliseconds: 1); + * + * startTimeout([int milliseconds]) { + * var duration = milliseconds == null ? TIMEOUT : ms * milliseconds; + * return new Timer(duration, handleTimeout); + * } + * ... + * void handleTimeout() { // callback function + * ... + * } + * + * Note: If Dart code using Timer is compiled to JavaScript, the finest + * granularity available in the browser is 4 milliseconds. + * + * See [Stopwatch] for measuring elapsed time. + */ +abstract class Timer { + + /** + * Creates a new timer. + * + * The [callback] function is invoked after the given [duration]. + * + */ + factory Timer(Duration duration, void callback()) { + if (Zone.current == Zone.ROOT) { + // No need to bind the callback. We know that the root's timer will + // be invoked in the root zone. + return Zone.current.createTimer(duration, callback); + } + return Zone.current.createTimer( + duration, Zone.current.bindCallback(callback, runGuarded: true)); + } + + /** + * Creates a new repeating timer. + * + * The [callback] is invoked repeatedly with [duration] intervals until + * canceled with the [cancel] function. + */ + factory Timer.periodic(Duration duration, + void callback(Timer timer)) { + if (Zone.current == Zone.ROOT) { + // No need to bind the callback. We know that the root's timer will + // be invoked in the root zone. + return Zone.current.createPeriodicTimer(duration, callback); + } + return Zone.current.createPeriodicTimer( + duration, Zone.current.bindUnaryCallback(callback, runGuarded: true)); + } + + /** + * Runs the given [callback] asynchronously as soon as possible. + * + * This function is equivalent to `new Timer(Duration.ZERO, callback)`. + */ + static void run(void callback()) { + new Timer(Duration.ZERO, callback); + } + + /** + * Cancels the timer. + */ + void cancel(); + + /** + * Returns whether the timer is still active. + * + * A non-periodic timer is active if the callback has not been executed, + * and the timer has not been canceled. + * + * A periodic timer is active if it has not been canceled. + */ + bool get isActive; +} + +external Timer _createTimer(Duration duration, void callback()); +external Timer _createPeriodicTimer(Duration duration, + void callback(Timer timer)); diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/zone.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/zone.dart new file mode 100644 index 0000000..9ea4f0e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/async/zone.dart @@ -0,0 +1,956 @@ +// Copyright (c) 2013, 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. + +part of dart.async; + +typedef dynamic ZoneCallback(); +typedef dynamic ZoneUnaryCallback(arg); +typedef dynamic ZoneBinaryCallback(arg1, arg2); + +typedef dynamic HandleUncaughtErrorHandler( + Zone self, ZoneDelegate parent, Zone zone, error, StackTrace stackTrace); +typedef dynamic RunHandler(Zone self, ZoneDelegate parent, Zone zone, f()); +typedef dynamic RunUnaryHandler( + Zone self, ZoneDelegate parent, Zone zone, f(arg), arg); +typedef dynamic RunBinaryHandler( + Zone self, ZoneDelegate parent, Zone zone, f(arg1, arg2), arg1, arg2); +typedef ZoneCallback RegisterCallbackHandler( + Zone self, ZoneDelegate parent, Zone zone, f()); +typedef ZoneUnaryCallback RegisterUnaryCallbackHandler( + Zone self, ZoneDelegate parent, Zone zone, f(arg)); +typedef ZoneBinaryCallback RegisterBinaryCallbackHandler( + Zone self, ZoneDelegate parent, Zone zone, f(arg1, arg2)); +typedef void ScheduleMicrotaskHandler( + Zone self, ZoneDelegate parent, Zone zone, f()); +typedef Timer CreateTimerHandler( + Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f()); +typedef Timer CreatePeriodicTimerHandler( + Zone self, ZoneDelegate parent, Zone zone, + Duration period, void f(Timer timer)); +typedef void PrintHandler( + Zone self, ZoneDelegate parent, Zone zone, String line); +typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone, + ZoneSpecification specification, + Map zoneValues); + +/** + * This class provides the specification for a forked zone. + * + * When forking a new zone (see [Zone.fork]) one can override the default + * behavior of the zone by providing callbacks. These callbacks must be + * given in an instance of this class. + * + * Handlers have the same signature as the same-named methods on [Zone] but + * receive three additional arguments: + * + * 1. the zone the handlers are attached to (the "self" zone). + * 2. a [ZoneDelegate] to the parent zone. + * 3. the zone that first received the request (before the request was + * bubbled up). + * + * Handlers can either stop propagation the request (by simply not calling the + * parent handler), or forward to the parent zone, potentially modifying the + * arguments on the way. + */ +abstract class ZoneSpecification { + /** + * Creates a specification with the provided handlers. + */ + const factory ZoneSpecification({ + dynamic handleUncaughtError(Zone self, ZoneDelegate parent, Zone zone, + error, StackTrace stackTrace), + dynamic run(Zone self, ZoneDelegate parent, Zone zone, f()), + dynamic runUnary( + Zone self, ZoneDelegate parent, Zone zone, f(arg), arg), + dynamic runBinary(Zone self, ZoneDelegate parent, Zone zone, + f(arg1, arg2), arg1, arg2), + ZoneCallback registerCallback( + Zone self, ZoneDelegate parent, Zone zone, f()), + ZoneUnaryCallback registerUnaryCallback( + Zone self, ZoneDelegate parent, Zone zone, f(arg)), + ZoneBinaryCallback registerBinaryCallback( + Zone self, ZoneDelegate parent, Zone zone, f(arg1, arg2)), + void scheduleMicrotask( + Zone self, ZoneDelegate parent, Zone zone, f()), + Timer createTimer(Zone self, ZoneDelegate parent, Zone zone, + Duration duration, void f()), + Timer createPeriodicTimer(Zone self, ZoneDelegate parent, Zone zone, + Duration period, void f(Timer timer)), + void print(Zone self, ZoneDelegate parent, Zone zone, String line), + Zone fork(Zone self, ZoneDelegate parent, Zone zone, + ZoneSpecification specification, Map zoneValues) + }) = _ZoneSpecification; + + /** + * Creates a specification from [other] with the provided handlers overriding + * the ones in [other]. + */ + factory ZoneSpecification.from(ZoneSpecification other, { + dynamic handleUncaughtError(Zone self, ZoneDelegate parent, Zone zone, + error, StackTrace stackTrace): null, + dynamic run(Zone self, ZoneDelegate parent, Zone zone, f()): null, + dynamic runUnary( + Zone self, ZoneDelegate parent, Zone zone, f(arg), arg): null, + dynamic runBinary(Zone self, ZoneDelegate parent, Zone zone, + f(arg1, arg2), arg1, arg2): null, + ZoneCallback registerCallback( + Zone self, ZoneDelegate parent, Zone zone, f()): null, + ZoneUnaryCallback registerUnaryCallback( + Zone self, ZoneDelegate parent, Zone zone, f(arg)): null, + ZoneBinaryCallback registerBinaryCallback( + Zone self, ZoneDelegate parent, Zone zone, f(arg1, arg2)): null, + void scheduleMicrotask( + Zone self, ZoneDelegate parent, Zone zone, f()): null, + Timer createTimer(Zone self, ZoneDelegate parent, Zone zone, + Duration duration, void f()): null, + Timer createPeriodicTimer(Zone self, ZoneDelegate parent, Zone zone, + Duration period, void f(Timer timer)): null, + void print(Zone self, ZoneDelegate parent, Zone zone, String line): null, + Zone fork(Zone self, ZoneDelegate parent, Zone zone, + ZoneSpecification specification, + Map zoneValues): null + }) { + return new ZoneSpecification( + handleUncaughtError: handleUncaughtError != null + ? handleUncaughtError + : other.handleUncaughtError, + run: run != null ? run : other.run, + runUnary: runUnary != null ? runUnary : other.runUnary, + runBinary: runBinary != null ? runBinary : other.runBinary, + registerCallback: registerCallback != null + ? registerCallback + : other.registerCallback, + registerUnaryCallback: registerUnaryCallback != null + ? registerUnaryCallback + : other.registerUnaryCallback, + registerBinaryCallback: registerBinaryCallback != null + ? registerBinaryCallback + : other.registerBinaryCallback, + scheduleMicrotask: scheduleMicrotask != null + ? scheduleMicrotask + : other.scheduleMicrotask, + createTimer : createTimer != null ? createTimer : other.createTimer, + createPeriodicTimer: createPeriodicTimer != null + ? createPeriodicTimer + : other.createPeriodicTimer, + print : print != null ? print : other.print, + fork: fork != null ? fork : other.fork); + } + + HandleUncaughtErrorHandler get handleUncaughtError; + RunHandler get run; + RunUnaryHandler get runUnary; + RunBinaryHandler get runBinary; + RegisterCallbackHandler get registerCallback; + RegisterUnaryCallbackHandler get registerUnaryCallback; + RegisterBinaryCallbackHandler get registerBinaryCallback; + ScheduleMicrotaskHandler get scheduleMicrotask; + CreateTimerHandler get createTimer; + CreatePeriodicTimerHandler get createPeriodicTimer; + PrintHandler get print; + ForkHandler get fork; +} + +/** + * Internal [ZoneSpecification] class. + * + * The implementation wants to rely on the fact that the getters cannot change + * dynamically. We thus require users to go through the redirecting + * [ZoneSpecification] constructor which instantiates this class. + */ +class _ZoneSpecification implements ZoneSpecification { + const _ZoneSpecification({ + this.handleUncaughtError: null, + this.run: null, + this.runUnary: null, + this.runBinary: null, + this.registerCallback: null, + this.registerUnaryCallback: null, + this.registerBinaryCallback: null, + this.scheduleMicrotask: null, + this.createTimer: null, + this.createPeriodicTimer: null, + this.print: null, + this.fork: null + }); + + // TODO(13406): Enable types when dart2js supports it. + final /*HandleUncaughtErrorHandler*/ handleUncaughtError; + final /*RunHandler*/ run; + final /*RunUnaryHandler*/ runUnary; + final /*RunBinaryHandler*/ runBinary; + final /*RegisterCallbackHandler*/ registerCallback; + final /*RegisterUnaryCallbackHandler*/ registerUnaryCallback; + final /*RegisterBinaryCallbackHandler*/ registerBinaryCallback; + final /*ScheduleMicrotaskHandler*/ scheduleMicrotask; + final /*CreateTimerHandler*/ createTimer; + final /*CreatePeriodicTimerHandler*/ createPeriodicTimer; + final /*PrintHandler*/ print; + final /*ForkHandler*/ fork; +} + +/** + * This class wraps zones for delegation. + * + * When forwarding to parent zones one can't just invoke the parent zone's + * exposed functions (like [Zone.run]), but one needs to provide more + * information (like the zone the `run` was initiated). Zone callbacks thus + * receive more information including this [ZoneDelegate] class. When delegating + * to the parent zone one should go through the given instance instead of + * directly invoking the parent zone. + */ +abstract class ZoneDelegate { + /// The [Zone] this class wraps. + Zone get _zone; + + dynamic handleUncaughtError(Zone zone, error, StackTrace stackTrace); + dynamic run(Zone zone, f()); + dynamic runUnary(Zone zone, f(arg), arg); + dynamic runBinary(Zone zone, f(arg1, arg2), arg1, arg2); + ZoneCallback registerCallback(Zone zone, f()); + ZoneUnaryCallback registerUnaryCallback(Zone zone, f(arg)); + ZoneBinaryCallback registerBinaryCallback(Zone zone, f(arg1, arg2)); + void scheduleMicrotask(Zone zone, f()); + Timer createTimer(Zone zone, Duration duration, void f()); + Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer)); + void print(Zone zone, String line); + Zone fork(Zone zone, ZoneSpecification specification, Map zoneValues); +} + +/** + * A Zone represents the asynchronous version of a dynamic extent. Asynchronous + * callbacks are executed in the zone they have been queued in. For example, + * the callback of a `future.then` is executed in the same zone as the one where + * the `then` was invoked. + */ +abstract class Zone { + // Private constructor so that it is not possible instantiate a Zone class. + Zone._(); + + /// The root zone that is implicitly created. + static const Zone ROOT = _ROOT_ZONE; + + /// The currently running zone. + static Zone _current = _ROOT_ZONE; + + static Zone get current => _current; + + dynamic handleUncaughtError(error, StackTrace stackTrace); + + /** + * Returns the parent zone. + * + * Returns `null` if `this` is the [ROOT] zone. + */ + Zone get parent; + + /** + * Returns true if `this` and [otherZone] are in the same error zone. + * + * Two zones are in the same error zone if they share the same + * [handleUncaughtError] callback. + */ + bool inSameErrorZone(Zone otherZone); + + /** + * Creates a new zone as a child of `this`. + * + * The new zone will have behavior like the current zone, except where + * overridden by functions in [specification]. + * + * The new zone will have the same stored values (accessed through + * `operator []`) as this zone, but updated with the keys and values + * in [zoneValues]. If a key is in both this zone's values and in + * `zoneValues`, the new zone will use the value from `zoneValues``. + */ + Zone fork({ ZoneSpecification specification, + Map zoneValues }); + + /** + * Executes the given function [f] in this zone. + */ + dynamic run(f()); + + /** + * Executes the given callback [f] with argument [arg] in this zone. + */ + dynamic runUnary(f(arg), var arg); + + /** + * Executes the given callback [f] with argument [arg1] and [arg2] in this + * zone. + */ + dynamic runBinary(f(arg1, arg2), var arg1, var arg2); + + /** + * Executes the given function [f] in this zone. + * + * Same as [run] but catches uncaught errors and gives them to + * [handleUncaughtError]. + */ + dynamic runGuarded(f()); + + /** + * Executes the given callback [f] in this zone. + * + * Same as [runUnary] but catches uncaught errors and gives them to + * [handleUncaughtError]. + */ + dynamic runUnaryGuarded(f(arg), var arg); + + /** + * Executes the given callback [f] in this zone. + * + * Same as [runBinary] but catches uncaught errors and gives them to + * [handleUncaughtError]. + */ + dynamic runBinaryGuarded(f(arg1, arg2), var arg1, var arg2); + + /** + * Registers the given callback in this zone. + * + * It is good practice to register asynchronous or delayed callbacks before + * invoking [run]. This gives the zone a chance to wrap the callback and + * to store information with the callback. For example, a zone may decide + * to store the stack trace (at the time of the registration) with the + * callback. + * + * Returns a potentially new callback that should be used in place of the + * given [callback]. + */ + ZoneCallback registerCallback(callback()); + + /** + * Registers the given callback in this zone. + * + * Similar to [registerCallback] but with a unary callback. + */ + ZoneUnaryCallback registerUnaryCallback(callback(arg)); + + /** + * Registers the given callback in this zone. + * + * Similar to [registerCallback] but with a unary callback. + */ + ZoneBinaryCallback registerBinaryCallback(callback(arg1, arg2)); + + /** + * Equivalent to: + * + * ZoneCallback registered = registerCallback(f); + * if (runGuarded) return () => this.runGuarded(registered); + * return () => this.run(registered); + * + */ + ZoneCallback bindCallback(f(), { bool runGuarded: true }); + + /** + * Equivalent to: + * + * ZoneCallback registered = registerUnaryCallback(f); + * if (runGuarded) return (arg) => this.runUnaryGuarded(registered, arg); + * return (arg) => thin.runUnary(registered, arg); + */ + ZoneUnaryCallback bindUnaryCallback(f(arg), { bool runGuarded: true }); + + /** + * Equivalent to: + * + * ZoneCallback registered = registerBinaryCallback(f); + * if (runGuarded) { + * return (arg1, arg2) => this.runBinaryGuarded(registered, arg); + * } + * return (arg1, arg2) => thin.runBinary(registered, arg1, arg2); + */ + ZoneBinaryCallback bindBinaryCallback( + f(arg1, arg2), { bool runGuarded: true }); + + /** + * Runs [f] asynchronously in this zone. + */ + void scheduleMicrotask(void f()); + + /** + * Creates a Timer where the callback is executed in this zone. + */ + Timer createTimer(Duration duration, void callback()); + + /** + * Creates a periodic Timer where the callback is executed in this zone. + */ + Timer createPeriodicTimer(Duration period, void callback(Timer timer)); + + /** + * Prints the given [line]. + */ + void print(String line); + + /** + * The error zone is the one that is responsible for dealing with uncaught + * errors. Errors are not allowed to cross zones with different error-zones. + */ + Zone get _errorZone; + + /** + * Call to enter the Zone. + * + * The previous current zone is returned. + */ + static Zone _enter(Zone zone) { + assert(zone != null); + assert(!identical(zone, _current)); + Zone previous = _current; + _current = zone; + return previous; + } + + /** + * Call to leave the Zone. + * + * The previous Zone must be provided as `previous`. + */ + static void _leave(Zone previous) { + assert(previous != null); + Zone._current = previous; + } + + /** + * Retrieves the zone-value associated with [key]. + * + * If this zone does not contain the value looks up the same key in the + * parent zone. If the [key] is not found returns `null`. + * + * Any object can be used as key, as long as it has compatible `operator ==` + * and `hashCode` implementations. + * By controlling access to the key, a zone can grant or deny access to the + * zone value. + */ + operator [](Object key); +} + +class _ZoneDelegate implements ZoneDelegate { + final _BaseZone _degelationTarget; + + Zone get _zone => _degelationTarget; + + const _ZoneDelegate(this._degelationTarget); + + dynamic handleUncaughtError(Zone zone, error, StackTrace stackTrace) { + _BaseZone parent = _degelationTarget; + while (parent._specification.handleUncaughtError == null) { + parent = parent.parent; + } + return (parent._specification.handleUncaughtError)( + parent, new _ZoneDelegate(parent.parent), zone, error, stackTrace); + } + + dynamic run(Zone zone, f()) { + _BaseZone parent = _degelationTarget; + while (parent._specification.run == null) { + parent = parent.parent; + } + return (parent._specification.run)( + parent, new _ZoneDelegate(parent.parent), zone, f); + } + + dynamic runUnary(Zone zone, f(arg), arg) { + _BaseZone parent = _degelationTarget; + while (parent._specification.runUnary == null) { + parent = parent.parent; + } + return (parent._specification.runUnary)( + parent, new _ZoneDelegate(parent.parent), zone, f, arg); + } + + dynamic runBinary(Zone zone, f(arg1, arg2), arg1, arg2) { + _BaseZone parent = _degelationTarget; + while (parent._specification.runBinary == null) { + parent = parent.parent; + } + return (parent._specification.runBinary)( + parent, new _ZoneDelegate(parent.parent), zone, f, arg1, arg2); + } + + ZoneCallback registerCallback(Zone zone, f()) { + _BaseZone parent = _degelationTarget; + while (parent._specification.registerCallback == null) { + parent = parent.parent; + } + return (parent._specification.registerCallback)( + parent, new _ZoneDelegate(parent.parent), zone, f); + } + + ZoneUnaryCallback registerUnaryCallback(Zone zone, f(arg)) { + _BaseZone parent = _degelationTarget; + while (parent._specification.registerUnaryCallback == null) { + parent = parent.parent; + } + return (parent._specification.registerUnaryCallback)( + parent, new _ZoneDelegate(parent.parent), zone, f); + } + + ZoneBinaryCallback registerBinaryCallback(Zone zone, f(arg1, arg2)) { + _BaseZone parent = _degelationTarget; + while (parent._specification.registerBinaryCallback == null) { + parent = parent.parent; + } + return (parent._specification.registerBinaryCallback)( + parent, new _ZoneDelegate(parent.parent), zone, f); + } + + void scheduleMicrotask(Zone zone, f()) { + _BaseZone parent = _degelationTarget; + while (parent._specification.scheduleMicrotask == null) { + parent = parent.parent; + } + _ZoneDelegate grandParent = new _ZoneDelegate(parent.parent); + Function scheduleMicrotask = parent._specification.scheduleMicrotask; + scheduleMicrotask(parent, grandParent, zone, f); + } + + Timer createTimer(Zone zone, Duration duration, void f()) { + _BaseZone parent = _degelationTarget; + while (parent._specification.createTimer == null) { + parent = parent.parent; + } + return (parent._specification.createTimer)( + parent, new _ZoneDelegate(parent.parent), zone, duration, f); + } + + Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer)) { + _BaseZone parent = _degelationTarget; + while (parent._specification.createPeriodicTimer == null) { + parent = parent.parent; + } + return (parent._specification.createPeriodicTimer)( + parent, new _ZoneDelegate(parent.parent), zone, period, f); + } + + void print(Zone zone, String line) { + _BaseZone parent = _degelationTarget; + while (parent._specification.print == null) { + parent = parent.parent; + } + (parent._specification.print)( + parent, new _ZoneDelegate(parent.parent), zone, line); + } + + Zone fork(Zone zone, ZoneSpecification specification, + Map zoneValues) { + _BaseZone parent = _degelationTarget; + while (parent._specification.fork == null) { + parent = parent.parent; + } + _ZoneDelegate grandParent = new _ZoneDelegate(parent.parent); + return (parent._specification.fork)( + parent, grandParent, zone, specification, zoneValues); + } +} + + +/** + * Base class for Zone implementations. + */ +abstract class _BaseZone implements Zone { + const _BaseZone(); + + /// The parent zone. + _BaseZone get parent; + /// The zone's handlers. + ZoneSpecification get _specification; + /** + * The closest error-handling zone. + * + * Returns `this` if `this` has an error-handler. Otherwise returns the + * parent's error-zone. + */ + Zone get _errorZone; + + bool inSameErrorZone(Zone otherZone) => _errorZone == otherZone._errorZone; + + dynamic runGuarded(f()) { + try { + return run(f); + } catch (e, s) { + return handleUncaughtError(e, s); + } + } + + dynamic runUnaryGuarded(f(arg), arg) { + try { + return runUnary(f, arg); + } catch (e, s) { + return handleUncaughtError(e, s); + } + } + + dynamic runBinaryGuarded(f(arg1, arg2), arg1, arg2) { + try { + return runBinary(f, arg1, arg2); + } catch (e, s) { + return handleUncaughtError(e, s); + } + } + + ZoneCallback bindCallback(f(), { bool runGuarded: true }) { + ZoneCallback registered = registerCallback(f); + if (runGuarded) { + return () => this.runGuarded(registered); + } else { + return () => this.run(registered); + } + } + + ZoneUnaryCallback bindUnaryCallback(f(arg), { bool runGuarded: true }) { + ZoneUnaryCallback registered = registerUnaryCallback(f); + if (runGuarded) { + return (arg) => this.runUnaryGuarded(registered, arg); + } else { + return (arg) => this.runUnary(registered, arg); + } + } + + ZoneBinaryCallback bindBinaryCallback( + f(arg1, arg2), { bool runGuarded: true }) { + ZoneBinaryCallback registered = registerBinaryCallback(f); + if (runGuarded) { + return (arg1, arg2) => this.runBinaryGuarded(registered, arg1, arg2); + } else { + return (arg1, arg2) => this.runBinary(registered, arg1, arg2); + } + } +} + + +/** + * Default implementation of a [Zone]. + */ +class _CustomizedZone extends _BaseZone { + final _BaseZone parent; + final ZoneSpecification _specification; + + /// The zone's value map. + final Map _map; + + const _CustomizedZone(this.parent, this._specification, this._map); + + Zone get _errorZone { + if (_specification.handleUncaughtError != null) return this; + return parent._errorZone; + } + + operator [](Object key) { + var result = _map[key]; + if (result != null || _map.containsKey(key)) return result; + // If we are not the root zone look up in the parent zone. + if (parent != null) return parent[key]; + assert(this == Zone.ROOT); + return null; + } + + // Methods that can be customized by the zone specification. + + dynamic handleUncaughtError(error, StackTrace stackTrace) { + return new _ZoneDelegate(this).handleUncaughtError(this, error, stackTrace); + } + + Zone fork({ZoneSpecification specification, Map zoneValues}) { + return new _ZoneDelegate(this).fork(this, specification, zoneValues); + } + + dynamic run(f()) { + return new _ZoneDelegate(this).run(this, f); + } + + dynamic runUnary(f(arg), arg) { + return new _ZoneDelegate(this).runUnary(this, f, arg); + } + + dynamic runBinary(f(arg1, arg2), arg1, arg2) { + return new _ZoneDelegate(this).runBinary(this, f, arg1, arg2); + } + + ZoneCallback registerCallback(f()) { + return new _ZoneDelegate(this).registerCallback(this, f); + } + + ZoneUnaryCallback registerUnaryCallback(f(arg)) { + return new _ZoneDelegate(this).registerUnaryCallback(this, f); + } + + ZoneBinaryCallback registerBinaryCallback(f(arg1, arg2)) { + return new _ZoneDelegate(this).registerBinaryCallback(this, f); + } + + void scheduleMicrotask(void f()) { + new _ZoneDelegate(this).scheduleMicrotask(this, f); + } + + Timer createTimer(Duration duration, void f()) { + return new _ZoneDelegate(this).createTimer(this, duration, f); + } + + Timer createPeriodicTimer(Duration duration, void f(Timer timer)) { + return new _ZoneDelegate(this).createPeriodicTimer(this, duration, f); + } + + void print(String line) { + new _ZoneDelegate(this).print(this, line); + } +} + +void _rootHandleUncaughtError( + Zone self, ZoneDelegate parent, Zone zone, error, StackTrace stackTrace) { + self.run(() { + _scheduleAsyncCallback(() { + print("Uncaught Error: ${error}"); + var trace = stackTrace; + if (trace == null && error is Error) trace = error.stackTrace; + if (trace != null) { + print("Stack Trace: \n$trace\n"); + } + throw error; + }); + }); +} + +dynamic _rootRun(Zone self, ZoneDelegate parent, Zone zone, f()) { + if (Zone._current == zone) return f(); + + Zone old = Zone._enter(zone); + try { + return f(); + } finally { + Zone._leave(old); + } +} + +dynamic _rootRunUnary(Zone self, ZoneDelegate parent, Zone zone, f(arg), arg) { + if (Zone._current == zone) return f(arg); + + Zone old = Zone._enter(zone); + try { + return f(arg); + } finally { + Zone._leave(old); + } +} + +dynamic _rootRunBinary(Zone self, ZoneDelegate parent, Zone zone, + f(arg1, arg2), arg1, arg2) { + if (Zone._current == zone) return f(arg1, arg2); + + Zone old = Zone._enter(zone); + try { + return f(arg1, arg2); + } finally { + Zone._leave(old); + } +} + +ZoneCallback _rootRegisterCallback( + Zone self, ZoneDelegate parent, Zone zone, f()) { + return f; +} + +ZoneUnaryCallback _rootRegisterUnaryCallback( + Zone self, ZoneDelegate parent, Zone zone, f(arg)) { + return f; +} + +ZoneBinaryCallback _rootRegisterBinaryCallback( + Zone self, ZoneDelegate parent, Zone zone, f(arg1, arg2)) { + return f; +} + +void _rootScheduleMicrotask(Zone self, ZoneDelegate parent, Zone zone, f()) { + if (Zone.ROOT != zone) { + f = zone.bindCallback(f); + } + _scheduleAsyncCallback(f); +} + +Timer _rootCreateTimer(Zone self, ZoneDelegate parent, Zone zone, + Duration duration, void callback()) { + if (Zone.ROOT != zone) { + callback = zone.bindCallback(callback); + } + return _createTimer(duration, callback); +} + +Timer _rootCreatePeriodicTimer( + Zone self, ZoneDelegate parent, Zone zone, + Duration duration, void callback(Timer timer)) { + if (Zone.ROOT != zone) { + callback = zone.bindUnaryCallback(callback); + } + return _createPeriodicTimer(duration, callback); +} + +void _rootPrint(Zone self, ZoneDelegate parent, Zone zone, String line) { + printToConsole(line); +} + +void _printToZone(String line) { + Zone.current.print(line); +} + +Zone _rootFork(Zone self, ZoneDelegate parent, Zone zone, + ZoneSpecification specification, + Map zoneValues) { + // TODO(floitsch): it would be nice if we could get rid of this hack. + // Change the static zoneOrDirectPrint function to go through zones + // from now on. + printToZone = _printToZone; + + if (specification == null) { + specification = const ZoneSpecification(); + } else if (specification is! _ZoneSpecification) { + throw new ArgumentError("ZoneSpecifications must be instantiated" + " with the provided constructor."); + } + Map copiedMap = new HashMap(); + if (zoneValues != null) { + zoneValues.forEach((key, value) { + copiedMap[key] = value; + }); + } + return new _CustomizedZone(zone, specification, copiedMap); +} + +class _RootZoneSpecification implements ZoneSpecification { + const _RootZoneSpecification(); + + HandleUncaughtErrorHandler get handleUncaughtError => + _rootHandleUncaughtError; + RunHandler get run => _rootRun; + RunUnaryHandler get runUnary => _rootRunUnary; + RunBinaryHandler get runBinary => _rootRunBinary; + RegisterCallbackHandler get registerCallback => _rootRegisterCallback; + RegisterUnaryCallbackHandler get registerUnaryCallback => + _rootRegisterUnaryCallback; + RegisterBinaryCallbackHandler get registerBinaryCallback => + _rootRegisterBinaryCallback; + ScheduleMicrotaskHandler get scheduleMicrotask => _rootScheduleMicrotask; + CreateTimerHandler get createTimer => _rootCreateTimer; + CreatePeriodicTimerHandler get createPeriodicTimer => + _rootCreatePeriodicTimer; + PrintHandler get print => _rootPrint; + ForkHandler get fork => _rootFork; +} + +class _RootZone extends _BaseZone { + const _RootZone(); + + Zone get parent => null; + ZoneSpecification get _specification => const _RootZoneSpecification(); + Zone get _errorZone => this; + + bool inSameErrorZone(Zone otherZone) => otherZone._errorZone == this; + + operator [](Object key) => null; + + // Methods that can be customized by the zone specification. + + dynamic handleUncaughtError(error, StackTrace stackTrace) => + _rootHandleUncaughtError(this, null, this, error, stackTrace); + + Zone fork({ZoneSpecification specification, Map zoneValues}) => + _rootFork(this, null, this, specification, zoneValues); + + dynamic run(f()) => _rootRun(this, null, this, f); + + dynamic runUnary(f(arg), arg) => _rootRunUnary(this, null, this, f, arg); + + dynamic runBinary(f(arg1, arg2), arg1, arg2) => + _rootRunBinary(this, null, this, f, arg1, arg2); + + ZoneCallback registerCallback(f()) => + _rootRegisterCallback(this, null, this, f); + + ZoneUnaryCallback registerUnaryCallback(f(arg)) => + _rootRegisterUnaryCallback(this, null, this, f); + + ZoneBinaryCallback registerBinaryCallback(f(arg1, arg2)) => + _rootRegisterBinaryCallback(this, null, this, f); + + void scheduleMicrotask(void f()) { + _rootScheduleMicrotask(this, null, this, f); + } + + Timer createTimer(Duration duration, void f()) => + _rootCreateTimer(this, null, this, duration, f); + + Timer createPeriodicTimer(Duration duration, void f(Timer timer)) => + _rootCreatePeriodicTimer(this, null, this, duration, f); + + void print(String line) => _rootPrint(this, null, this, line); +} + +const _ROOT_ZONE = const _RootZone(); + + +/** + * Runs [body] in its own zone. + * + * If [onError] is non-null the zone is considered an error zone. All uncaught + * errors, synchronous or asynchronous, in the zone are caught and handled + * by the callback. + * + * Errors may never cross error-zone boundaries. This is intuitive for leaving + * a zone, but it also applies for errors that would enter an error-zone. + * Errors that try to cross error-zone boundaries are considered uncaught. + * + * var future = new Future.value(499); + * runZoned(() { + * future = future.then((_) { throw "error in first error-zone"; }); + * runZoned(() { + * future = future.catchError((e) { print("Never reached!"); }); + * }, onError: (e) { print("unused error handler"); }); + * }, onError: (e) { print("catches error of first error-zone."); }); + * + * Example: + * + * runZoned(() { + * new Future(() { throw "asynchronous error"; }); + * }, onError: print); // Will print "asynchronous error". + */ +dynamic runZoned(body(), + { Map zoneValues, + ZoneSpecification zoneSpecification, + Function onError }) { + HandleUncaughtErrorHandler errorHandler; + if (onError != null) { + errorHandler = (Zone self, ZoneDelegate parent, Zone zone, + error, StackTrace stackTrace) { + try { + if (onError is ZoneBinaryCallback) { + return self.parent.runBinary(onError, error, stackTrace); + } + return self.parent.runUnary(onError, error); + } catch(e, s) { + if (identical(e, error)) { + return parent.handleUncaughtError(zone, error, stackTrace); + } else { + return parent.handleUncaughtError(zone, e, s); + } + } + }; + } + if (zoneSpecification == null) { + zoneSpecification = + new ZoneSpecification(handleUncaughtError: errorHandler); + } else if (errorHandler != null) { + zoneSpecification = + new ZoneSpecification.from(zoneSpecification, + handleUncaughtError: errorHandler); + } + Zone zone = Zone.current.fork(specification: zoneSpecification, + zoneValues: zoneValues); + if (onError != null) { + return zone.runGuarded(body); + } else { + return zone.run(body); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/collection.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/collection.dart new file mode 100644 index 0000000..4620bc0 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/collection.dart @@ -0,0 +1,24 @@ +// Copyright (c) 2012, 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. + +/** + * Classes and utilities that supplement the collection support in dart:core. + */ +library dart.collection; + +import 'dart:_internal'; +import 'dart:math' show Random; // Used by ListMixin.shuffle. + +part 'collections.dart'; +part 'iterable.dart'; +part 'iterator.dart'; +part 'maps.dart'; +part 'queue.dart'; +part 'splay_tree.dart'; +part 'linked_list.dart'; +part 'hash_set.dart'; +part 'hash_map.dart'; +part 'list.dart'; +part 'linked_hash_set.dart'; +part 'linked_hash_map.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/collections.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/collections.dart new file mode 100644 index 0000000..fb9a94b --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/collections.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2013, 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. + +part of dart.collection; + +/** + * An unmodifiable [List] view of another List. + * + * The source of the elements may be a [List] or any [Iterable] with + * efficient [Iterable.length] and [Iterable.elementAt]. + */ +class UnmodifiableListView extends UnmodifiableListBase { + final Iterable _source; + /** Create an unmodifiable list backed by [source]. */ + UnmodifiableListView(Iterable source) : _source = source; + int get length => _source.length; + E operator[](int index) => _source.elementAt(index); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/hash_map.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/hash_map.dart new file mode 100644 index 0000000..2a42860 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/hash_map.dart @@ -0,0 +1,125 @@ +// Copyright (c) 2013, 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. + +part of dart.collection; + +/** Default function for equality comparison in customized HashMaps */ +bool _defaultEquals(a, b) => a == b; +/** Default function for hash-code computation in customized HashMaps */ +int _defaultHashCode(a) => a.hashCode; + +/** Type of custom equality function */ +typedef bool _Equality(K a, K b); +/** Type of custom hash code function. */ +typedef int _Hasher(K object); + +/** + * A hash-table based implementation of [Map]. + * + * The keys of a `HashMap` must have consistent [Object.operator==] + * and [Object.hashCode] implementations. This means that the `==` operator + * must define a stable equivalence relation on the keys (reflexive, + * anti-symmetric, transitive, and consistent over time), and that `hashCode` + * must be the same for objects that are considered equal by `==`. + * + * The map allows `null` as a key. + * + * Iterating the map's keys, values or entries (through [forEach]) + * may happen in any order. + * The itearation order only changes when the map is modified. + * Values are iterated in the same order as their associated keys, + * so iterating the [keys] and [values] in parallel + * will give matching key and value pairs. + */ +abstract class HashMap implements Map { + /** + * Creates an unordered hash-table based [Map]. + * + * The created map is not ordered in any way. When iterating the keys or + * values, the iteration order is unspecified except that it will stay the + * same as long as the map isn't changed. + * + * If [equals] is provided, it is used to compare the keys in the table with + * new keys. If [equals] is omitted, the key's own [Object.operator==] is used + * instead. + * + * Similar, if [hashCode] is provided, it is used to produce a hash value + * for keys in order to place them in the hash table. If it is omitted, the + * key's own [Object.hashCode] is used. + * + * If using methods like [operator[]], [remove] and [containsKey] together + * with a custom equality and hashcode, an extra `isValidKey` function + * can be supplied. This function is called before calling [equals] or + * [hashCode] with an argument that may not be a [K] instance, and if the + * call returns false, the key is assumed to not be in the set. + * The [isValidKey] function defaults to just testing if the object is a + * [K] instance. + * + * The used `equals` and `hashCode` method should always be consistent, + * so that if `equals(a, b)` then `hashCode(a) == hashCode(b)`. The hash + * of an object, or what it compares equal to, should not change while the + * object is in the table. If it does change, the result is unpredictable. + * + * If you supply one of [equals] and [hashCode], + * you should generally also to supply the other. + * An example would be using [identical] and [identityHashCode], + * which is equivalent to using the shorthand [HashMap.identity]). + */ + external factory HashMap({bool equals(K key1, K key2), + int hashCode(K key), + bool isValidKey(potentialKey)}); + + /** + * Creates an unordered identity-based map. + * + * Effectively a shorthand for: + * + * new HashMap(equals: identical, hashCode: identityHashCodeOf) + */ + external factory HashMap.identity(); + + /** + * Creates a [HashMap] that contains all key value pairs of [other]. + */ + factory HashMap.from(Map other) { + return new HashMap()..addAll(other); + } + + /** + * Creates a [HashMap] where the keys and values are computed from the + * [iterable]. + * + * For each element of the [iterable] this constructor computes a key/value + * pair, by applying [key] and [value] respectively. + * + * The keys of the key/value pairs do not need to be unique. The last + * occurrence of a key will simply overwrite any previous value. + * + * If no values are specified for [key] and [value] the default is the + * identity function. + */ + factory HashMap.fromIterable(Iterable iterable, + {K key(element), V value(element)}) { + HashMap map = new HashMap(); + Maps._fillMapWithMappedIterable(map, iterable, key, value); + return map; + } + + /** + * Creates a [HashMap] associating the given [keys] to [values]. + * + * This constructor iterates over [keys] and [values] and maps each element of + * [keys] to the corresponding element of [values]. + * + * If [keys] contains the same object multiple times, the last occurrence + * overwrites the previous value. + * + * It is an error if the two [Iterable]s don't have the same length. + */ + factory HashMap.fromIterables(Iterable keys, Iterable values) { + HashMap map = new HashMap(); + Maps._fillMapWithIterables(map, keys, values); + return map; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/hash_set.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/hash_set.dart new file mode 100644 index 0000000..ff95b43 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/hash_set.dart @@ -0,0 +1,134 @@ +// Copyright (c) 2013, 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. + +part of dart.collection; + +/** Common parts of [HashSet] and [LinkedHashSet] implementations. */ +abstract class _HashSetBase extends IterableBase implements Set { + + // Set. + bool containsAll(Iterable other) { + for (Object object in other) { + if (!this.contains(object)) return false; + } + return true; + } + + /** Create a new Set of the same type as this. */ + HashSet _newSet(); + + Set intersection(Set other) { + Set result = _newSet(); + for (var element in this) { + if (other.contains(element)) result.add(element); + } + return result; + } + + Set union(Set other) { + return _newSet()..addAll(this)..addAll(other); + } + + Set difference(Set other) { + HashSet result = _newSet(); + for (E element in this) { + if (!other.contains(element)) result.add(element); + } + return result; + } + + void _retainAll(Iterable objectsToRetain, bool isValidKey(Object o)) { + // TODO(lrn): Consider optimizing table based versions by + // building a new table of the entries to retain. + Set retainSet = _newSet(); + for (Object o in objectsToRetain) { + if (isValidKey(o)) { + retainSet.add(o); + } + } + retainWhere(retainSet.contains); + } + + List toList({bool growable: true}) { + List result = growable ? (new List()..length = this.length) + : new List(this.length); + int i = 0; + for (E element in this) result[i++] = element; + return result; + } + + Set toSet() => _newSet()..addAll(this); + + String toString() => IterableMixinWorkaround.toStringIterable(this, '{', '}'); +} + +/** + * An unordered hash-table based [Set] implementation. + * + * The elements of a `HashSet` must have consistent equality + * and hashCode implementations. This means that the equals operation + * must define a stable equivalence relation on the elements (reflexive, + * anti-symmetric, transitive, and consistent over time), and that the hashCode + * must consistent with equality, so that the same for objects that are + * considered equal. + * + * The set allows `null` as an element. + * + * Most simple operations on `HashSet` are done in (potentially amorteized) + * constant time: [add], [contains], [remove], and [length], provided the hash + * codes of objects are well distributed. + */ +abstract class HashSet implements Set { + /** + * Create a hash set using the provided [equals] as equality. + * + * The provided [equals] must define a stable equivalence relation, and + * [hashCode] must be consistent with [equals]. If the [equals] or [hashCode] + * methods won't work on all objects, but only to instances of E, the + * [isValidKey] predicate can be used to restrict the keys that they are + * applied to. Any key for which [isValidKey] returns false is automatically + * assumed to not be in the set. + * + * If [equals] or [hashCode] are omitted, the set uses + * the objects' intrinsic [Object.operator==] and [Object.hashCode]. + * + * If [isValidKey] is omitted, it defaults to testing if the object is an + * [E] instance. + * + * If you supply one of [equals] and [hashCode], + * you should generally also to supply the other. + * An example would be using [identical] and [identityHashCode], + * which is equivalent to using the shorthand [LinkedSet.identity]). + */ + external factory HashSet({ bool equals(E e1, E e2), + int hashCode(E e), + bool isValidKey(potentialKey) }); + + /** + * Creates an unordered identity-based set. + * + * Effectively a shorthand for: + * + * new HashSet(equals: identical, hashCode: identityHashCodeOf) + */ + external factory HashSet.identity(); + + /** + * Create a hash set containing the elements of [iterable]. + * + * Creates a hash set as by `new HashSet()` and adds each element of + * `iterable` to this set in the order they are iterated. + */ + factory HashSet.from(Iterable iterable) { + return new HashSet()..addAll(iterable); + } + + /** + * Provides an iterator that iterates over the elements of this set. + * + * The order of iteration is unspecified, + * but consistent between changes to the set. + */ + Iterator get iterator; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/iterable.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/iterable.dart new file mode 100644 index 0000000..d74448a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/iterable.dart @@ -0,0 +1,525 @@ +// Copyright (c) 2012, 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. + +part of dart.collection; + +/** + * This [Iterable] mixin implements all [Iterable] members except `iterator`. + * + * All other methods are implemented in terms of `iterator`. + */ +abstract class IterableMixin implements Iterable { + Iterable map(f(E element)) => new MappedIterable(this, f); + + Iterable where(bool f(E element)) => new WhereIterable(this, f); + + Iterable expand(Iterable f(E element)) => + new ExpandIterable(this, f); + + bool contains(Object element) { + for (E e in this) { + if (e == element) return true; + } + return false; + } + + void forEach(void f(E element)) { + for (E element in this) f(element); + } + + E reduce(E combine(E value, E element)) { + Iterator iterator = this.iterator; + if (!iterator.moveNext()) { + throw new StateError("No elements"); + } + E value = iterator.current; + while (iterator.moveNext()) { + value = combine(value, iterator.current); + } + return value; + } + + dynamic fold(var initialValue, + dynamic combine(var previousValue, E element)) { + var value = initialValue; + for (E element in this) value = combine(value, element); + return value; + } + + bool every(bool f(E element)) { + for (E element in this) { + if (!f(element)) return false; + } + return true; + } + + String join([String separator = ""]) { + Iterator iterator = this.iterator; + if (!iterator.moveNext()) return ""; + StringBuffer buffer = new StringBuffer(); + if (separator == null || separator == "") { + do { + buffer.write("${iterator.current}"); + } while (iterator.moveNext()); + } else { + buffer.write("${iterator.current}"); + while (iterator.moveNext()) { + buffer.write(separator); + buffer.write("${iterator.current}"); + } + } + return buffer.toString(); + } + + bool any(bool f(E element)) { + for (E element in this) { + if (f(element)) return true; + } + return false; + } + + List toList({ bool growable: true }) => + new List.from(this, growable: growable); + + Set toSet() => new Set.from(this); + + int get length { + assert(this is! EfficientLength); + int count = 0; + Iterator it = iterator; + while (it.moveNext()) { + count++; + } + return count; + } + + bool get isEmpty => !iterator.moveNext(); + + bool get isNotEmpty => !isEmpty; + + Iterable take(int n) { + return new TakeIterable(this, n); + } + + Iterable takeWhile(bool test(E value)) { + return new TakeWhileIterable(this, test); + } + + Iterable skip(int n) { + return new SkipIterable(this, n); + } + + Iterable skipWhile(bool test(E value)) { + return new SkipWhileIterable(this, test); + } + + E get first { + Iterator it = iterator; + if (!it.moveNext()) { + throw new StateError("No elements"); + } + return it.current; + } + + E get last { + Iterator it = iterator; + if (!it.moveNext()) { + throw new StateError("No elements"); + } + E result; + do { + result = it.current; + } while(it.moveNext()); + return result; + } + + E get single { + Iterator it = iterator; + if (!it.moveNext()) throw new StateError("No elements"); + E result = it.current; + if (it.moveNext()) throw new StateError("More than one element"); + return result; + } + + dynamic firstWhere(bool test(E value), { Object orElse() }) { + for (E element in this) { + if (test(element)) return element; + } + if (orElse != null) return orElse(); + throw new StateError("No matching element"); + } + + dynamic lastWhere(bool test(E value), { Object orElse() }) { + E result = null; + bool foundMatching = false; + for (E element in this) { + if (test(element)) { + result = element; + foundMatching = true; + } + } + if (foundMatching) return result; + if (orElse != null) return orElse(); + throw new StateError("No matching element"); + } + + E singleWhere(bool test(E value)) { + E result = null; + bool foundMatching = false; + for (E element in this) { + if (test(element)) { + if (foundMatching) { + throw new StateError("More than one matching element"); + } + result = element; + foundMatching = true; + } + } + if (foundMatching) return result; + throw new StateError("No matching element"); + } + + E elementAt(int index) { + if (index is! int || index < 0) throw new RangeError.value(index); + int remaining = index; + for (E element in this) { + if (remaining == 0) return element; + remaining--; + } + throw new RangeError.value(index); + } + + String toString() => _iterableToString(this); +} + +/** + * Base class for implementing [Iterable]. + * + * This class implements all methods of [Iterable] except [Iterable.iterator] + * in terms of `iterator`. + */ +abstract class IterableBase implements Iterable { + // TODO(lrn): Base this on IterableMixin if there ever becomes a way + // to combine const constructors and mixins. + const IterableBase(); + + Iterable map(f(E element)) => new MappedIterable(this, f); + + Iterable where(bool f(E element)) => new WhereIterable(this, f); + + Iterable expand(Iterable f(E element)) => + new ExpandIterable(this, f); + + bool contains(Object element) { + for (E e in this) { + if (e == element) return true; + } + return false; + } + + void forEach(void f(E element)) { + for (E element in this) f(element); + } + + E reduce(E combine(E value, E element)) { + Iterator iterator = this.iterator; + if (!iterator.moveNext()) { + throw new StateError("No elements"); + } + E value = iterator.current; + while (iterator.moveNext()) { + value = combine(value, iterator.current); + } + return value; + } + + dynamic fold(var initialValue, + dynamic combine(var previousValue, E element)) { + var value = initialValue; + for (E element in this) value = combine(value, element); + return value; + } + + bool every(bool f(E element)) { + for (E element in this) { + if (!f(element)) return false; + } + return true; + } + + String join([String separator = ""]) { + Iterator iterator = this.iterator; + if (!iterator.moveNext()) return ""; + StringBuffer buffer = new StringBuffer(); + if (separator == null || separator == "") { + do { + buffer.write("${iterator.current}"); + } while (iterator.moveNext()); + } else { + buffer.write("${iterator.current}"); + while (iterator.moveNext()) { + buffer.write(separator); + buffer.write("${iterator.current}"); + } + } + return buffer.toString(); + } + + bool any(bool f(E element)) { + for (E element in this) { + if (f(element)) return true; + } + return false; + } + + List toList({ bool growable: true }) => + new List.from(this, growable: growable); + + Set toSet() => new Set.from(this); + + int get length { + assert(this is! EfficientLength); + int count = 0; + Iterator it = iterator; + while (it.moveNext()) { + count++; + } + return count; + } + + bool get isEmpty => !iterator.moveNext(); + + bool get isNotEmpty => !isEmpty; + + Iterable take(int n) { + return new TakeIterable(this, n); + } + + Iterable takeWhile(bool test(E value)) { + return new TakeWhileIterable(this, test); + } + + Iterable skip(int n) { + return new SkipIterable(this, n); + } + + Iterable skipWhile(bool test(E value)) { + return new SkipWhileIterable(this, test); + } + + E get first { + Iterator it = iterator; + if (!it.moveNext()) { + throw new StateError("No elements"); + } + return it.current; + } + + E get last { + Iterator it = iterator; + if (!it.moveNext()) { + throw new StateError("No elements"); + } + E result; + do { + result = it.current; + } while(it.moveNext()); + return result; + } + + E get single { + Iterator it = iterator; + if (!it.moveNext()) throw new StateError("No elements"); + E result = it.current; + if (it.moveNext()) throw new StateError("More than one element"); + return result; + } + + dynamic firstWhere(bool test(E value), { Object orElse() }) { + for (E element in this) { + if (test(element)) return element; + } + if (orElse != null) return orElse(); + throw new StateError("No matching element"); + } + + dynamic lastWhere(bool test(E value), { Object orElse() }) { + E result = null; + bool foundMatching = false; + for (E element in this) { + if (test(element)) { + result = element; + foundMatching = true; + } + } + if (foundMatching) return result; + if (orElse != null) return orElse(); + throw new StateError("No matching element"); + } + + E singleWhere(bool test(E value)) { + E result = null; + bool foundMatching = false; + for (E element in this) { + if (test(element)) { + if (foundMatching) { + throw new StateError("More than one matching element"); + } + result = element; + foundMatching = true; + } + } + if (foundMatching) return result; + throw new StateError("No matching element"); + } + + E elementAt(int index) { + if (index is! int || index < 0) throw new RangeError.value(index); + int remaining = index; + for (E element in this) { + if (remaining == 0) return element; + remaining--; + } + throw new RangeError.value(index); + } + + /** + * Returns a string representation of (some of) the elements of `this`. + * + * Elements are represented by their own `toString` results. + * + * The representation always contains the first three elements. + * If there are less than a hundred elements in the iterable, it also + * contains the last two elements. + * + * If the resulting string isn't above 80 characters, more elements are + * included from the start of the iterable. + * + * The conversion may omit calling `toString` on some elements if they + * are known to now occur in the output, and it may stop iterating after + * a hundred elements. + */ + String toString() => _iterableToString(this); +} + +String _iterableToString(Iterable iterable) { + if (_toStringVisiting.contains(iterable)) return "(...)"; + _toStringVisiting.add(iterable); + List parts = []; + try { + _iterablePartsToStrings(iterable, parts); + } finally { + _toStringVisiting.remove(iterable); + } + return (new StringBuffer("(")..writeAll(parts, ", ")..write(")")).toString(); +} + +/** Convert elments of [iterable] to strings and store them in [parts]. */ +void _iterablePartsToStrings(Iterable iterable, List parts) { + /// Try to stay below this many characters. + const int LENGTH_LIMIT = 80; + /// Always at least this many elements at the start. + const int HEAD_COUNT = 3; + /// Always at least this many elements at the end. + const int TAIL_COUNT = 2; + /// Stop iterating after this many elements. Iterables can be infinite. + const int MAX_COUNT = 100; + // Per entry length overhead. It's for ", " for all after the first entry, + // and for "(" and ")" for the initial entry. By pure luck, that's the same + // number. + const int OVERHEAD = 2; + const int ELLIPSIS_SIZE = 3; // "...".length. + + int length = 0; + int count = 0; + Iterator it = iterable.iterator; + // Initial run of elements, at least HEAD_COUNT, and then continue until + // passing at most LENGTH_LIMIT characters. + while (length < LENGTH_LIMIT || count < HEAD_COUNT) { + if (!it.moveNext()) return; + String next = "${it.current}"; + parts.add(next); + length += next.length + OVERHEAD; + count++; + } + + String penultimateString; + String ultimateString; + + // Find last two elements. One or more of them may already be in the + // parts array. Include their length in `length`. + var penultimate = null; + var ultimate = null; + if (!it.moveNext()) { + if (count <= HEAD_COUNT + TAIL_COUNT) return; + ultimateString = parts.removeLast(); + penultimateString = parts.removeLast(); + } else { + penultimate = it.current; + count++; + if (!it.moveNext()) { + if (count <= HEAD_COUNT + 1) { + parts.add("$penultimate"); + return; + } + ultimateString = "$penultimate"; + penultimateString = parts.removeLast(); + length += ultimateString.length + OVERHEAD; + } else { + ultimate = it.current; + count++; + // Then keep looping, keeping the last two elements in variables. + assert(count < MAX_COUNT); + while (it.moveNext()) { + penultimate = ultimate; + ultimate = it.current; + count++; + if (count > MAX_COUNT) { + // If we haven't found the end before MAX_COUNT, give up. + // This cannot happen in the code above because each entry + // increases length by at least two, so there is no way to + // visit more than ~40 elements before this loop. + + // Remove any surplus elements until length, including ", ...)", + // is at most LENGTH_LIMIT. + while (length > LENGTH_LIMIT - ELLIPSIS_SIZE - OVERHEAD && + count > HEAD_COUNT) { + length -= parts.removeLast().length + OVERHEAD; + count--; + } + parts.add("..."); + return; + } + } + penultimateString = "$penultimate"; + ultimateString = "$ultimate"; + length += + ultimateString.length + penultimateString.length + 2 * OVERHEAD; + } + } + + // If there is a gap between the initial run and the last two, + // prepare to add an ellipsis. + String elision = null; + if (count > parts.length + TAIL_COUNT) { + elision = "..."; + length += ELLIPSIS_SIZE + OVERHEAD; + } + + // If the last two elements were very long, and we have more than + // HEAD_COUNT elements in the initial run, drop some to make room for + // the last two. + while (length > LENGTH_LIMIT && parts.length > HEAD_COUNT) { + length -= parts.removeLast().length + OVERHEAD; + if (elision == null) { + elision = "..."; + length += ELLIPSIS_SIZE + OVERHEAD; + } + } + if (elision != null) { + parts.add(elision); + } + parts.add(penultimateString); + parts.add(ultimateString); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/iterator.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/iterator.dart new file mode 100644 index 0000000..1fe49cd --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/iterator.dart @@ -0,0 +1,45 @@ +// Copyright (c) 2012, 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. + +part of dart.collection; + +/** + * The [HasNextIterator] class wraps an [Iterator] and provides methods to + * iterate over an object using `hasNext` and `next`. + * + * An [HasNextIterator] does not implement the [Iterator] interface. + */ +class HasNextIterator { + static const int _HAS_NEXT_AND_NEXT_IN_CURRENT = 0; + static const int _NO_NEXT = 1; + static const int _NOT_MOVED_YET = 2; + + Iterator _iterator; + int _state = _NOT_MOVED_YET; + + HasNextIterator(this._iterator); + + bool get hasNext { + if (_state == _NOT_MOVED_YET) _move(); + return _state == _HAS_NEXT_AND_NEXT_IN_CURRENT; + } + + E next() { + // Call to hasNext is necessary to make sure we are positioned at the first + // element when we start iterating. + if (!hasNext) throw new StateError("No more elements"); + assert(_state == _HAS_NEXT_AND_NEXT_IN_CURRENT); + E result = _iterator.current; + _move(); + return result; + } + + void _move() { + if (_iterator.moveNext()) { + _state = _HAS_NEXT_AND_NEXT_IN_CURRENT; + } else { + _state = _NO_NEXT; + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/linked_hash_map.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/linked_hash_map.dart new file mode 100644 index 0000000..48190a2 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/linked_hash_map.dart @@ -0,0 +1,112 @@ +// Copyright (c) 2013, 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. + +part of dart.collection; + +/** + * A hash-table based implementation of [Map]. + * + * The insertion order of keys is remembered, + * and keys are iterated in the order they were insertion into the map. + * Values are iterated in their corresponding key's order. + * Changing a key's value, when the key is already in the map, + * does not change the iteration order, + * but removing the key and adding it again + * will make it be last in the iteration order. + * + * The keys of a `LinkedHashMap` must have consistent [Object.operator==] + * and [Object.hashCode] implementations. This means that the `==` operator + * must define a stable equivalence relation on the keys (reflexive, + * anti-symmetric, transitive, and consistent over time), and that `hashCode` + * must be the same for objects that are considered equal by `==`. + * + * The map allows `null` as a key. + */ +abstract class LinkedHashMap implements HashMap { + /** + * Creates an insertion-ordered hash-table based [Map]. + * + * If [equals] is provided, it is used to compare the keys in the table with + * new keys. If [equals] is omitted, the key's own [Object.operator==] is used + * instead. + * + * Similar, if [hashCode] is provided, it is used to produce a hash value + * for keys in order to place them in the hash table. If it is omitted, the + * key's own [Object.hashCode] is used. + * + * If using methods like [operator[]], [remove] and [containsKey] together + * with a custom equality and hashcode, an extra `isValidKey` function + * can be supplied. This function is called before calling [equals] or + * [hashCode] with an argument that may not be a [K] instance, and if the + * call returns false, the key is assumed to not be in the set. + * The [isValidKey] function defaults to just testing if the object is a + * [K] instance. + * + * The used `equals` and `hashCode` method should always be consistent, + * so that if `equals(a, b)` then `hashCode(a) == hashCode(b)`. The hash + * of an object, or what it compares equal to, should not change while the + * object is in the table. If it does change, the result is unpredictable. + * + * If you supply one of [equals] and [hashCode], + * you should generally also to supply the other. + * An example would be using [identical] and [identityHashCode], + * which is equivalent to using the shorthand [LinkedHashMap.identity]). + */ + external factory LinkedHashMap({ bool equals(K key1, K key2), + int hashCode(K key), + bool isValidKey(potentialKey) }); + + /** + * Creates an insertion-ordered identity-based map. + * + * Effectively a shorthand for: + * + * new LinkedHashMap(equals: identical, hashCode: identityHashCodeOf) + */ + external factory LinkedHashMap.identity(); + + /** + * Creates a [LinkedHashMap] that contains all key value pairs of [other]. + */ + factory LinkedHashMap.from(Map other) { + return new LinkedHashMap()..addAll(other); + } + + /** + * Creates a [LinkedHashMap] where the keys and values are computed from the + * [iterable]. + * + * For each element of the [iterable] this constructor computes a key/value + * pair, by applying [key] and [value] respectively. + * + * The keys of the key/value pairs do not need to be unique. The last + * occurrence of a key will simply overwrite any previous value. + * + * If no values are specified for [key] and [value] the default is the + * identity function. + */ + factory LinkedHashMap.fromIterable(Iterable iterable, + {K key(element), V value(element)}) { + LinkedHashMap map = new LinkedHashMap(); + Maps._fillMapWithMappedIterable(map, iterable, key, value); + return map; + } + + /** + * Creates a [LinkedHashMap] associating the given [keys] to [values]. + * + * This constructor iterates over [keys] and [values] and maps each element of + * [keys] to the corresponding element of [values]. + * + * If [keys] contains the same object multiple times, the last occurrence + * overwrites the previous value. + * + * It is an error if the two [Iterable]s don't have the same length. + */ + factory LinkedHashMap.fromIterables(Iterable keys, Iterable values) { + LinkedHashMap map = new LinkedHashMap(); + Maps._fillMapWithIterables(map, keys, values); + return map; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/linked_hash_set.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/linked_hash_set.dart new file mode 100644 index 0000000..0f50e09 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/linked_hash_set.dart @@ -0,0 +1,83 @@ +// Copyright (c) 2013, 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. + +part of dart.collection; + +/** + * A [LinkedHashSet] is a hash-table based [Set] implementation. + * + * The `LinkedHashSet` also keep track of the order that elements were inserted + * in, and iteration happens in first-to-last insertion order. + * + * The elements of a `LinkedHashSet` must have consistent [Object.operator==] + * and [Object.hashCode] implementations. This means that the `==` operator + * must define a stable equivalence relation on the elements (reflexive, + * anti-symmetric, transitive, and consistent over time), and that `hashCode` + * must be the same for objects that are considered equal by `==`. + * + * The set allows `null` as an element. + * + * Iteration of elements is done in element insertion order. + * An element that was added after another will occur later in the iteration. + * Adding an element that is already in the set + * does not change its position in the iteration order, + * but removing an element and adding it again, + * will make it the last element of an iteration. + * + * Most simple operations on `HashSet` are done in (potentially amortized) + * constant time: [add], [contains], [remove], and [length], provided the hash + * codes of objects are well distributed.. + */ +abstract class LinkedHashSet implements HashSet { + /** + * Create an insertion-ordered hash set using the provided + * [equals] and [hashCode]. + * + * The provided [equals] must define a stable equivalence relation, and + * [hashCode] must be consistent with [equals]. If the [equals] or [hashCode] + * methods won't work on all objects, but only to instances of E, the + * [isValidKey] predicate can be used to restrict the keys that they are + * applied to. Any key for which [isValidKey] returns false is automatically + * assumed to not be in the set. + * + * If [equals] or [hashCode] are omitted, the set uses + * the objects' intrinsic [Object.operator==] and [Object.hashCode], + * + * If [isValidKey] is omitted, it defaults to testing if the object is an + * [E] instance. + * + * If you supply one of [equals] and [hashCode], + * you should generally also to supply the other. + * An example would be using [identical] and [identityHashCode], + * which is equivalent to using the shorthand [LinkedSet.identity]). + */ + external factory LinkedHashSet({ bool equals(E e1, E e2), + int hashCode(E e), + bool isValidKey(potentialKey) }); + + /** + * Creates an insertion-ordered identity-based set. + * + * Effectively a shorthand for: + * + * new LinkedHashSet(equals: identical, hashCode: identityHashCodeOf) + */ + external factory LinkedHashSet.identity(); + + factory LinkedHashSet.from(Iterable iterable) { + return new LinkedHashSet()..addAll(iterable); + } + + /** + * Executes a function on each element of the set. + * + * The elements are iterated in insertion order. + */ + void forEach(void action(E element)); + + /** + * Provides an iterator that iterates over the elements in insertion order. + */ + Iterator get iterator; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/linked_list.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/linked_list.dart new file mode 100644 index 0000000..5bff429 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/linked_list.dart @@ -0,0 +1,283 @@ +// Copyright (c) 2013, 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. + +part of dart.collection; + + +/** + * A specialized double-linked list of elements that extends [LinkedListEntry]. + * + * This is not a generic data structure. It only accepts elements that extend + * the [LinkedListEntry] class. See the [Queue] implementations for + * generic collections that allow constant time adding and removing at the ends. + * + * This is not a [List] implementation. Despite its name, this class does not + * implement the [List] interface. It does not allow constant time lookup by + * index. + * + * Because the elements themselves contain the links of this linked list, + * each element can be in only one list at a time. To add an element to another + * list, it must first be removed from its current list (if any). + * + * In return, each element knows its own place in the linked list, as well as + * which list it is in. This allows constant time [LinkedListEntry.addAfter], + * [LinkedListEntry.addBefore] and [LinkedListEntry.unlink] operations + * when all you have is the element. + * + * A `LinkedList` also allows constant time adding and removing at either end, + * and a constant time length getter. + */ +class LinkedList> + extends IterableBase + implements _LinkedListLink { + + int _modificationCount = 0; + int _length = 0; + _LinkedListLink _next; + _LinkedListLink _previous; + + /** + * Construct a new empty linked list. + */ + LinkedList() { + _next = _previous = this; + } + + /** + * Add [entry] to the beginning of the linked list. + */ + void addFirst(E entry) { + _insertAfter(this, entry); + } + + /** + * Add [entry] to the end of the linked list. + */ + void add(E entry) { + _insertAfter(_previous, entry); + } + + /** + * Add [entries] to the end of the linked list. + */ + void addAll(Iterable entries) { + entries.forEach((entry) => _insertAfter(_previous, entry)); + } + + /** + * Remove [entry] from the linked list. + * + * Returns false and does nothing if [entry] is not in this linked list. + * + * This is equivalent to calling `entry.unlink()` if the entry is in this + * list. + */ + bool remove(E entry) { + if (entry._list != this) return false; + _unlink(entry); // Unlink will decrement length. + return true; + } + + Iterator get iterator => new _LinkedListIterator(this); + + int get length => _length; + + /** + * Remove all elements from this linked list. + */ + void clear() { + _modificationCount++; + _LinkedListLink next = _next; + while (!identical(next, this)) { + E entry = next; + next = entry._next; + entry._next = entry._previous = entry._list = null; + } + _next = _previous = this; + _length = 0; + } + + E get first { + if (identical(_next, this)) { + throw new StateError('No such element'); + } + return _next; + } + + E get last { + if (identical(_previous, this)) { + throw new StateError('No such element'); + } + return _previous; + } + + E get single { + if (identical(_previous, this)) { + throw new StateError('No such element'); + } + if (!identical(_previous, _next)) { + throw new StateError('Too many elements'); + } + return _next; + } + + /** + * Call [action] with each entry in this linked list. + * + * It's an error if [action] modify the linked list. + */ + void forEach(void action(E entry)) { + int modificationCount = _modificationCount; + _LinkedListLink current = _next; + while (!identical(current, this)) { + action(current); + if (modificationCount != _modificationCount) { + throw new ConcurrentModificationError(this); + } + current = current._next; + } + } + + bool get isEmpty => _length == 0; + + void _insertAfter(_LinkedListLink entry, E newEntry) { + if (newEntry.list != null) { + throw new StateError( + 'LinkedListEntry is already in a LinkedList'); + } + _modificationCount++; + newEntry._list = this; + var predecessor = entry; + var successor = entry._next; + successor._previous = newEntry; + newEntry._previous = predecessor; + newEntry._next = successor; + predecessor._next = newEntry; + _length++; + } + + void _unlink(LinkedListEntry entry) { + _modificationCount++; + entry._next._previous = entry._previous; + entry._previous._next = entry._next; + _length--; + entry._list = entry._next = entry._previous = null; + } +} + + +class _LinkedListIterator> + implements Iterator { + final LinkedList _list; + final int _modificationCount; + E _current; + _LinkedListLink _next; + + _LinkedListIterator(LinkedList list) + : _list = list, + _modificationCount = list._modificationCount, + _next = list._next; + + E get current => _current; + + bool moveNext() { + if (identical(_next, _list)) { + _current = null; + return false; + } + if (_modificationCount != _list._modificationCount) { + throw new ConcurrentModificationError(this); + } + _current = _next; + _next = _next._next; + return true; + } +} + + +class _LinkedListLink { + _LinkedListLink _next; + _LinkedListLink _previous; +} + + +/** + * An object that can be an element in a [LinkedList]. + * + * All elements of a `LinkedList` must extend this class. + * The class provides the internal links that link elements together + * in the `LinkedList`, and a reference to the linked list itself + * that an element is currently part of. + * + * An entry can be in at most one linked list at a time. + * While an entry is in a linked list, the [list] property points to that + * linked list, and otherwise the `list` property is `null`. + * + * When created, an entry is not in any linked list. + */ +abstract class LinkedListEntry> + implements _LinkedListLink { + LinkedList _list; + _LinkedListLink _next; + _LinkedListLink _previous; + + /** + * Get the linked list containing this element. + * + * Returns `null` if this entry is not currently in any list. + */ + LinkedList get list => _list; + + /** + * Unlink the element from its linked list. + * + * The entry must currently be in a linked list when this method is called. + */ + void unlink() { + _list._unlink(this); + } + + /** + * Return the succeessor of this element in its linked list. + * + * Returns `null` if there is no successor in the linked list, or if this + * entry is not currently in any list. + */ + E get next { + if (identical(_next, _list)) return null; + E result = _next; + return result; + } + + /** + * Return the predecessor of this element in its linked list. + * + * Returns `null` if there is no predecessor in the linked list, or if this + * entry is not currently in any list. + */ + E get previous { + if (identical(_previous, _list)) return null; + return _previous as E; + } + + /** + * Insert an element after this element in this element's linked list. + * + * This entry must be in a linked list when this method is called. + * The [entry] must not be in a linked list. + */ + void insertAfter(E entry) { + _list._insertAfter(this, entry); + } + + /** + * Insert an element before this element in this element's linked list. + * + * This entry must be in a linked list when this method is called. + * The [entry] must not be in a linked list. + */ + void insertBefore(E entry) { + _list._insertAfter(_previous, entry); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/list.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/list.dart new file mode 100644 index 0000000..7669a70 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/list.dart @@ -0,0 +1,521 @@ +// Copyright (c) 2013, 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. + +part of dart.collection; + +/** A reusable set used to identify cyclic lists during toString() calls. */ +Set _toStringVisiting = new HashSet.identity(); + +/** + * Abstract implementation of a list. + * + * All operations are defined in terms of `length`, `operator[]`, + * `operator[]=` and `length=`, which need to be implemented. + * + * *NOTICE*: Forwarding just these four operations to a normal growable [List] + * (as created by `new List()`) will give very bad performance for `add` and + * `addAll` operations of `ListBase`. These operations are implemented by + * increasing the length of the list by one for each `add` operation, and + * repeatedly increasing the length of a growable list is not efficient. + * To avoid this, either override 'add' and 'addAll' to also forward directly + * to the growable list, or, preferably, use `DelegatingList` from + * "package:collection/wrappers.dart" instead. + */ +abstract class ListBase = Object with ListMixin; + +/** + * Base implementation of a [List] class. + * + * This class can be used as a mixin. + * + * This implements all read operations using only the `length` and + * `operator[]` members. It implements write operations using those and + * `length=` and `operator[]=` + * + * *NOTICE*: Forwarding just these four operations to a normal growable [List] + * (as created by `new List()`) will give very bad performance for `add` and + * `addAll` operations of `ListBase`. These operations are implemented by + * increasing the length of the list by one for each `add` operation, and + * repeatedly increasing the length of a growable list is not efficient. + * To avoid this, either override 'add' and 'addAll' to also forward directly + * to the growable list, or, if possible, use `DelegatingList` from + * "package:collection/wrappers.dart" instead. + */ +abstract class ListMixin implements List { + + // Iterable interface. + Iterator get iterator => new ListIterator(this); + + E elementAt(int index) => this[index]; + + void forEach(void action(E element)) { + int length = this.length; + for (int i = 0; i < length; i++) { + action(this[i]); + if (length != this.length) { + throw new ConcurrentModificationError(this); + } + } + } + + bool get isEmpty => length == 0; + + bool get isNotEmpty => !isEmpty; + + E get first { + if (length == 0) throw new StateError("No elements"); + return this[0]; + } + + E get last { + if (length == 0) throw new StateError("No elements"); + return this[length - 1]; + } + + E get single { + if (length == 0) throw new StateError("No elements"); + if (length > 1) throw new StateError("Too many elements"); + return this[0]; + } + + bool contains(Object element) { + int length = this.length; + for (int i = 0; i < this.length; i++) { + if (this[i] == element) return true; + if (length != this.length) { + throw new ConcurrentModificationError(this); + } + } + return false; + } + + bool every(bool test(E element)) { + int length = this.length; + for (int i = 0; i < length; i++) { + if (!test(this[i])) return false; + if (length != this.length) { + throw new ConcurrentModificationError(this); + } + } + return true; + } + + bool any(bool test(E element)) { + int length = this.length; + for (int i = 0; i < length; i++) { + if (test(this[i])) return true; + if (length != this.length) { + throw new ConcurrentModificationError(this); + } + } + return false; + } + + dynamic firstWhere(bool test(E element), { Object orElse() }) { + int length = this.length; + for (int i = 0; i < length; i++) { + E element = this[i]; + if (test(element)) return element; + if (length != this.length) { + throw new ConcurrentModificationError(this); + } + } + if (orElse != null) return orElse(); + throw new StateError("No matching element"); + } + + dynamic lastWhere(bool test(E element), { Object orElse() }) { + int length = this.length; + for (int i = length - 1; i >= 0; i--) { + E element = this[i]; + if (test(element)) return element; + if (length != this.length) { + throw new ConcurrentModificationError(this); + } + } + if (orElse != null) return orElse(); + throw new StateError("No matching element"); + } + + E singleWhere(bool test(E element)) { + int length = this.length; + E match = null; + bool matchFound = false; + for (int i = 0; i < length; i++) { + E element = this[i]; + if (test(element)) { + if (matchFound) { + throw new StateError("More than one matching element"); + } + matchFound = true; + match = element; + } + if (length != this.length) { + throw new ConcurrentModificationError(this); + } + } + if (matchFound) return match; + throw new StateError("No matching element"); + } + + String join([String separator = ""]) { + if (length == 0) return ""; + StringBuffer buffer = new StringBuffer()..writeAll(this, separator); + return buffer.toString(); + } + + Iterable where(bool test(E element)) => new WhereIterable(this, test); + + Iterable map(f(E element)) => new MappedListIterable(this, f); + + Iterable expand(Iterable f(E element)) => + new ExpandIterable(this, f); + + E reduce(E combine(E previousValue, E element)) { + if (length == 0) throw new StateError("No elements"); + E value = this[0]; + for (int i = 1; i < length; i++) { + value = combine(value, this[i]); + } + return value; + } + + fold(var initialValue, combine(var previousValue, E element)) { + var value = initialValue; + int length = this.length; + for (int i = 0; i < length; i++) { + value = combine(value, this[i]); + if (length != this.length) { + throw new ConcurrentModificationError(this); + } + } + return value; + } + + Iterable skip(int count) => new SubListIterable(this, count, null); + + Iterable skipWhile(bool test(E element)) { + return new SkipWhileIterable(this, test); + } + + Iterable take(int count) => new SubListIterable(this, 0, count); + + Iterable takeWhile(bool test(E element)) { + return new TakeWhileIterable(this, test); + } + + List toList({ bool growable: true }) { + List result; + if (growable) { + result = new List()..length = length; + } else { + result = new List(length); + } + for (int i = 0; i < length; i++) { + result[i] = this[i]; + } + return result; + } + + Set toSet() { + Set result = new Set(); + for (int i = 0; i < length; i++) { + result.add(this[i]); + } + return result; + } + + // Collection interface. + void add(E element) { + this[this.length++] = element; + } + + void addAll(Iterable iterable) { + for (E element in iterable) { + this[this.length++] = element; + } + } + + bool remove(Object element) { + for (int i = 0; i < this.length; i++) { + if (this[i] == element) { + this.setRange(i, this.length - 1, this, i + 1); + this.length -= 1; + return true; + } + } + return false; + } + + void removeWhere(bool test(E element)) { + _filter(this, test, false); + } + + void retainWhere(bool test(E element)) { + _filter(this, test, true); + } + + static void _filter(List source, + bool test(var element), + bool retainMatching) { + List retained = []; + int length = source.length; + for (int i = 0; i < length; i++) { + var element = source[i]; + if (test(element) == retainMatching) { + retained.add(element); + } + if (length != source.length) { + throw new ConcurrentModificationError(source); + } + } + if (retained.length != source.length) { + source.setRange(0, retained.length, retained); + source.length = retained.length; + } + } + + void clear() { this.length = 0; } + + // List interface. + + E removeLast() { + if (length == 0) { + throw new StateError("No elements"); + } + E result = this[length - 1]; + length--; + return result; + } + + void sort([int compare(E a, E b)]) { + if (compare == null) { + var defaultCompare = Comparable.compare; + compare = defaultCompare; + } + Sort.sort(this, compare); + } + + void shuffle([Random random]) { + if (random == null) random = new Random(); + int length = this.length; + while (length > 1) { + int pos = random.nextInt(length); + length -= 1; + var tmp = this[length]; + this[length] = this[pos]; + this[pos] = tmp; + } + } + + Map asMap() { + return new ListMapView(this); + } + + void _rangeCheck(int start, int end) { + if (start < 0 || start > this.length) { + throw new RangeError.range(start, 0, this.length); + } + if (end < start || end > this.length) { + throw new RangeError.range(end, start, this.length); + } + } + + List sublist(int start, [int end]) { + if (end == null) end = this.length; + _rangeCheck(start, end); + int length = end - start; + List result = new List()..length = length; + for (int i = 0; i < length; i++) { + result[i] = this[start + i]; + } + return result; + } + + Iterable getRange(int start, int end) { + _rangeCheck(start, end); + return new SubListIterable(this, start, end); + } + + void removeRange(int start, int end) { + _rangeCheck(start, end); + int length = end - start; + setRange(start, this.length - length, this, end); + this.length -= length; + } + + void fillRange(int start, int end, [E fill]) { + _rangeCheck(start, end); + for (int i = start; i < end; i++) { + this[i] = fill; + } + } + + void setRange(int start, int end, Iterable iterable, [int skipCount = 0]) { + _rangeCheck(start, end); + int length = end - start; + if (length == 0) return; + + if (skipCount < 0) throw new ArgumentError(skipCount); + + List otherList; + int otherStart; + // TODO(floitsch): Make this accept more. + if (iterable is List) { + otherList = iterable; + otherStart = skipCount; + } else { + otherList = iterable.skip(skipCount).toList(growable: false); + otherStart = 0; + } + if (otherStart + length > otherList.length) { + throw new StateError("Not enough elements"); + } + if (otherStart < start) { + // Copy backwards to ensure correct copy if [from] is this. + for (int i = length - 1; i >= 0; i--) { + this[start + i] = otherList[otherStart + i]; + } + } else { + for (int i = 0; i < length; i++) { + this[start + i] = otherList[otherStart + i]; + } + } + } + + void replaceRange(int start, int end, Iterable newContents) { + _rangeCheck(start, end); + if (newContents is! EfficientLength) { + newContents = newContents.toList(); + } + int removeLength = end - start; + int insertLength = newContents.length; + if (removeLength >= insertLength) { + int delta = removeLength - insertLength; + int insertEnd = start + insertLength; + int newLength = this.length - delta; + this.setRange(start, insertEnd, newContents); + if (delta != 0) { + this.setRange(insertEnd, newLength, this, end); + this.length = newLength; + } + } else { + int delta = insertLength - removeLength; + int newLength = this.length + delta; + int insertEnd = start + insertLength; // aka. end + delta. + this.length = newLength; + this.setRange(insertEnd, newLength, this, end); + this.setRange(start, insertEnd, newContents); + } + } + + int indexOf(Object element, [int startIndex = 0]) { + if (startIndex >= this.length) { + return -1; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < this.length; i++) { + if (this[i] == element) { + return i; + } + } + return -1; + } + + /** + * Returns the last index in the list [a] of the given [element], starting + * the search at index [startIndex] to 0. + * Returns -1 if [element] is not found. + */ + int lastIndexOf(Object element, [int startIndex]) { + if (startIndex == null) { + startIndex = this.length - 1; + } else { + if (startIndex < 0) { + return -1; + } + if (startIndex >= this.length) { + startIndex = this.length - 1; + } + } + for (int i = startIndex; i >= 0; i--) { + if (this[i] == element) { + return i; + } + } + return -1; + } + + void insert(int index, E element) { + if (index < 0 || index > length) { + throw new RangeError.range(index, 0, length); + } + if (index == this.length) { + add(element); + return; + } + // We are modifying the length just below the is-check. Without the check + // Array.copy could throw an exception, leaving the list in a bad state + // (with a length that has been increased, but without a new element). + if (index is! int) throw new ArgumentError(index); + this.length++; + setRange(index + 1, this.length, this, index); + this[index] = element; + } + + E removeAt(int index) { + E result = this[index]; + setRange(index, this.length - 1, this, index + 1); + length--; + return result; + } + + void insertAll(int index, Iterable iterable) { + if (index < 0 || index > length) { + throw new RangeError.range(index, 0, length); + } + if (iterable is EfficientLength) { + iterable = iterable.toList(); + } + int insertionLength = iterable.length; + // There might be errors after the length change, in which case the list + // will end up being modified but the operation not complete. Unless we + // always go through a "toList" we can't really avoid that. + this.length += insertionLength; + setRange(index + insertionLength, this.length, this, index); + setAll(index, iterable); + } + + void setAll(int index, Iterable iterable) { + if (iterable is List) { + setRange(index, index + iterable.length, iterable); + } else { + for (E element in iterable) { + this[index++] = element; + } + } + } + + Iterable get reversed => new ReversedListIterable(this); + + String toString() { + if (_toStringVisiting.contains(this)) { + return '[...]'; + } + + var result = new StringBuffer(); + try { + _toStringVisiting.add(this); + result.write('['); + result.writeAll(this, ', '); + result.write(']'); + } finally { + _toStringVisiting.remove(this); + } + + return result.toString(); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/maps.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/maps.dart new file mode 100644 index 0000000..2d605e8 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/maps.dart @@ -0,0 +1,150 @@ +// Copyright (c) 2012, 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. + +part of dart.collection; + +/** + * Helper class which implements complex [Map] operations + * in term of basic ones ([Map.keys], [Map.operator []], + * [Map.operator []=] and [Map.remove].) Not all methods are + * necessary to implement each particular operation. + */ +class Maps { + static bool containsValue(Map map, value) { + for (final v in map.values) { + if (value == v) { + return true; + } + } + return false; + } + + static bool containsKey(Map map, key) { + for (final k in map.keys) { + if (key == k) { + return true; + } + } + return false; + } + + static putIfAbsent(Map map, key, ifAbsent()) { + if (map.containsKey(key)) { + return map[key]; + } + final v = ifAbsent(); + map[key] = v; + return v; + } + + static clear(Map map) { + for (final k in map.keys.toList()) { + map.remove(k); + } + } + + static forEach(Map map, void f(key, value)) { + for (final k in map.keys) { + f(k, map[k]); + } + } + + static Iterable getValues(Map map) { + return map.keys.map((key) => map[key]); + } + + static int length(Map map) => map.keys.length; + + static bool isEmpty(Map map) => map.keys.isEmpty; + + static bool isNotEmpty(Map map) => map.keys.isNotEmpty; + + // A list to identify cyclic maps during toString() calls. + static List _toStringList = new List(); + + /** + * Returns a string representing the specified map. The returned string + * looks like this: [:'{key0: value0, key1: value1, ... keyN: valueN}':]. + * The value returned by its [toString] method is used to represent each + * key or value. + * + * If the map collection contains a reference to itself, either + * directly as a key or value, or indirectly through other collections + * or maps, the contained reference is rendered as [:'{...}':]. This + * prevents the infinite regress that would otherwise occur. So, for example, + * calling this method on a map whose sole entry maps the string key 'me' + * to a reference to the map would return [:'{me: {...}}':]. + * + * A typical implementation of a map's [toString] method will + * simply return the results of this method applied to the collection. + */ + static String mapToString(Map m) { + for (int i = 0; i < _toStringList.length; i++) { + if (identical(_toStringList[i], m)) { return '{...}'; } + } + + var result = new StringBuffer(); + try { + _toStringList.add(m); + result.write('{'); + bool first = true; + m.forEach((k, v) { + if(!first) { + result.write(', '); + } + first = false; + result.write(k); + result.write(': '); + result.write(v); + }); + result.write('}'); + } finally { + assert(identical(_toStringList.last, m)); + _toStringList.removeLast(); + } + + return result.toString(); + } + + static _id(x) => x; + + /** + * Fills a map with key/value pairs computed from [iterable]. + * + * This method is used by Map classes in the named constructor fromIterable. + */ + static void _fillMapWithMappedIterable(Map map, Iterable iterable, + key(element), value(element)) { + if (key == null) key = _id; + if (value == null) value = _id; + + for (var element in iterable) { + map[key(element)] = value(element); + } + } + + /** + * Fills a map by associating the [keys] to [values]. + * + * This method is used by Map classes in the named constructor fromIterables. + */ + static void _fillMapWithIterables(Map map, Iterable keys, + Iterable values) { + Iterator keyIterator = keys.iterator; + Iterator valueIterator = values.iterator; + + bool hasNextKey = keyIterator.moveNext(); + bool hasNextValue = valueIterator.moveNext(); + + while (hasNextKey && hasNextValue) { + map[keyIterator.current] = valueIterator.current; + hasNextKey = keyIterator.moveNext(); + hasNextValue = valueIterator.moveNext(); + } + + if (hasNextKey || hasNextValue) { + throw new ArgumentError("Iterables do not have same length."); + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/queue.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/queue.dart new file mode 100644 index 0000000..5dfe170 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/queue.dart @@ -0,0 +1,719 @@ +// Copyright (c) 2011, 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. + +part of dart.collection; + +/** + * A [Queue] is a collection that can be manipulated at both ends. One + * can iterate over the elements of a queue through [forEach] or with + * an [Iterator]. + * + * It is generally not allowed to modify the queue (add or remove entries) while + * an operation on the queue is being performed, for example during a call to + * [forEach]. + * Modifying the queue while it is being iterated will most likely break the + * iteration. + * This goes both for using the [iterator] directly, or for iterating an + * `Iterable` returned by a method like [map] or [where]. + */ +abstract class Queue implements Iterable, EfficientLength { + + /** + * Creates a queue. + */ + factory Queue() = ListQueue; + + /** + * Creates a queue with the elements of [other]. The order in + * the queue will be the order provided by the iterator of [other]. + */ + factory Queue.from(Iterable other) = ListQueue.from; + + /** + * Removes and returns the first element of this queue. Throws an + * [StateError] exception if this queue is empty. + */ + E removeFirst(); + + /** + * Removes and returns the last element of the queue. Throws an + * [StateError] exception if this queue is empty. + */ + E removeLast(); + + /** + * Adds [value] at the beginning of the queue. + */ + void addFirst(E value); + + /** + * Adds [value] at the end of the queue. + */ + void addLast(E value); + + /** + * Adds [value] at the end of the queue. + */ + void add(E value); + + /** + * Remove a single instance of [value] from the queue. + * + * Returns `true` if a value was removed, or `false` if the queue + * contained no element equal to [value]. + */ + bool remove(Object object); + + /** + * Adds all elements of [iterable] at the end of the queue. The + * length of the queue is extended by the length of [iterable]. + */ + void addAll(Iterable iterable); + + /** + * Removes all elements matched by [test] from the queue. + * + * The `test` function must not throw or modify the queue. + */ + void removeWhere(bool test(E element)); + + /** + * Removes all elements not matched by [test] from the queue. + * + * The `test` function must not throw or modify the queue. + */ + void retainWhere(bool test(E element)); + + /** + * Removes all elements in the queue. The size of the queue becomes zero. + */ + void clear(); +} + + +/** + * An entry in a doubly linked list. It contains a pointer to the next + * entry, the previous entry, and the boxed element. + */ +class DoubleLinkedQueueEntry { + DoubleLinkedQueueEntry _previous; + DoubleLinkedQueueEntry _next; + E _element; + + DoubleLinkedQueueEntry(E e) : _element = e; + + void _link(DoubleLinkedQueueEntry previous, + DoubleLinkedQueueEntry next) { + _next = next; + _previous = previous; + previous._next = this; + next._previous = this; + } + + void append(E e) { + new DoubleLinkedQueueEntry(e)._link(this, _next); + } + + void prepend(E e) { + new DoubleLinkedQueueEntry(e)._link(_previous, this); + } + + E remove() { + _previous._next = _next; + _next._previous = _previous; + _next = null; + _previous = null; + return _element; + } + + DoubleLinkedQueueEntry _asNonSentinelEntry() { + return this; + } + + DoubleLinkedQueueEntry previousEntry() { + return _previous._asNonSentinelEntry(); + } + + DoubleLinkedQueueEntry nextEntry() { + return _next._asNonSentinelEntry(); + } + + E get element { + return _element; + } + + void set element(E e) { + _element = e; + } +} + +/** + * A sentinel in a double linked list is used to manipulate the list + * at both ends. + * A double linked list has exactly one sentinel, + * which is the only entry when the list is constructed. + * Initially, a sentinel has its next and previous entry point to itself. + * A sentinel does not box any user element. + */ +class _DoubleLinkedQueueEntrySentinel extends DoubleLinkedQueueEntry { + _DoubleLinkedQueueEntrySentinel() : super(null) { + _link(this, this); + } + + E remove() { + throw new StateError("Empty queue"); + } + + DoubleLinkedQueueEntry _asNonSentinelEntry() { + return null; + } + + void set element(E e) { + // This setter is unreachable. + // TODO(lrn): Don't inherit the field if we don't use it. + assert(false); + } + + E get element { + throw new StateError("Empty queue"); + } +} + +/** + * A [Queue] implementation based on a double-linked list. + * + * Allows constant time add, remove-at-ends and peek operations. + */ +class DoubleLinkedQueue extends IterableBase implements Queue { + _DoubleLinkedQueueEntrySentinel _sentinel; + int _elementCount = 0; + + DoubleLinkedQueue() { + _sentinel = new _DoubleLinkedQueueEntrySentinel(); + } + + factory DoubleLinkedQueue.from(Iterable other) { + Queue list = new DoubleLinkedQueue(); + for (final e in other) { + list.addLast(e); + } + return list; + } + + int get length => _elementCount; + + void addLast(E value) { + _sentinel.prepend(value); + _elementCount++; + } + + void addFirst(E value) { + _sentinel.append(value); + _elementCount++; + } + + void add(E value) { + _sentinel.prepend(value); + _elementCount++; + } + + void addAll(Iterable iterable) { + for (final E value in iterable) { + _sentinel.prepend(value); + _elementCount++; + } + } + + E removeLast() { + E result = _sentinel._previous.remove(); + _elementCount--; + return result; + } + + E removeFirst() { + E result = _sentinel._next.remove(); + _elementCount--; + return result; + } + + bool remove(Object o) { + DoubleLinkedQueueEntry entry = _sentinel._next; + while (!identical(entry, _sentinel)) { + if (entry.element == o) { + entry.remove(); + _elementCount--; + return true; + } + entry = entry._next; + } + return false; + } + + void _filter(bool test(E element), bool removeMatching) { + DoubleLinkedQueueEntry entry = _sentinel._next; + while (!identical(entry, _sentinel)) { + DoubleLinkedQueueEntry next = entry._next; + if (identical(removeMatching, test(entry.element))) { + entry.remove(); + _elementCount--; + } + entry = next; + } + } + + void removeWhere(bool test(E element)) { + _filter(test, true); + } + + void retainWhere(bool test(E element)) { + _filter(test, false); + } + + E get first { + return _sentinel._next.element; + } + + E get last { + return _sentinel._previous.element; + } + + E get single { + // Note that this also covers the case where the queue is empty. + if (identical(_sentinel._next, _sentinel._previous)) { + return _sentinel._next.element; + } + throw new StateError("More than one element"); + } + + DoubleLinkedQueueEntry lastEntry() { + return _sentinel.previousEntry(); + } + + DoubleLinkedQueueEntry firstEntry() { + return _sentinel.nextEntry(); + } + + bool get isEmpty { + return (identical(_sentinel._next, _sentinel)); + } + + void clear() { + _sentinel._next = _sentinel; + _sentinel._previous = _sentinel; + _elementCount = 0; + } + + void forEachEntry(void f(DoubleLinkedQueueEntry element)) { + DoubleLinkedQueueEntry entry = _sentinel._next; + while (!identical(entry, _sentinel)) { + DoubleLinkedQueueEntry nextEntry = entry._next; + f(entry); + entry = nextEntry; + } + } + + _DoubleLinkedQueueIterator get iterator { + return new _DoubleLinkedQueueIterator(_sentinel); + } + + // TODO(zarah) Remove this, and let it be inherited by IterableBase + String toString() => IterableMixinWorkaround.toStringIterable(this, '{', '}'); +} + +class _DoubleLinkedQueueIterator implements Iterator { + _DoubleLinkedQueueEntrySentinel _sentinel; + DoubleLinkedQueueEntry _nextEntry = null; + E _current; + + _DoubleLinkedQueueIterator(_DoubleLinkedQueueEntrySentinel sentinel) + : _sentinel = sentinel, _nextEntry = sentinel._next; + + bool moveNext() { + // When [_currentEntry] it is set to [:null:] then it is at the end. + if (!identical(_nextEntry, _sentinel)) { + _current = _nextEntry._element; + _nextEntry = _nextEntry._next; + return true; + } + _current = null; + _nextEntry = _sentinel = null; // Still identical. + return false; + } + + E get current => _current; +} + +/** + * List based [Queue]. + * + * Keeps a cyclic buffer of elements, and grows to a larger buffer when + * it fills up. This guarantees constant time peek and remove operations, and + * amortized constant time add operations. + * + * The structure is efficient for any queue or stack usage. + */ +class ListQueue extends IterableBase implements Queue { + static const int _INITIAL_CAPACITY = 8; + List _table; + int _head; + int _tail; + int _modificationCount = 0; + + /** + * Create an empty queue. + * + * If [initialCapacity] is given, prepare the queue for at least that many + * elements. + */ + ListQueue([int initialCapacity]) : _head = 0, _tail = 0 { + if (initialCapacity == null || initialCapacity < _INITIAL_CAPACITY) { + initialCapacity = _INITIAL_CAPACITY; + } else if (!_isPowerOf2(initialCapacity)) { + initialCapacity = _nextPowerOf2(initialCapacity); + } + assert(_isPowerOf2(initialCapacity)); + _table = new List(initialCapacity); + } + + /** + * Create a queue initially containing the elements of [source]. + */ + factory ListQueue.from(Iterable source) { + if (source is List) { + int length = source.length; + ListQueue queue = new ListQueue(length + 1); + assert(queue._table.length > length); + List sourceList = source; + queue._table.setRange(0, length, sourceList, 0); + queue._tail = length; + return queue; + } else { + return new ListQueue()..addAll(source); + } + } + + // Iterable interface. + + Iterator get iterator => new _ListQueueIterator(this); + + void forEach(void action (E element)) { + int modificationCount = _modificationCount; + for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) { + action(_table[i]); + _checkModification(modificationCount); + } + } + + bool get isEmpty => _head == _tail; + + int get length => (_tail - _head) & (_table.length - 1); + + E get first { + if (_head == _tail) throw new StateError("No elements"); + return _table[_head]; + } + + E get last { + if (_head == _tail) throw new StateError("No elements"); + return _table[(_tail - 1) & (_table.length - 1)]; + } + + E get single { + if (_head == _tail) throw new StateError("No elements"); + if (length > 1) throw new StateError("Too many elements"); + return _table[_head]; + } + + E elementAt(int index) { + if (index < 0 || index > length) { + throw new RangeError.range(index, 0, length); + } + return _table[(_head + index) & (_table.length - 1)]; + } + + List toList({ bool growable: true }) { + List list; + if (growable) { + list = new List()..length = length; + } else { + list = new List(length); + } + _writeToList(list); + return list; + } + + // Collection interface. + + void add(E element) { + _add(element); + } + + void addAll(Iterable elements) { + if (elements is List) { + List list = elements; + int addCount = list.length; + int length = this.length; + if (length + addCount >= _table.length) { + _preGrow(length + addCount); + // After preGrow, all elements are at the start of the list. + _table.setRange(length, length + addCount, list, 0); + _tail += addCount; + } else { + // Adding addCount elements won't reach _head. + int endSpace = _table.length - _tail; + if (addCount < endSpace) { + _table.setRange(_tail, _tail + addCount, list, 0); + _tail += addCount; + } else { + int preSpace = addCount - endSpace; + _table.setRange(_tail, _tail + endSpace, list, 0); + _table.setRange(0, preSpace, list, endSpace); + _tail = preSpace; + } + } + _modificationCount++; + } else { + for (E element in elements) _add(element); + } + } + + bool remove(Object object) { + for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) { + E element = _table[i]; + if (element == object) { + _remove(i); + _modificationCount++; + return true; + } + } + return false; + } + + void _filterWhere(bool test(E element), bool removeMatching) { + int index = _head; + int modificationCount = _modificationCount; + int i = _head; + while (i != _tail) { + E element = _table[i]; + bool remove = identical(removeMatching, test(element)); + _checkModification(modificationCount); + if (remove) { + i = _remove(i); + modificationCount = ++_modificationCount; + } else { + i = (i + 1) & (_table.length - 1); + } + } + } + + /** + * Remove all elements matched by [test]. + * + * This method is inefficient since it works by repeatedly removing single + * elements, each of which can take linear time. + */ + void removeWhere(bool test(E element)) { + _filterWhere(test, true); + } + + /** + * Remove all elements not matched by [test]. + * + * This method is inefficient since it works by repeatedly removing single + * elements, each of which can take linear time. + */ + void retainWhere(bool test(E element)) { + _filterWhere(test, false); + } + + void clear() { + if (_head != _tail) { + for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) { + _table[i] = null; + } + _head = _tail = 0; + _modificationCount++; + } + } + + // TODO(zarah) Remove this, and let it be inherited by IterableBase + String toString() => IterableMixinWorkaround.toStringIterable(this, '{', '}'); + + // Queue interface. + + void addLast(E element) { _add(element); } + + void addFirst(E element) { + _head = (_head - 1) & (_table.length - 1); + _table[_head] = element; + if (_head == _tail) _grow(); + _modificationCount++; + } + + E removeFirst() { + if (_head == _tail) throw new StateError("No elements"); + _modificationCount++; + E result = _table[_head]; + _table[_head] = null; + _head = (_head + 1) & (_table.length - 1); + return result; + } + + E removeLast() { + if (_head == _tail) throw new StateError("No elements"); + _modificationCount++; + _tail = (_tail - 1) & (_table.length - 1); + E result = _table[_tail]; + _table[_tail] = null; + return result; + } + + // Internal helper functions. + + /** + * Whether [number] is a power of two. + * + * Only works for positive numbers. + */ + static bool _isPowerOf2(int number) => (number & (number - 1)) == 0; + + /** + * Rounds [number] up to the nearest power of 2. + * + * If [number] is a power of 2 already, it is returned. + * + * Only works for positive numbers. + */ + static int _nextPowerOf2(int number) { + assert(number > 0); + number = (number << 2) - 1; + for(;;) { + int nextNumber = number & (number - 1); + if (nextNumber == 0) return number; + number = nextNumber; + } + } + + /** Check if the queue has been modified during iteration. */ + void _checkModification(int expectedModificationCount) { + if (expectedModificationCount != _modificationCount) { + throw new ConcurrentModificationError(this); + } + } + + /** Adds element at end of queue. Used by both [add] and [addAll]. */ + void _add(E element) { + _table[_tail] = element; + _tail = (_tail + 1) & (_table.length - 1); + if (_head == _tail) _grow(); + _modificationCount++; + } + + /** + * Removes the element at [offset] into [_table]. + * + * Removal is performed by linerarly moving elements either before or after + * [offset] by one position. + * + * Returns the new offset of the following element. This may be the same + * offset or the following offset depending on how elements are moved + * to fill the hole. + */ + int _remove(int offset) { + int mask = _table.length - 1; + int startDistance = (offset - _head) & mask; + int endDistance = (_tail - offset) & mask; + if (startDistance < endDistance) { + // Closest to start. + int i = offset; + while (i != _head) { + int prevOffset = (i - 1) & mask; + _table[i] = _table[prevOffset]; + i = prevOffset; + } + _table[_head] = null; + _head = (_head + 1) & mask; + return (offset + 1) & mask; + } else { + _tail = (_tail - 1) & mask; + int i = offset; + while (i != _tail) { + int nextOffset = (i + 1) & mask; + _table[i] = _table[nextOffset]; + i = nextOffset; + } + _table[_tail] = null; + return offset; + } + } + + /** + * Grow the table when full. + */ + void _grow() { + List newTable = new List(_table.length * 2); + int split = _table.length - _head; + newTable.setRange(0, split, _table, _head); + newTable.setRange(split, split + _head, _table, 0); + _head = 0; + _tail = _table.length; + _table = newTable; + } + + int _writeToList(List target) { + assert(target.length >= length); + if (_head <= _tail) { + int length = _tail - _head; + target.setRange(0, length, _table, _head); + return length; + } else { + int firstPartSize = _table.length - _head; + target.setRange(0, firstPartSize, _table, _head); + target.setRange(firstPartSize, firstPartSize + _tail, _table, 0); + return _tail + firstPartSize; + } + } + + /** Grows the table even if it is not full. */ + void _preGrow(int newElementCount) { + assert(newElementCount >= length); + int newCapacity = _nextPowerOf2(newElementCount); + List newTable = new List(newCapacity); + _tail = _writeToList(newTable); + _table = newTable; + _head = 0; + } +} + +/** + * Iterator for a [ListQueue]. + * + * Considers any add or remove operation a concurrent modification. + */ +class _ListQueueIterator implements Iterator { + final ListQueue _queue; + final int _end; + final int _modificationCount; + int _position; + E _current; + + _ListQueueIterator(ListQueue queue) + : _queue = queue, + _end = queue._tail, + _modificationCount = queue._modificationCount, + _position = queue._head; + + E get current => _current; + + bool moveNext() { + _queue._checkModification(_modificationCount); + if (_position == _end) { + _current = null; + return false; + } + _current = _queue._table[_position]; + _position = (_position + 1) & (_queue._table.length - 1); + return true; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/splay_tree.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/splay_tree.dart new file mode 100644 index 0000000..4674f8c --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/collection/splay_tree.dart @@ -0,0 +1,827 @@ +// Copyright (c) 2012, 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. + +part of dart.collection; + +typedef bool _Predicate(T value); + +/** + * A node in a splay tree. It holds the sorting key and the left + * and right children in the tree. + */ +class _SplayTreeNode { + final K key; + _SplayTreeNode left; + _SplayTreeNode right; + + _SplayTreeNode(K this.key); +} + +/** + * A node in a splay tree based map. + * + * A [_SplayTreeNode] that also contains a value + */ +class _SplayTreeMapNode extends _SplayTreeNode { + V value; + _SplayTreeMapNode(K key, V this.value) : super(key); +} + +/** + * A splay tree is a self-balancing binary search tree. + * + * It has the additional property that recently accessed elements + * are quick to access again. + * It performs basic operations such as insertion, look-up and + * removal, in O(log(n)) amortized time. + */ +abstract class _SplayTree { + // The root node of the splay tree. It will contain either the last + // element inserted or the last element looked up. + _SplayTreeNode _root; + + // The dummy node used when performing a splay on the tree. Reusing it + // avoids allocating a node each time a splay is performed. + _SplayTreeNode _dummy = new _SplayTreeNode(null); + + // Number of elements in the splay tree. + int _count = 0; + + /** + * Counter incremented whenever the keys in the map changes. + * + * Used to detect concurrent modifications. + */ + int _modificationCount = 0; + + /** + * Counter incremented whenever the tree structure changes. + * + * Used to detect that an in-place traversal cannot use + * cached information that relies on the tree structure. + */ + int _splayCount = 0; + + /** Comparison used to compare keys. */ + int _compare(K key1, K key2); + + /** + * Perform the splay operation for the given key. Moves the node with + * the given key to the top of the tree. If no node has the given + * key, the last node on the search path is moved to the top of the + * tree. This is the simplified top-down splaying algorithm from: + * "Self-adjusting Binary Search Trees" by Sleator and Tarjan. + * + * Returns the result of comparing the new root of the tree to [key]. + * Returns -1 if the table is empty. + */ + int _splay(K key) { + if (_root == null) return -1; + + // The right child of the dummy node will hold + // the L tree of the algorithm. The left child of the dummy node + // will hold the R tree of the algorithm. Using a dummy node, left + // and right will always be nodes and we avoid special cases. + _SplayTreeNode left = _dummy; + _SplayTreeNode right = _dummy; + _SplayTreeNode current = _root; + int comp; + while (true) { + comp = _compare(current.key, key); + if (comp > 0) { + if (current.left == null) break; + comp = _compare(current.left.key, key); + if (comp > 0) { + // Rotate right. + _SplayTreeNode tmp = current.left; + current.left = tmp.right; + tmp.right = current; + current = tmp; + if (current.left == null) break; + } + // Link right. + right.left = current; + right = current; + current = current.left; + } else if (comp < 0) { + if (current.right == null) break; + comp = _compare(current.right.key, key); + if (comp < 0) { + // Rotate left. + _SplayTreeNode tmp = current.right; + current.right = tmp.left; + tmp.left = current; + current = tmp; + if (current.right == null) break; + } + // Link left. + left.right = current; + left = current; + current = current.right; + } else { + break; + } + } + // Assemble. + left.right = current.left; + right.left = current.right; + current.left = _dummy.right; + current.right = _dummy.left; + _root = current; + + _dummy.right = null; + _dummy.left = null; + _splayCount++; + return comp; + } + + // Emulates splaying with a key that is smaller than any in the subtree + // anchored at [node]. + // and that node is returned. It should replace the reference to [node] + // in any parent tree or root pointer. + _SplayTreeNode _splayMin(_SplayTreeNode node) { + _SplayTreeNode current = node; + while (current.left != null) { + _SplayTreeNode left = current.left; + current.left = left.right; + left.right = current; + current = left; + } + return current; + } + + // Emulates splaying with a key that is greater than any in the subtree + // anchored at [node]. + // After this, the largest element in the tree is the root of the subtree, + // and that node is returned. It should replace the reference to [node] + // in any parent tree or root pointer. + _SplayTreeNode _splayMax(_SplayTreeNode node) { + _SplayTreeNode current = node; + while (current.right != null) { + _SplayTreeNode right = current.right; + current.right = right.left; + right.left = current; + current = right; + } + return current; + } + + _SplayTreeNode _remove(K key) { + if (_root == null) return null; + int comp = _splay(key); + if (comp != 0) return null; + _SplayTreeNode result = _root; + _count--; + // assert(_count >= 0); + if (_root.left == null) { + _root = _root.right; + } else { + _SplayTreeNode right = _root.right; + // Splay to make sure that the new root has an empty right child. + _root = _splayMax(_root.left); + // Insert the original right child as the right child of the new + // root. + _root.right = right; + } + _modificationCount++; + return result; + } + + /** + * Adds a new root node with the given [key] or [value]. + * + * The [comp] value is the result of comparing the existing root's key + * with key. + */ + void _addNewRoot(_SplayTreeNode node, int comp) { + _count++; + _modificationCount++; + if (_root == null) { + _root = node; + return; + } + // assert(_count >= 0); + if (comp < 0) { + node.left = _root; + node.right = _root.right; + _root.right = null; + } else { + node.right = _root; + node.left = _root.left; + _root.left = null; + } + _root = node; + } + + _SplayTreeNode get _first { + if (_root == null) return null; + _root = _splayMin(_root); + return _root; + } + + _SplayTreeNode get _last { + if (_root == null) return null; + _root = _splayMax(_root); + return _root; + } + + void _clear() { + _root = null; + _count = 0; + _modificationCount++; + } +} + +class _TypeTest { + bool test(v) => v is T; +} + +/** + * A [Map] of objects that can be ordered relative to each other. + * + * The map is based on a self-balancing binary tree. It allows most operations + * in amortized logarithmic time. + * + * Keys of the map are compared using the `compare` function passed in + * the constructor. If that is omitted, the objects are assumed to be + * [Comparable], and are compared using their [Comparable.compareTo] + * method. Non-comparable objects (including `null`) will not work as keys + * in that case. + * + * To allow calling [operator[]], [remove] or [containsKey] with objects + * that are not supported by the `compare` function, an extra `isValidKey` + * predicate function can be supplied. This function is tested before + * using the `compare` function on an argument value that may not be a [K] + * value. If omitted, the `isValidKey` function defaults to testing if the + * value is a [K]. + */ +class SplayTreeMap extends _SplayTree implements Map { + Comparator _comparator; + _Predicate _validKey; + + SplayTreeMap([int compare(K key1, K key2), bool isValidKey(potentialKey)]) + : _comparator = (compare == null) ? Comparable.compare : compare, + _validKey = (isValidKey != null) ? isValidKey : ((v) => v is K); + + /** + * Creates a [SplayTreeMap] that contains all key value pairs of [other]. + */ + factory SplayTreeMap.from(Map other, + [ int compare(K key1, K key2), + bool isValidKey(potentialKey)]) => + new SplayTreeMap(compare, isValidKey)..addAll(other); + + /** + * Creates a [SplayTreeMap] where the keys and values are computed from the + * [iterable]. + * + * For each element of the [iterable] this constructor computes a key/value + * pair, by applying [key] and [value] respectively. + * + * The keys of the key/value pairs do not need to be unique. The last + * occurrence of a key will simply overwrite any previous value. + * + * If no values are specified for [key] and [value] the default is the + * identity function. + */ + factory SplayTreeMap.fromIterable(Iterable iterable, + {K key(element), V value(element), int compare(K key1, K key2), + bool isValidKey(potentialKey) }) { + SplayTreeMap map = new SplayTreeMap(compare, isValidKey); + Maps._fillMapWithMappedIterable(map, iterable, key, value); + return map; + } + + /** + * Creates a [SplayTreeMap] associating the given [keys] to [values]. + * + * This constructor iterates over [keys] and [values] and maps each element of + * [keys] to the corresponding element of [values]. + * + * If [keys] contains the same object multiple times, the last occurrence + * overwrites the previous value. + * + * It is an error if the two [Iterable]s don't have the same length. + */ + factory SplayTreeMap.fromIterables(Iterable keys, Iterable values, + [int compare(K key1, K key2), bool isValidKey(potentialKey)]) { + SplayTreeMap map = new SplayTreeMap(compare, isValidKey); + Maps._fillMapWithIterables(map, keys, values); + return map; + } + + int _compare(K key1, K key2) => _comparator(key1, key2); + + SplayTreeMap._internal(); + + V operator [](Object key) { + if (key == null) throw new ArgumentError(key); + if (!_validKey(key)) return null; + if (_root != null) { + int comp = _splay(key); + if (comp == 0) { + _SplayTreeMapNode mapRoot = _root; + return mapRoot.value; + } + } + return null; + } + + V remove(Object key) { + if (!_validKey(key)) return null; + _SplayTreeMapNode mapRoot = _remove(key); + if (mapRoot != null) return mapRoot.value; + return null; + } + + void operator []=(K key, V value) { + if (key == null) throw new ArgumentError(key); + // Splay on the key to move the last node on the search path for + // the key to the root of the tree. + int comp = _splay(key); + if (comp == 0) { + _SplayTreeMapNode mapRoot = _root; + mapRoot.value = value; + return; + } + _addNewRoot(new _SplayTreeMapNode(key, value), comp); + } + + + V putIfAbsent(K key, V ifAbsent()) { + if (key == null) throw new ArgumentError(key); + int comp = _splay(key); + if (comp == 0) { + _SplayTreeMapNode mapRoot = _root; + return mapRoot.value; + } + int modificationCount = _modificationCount; + int splayCount = _splayCount; + V value = ifAbsent(); + if (modificationCount != _modificationCount) { + throw new ConcurrentModificationError(this); + } + if (splayCount != _splayCount) { + comp = _splay(key); + // Key is still not there, otherwise _modificationCount would be changed. + assert(comp != 0); + } + _addNewRoot(new _SplayTreeMapNode(key, value), comp); + return value; + } + + void addAll(Map other) { + other.forEach((K key, V value) { this[key] = value; }); + } + + bool get isEmpty { + return (_root == null); + } + + bool get isNotEmpty => !isEmpty; + + void forEach(void f(K key, V value)) { + Iterator<_SplayTreeNode> nodes = + new _SplayTreeNodeIterator(this); + while (nodes.moveNext()) { + _SplayTreeMapNode node = nodes.current; + f(node.key, node.value); + } + } + + int get length { + return _count; + } + + void clear() { + _clear(); + } + + bool containsKey(Object key) { + return _validKey(key) && _splay(key) == 0; + } + + bool containsValue(Object value) { + bool found = false; + int initialSplayCount = _splayCount; + bool visit(_SplayTreeMapNode node) { + while (node != null) { + if (node.value == value) return true; + if (initialSplayCount != _splayCount) { + throw new ConcurrentModificationError(this); + } + if (node.right != null && visit(node.right)) return true; + node = node.left; + } + return false; + } + return visit(_root); + } + + Iterable get keys => new _SplayTreeKeyIterable(this); + + Iterable get values => new _SplayTreeValueIterable(this); + + String toString() { + return Maps.mapToString(this); + } + + /** + * Get the first key in the map. Returns [:null:] if the map is empty. + */ + K firstKey() { + if (_root == null) return null; + return _first.key; + } + + /** + * Get the last key in the map. Returns [:null:] if the map is empty. + */ + K lastKey() { + if (_root == null) return null; + return _last.key; + } + + /** + * Get the last key in the map that is strictly smaller than [key]. Returns + * [:null:] if no key was not found. + */ + K lastKeyBefore(K key) { + if (key == null) throw new ArgumentError(key); + if (_root == null) return null; + int comp = _splay(key); + if (comp < 0) return _root.key; + _SplayTreeNode node = _root.left; + if (node == null) return null; + while (node.right != null) { + node = node.right; + } + return node.key; + } + + /** + * Get the first key in the map that is strictly larger than [key]. Returns + * [:null:] if no key was not found. + */ + K firstKeyAfter(K key) { + if (key == null) throw new ArgumentError(key); + if (_root == null) return null; + int comp = _splay(key); + if (comp > 0) return _root.key; + _SplayTreeNode node = _root.right; + if (node == null) return null; + while (node.left != null) { + node = node.left; + } + return node.key; + } +} + +abstract class _SplayTreeIterator implements Iterator { + final _SplayTree _tree; + /** + * Worklist of nodes to visit. + * + * These nodes have been passed over on the way down in a + * depth-first left-to-right traversal. Visiting each node, + * and their right subtrees will visit the remainder of + * the nodes of a full traversal. + * + * Only valid as long as the original tree isn't reordered. + */ + final List<_SplayTreeNode> _workList = <_SplayTreeNode>[]; + + /** + * Original modification counter of [_tree]. + * + * Incremented on [_tree] when a key is added or removed. + * If it changes, iteration is aborted. + * + * Not final because some iterators may modify the tree knowingly, + * and they update the modification count in that case. + */ + int _modificationCount; + + /** + * Count of splay operations on [_tree] when [_workList] was built. + * + * If the splay count on [_tree] increases, [_workList] becomes invalid. + */ + int _splayCount; + + /** Current node. */ + _SplayTreeNode _currentNode; + + _SplayTreeIterator(_SplayTree tree) + : _tree = tree, + _modificationCount = tree._modificationCount, + _splayCount = tree._splayCount { + _findLeftMostDescendent(tree._root); + } + + _SplayTreeIterator.startAt(_SplayTree tree, var startKey) + : _tree = tree, + _modificationCount = tree._modificationCount { + int compare = tree._splay(startKey); + _splayCount = tree._splayCount; + if (compare < 0) { + // Don't include the root, start at the next element after the root. + _findLeftMostDescendent(tree._root.right); + } else { + _workList.add(tree._root); + } + } + + T get current { + if (_currentNode == null) return null; + return _getValue(_currentNode); + } + + void _findLeftMostDescendent(_SplayTreeNode node) { + while (node != null) { + _workList.add(node); + node = node.left; + } + } + + /** + * Called when the tree structure of the tree has changed. + * + * This can be caused by a splay operation. + * If the key-set changes, iteration is aborted before getting + * here, so we know that the keys are the same as before, it's + * only the tree that has been reordered. + */ + void _rebuildWorkList(_SplayTreeNode currentNode) { + assert(!_workList.isEmpty); + _workList.clear(); + if (currentNode == null) { + _findLeftMostDescendent(_tree._root); + } else { + _tree._splay(currentNode.key); + _findLeftMostDescendent(_tree._root.right); + assert(!_workList.isEmpty); + } + } + + bool moveNext() { + if (_modificationCount != _tree._modificationCount) { + throw new ConcurrentModificationError(_tree); + } + // Picks the next element in the worklist as current. + // Updates the worklist with the left-most path of the current node's + // right-hand child. + // If the worklist is no longer valid (after a splay), it is rebuild + // from scratch. + if (_workList.isEmpty) { + _currentNode = null; + return false; + } + if (_tree._splayCount != _splayCount && _currentNode != null) { + _rebuildWorkList(_currentNode); + } + _currentNode = _workList.removeLast(); + _findLeftMostDescendent(_currentNode.right); + return true; + } + + T _getValue(_SplayTreeNode node); +} + +class _SplayTreeKeyIterable extends IterableBase + implements EfficientLength { + _SplayTree _tree; + _SplayTreeKeyIterable(this._tree); + int get length => _tree._count; + bool get isEmpty => _tree._count == 0; + Iterator get iterator => new _SplayTreeKeyIterator(_tree); +} + +class _SplayTreeValueIterable extends IterableBase + implements EfficientLength { + SplayTreeMap _map; + _SplayTreeValueIterable(this._map); + int get length => _map._count; + bool get isEmpty => _map._count == 0; + Iterator get iterator => new _SplayTreeValueIterator(_map); +} + +class _SplayTreeKeyIterator extends _SplayTreeIterator { + _SplayTreeKeyIterator(_SplayTree map): super(map); + K _getValue(_SplayTreeNode node) => node.key; +} + +class _SplayTreeValueIterator extends _SplayTreeIterator { + _SplayTreeValueIterator(SplayTreeMap map): super(map); + V _getValue(_SplayTreeMapNode node) => node.value; +} + +class _SplayTreeNodeIterator + extends _SplayTreeIterator<_SplayTreeNode> { + _SplayTreeNodeIterator(_SplayTree tree): super(tree); + _SplayTreeNodeIterator.startAt(_SplayTree tree, var startKey) + : super.startAt(tree, startKey); + _SplayTreeNode _getValue(_SplayTreeNode node) => node; +} + + +/** + * A [Set] of objects that can be ordered relative to each other. + * + * The set is based on a self-balancing binary tree. It allows most operations + * in amortized logarithmic time. + * + * Elements of the set are compared using the `compare` function passed in + * the constructor. If that is omitted, the objects are assumed to be + * [Comparable], and are compared using their [Comparable.compareTo] + * method. Non-comparable objects (including `null`) will not work as an element + * in that case. + */ +class SplayTreeSet extends _SplayTree with IterableMixin + implements Set { + Comparator _comparator; + _Predicate _validKey; + + /** + * Create a new [SplayTreeSet] with the given compare function. + * + * If the [compare] function is omitted, it defaults to [Comparable.compare], + * and the elements must be comparable. + * + * A provided `compare` function may not work on all objects. It may not even + * work on all `E` instances. + * + * For operations that add elements to the set, the user is supposed to not + * pass in objects that doesn't work with the compare function. + * + * The methods [contains], [remove], [lookup], [removeAll] or [retainAll] + * are typed to accept any object(s), and the [isValidKey] test can used to + * filter those objects before handing them to the `compare` function. + * + * If [isValidKey] is provided, only values satisfying `isValidKey(other)` + * are compared using the `compare` method in the methods mentioned above. + * If the `isValidKey` function returns false for an object, it is assumed to + * not be in the set. + * + * If omitted, the `isValidKey` function defaults to checking against the + * type parameter: `other is E`. + */ + SplayTreeSet([int compare(E key1, E key2), bool isValidKey(potentialKey)]) + : _comparator = (compare == null) ? Comparable.compare : compare, + _validKey = (isValidKey != null) ? isValidKey : ((v) => v is E); + + int _compare(E e1, E e2) => _comparator(e1, e2); + + // From Iterable. + + Iterator get iterator => new _SplayTreeKeyIterator(this); + + int get length => _count; + bool get isEmpty => _root == null; + bool get isNotEmpty => _root != null; + + E get first { + if (_count == 0) throw new StateError("no such element"); + return _first.key; + } + + E get last { + if (_count == 0) throw new StateError("no such element"); + return _last.key; + } + + E get single { + if (_count == 0) throw new StateError("no such element"); + if (_count > 1) throw new StateError("too many elements"); + return _root.key; + } + + // From Set. + bool contains(Object object) { + return _validKey(object) && _splay(object) == 0; + } + + bool add(E element) { + int compare = _splay(element); + if (compare == 0) return false; + _addNewRoot(new _SplayTreeNode(element), compare); + return true; + } + + bool remove(Object object) { + if (!_validKey(object)) return false; + return _remove(object) != null; + } + + void addAll(Iterable elements) { + for (E element in elements) { + int compare = _splay(element); + if (compare != 0) { + _addNewRoot(new _SplayTreeNode(element), compare); + } + } + } + + void removeAll(Iterable elements) { + for (Object element in elements) { + if (_validKey(element)) _remove(element); + } + } + + /** + * Removes all elements not in [elements]. + */ + void retainAll(Iterable elements) { + // Build a set with the same sense of equality as this set. + SplayTreeSet retainSet = new SplayTreeSet(_comparator, _validKey); + int modificationCount = _modificationCount; + for (Object object in elements) { + if (modificationCount != _modificationCount) { + // The iterator should not have side effects. + throw new ConcurrentModificationError(this); + } + // Equivalent to this.contains(object). + if (_validKey(object) && _splay(object) == 0) retainSet.add(_root.key); + } + // Take over the elements from the retained set, if it differs. + if (retainSet._count != _count) { + _root = retainSet._root; + _count = retainSet._count; + _modificationCount++; + } + } + + void _filterWhere(bool test(E element), bool removeMatching) { + _SplayTreeNodeIterator it = new _SplayTreeNodeIterator(this); + while (it.moveNext()) { + _SplayTreeNode node = it.current; + int modificationCount = _modificationCount; + bool matches = test(node.key); + if (modificationCount != _modificationCount) { + throw new ConcurrentModificationError(this); + } + if (matches == removeMatching) { + _remove(node.key); + it = new _SplayTreeNodeIterator.startAt(this, node.key); + } + } + } + + void removeWhere(bool test(E element)) { + _filterWhere(test, true); + } + + void retainWhere(bool test(E element)) { + _filterWhere(test, false); + } + + E lookup(Object object) { + if (!_validKey(object)) return null; + int comp = _splay(object); + if (comp != 0) return null; + return _root.key; + } + + Set intersection(Set other) { + Set result = new SplayTreeSet(_compare, _validKey); + for (E element in this) { + if (other.contains(element)) result.add(element); + } + return result; + } + + Set difference(Set other) { + Set result = new SplayTreeSet(_compare, _validKey); + for (E element in this) { + if (!other.contains(element)) result.add(element); + } + return result; + } + + Set union(Set other) { + return _clone()..addAll(other); + } + + SplayTreeSet _clone() { + var set = new SplayTreeSet(_compare, _validKey); + set._count = _count; + set._root = _cloneNode(_root); + return set; + } + + _SplayTreeNode _cloneNode(_SplayTreeNode node) { + if (node == null) return null; + return new _SplayTreeNode(node.key)..left = _cloneNode(node.left) + ..right = _cloneNode(node.right); + } + + bool containsAll(Iterable other) { + for (var element in other) { + if (!this.contains(element)) return false; + } + return true; + } + + void clear() { _clear(); } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/ascii.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/ascii.dart new file mode 100644 index 0000000..4167a8d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/ascii.dart @@ -0,0 +1,318 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +/** + * An instance of the default implementation of the [AsciiCodec]. + * + * This instance provides a convenient access to the most common ASCII + * use cases. + * + * Examples: + * + * var encoded = ASCII.encode("This is ASCII!"); + * var decoded = ASCII.decode([0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, + * 0x20, 0x41, 0x53, 0x43, 0x49, 0x49, 0x21]); + */ +const AsciiCodec ASCII = const AsciiCodec(); + +const int _ASCII_MASK = 0x7F; + +/** + * An [AsciiCodec] allows encoding strings as ASCII bytes + * and decoding ASCII bytes to strings. + */ +class AsciiCodec extends Encoding { + final bool _allowInvalid; + /** + * Instantiates a new [AsciiCodec]. + * + * If [allowInvalid] is true, the [decode] method and the converter + * returned by [decoder] will default to allowing invalid values. + * If allowing invalid values, the values will be decoded into the Unicode + * Replacement character (U+FFFD). If not, an exception will be thrown. + * Calls to the [decode] method can choose to override this default. + * + * Encoders will not accept invalid (non Latin-1) characters. + */ + const AsciiCodec({bool allowInvalid: false}) : _allowInvalid = allowInvalid; + + String get name => "us-ascii"; + + /** + * Decodes the ASCII [bytes] (a list of unsigned 7-bit integers) to the + * corresponding string. + * + * If [bytes] contains values that are not in the range 0 .. 127, the decoder + * will eventually throw a [FormatException]. + * + * If [allowInvalid] is not provided, it defaults to the value used to create + * this [AsciiCodec]. + */ + String decode(List bytes, { bool allowInvalid }) { + if (allowInvalid == null) allowInvalid = _allowInvalid; + if (allowInvalid) { + return const AsciiDecoder(allowInvalid: true).convert(bytes); + } else { + return const AsciiDecoder(allowInvalid: false).convert(bytes); + } + } + + Converter> get encoder => const AsciiEncoder(); + + Converter, String> get decoder => + _allowInvalid ? const AsciiDecoder(allowInvalid: true) + : const AsciiDecoder(allowInvalid: false); +} + +// Superclass for [AsciiEncoder] and [Latin1Encoder]. +// Generalizes common operations that only differ by a mask; +class _UnicodeSubsetEncoder extends Converter> { + final int _subsetMask; + + const _UnicodeSubsetEncoder(this._subsetMask); + + List convert(String string) { + // TODO(11971): Use Uint8List when possible. + List result = new List(string.length); + for (int i = 0; i < string.length; i++) { + var codeUnit = string.codeUnitAt(i); + if ((codeUnit & ~_subsetMask) != 0) { + throw new ArgumentError("String contains invalid characters."); + } + result[i] = codeUnit; + } + return result; + } + + /** + * Starts a chunked conversion. + * + * The converter works more efficiently if the given [sink] is a + * [ByteConversionSink]. + */ + StringConversionSink startChunkedConversion(Sink> sink) { + if (sink is! ByteConversionSink) { + sink = new ByteConversionSink.from(sink); + } + return new _UnicodeSubsetEncoderSink(_subsetMask, sink); + } + + // Override the base-class' bind, to provide a better type. + Stream> bind(Stream stream) => super.bind(stream); +} + +/** + * This class converts strings of only ASCII characters to bytes. + */ +class AsciiEncoder extends _UnicodeSubsetEncoder { + const AsciiEncoder() : super(_ASCII_MASK); +} + +/** + * This class encodes chunked strings to bytes (unsigned 8-bit + * integers). + */ +class _UnicodeSubsetEncoderSink extends StringConversionSinkBase { + final ByteConversionSink _sink; + final int _subsetMask; + + _UnicodeSubsetEncoderSink(this._subsetMask, this._sink); + + void close() { + _sink.close(); + } + + void addSlice(String source, int start, int end, bool isLast) { + if (start < 0 || start > source.length) { + throw new RangeError.range(start, 0, source.length); + } + if (end < start || end > source.length) { + throw new RangeError.range(end, start, source.length); + } + for (int i = start; i < end; i++) { + int codeUnit = source.codeUnitAt(i); + if ((codeUnit & ~_subsetMask) != 0) { + throw new ArgumentError( + "Source contains invalid character with code point: $codeUnit."); + } + } + _sink.add(source.codeUnits.sublist(start, end)); + if (isLast) { + close(); + } + } +} + +/** + * This class converts Latin-1 bytes (lists of unsigned 8-bit integers) + * to a string. + */ +abstract class _UnicodeSubsetDecoder extends Converter, String> { + final bool _allowInvalid; + final int _subsetMask; + + /** + * Instantiates a new decoder. + * + * The [_allowInvalid] argument defines how [convert] deals + * with invalid bytes. + * + * The [_subsetMask] argument is a bit mask used to define the subset + * of Unicode being decoded. Use [_LATIN1_MASK] for Latin-1 (8-bit) or + * [_ASCII_MASK] for ASCII (7-bit). + * + * If [_allowInvalid] is `true`, [convert] replaces invalid bytes with the + * Unicode Replacement character `U+FFFD` (�). + * Otherwise it throws a [FormatException]. + */ + const _UnicodeSubsetDecoder(this._allowInvalid, this._subsetMask); + + /** + * Converts the [bytes] (a list of unsigned 7- or 8-bit integers) to the + * corresponding string. + */ + String convert(List bytes) { + for (int i = 0; i < bytes.length; i++) { + int byte = bytes[i]; + if ((byte & ~_subsetMask) != 0) { + if (!_allowInvalid) { + throw new FormatException("Invalid value in input: $byte"); + } + return _convertInvalid(bytes); + } + } + return new String.fromCharCodes(bytes); + } + + String _convertInvalid(List bytes) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < bytes.length; i++) { + int value = bytes[i]; + if ((value & ~_subsetMask) != 0) value = 0xFFFD; + buffer.writeCharCode(value); + } + return buffer.toString(); + } + + /** + * Starts a chunked conversion. + * + * The converter works more efficiently if the given [sink] is a + * [StringConversionSink]. + */ + ByteConversionSink startChunkedConversion(Sink sink) { + StringConversionSink stringSink; + if (sink is StringConversionSink) { + stringSink = sink; + } else { + stringSink = new StringConversionSink.from(sink); + } + // TODO(lrn): Use stringSink.asUtf16Sink() if it becomes available. + return new _Latin1DecoderSink(_allowInvalid, stringSink); + } + + // Override the base-class's bind, to provide a better type. + Stream bind(Stream> stream) => super.bind(stream); +} + +class AsciiDecoder extends _UnicodeSubsetDecoder { + const AsciiDecoder({bool allowInvalid: false}) + : super(allowInvalid, _ASCII_MASK); + + /** + * Starts a chunked conversion. + * + * The converter works more efficiently if the given [sink] is a + * [StringConversionSink]. + */ + ByteConversionSink startChunkedConversion(Sink sink) { + StringConversionSink stringSink; + if (sink is StringConversionSink) { + stringSink = sink; + } else { + stringSink = new StringConversionSink.from(sink); + } + // TODO(lrn): Use asUtf16Sink when it becomes available. It + // works just as well, is likely to have less decoding overhead, + // and make adding U+FFFD easier. + // At that time, merge this with _Latin1DecoderSink; + if (_allowInvalid) { + return new _ErrorHandlingAsciiDecoderSink(stringSink.asUtf8Sink(false)); + } else { + return new _SimpleAsciiDecoderSink(stringSink); + } + } +} + +class _ErrorHandlingAsciiDecoderSink extends ByteConversionSinkBase { + ByteConversionSink _utf8Sink; + _ErrorHandlingAsciiDecoderSink(this._utf8Sink); + + void close() { + _utf8Sink.close(); + } + + void add(List source) { + addSlice(source, 0, source.length, false); + } + + void addSlice(List source, int start, int end, bool isLast) { + if (start < 0 || start > source.length) { + throw new RangeError.range(start, 0, source.length); + } + if (end < start || end > source.length) { + throw new RangeError.range(end, start, source.length); + } + for (int i = start; i < end; i++) { + if ((source[i] & ~_ASCII_MASK) != 0) { + if (i > start) _utf8Sink.addSlice(source, start, i, false); + // Add UTF-8 encoding of U+FFFD. + _utf8Sink.add(const[0xEF, 0xBF, 0xBD]); + start = i + 1; + } + } + if (start < end) { + _utf8Sink.addSlice(source, start, end, isLast); + } else if (isLast) { + close(); + } + } +} + +class _SimpleAsciiDecoderSink extends ByteConversionSinkBase { + Sink _sink; + _SimpleAsciiDecoderSink(this._sink); + + void close() { + _sink.close(); + } + + void add(List source) { + for (int i = 0; i < source.length; i++) { + if ((source[i] & ~_ASCII_MASK) != 0) { + throw new FormatException("Source contains non-ASCII bytes."); + } + } + _sink.add(new String.fromCharCodes(source)); + } + + void addSlice(List source, int start, int end, bool isLast) { + final int length = source.length; + if (start < 0 || start > length) { + throw new RangeError.range(start, 0, length - 1); + } + if (end < start || end > length) { + throw new RangeError.range(end, start, length - 1); + } + if (start < end) { + if (start != 0 || end != length) { + source = source.sublist(start, end); + } + add(source); + } + if (isLast) close(); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/byte_conversion.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/byte_conversion.dart new file mode 100644 index 0000000..bb94991 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/byte_conversion.dart @@ -0,0 +1,118 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +/** + * The [ByteConversionSink] provides an interface for converters to + * efficiently transmit byte data. + * + * Instead of limiting the interface to one non-chunked list of bytes it + * accepts its input in chunks (themselves being lists of bytes). + * + * This abstract class will likely get more methods over time. Implementers are + * urged to extend or mix in [ByteConversionSinkBase] to ensure that their + * class covers the newly added methods. + */ +abstract class ByteConversionSink extends ChunkedConversionSink> { + ByteConversionSink(); + factory ByteConversionSink.withCallback(void callback(List accumulated)) + = _ByteCallbackSink; + factory ByteConversionSink.from(Sink> sink) + = _ByteAdapterSink; + + /** + * Adds the next [chunk] to `this`. + * + * Adds the bytes defined by [start] and [end]-exclusive to `this`. + * + * If [isLast] is `true` closes `this`. + * + * Contrary to `add` the given [chunk] must not be held onto. Once the method + * returns, it is safe to overwrite the data in it. + */ + void addSlice(List chunk, int start, int end, bool isLast); + + // TODO(floitsch): add more methods: + // - iterateBytes. +} + +/** + * This class provides a base-class for converters that need to accept byte + * inputs. + */ +abstract class ByteConversionSinkBase extends ByteConversionSink { + + void add(List chunk); + void close(); + + void addSlice(List chunk, int start, int end, bool isLast) { + add(chunk.sublist(start, end)); + if (isLast) close(); + } +} + +/** + * This class adapts a simple [Sink] to a [ByteConversionSink]. + * + * All additional methods of the [ByteConversionSink] (compared to the + * ChunkedConversionSink) are redirected to the `add` method. + */ +class _ByteAdapterSink extends ByteConversionSinkBase { + final Sink> _sink; + + _ByteAdapterSink(this._sink); + + void add(List chunk) => _sink.add(chunk); + void close() => _sink.close(); +} + +/** + * This class accumulates all chunks into one list of bytes + * and invokes a callback when the sink is closed. + * + * This class can be used to terminate a chunked conversion. + */ +class _ByteCallbackSink extends ByteConversionSinkBase { + static const _INITIAL_BUFFER_SIZE = 1024; + + final _ChunkedConversionCallback> _callback; + // TODO(11971, floitsch): use Uint8List instead of normal lists. + List _buffer = new List(_INITIAL_BUFFER_SIZE); + int _bufferIndex = 0; + + _ByteCallbackSink(void callback(List accumulated)) + : this._callback = callback; + + void add(Iterable chunk) { + int freeCount = _buffer.length - _bufferIndex; + if (chunk.length > freeCount) { + // Grow the buffer. + int oldLength = _buffer.length; + int newLength = _roundToPowerOf2(chunk.length + oldLength) * 2; + // TODO(11971, floitsch): use Uint8List instead of normal lists. + List grown = new List(newLength); + grown.setRange(0, _buffer.length, _buffer); + _buffer = grown; + } + _buffer.setRange(_bufferIndex, _bufferIndex + chunk.length, chunk); + _bufferIndex += chunk.length; + } + + static int _roundToPowerOf2(int v) { + assert(v > 0); + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; + } + + void close() { + _callback(_buffer.sublist(0, _bufferIndex)); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/chunked_conversion.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/chunked_conversion.dart new file mode 100644 index 0000000..c9a874c --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/chunked_conversion.dart @@ -0,0 +1,94 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +typedef void _ChunkedConversionCallback(T accumulated); + +/** + * A [ChunkedConversionSink] is used to transmit data more efficiently between + * two converters during chunked conversions. + * + * The basic `ChunkedConversionSink` is just a [Sink], and converters should + * work with a plain `Sink`, but may work more efficiently with certain + * specialized types of `ChunkedConversionSink`. + * + * It is recommended that implementations of `ChunkedConversionSink` extends + * this class, to inherit any further methods that may be added to the class. + */ +abstract class ChunkedConversionSink implements Sink { + ChunkedConversionSink(); + factory ChunkedConversionSink.withCallback( + void callback(List accumulated)) = _SimpleCallbackSink; + + /** + * Adds chunked data to this sink. + * + * This method is also used when converters are used as [StreamTransformer]s. + */ + void add(T chunk); + + /** + * Closes the sink. + * + * This signals the end of the chunked conversion. This method is called + * when converters are used as [StreamTransformer]'s. + */ + void close(); +} + +/** + * This class accumulates all chunks and invokes a callback with a list of + * the chunks when the sink is closed. + * + * This class can be used to terminate a chunked conversion. + */ +class _SimpleCallbackSink extends ChunkedConversionSink { + final _ChunkedConversionCallback> _callback; + final List _accumulated = []; + + _SimpleCallbackSink(this._callback); + + void add(T chunk) { _accumulated.add(chunk); } + void close() { _callback(_accumulated); } +} + +class _EventSinkAdapter implements ChunkedConversionSink { + final EventSink _sink; + + _EventSinkAdapter(this._sink); + + void add(T data) => _sink.add(data); + void close() => _sink.close(); +} + +/** + * This class converts implements the logic for a chunked conversion as a + * stream transformer. + * + * It is used as strategy in the [EventTransformStream]. + * + * It also implements the [ChunkedConversionSink] interface so that it + * can be used as output sink in a chunked conversion. + */ +class _ConverterStreamEventSink implements EventSink { + /** The output sink for the converter. */ + final EventSink _eventSink; + + /** + * The input sink for new data. All data that is received with + * [handleData] is added into this sink. + */ + ChunkedConversionSink _chunkedSink; + + _ConverterStreamEventSink(Converter converter, EventSink sink) + : this._eventSink = sink, + _chunkedSink = converter.startChunkedConversion(sink); + + void add(S o) => _chunkedSink.add(o); + void addError(Object error, [StackTrace stackTrace]) { + _eventSink.addError(error, stackTrace); + } + void close() => _chunkedSink.close(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/codec.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/codec.dart new file mode 100644 index 0000000..1afe63a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/codec.dart @@ -0,0 +1,102 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +/** + * A [Codec] encodes and (if supported) decodes data. + * + * Codecs can be fused. For example fusing [JSON] and [UTF8] produces + * an encoder that can convert Json objects directly to bytes, or can decode + * bytes directly to json objects. + * + * Fused codecs generally attempt to optimize the operations and can be faster + * than executing each step of an encoding separately. + * + * *Codecs are still experimental and are subject to change without notice.* + */ +abstract class Codec { + const Codec(); + + T encode(S input) => encoder.convert(input); + S decode(T encoded) => decoder.convert(encoded); + + /** + * Returns the encoder from [S] to [T]. + * + * It may be stateful and should not be reused. + */ + Converter get encoder; + /** + * Returns the decoder of `this`, converting from [T] to [S]. + * + * It may be stateful an should not be reused. + */ + Converter get decoder; + + /** + * Fuses `this` with `other`. + * + * When encoding, the resulting codec encodes with `this` before + * encoding with [other]. + * + * When decoding, the resulting codec decodes with [other] before decoding + * with `this`. + * + * In some cases one needs to use the [inverted] codecs to be able to fuse + * them correctly. That is, the output type of `this` ([T]) must match the + * input type of the second codec [other]. + * + * Examples: + * + * final JSON_TO_BYTES = JSON.fuse(UTF8); + * List bytes = JSON_TO_BYTES.encode(["json-object"]); + * var decoded = JSON_TO_BYTES.decode(bytes); + * assert(decoded is List && decoded[0] == "json-object"); + * + * var inverted = JSON.inverted; + * var jsonIdentity = JSON.fuse(inverted); + * var jsonObject = jsonIdentity.encode(["1", 2]); + * assert(jsonObject is List && jsonObject[0] == "1" && jsonObject[1] == 2); + */ + // TODO(floitsch): use better example with line-splitter once that one is + // in this library. + Codec fuse(Codec other) { + return new _FusedCodec(this, other); + } + + /** + * Inverts `this`. + * + * The [encoder] and [decoder] of the resulting codec are swapped. + */ + Codec get inverted => new _InvertedCodec(this); +} + +/** + * Fuses the given codecs. + * + * In the non-chunked conversion simply invokes the non-chunked conversions in + * sequence. + */ +class _FusedCodec extends Codec { + final Codec _first; + final Codec _second; + + Converter get encoder => _first.encoder.fuse(_second.encoder); + Converter get decoder => _second.decoder.fuse(_first.decoder); + + _FusedCodec(this._first, this._second); +} + +class _InvertedCodec extends Codec { + final Codec _codec; + + _InvertedCodec(Codec codec) : _codec = codec; + + Converter get encoder => _codec.decoder; + Converter get decoder => _codec.encoder; + + Codec get inverted => _codec; +} \ No newline at end of file diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/convert.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/convert.dart new file mode 100644 index 0000000..e493ec0 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/convert.dart @@ -0,0 +1,71 @@ +// Copyright (c) 2013, 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. + +/** + * + * Encoders and decoders for converting between different data representations, + * including JSON and UTF-8. + * + * In addition to converters for common data representations, this library + * provides support for implementing converters in a way which makes them easy to + * chain and to use with streams. + * + * The `dart:convert` library works in both web apps and command-line apps. + * To use it: + * + * import 'dart:convert'; + * + * Two commonly used converters are the top-level instances of + * [JsonCodec] and [Utf8Codec], named JSON and UTF8, respectively. + * + * JSON is a simple text format for representing + * structured objects and collections. + * The JSON encoder/decoder transforms between strings and + * object structures, such as lists and maps, using the JSON format. + * + * UTF-8 is a common variable-width encoding that can represent + * every character in the Unicode character set. + * The UTF-8 encoder/decoder transforms between Strings and bytes. + * + * Converters are often used with streams + * to transform the data that comes through the stream + * as it becomes available. + * The following code uses two converters. + * The first is a UTF-8 decoder, which converts the data from bytes to UTF-8 + * as it's read from a file, + * The second is an instance of [LineSplitter], + * which splits the data on newline boundaries. + * + * int lineNumber = 1; + * Stream> stream = new File('quotes.txt').openRead(); + * + * stream.transform(UTF8.decoder) + * .transform(const LineSplitter()) + * .listen((line) { + * if (showLineNumbers) { + * stdout.write('${lineNumber++} '); + * } + * stdout.writeln(line); + * }); + * + * See the documentation for the [Codec] and [Converter] classes + * for information about creating your own converters. + */ +library dart.convert; + +import 'dart:async'; +import "dart:collection" show HashSet; + +part 'ascii.dart'; +part 'byte_conversion.dart'; +part 'chunked_conversion.dart'; +part 'codec.dart'; +part 'converter.dart'; +part 'encoding.dart'; +part 'html_escape.dart'; +part 'json.dart'; +part 'latin1.dart'; +part 'line_splitter.dart'; +part 'string_conversion.dart'; +part 'utf.dart'; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/converter.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/converter.dart new file mode 100644 index 0000000..562f861 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/converter.dart @@ -0,0 +1,63 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +/** + * A [Converter] converts data from one representation into another. + * + * It is recommended that implementations of `Converter` extend this class, + * to inherit any further methods that may be added to the class. + */ +abstract class Converter implements StreamTransformer { + const Converter(); + + /** + * Converts [input] and returns the result of the conversion. + */ + T convert(S input); + + /** + * Fuses `this` with [other]. + * + * Encoding with the resulting converter is equivalent to converting with + * `this` before converting with `other`. + */ + Converter fuse(Converter other) { + return new _FusedConverter(this, other); + } + + /** + * Starts a chunked conversion. + */ + ChunkedConversionSink startChunkedConversion(Sink sink) { + throw new UnsupportedError( + "This converter does not support chunked conversions: $this"); + } + + // Subclasses are encouraged to provide better types. + Stream bind(Stream source) { + return new Stream.eventTransformed( + source, + (EventSink sink) => new _ConverterStreamEventSink(this, sink)); + } +} + +/** + * Fuses two converters. + * + * For a non-chunked conversion converts the input in sequence. + */ +class _FusedConverter extends Converter { + final Converter _first; + final Converter _second; + + _FusedConverter(this._first, this._second); + + T convert(S input) => _second.convert(_first.convert(input)); + + ChunkedConversionSink startChunkedConversion(Sink sink) { + return _first.startChunkedConversion(_second.startChunkedConversion(sink)); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/encoding.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/encoding.dart new file mode 100644 index 0000000..cc67f6f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/encoding.dart @@ -0,0 +1,76 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +/** + * Open-ended Encoding enum. + */ +abstract class Encoding extends Codec> { + const Encoding(); + + Future decodeStream(Stream> byteStream) { + return byteStream + .transform(decoder) + .fold(new StringBuffer(), (buffer, string) => buffer..write(string)) + .then((buffer) => buffer.toString()); + } + + /** + * Name of the encoding. + * + * If the encoding is standardized, this is the lower-case version of one of + * the IANA official names for the character set (see + * http://www.iana.org/assignments/character-sets/character-sets.xml) + */ + String get name; + + // All aliases (in lowercase) of supported encoding from + // http://www.iana.org/assignments/character-sets/character-sets.xml. + static Map _nameToEncoding = { + // ISO_8859-1:1987. + "iso_8859-1:1987": LATIN1, + "iso-ir-100": LATIN1, + "iso_8859-1": LATIN1, + "iso-8859-1": LATIN1, + "latin1": LATIN1, + "l1": LATIN1, + "ibm819": LATIN1, + "cp819": LATIN1, + "csisolatin1": LATIN1, + + // US-ASCII. + "iso-ir-6": ASCII, + "ansi_x3.4-1968": ASCII, + "ansi_x3.4-1986": ASCII, + "iso_646.irv:1991": ASCII, + "iso646-us": ASCII, + "us-ascii": ASCII, + "us": ASCII, + "ibm367": ASCII, + "cp367": ASCII, + "csascii": ASCII, + "ascii": ASCII, // This is not in the IANA official names. + + // UTF-8. + "csutf8": UTF8, + "utf-8": UTF8 + }; + + /** + * Gets an [Encoding] object from the name of the character set + * name. The names used are the IANA official names for the + * character set (see + * http://www.iana.org/assignments/character-sets/character-sets.xml). + * + * The [name] passed is case insensitive. + * + * If character set is not supported [:null:] is returned. + */ + static Encoding getByName(String name) { + if (name == null) return null; + name = name.toLowerCase(); + return _nameToEncoding[name]; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/html_escape.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/html_escape.dart new file mode 100644 index 0000000..512d9c3 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/html_escape.dart @@ -0,0 +1,100 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +// TODO(floitsch) - Document - Issue 13097 +const HtmlEscape HTML_ESCAPE = const HtmlEscape(); + +class HtmlEscapeMode { + final String _name; + final bool escapeLtGt; + final bool escapeQuot; + final bool escapeApos; + final bool escapeSlash; + + // TODO(floitsch) - Document - Issue 13097 + static const HtmlEscapeMode UNKNOWN = + const HtmlEscapeMode._('unknown', true, true, true, true); + + // TODO(floitsch) - Document - Issue 13097 + static const HtmlEscapeMode ATTRIBUTE = + const HtmlEscapeMode._('attribute', false, true, false, false); + + // TODO(floitsch) - Document - Issue 13097 + static const HtmlEscapeMode ELEMENT = + const HtmlEscapeMode._('element', true, false, false, true); + + // TODO(floitsch) - Document - Issue 13097 + const HtmlEscapeMode._(this._name, this.escapeLtGt, this.escapeQuot, + this.escapeApos, this.escapeSlash); + + String toString() => _name; +} + + // TODO(floitsch) - Document - Issue 13097 +class HtmlEscape extends Converter { + + // TODO(floitsch) - Document - Issue 13097 + final HtmlEscapeMode mode; + + // TODO(floitsch) - Document - Issue 13097 + const HtmlEscape([this.mode = HtmlEscapeMode.UNKNOWN]); + + String convert(String text) { + var val = _convert(text, 0, text.length); + return val == null ? text : val; + } + + String _convert(String text, int start, int end) { + StringBuffer result = null; + for (int i = start; i < end; i++) { + var ch = text[i]; + String replace = null; + switch (ch) { + case '&': replace = '&'; break; + case '\u00A0'/*NO-BREAK SPACE*/: replace = ' '; break; + case '"': if (mode.escapeQuot) replace = '"'; break; + case "'": if (mode.escapeApos) replace = '''; break; + case '<': if (mode.escapeLtGt) replace = '<'; break; + case '>': if (mode.escapeLtGt) replace = '>'; break; + case '/': if (mode.escapeSlash) replace = '/'; break; + } + if (replace != null) { + if (result == null) result = new StringBuffer(text.substring(start, i)); + result.write(replace); + } else if (result != null) { + result.write(ch); + } + } + + return result != null ? result.toString() : null; + } + + StringConversionSink startChunkedConversion(Sink sink) { + if (sink is! StringConversionSink) { + sink = new StringConversionSink.from(sink); + } + return new _HtmlEscapeSink(this, sink); + } +} + +class _HtmlEscapeSink extends StringConversionSinkBase { + final HtmlEscape _escape; + final StringConversionSink _sink; + + _HtmlEscapeSink(this._escape, this._sink); + + void addSlice(String chunk, int start, int end, bool isLast) { + var val = _escape._convert(chunk, start, end); + if(val == null) { + _sink.addSlice(chunk, start, end, isLast); + } else { + _sink.add(val); + if (isLast) _sink.close(); + } + } + + void close() => _sink.close(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/json.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/json.dart new file mode 100644 index 0000000..07de8f4 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/json.dart @@ -0,0 +1,517 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +/** + * Error thrown by JSON serialization if an object cannot be serialized. + * + * The [unsupportedObject] field holds that object that failed to be serialized. + * + * If an object isn't directly serializable, the serializer calls the 'toJson' + * method on the object. If that call fails, the error will be stored in the + * [cause] field. If the call returns an object that isn't directly + * serializable, the [cause] is be null. + */ +class JsonUnsupportedObjectError extends Error { + /** The object that could not be serialized. */ + final unsupportedObject; + /** The exception thrown when trying to convert the object. */ + final cause; + + JsonUnsupportedObjectError(this.unsupportedObject, { this.cause }); + + String toString() { + if (cause != null) { + return "Converting object to an encodable object failed."; + } else { + return "Converting object did not return an encodable object."; + } + } +} + + +/** + * Reports that an object could not be stringified due to cyclic references. + * + * An object that references itself cannot be serialized by [stringify]. + * When the cycle is detected, a [JsonCyclicError] is thrown. + */ +class JsonCyclicError extends JsonUnsupportedObjectError { + /** The first object that was detected as part of a cycle. */ + JsonCyclicError(Object object): super(object); + String toString() => "Cyclic error in JSON stringify"; +} + + +/** + * An instance of the default implementation of the [JsonCodec]. + * + * This instance provides a convenient access to the most common JSON + * use cases. + * + * Examples: + * + * var encoded = JSON.encode([1, 2, { "a": null }]); + * var decoded = JSON.decode('["foo", { "bar": 499 }]'); + */ +const JsonCodec JSON = const JsonCodec(); + +typedef _Reviver(var key, var value); +typedef _ToEncodable(var o); + + +/** + * A [JsonCodec] encodes JSON objects to strings and decodes strings to + * JSON objects. + */ +class JsonCodec extends Codec { + final _Reviver _reviver; + final _ToEncodable _toEncodable; + + /** + * Creates a `JsonCodec` with the given reviver and encoding function. + * + * The [reviver] function is called during decoding. It is invoked + * once for each object or list property that has been parsed. + * The `key` argument is either the + * integer list index for a list property, the string map key for object + * properties, or `null` for the final result. + * + * If [reviver] is omitted, it defaults to returning the value argument. + * + * The [toEncodable] function is used during encoding. It is invoked for + * values that are not directly encodable to a JSON1toE + * string (a value that is not a number, boolean, string, null, list or a map + * with string keys). The function must return an object that is directly + * encodable. The elements of a returned list and values of a returned map + * do not need be directly encodable, and if they aren't, `toEncodable` will + * be used on them as well. + * Please notice that it is possible to cause an infinite recursive + * regress in this way, by effectively creating an infinite data structure + * through repeated call to `toEncodable`. + * + * If [toEncodable] is omitted, it defaults to a function that returns the + * result of calling `.toJson()` on the unencodable object. + */ + const JsonCodec({reviver(var key, var value), toEncodable(var object)}) + : _reviver = reviver, + _toEncodable = toEncodable; + + /** + * Creates a `JsonCodec` with the given reviver. + * + * The [reviver] function is called once for each object or list property + * that has been parsed during decoding. The `key` argument is either the + * integer list index for a list property, the string map key for object + * properties, or `null` for the final result. + */ + JsonCodec.withReviver(reviver(var key, var value)) : this(reviver: reviver); + + /** + * Parses the string and returns the resulting Json object. + * + * The optional [reviver] function is called once for each object or list + * property that has been parsed during decoding. The `key` argument is either + * the integer list index for a list property, the string map key for object + * properties, or `null` for the final result. + * + * The default [reviver] (when not provided) is the identity function. + */ + dynamic decode(String source, {reviver(var key, var value)}) { + if (reviver == null) reviver = _reviver; + if (reviver == null) return decoder.convert(source); + return new JsonDecoder(reviver).convert(source); + } + + /** + * Converts [value] to a JSON string. + * + * If value contains objects that are not directly encodable to a JSON + * string (a value that is not a number, boolean, string, null, list or a map + * with string keys), the [toEncodable] function is used to convert it to an + * object that must be directly encodable. + * + * If [toEncodable] is omitted, it defaults to a function that returns the + * result of calling `.toJson()` on the unencodable object. + */ + String encode(Object value, {toEncodable(var object)}) { + if (toEncodable == null) toEncodable = _toEncodable; + if (toEncodable == null) return encoder.convert(value); + return new JsonEncoder(toEncodable).convert(value); + } + + JsonEncoder get encoder { + if (_toEncodable == null) return const JsonEncoder(); + return new JsonEncoder(_toEncodable); + } + + JsonDecoder get decoder { + if (_reviver == null) return const JsonDecoder(); + return new JsonDecoder(_reviver); + } +} + +/** + * This class converts JSON objects to strings. + */ +class JsonEncoder extends Converter { + final _toEncodableFunction; + + /** + * Creates a JSON encoder. + * + * The JSON encoder handles numbers, strings, booleans, null, lists and + * maps directly. + * + * Any other object is attempted converted by [toEncodable] to an + * object that is of one of the convertible types. + * + * If [toEncodable] is omitted, it defaults to calling `.toJson()` on + * the object. + */ + const JsonEncoder([Object toEncodable(Object nonSerializable)]) + : this._toEncodableFunction = toEncodable; + + /** + * Converts the given object [o] to its JSON representation. + * + * Directly serializable values are [num], [String], [bool], and [Null], as + * well as some [List] and [Map] values. + * For [List], the elements must all be serializable. + * For [Map], the keys must be [String] and the values must be serializable. + * + * If a value is any other type is attempted serialized, the conversion + * function provided in the constructor is invoked with the object as argument + * and the result, which must be a directly serializable value, + * is serialized instead of the original value. + * + * If the conversion throws, or returns a value that is not directly + * serializable, a [JsonUnsupportedObjectError] exception is thrown. + * If the call throws, the error is caught and stored in the + * [JsonUnsupportedObjectError]'s [:cause:] field. + * + * If a [List] or [Map] contains a reference to itself, directly or through + * other lists or maps, it cannot be serialized and a [JsonCyclicError] is + * thrown. + * + * Json Objects should not change during serialization. + * If an object is serialized more than once, [stringify] is allowed to cache + * the JSON text for it. I.e., if an object changes after it is first + * serialized, the new values may or may not be reflected in the result. + */ + String convert(Object o) => + _JsonStringifier.stringify(o, _toEncodableFunction); + + /** + * Starts a chunked conversion. + * + * The converter works more efficiently if the given [sink] is a + * [StringConversionSink]. + * + * Returns a chunked-conversion sink that accepts at most one object. It is + * an error to invoke `add` more than once on the returned sink. + */ + ChunkedConversionSink startChunkedConversion(Sink sink) { + if (sink is! StringConversionSink) { + sink = new StringConversionSink.from(sink); + } + return new _JsonEncoderSink(sink, _toEncodableFunction); + } + + // Override the base-classes bind, to provide a better type. + Stream bind(Stream stream) => super.bind(stream); +} + +/** + * Implements the chunked conversion from object to its JSON representation. + * + * The sink only accepts one value, but will produce output in a chunked way. + */ +class _JsonEncoderSink extends ChunkedConversionSink { + final Function _toEncodableFunction; + final StringConversionSink _sink; + bool _isDone = false; + + _JsonEncoderSink(this._sink, this._toEncodableFunction); + + /** + * Encodes the given object [o]. + * + * It is an error to invoke this method more than once on any instance. While + * this makes the input effectly non-chunked the output will be generated in + * a chunked way. + */ + void add(Object o) { + if (_isDone) { + throw new StateError("Only one call to add allowed"); + } + _isDone = true; + ClosableStringSink stringSink = _sink.asStringSink(); + _JsonStringifier.printOn(o, stringSink, _toEncodableFunction); + stringSink.close(); + } + + void close() { /* do nothing */ } +} + +/** + * This class parses JSON strings and builds the corresponding objects. + */ +class JsonDecoder extends Converter { + final _Reviver _reviver; + /** + * Constructs a new JsonDecoder. + * + * The [reviver] may be `null`. + */ + const JsonDecoder([reviver(var key, var value)]) : this._reviver = reviver; + + /** + * Converts the given JSON-string [input] to its corresponding object. + * + * Parsed JSON values are of the types [num], [String], [bool], [Null], + * [List]s of parsed JSON values or [Map]s from [String] to parsed + * JSON values. + * + * If `this` was initialized with a reviver, then the parsing operation + * invokes the reviver on every object or list property that has been parsed. + * The arguments are the property name ([String]) or list index ([int]), and + * the value is the parsed value. The return value of the reviver is used as + * the value of that property instead the parsed value. + * + * Throws [FormatException] if the input is not valid JSON text. + */ + dynamic convert(String input) => _parseJson(input, _reviver); + + /** + * Starts a conversion from a chunked JSON string to its corresponding + * object. + * + * The output [sink] receives exactly one decoded element through `add`. + */ + StringConversionSink startChunkedConversion(Sink sink) { + return new _JsonDecoderSink(_reviver, sink); + } + + // Override the base-classes bind, to provide a better type. + Stream bind(Stream stream) => super.bind(stream); +} + +/** + * Implements the chunked conversion from a JSON string to its corresponding + * object. + * + * The sink only creates one object, but its input can be chunked. + */ +// TODO(floitsch): don't accumulate everything before starting to decode. +class _JsonDecoderSink extends _StringSinkConversionSink { + final _Reviver _reviver; + final Sink _sink; + + _JsonDecoderSink(this._reviver, this._sink) + : super(new StringBuffer()); + + void close() { + super.close(); + StringBuffer buffer = _stringSink; + String accumulated = buffer.toString(); + buffer.clear(); + Object decoded = _parseJson(accumulated, _reviver); + _sink.add(decoded); + _sink.close(); + } +} + +// Internal optimized JSON parsing implementation. +external _parseJson(String source, reviver(key, value)); + + +// Implementation of encoder/stringifier. + +Object _defaultToEncodable(object) => object.toJson(); + +class _JsonStringifier { + // Character code constants. + static const int BACKSPACE = 0x08; + static const int TAB = 0x09; + static const int NEWLINE = 0x0a; + static const int CARRIAGE_RETURN = 0x0d; + static const int FORM_FEED = 0x0c; + static const int QUOTE = 0x22; + static const int CHAR_0 = 0x30; + static const int BACKSLASH = 0x5c; + static const int CHAR_b = 0x62; + static const int CHAR_f = 0x66; + static const int CHAR_n = 0x6e; + static const int CHAR_r = 0x72; + static const int CHAR_t = 0x74; + static const int CHAR_u = 0x75; + + final Function _toEncodable; + final StringSink _sink; + final List _seen; + + _JsonStringifier(this._sink, this._toEncodable) + : this._seen = new List(); + + static String stringify(object, toEncodable(object)) { + if (toEncodable == null) toEncodable = _defaultToEncodable; + StringBuffer output = new StringBuffer(); + printOn(object, output, toEncodable); + return output.toString(); + } + + static void printOn(object, StringSink output, toEncodable(object)) { + _JsonStringifier stringifier = new _JsonStringifier(output, toEncodable); + stringifier.stringifyValue(object); + } + + static String numberToString(num x) { + return x.toString(); + } + + // ('0' + x) or ('a' + x - 10) + static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; + + void escape(String s) { + int offset = 0; + final int length = s.length; + for (int i = 0; i < length; i++) { + int charCode = s.codeUnitAt(i); + if (charCode > BACKSLASH) continue; + if (charCode < 32) { + if (i > offset) _sink.write(s.substring(offset, i)); + offset = i + 1; + _sink.writeCharCode(BACKSLASH); + switch (charCode) { + case BACKSPACE: + _sink.writeCharCode(CHAR_b); + break; + case TAB: + _sink.writeCharCode(CHAR_t); + break; + case NEWLINE: + _sink.writeCharCode(CHAR_n); + break; + case FORM_FEED: + _sink.writeCharCode(CHAR_f); + break; + case CARRIAGE_RETURN: + _sink.writeCharCode(CHAR_r); + break; + default: + _sink.writeCharCode(CHAR_u); + _sink.writeCharCode(CHAR_0); + _sink.writeCharCode(CHAR_0); + _sink.writeCharCode(hexDigit((charCode >> 4) & 0xf)); + _sink.writeCharCode(hexDigit(charCode & 0xf)); + break; + } + } else if (charCode == QUOTE || charCode == BACKSLASH) { + if (i > offset) _sink.write(s.substring(offset, i)); + offset = i + 1; + _sink.writeCharCode(BACKSLASH); + _sink.writeCharCode(charCode); + } + } + if (offset == 0) { + _sink.write(s); + } else if (offset < length) { + _sink.write(s.substring(offset, length)); + } + } + + void checkCycle(object) { + for (int i = 0; i < _seen.length; i++) { + if (identical(object, _seen[i])) { + throw new JsonCyclicError(object); + } + } + _seen.add(object); + } + + void stringifyValue(object) { + // Tries stringifying object directly. If it's not a simple value, List or + // Map, call toJson() to get a custom representation and try serializing + // that. + if (!stringifyJsonValue(object)) { + checkCycle(object); + try { + var customJson = _toEncodable(object); + if (!stringifyJsonValue(customJson)) { + throw new JsonUnsupportedObjectError(object); + } + _removeSeen(object); + } catch (e) { + throw new JsonUnsupportedObjectError(object, cause: e); + } + } + } + + /** + * Serializes a [num], [String], [bool], [Null], [List] or [Map] value. + * + * Returns true if the value is one of these types, and false if not. + * If a value is both a [List] and a [Map], it's serialized as a [List]. + */ + bool stringifyJsonValue(object) { + if (object is num) { + if (!object.isFinite) return false; + _sink.write(numberToString(object)); + return true; + } else if (identical(object, true)) { + _sink.write('true'); + return true; + } else if (identical(object, false)) { + _sink.write('false'); + return true; + } else if (object == null) { + _sink.write('null'); + return true; + } else if (object is String) { + _sink.write('"'); + escape(object); + _sink.write('"'); + return true; + } else if (object is List) { + checkCycle(object); + List a = object; + _sink.write('['); + if (a.length > 0) { + stringifyValue(a[0]); + for (int i = 1; i < a.length; i++) { + _sink.write(','); + stringifyValue(a[i]); + } + } + _sink.write(']'); + _removeSeen(object); + return true; + } else if (object is Map) { + checkCycle(object); + Map m = object; + _sink.write('{'); + String separator = '"'; + for (String key in m.keys) { + _sink.write(separator); + separator = ',"'; + escape(key); + _sink.write('":'); + stringifyValue(m[key]); + } + _sink.write('}'); + _removeSeen(object); + return true; + } else { + return false; + } + } + + void _removeSeen(object) { + assert(!_seen.isEmpty); + assert(identical(_seen.last, object)); + _seen.removeLast(); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/latin1.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/latin1.dart new file mode 100644 index 0000000..9ac6379 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/latin1.dart @@ -0,0 +1,164 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +/** + * An instance of the default implementation of the [Latin1Codec]. + * + * This instance provides a convenient access to the most common ISO Latin 1 + * use cases. + * + * Examples: + * + * var encoded = LATIN1.encode("blåbærgrød"); + * var decoded = LATIN1.decode([0x62, 0x6c, 0xe5, 0x62, 0xe6, + * 0x72, 0x67, 0x72, 0xf8, 0x64]); + */ +const Latin1Codec LATIN1 = const Latin1Codec(); + +const int _LATIN1_MASK = 0xFF; + +/** + * A [LatinCodec] encodes strings to ISO Latin-1 (aka ISO-8859-1) bytes + * and decodes Latin-1 bytes to strings. + */ +class Latin1Codec extends Encoding { + final bool _allowInvalid; + /** + * Instantiates a new [Latin1Codec]. + * + * If [allowInvalid] is true, the [decode] method and the converter + * returned by [decoder] will default to allowing invalid values. Invalid + * values are decoded into the Unicode Replacement character (U+FFFD). + * Calls to the [decode] method can override this default. + * + * Encoders will not accept invalid (non Latin-1) characters. + */ + const Latin1Codec({bool allowInvalid: false}) : _allowInvalid = allowInvalid; + + String get name => "iso-8859-1"; + + /** + * Decodes the Latin-1 [bytes] (a list of unsigned 8-bit integers) to the + * corresponding string. + * + * If [bytes] contains values that are not in the range 0 .. 255, the decoder + * will eventually throw a [FormatException]. + * + * If [allowInvalid] is not provided, it defaults to the value used to create + * this [Latin1Codec]. + */ + String decode(List bytes, { bool allowInvalid }) { + if (allowInvalid == null) allowInvalid = _allowInvalid; + if (allowInvalid) { + return const Latin1Decoder(allowInvalid: true).convert(bytes); + } else { + return const Latin1Decoder(allowInvalid: false).convert(bytes); + } + } + + Converter> get encoder => const Latin1Encoder(); + + Converter, String> get decoder => + _allowInvalid ? const Latin1Decoder(allowInvalid: true) + : const Latin1Decoder(allowInvalid: false); +} + +/** + * This class converts strings of only ISO Latin-1 characters to bytes. + */ +class Latin1Encoder extends _UnicodeSubsetEncoder { + const Latin1Encoder() : super(_LATIN1_MASK); +} + +/** + * This class converts Latin-1 bytes (lists of unsigned 8-bit integers) + * to a string. + */ +class Latin1Decoder extends _UnicodeSubsetDecoder { + /** + * Instantiates a new [Latin1Decoder]. + * + * The optional [allowInvalid] argument defines how [convert] deals + * with invalid bytes. + * + * If it is `true`, [convert] replaces invalid bytes with the Unicode + * Replacement character `U+FFFD` (�). + * Otherwise it throws a [FormatException]. + */ + const Latin1Decoder({ bool allowInvalid: false }) + : super(allowInvalid, _LATIN1_MASK); + + /** + * Starts a chunked conversion. + * + * The converter works more efficiently if the given [sink] is a + * [StringConversionSink]. + */ + ByteConversionSink startChunkedConversion(Sink sink) { + StringConversionSink stringSink; + if (sink is StringConversionSink) { + stringSink = sink; + } else { + stringSink = new StringConversionSink.from(sink); + } + // TODO(lrn): Use stringSink.asUtf16Sink() if it becomes available. + return new _Latin1DecoderSink(_allowInvalid, stringSink); + } +} + +class _Latin1DecoderSink extends ByteConversionSinkBase { + final bool _allowInvalid; + StringConversionSink _sink; + _Latin1DecoderSink(this._allowInvalid, this._sink); + + void close() { + _sink.close(); + } + + void add(List source) { + addSlice(source, 0, source.length, false); + } + + void _addSliceToSink(List source, int start, int end, bool isLast) { + // If _sink was a UTF-16 conversion sink, just add the slice directly with + // _sink.addSlice(source, start, end, isLast). + // The code below is an moderately stupid workaround until a real + // solution can be made. + if (start == 0 && end == source.length) { + _sink.add(new String.fromCharCodes(source)); + } else { + _sink.add(new String.fromCharCodes(source.sublist(start, end))); + } + if (isLast) close(); + } + + void addSlice(List source, int start, int end, bool isLast) { + if (start < 0 || start > source.length) { + throw new RangeError.range(start, 0, source.length); + } + if (end < start || end > source.length) { + throw new RangeError.range(end, start, source.length); + } + for (int i = start; i < end; i++) { + if ((source[i] & ~_LATIN1_MASK) != 0) { + if (_allowInvalid) { + if (i > start) _addSliceToSink(source, start, i, false); + // Add UTF-8 encoding of U+FFFD. + _addSliceToSink(const[0xFFFD], 0, 1, false); + start = i + 1; + } else { + throw new FormatException("Source contains non-Latin-1 characters."); + } + } + } + if (start < end) { + _addSliceToSink(source, start, end, isLast); + } + if (isLast) { + close(); + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/line_splitter.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/line_splitter.dart new file mode 100644 index 0000000..84cb22c --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/line_splitter.dart @@ -0,0 +1,93 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +/** + * This class splits [String] values into individual lines. + */ +class LineSplitter extends Converter> { + + const LineSplitter(); + + List convert(String data) { + var lines = new List(); + + _LineSplitterSink._addSlice(data, 0, data.length, true, lines.add); + + return lines; + } + + StringConversionSink startChunkedConversion(Sink sink) { + if (sink is! StringConversionSink) { + sink = new StringConversionSink.from(sink); + } + return new _LineSplitterSink(sink); + } +} + +// TODO(floitsch): deal with utf8. +class _LineSplitterSink extends StringConversionSinkBase { + static const int _LF = 10; + static const int _CR = 13; + + final StringConversionSink _sink; + + String _carry; + + _LineSplitterSink(this._sink); + + void addSlice(String chunk, int start, int end, bool isLast) { + if (_carry != null) { + chunk = _carry + chunk.substring(start, end); + start = 0; + end = chunk.length; + _carry = null; + } + _carry = _addSlice(chunk, start, end, isLast, _sink.add); + if (isLast) _sink.close(); + } + + void close() { + addSlice('', 0, 0, true); + } + + static String _addSlice(String chunk, int start, int end, bool isLast, + void adder(String val)) { + + int pos = start; + while (pos < end) { + int skip = 0; + int char = chunk.codeUnitAt(pos); + if (char == _LF) { + skip = 1; + } else if (char == _CR) { + skip = 1; + if (pos + 1 < end) { + if (chunk.codeUnitAt(pos + 1) == _LF) { + skip = 2; + } + } else if (!isLast) { + return chunk.substring(start, end); + } + } + if (skip > 0) { + adder(chunk.substring(start, pos)); + start = pos = pos + skip; + } else { + pos++; + } + } + if (pos != start) { + var carry = chunk.substring(start, pos); + if (isLast) { + // Add remaining + adder(carry); + } else { + return carry; + } + } + return null; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/string_conversion.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/string_conversion.dart new file mode 100644 index 0000000..4732992 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/string_conversion.dart @@ -0,0 +1,343 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +/** + * This class provides an interface for converters to + * efficiently transmit String data. + * + * Instead of limiting the interface to one non-chunked String it accepts + * partial strings or can be transformed into a byte sink that + * accepts UTF-8 code units. + * + * This abstract class will likely get more methods over time. Implementers are + * urged to extend [StringConversionSinkBase] or to mix in + * [StringConversionSinkMixin], to ensure that their class covers the newly + * added methods. + */ +abstract class StringConversionSink + extends ChunkedConversionSink { + StringConversionSink(); + factory StringConversionSink.withCallback(void callback(String accumulated)) + = _StringCallbackSink; + factory StringConversionSink.from(Sink sink) + = _StringAdapterSink; + + /** + * Creates a new instance wrapping the given [sink]. + * + * Every string that is added to the returned instance is forwarded to + * the [sink]. The instance is allowed to buffer and is not required to + * forward immediately. + */ + factory StringConversionSink.fromStringSink(StringSink sink) = + _StringSinkConversionSink; + + /** + * Adds the next [chunk] to `this`. + * + * Adds the substring defined by [start] and [end]-exclusive to `this`. + * + * If [isLast] is `true` closes `this`. + */ + void addSlice(String chunk, int start, int end, bool isLast); + + /** + * Returns `this` as a sink that accepts UTF-8 input. + * + * If used, this method must be the first and only call to `this`. It + * invalidates `this`. All further operations must be performed on the result. + */ + ByteConversionSink asUtf8Sink(bool allowMalformed); + // - asRuneSink + // - asCodeUnitsSink + + /** + * Returns `this` as a [ClosableStringSink]. + * + * If used, this method must be the first and only call to `this`. It + * invalidates `this`. All further operations must be performed on the result. + */ + ClosableStringSink asStringSink(); +} + +/** + * A [ClosableStringSink] extends the [StringSink] interface by adding a + * `close` method. + */ +abstract class ClosableStringSink extends StringSink { + /** + * Creates a new instance combining a [StringSink] [sink] and a callback + * [onClose] which is invoked when the returned instance is closed. + */ + factory ClosableStringSink.fromStringSink(StringSink sink, void onClose()) + = _ClosableStringSink; + + /** + * Closes `this` and flushes any outstanding data. + */ + void close(); +} + +typedef void _StringSinkCloseCallback(); + +/** + * This class wraps an existing [StringSink] and invokes a + * closure when [close] is invoked. + */ +class _ClosableStringSink implements ClosableStringSink { + final _StringSinkCloseCallback _callback; + final StringSink _sink; + + _ClosableStringSink(this._sink, this._callback); + + void close() => _callback(); + + void writeCharCode(int charCode) => _sink.writeCharCode(charCode); + void write(Object o) => _sink.write(o); + void writeln([Object o = ""]) => _sink.writeln(o); + void writeAll(Iterable objects, [String separator = ""]) + => _sink.writeAll(objects, separator); +} + +/** + * This class wraps an existing [StringConversionSink] and exposes a + * [ClosableStringSink] interface. The wrapped sink only needs to implement + * `add` and `close`. + */ +// TODO(floitsch): make this class public? +class _StringConversionSinkAsStringSinkAdapter implements ClosableStringSink { + static const _MIN_STRING_SIZE = 16; + + StringBuffer _buffer; + StringConversionSink _chunkedSink; + + _StringConversionSinkAsStringSinkAdapter(this._chunkedSink) + : _buffer = new StringBuffer(); + + void close() { + if (_buffer.isNotEmpty) _flush(); + _chunkedSink.close(); + } + + void writeCharCode(int charCode) { + _buffer.writeCharCode(charCode); + if (_buffer.length > _MIN_STRING_SIZE) _flush(); + } + + void write(Object o) { + if (_buffer.isNotEmpty) _flush(); + String str = o.toString(); + _chunkedSink.add(o.toString()); + } + + void writeln([Object o = ""]) { + _buffer.writeln(o); + if (_buffer.length > _MIN_STRING_SIZE) _flush(); + } + + void writeAll(Iterable objects, [String separator = ""]) { + if (_buffer.isNotEmpty) _flush(); + Iterator iterator = objects.iterator; + if (!iterator.moveNext()) return; + if (separator.isEmpty) { + do { + _chunkedSink.add(iterator.current.toString()); + } while (iterator.moveNext()); + } else { + _chunkedSink.add(iterator.current.toString()); + while (iterator.moveNext()) { + write(separator); + _chunkedSink.add(iterator.current.toString()); + } + } + } + + void _flush() { + String accumulated = _buffer.toString(); + _buffer.clear(); + _chunkedSink.add(accumulated); + } +} + +/** + * This class provides a base-class for converters that need to accept String + * inputs. + */ +abstract class StringConversionSinkBase extends StringConversionSinkMixin { +} + +/** + * This class provides a mixin for converters that need to accept String + * inputs. + */ +abstract class StringConversionSinkMixin implements StringConversionSink { + + void addSlice(String str, int start, int end, bool isLast); + void close(); + + void add(String str) => addSlice(str, 0, str.length, false); + + ByteConversionSink asUtf8Sink(bool allowMalformed) { + return new _Utf8ConversionSink(this, allowMalformed); + } + + ClosableStringSink asStringSink() { + return new _StringConversionSinkAsStringSinkAdapter(this); + } +} + +/** + * This class is a [StringConversionSink] that wraps a [StringSink]. + */ +class _StringSinkConversionSink extends StringConversionSinkBase { + StringSink _stringSink; + _StringSinkConversionSink(StringSink this._stringSink); + + void close() {} + void addSlice(String str, int start, int end, bool isLast) { + if (start != 0 || end != str.length) { + for (int i = start; i < end; i++) { + _stringSink.writeCharCode(str.codeUnitAt(i)); + } + } else { + _stringSink.write(str); + } + if (isLast) close(); + } + + void add(String str) => _stringSink.write(str); + + ByteConversionSink asUtf8Sink(bool allowMalformed) { + return new _Utf8StringSinkAdapter(this, _stringSink, allowMalformed); + } + + ClosableStringSink asStringSink() { + return new ClosableStringSink.fromStringSink(_stringSink, this.close); + } +} + +/** + * This class accumulates all chunks into one string + * and invokes a callback when the sink is closed. + * + * This class can be used to terminate a chunked conversion. + */ +class _StringCallbackSink extends _StringSinkConversionSink { + final _ChunkedConversionCallback _callback; + _StringCallbackSink(this._callback) : super(new StringBuffer()); + + void close() { + StringBuffer buffer = _stringSink; + String accumulated = buffer.toString(); + buffer.clear(); + _callback(accumulated); + } + + ByteConversionSink asUtf8Sink(bool allowMalformed) { + return new _Utf8StringSinkAdapter( + this, _stringSink, allowMalformed); + } +} + +/** + * This class adapts a simple [ChunkedConversionSink] to a + * [StringConversionSink]. + * + * All additional methods of the [StringConversionSink] (compared to the + * ChunkedConversionSink) are redirected to the `add` method. + */ +class _StringAdapterSink extends StringConversionSinkBase { + final Sink _sink; + + _StringAdapterSink(this._sink); + + void add(String str) => _sink.add(str); + + void addSlice(String str, int start, int end, bool isLast) { + if (start == 0 && end == str.length) { + add(str); + } else { + add(str.substring(start, end)); + } + if (isLast) close(); + } + + void close() => _sink.close(); +} + + +/** + * Decodes UTF-8 code units and stores them in a [StringSink]. + */ +class _Utf8StringSinkAdapter extends ByteConversionSink { + final _Utf8Decoder _decoder; + final Sink _sink; + + _Utf8StringSinkAdapter(this._sink, + StringSink stringSink, bool allowMalformed) + : _decoder = new _Utf8Decoder(stringSink, allowMalformed); + + void close() { + _decoder.close(); + if(_sink != null) _sink.close(); + } + + void add(List chunk) { + addSlice(chunk, 0, chunk.length, false); + } + + void addSlice(List codeUnits, int startIndex, int endIndex, + bool isLast) { + _decoder.convert(codeUnits, startIndex, endIndex); + if (isLast) close(); + } +} + +/** + * Decodes UTF-8 code units. + * + * Forwards the decoded strings to the given [StringConversionSink]. + */ +// TODO(floitsch): make this class public? +class _Utf8ConversionSink extends ByteConversionSink { + + final _Utf8Decoder _decoder; + final StringConversionSink _chunkedSink; + final StringBuffer _buffer; + _Utf8ConversionSink(StringConversionSink sink, bool allowMalformed) + : this._(sink, new StringBuffer(), allowMalformed); + + _Utf8ConversionSink._(this._chunkedSink, StringBuffer stringBuffer, + bool allowMalformed) + : _decoder = new _Utf8Decoder(stringBuffer, allowMalformed), + _buffer = stringBuffer; + + void close() { + _decoder.close(); + if (_buffer.isNotEmpty) { + String accumulated = _buffer.toString(); + _buffer.clear(); + _chunkedSink.addSlice(accumulated, 0, accumulated.length, true); + } else { + _chunkedSink.close(); + } + } + + void add(List chunk) { + addSlice(chunk, 0, chunk.length, false); + } + + void addSlice(List chunk, int startIndex, int endIndex, bool isLast) { + _decoder.convert(chunk, startIndex, endIndex); + if (_buffer.isNotEmpty) { + String accumulated = _buffer.toString(); + _chunkedSink.addSlice(accumulated, 0, accumulated.length, isLast); + _buffer.clear(); + return; + } + if (isLast) close(); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/utf.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/utf.dart new file mode 100644 index 0000000..5b28193 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/convert/utf.dart @@ -0,0 +1,550 @@ +// Copyright (c) 2013, 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. + +part of dart.convert; + +/** The Unicode Replacement character `U+FFFD` (�). */ +const int UNICODE_REPLACEMENT_CHARACTER_RUNE = 0xFFFD; + +/** The Unicode Byte Order Marker (BOM) character `U+FEFF`. */ +const int UNICODE_BOM_CHARACTER_RUNE = 0xFEFF; + +/** + * An instance of the default implementation of the [Utf8Codec]. + * + * This instance provides a convenient access to the most common UTF-8 + * use cases. + * + * Examples: + * + * var encoded = UTF8.encode("Îñţérñåţîöñåļîžåţîờñ"); + * var decoded = UTF8.decode([0x62, 0x6c, 0xc3, 0xa5, 0x62, 0xc3, 0xa6, + * 0x72, 0x67, 0x72, 0xc3, 0xb8, 0x64]); + */ +const Utf8Codec UTF8 = const Utf8Codec(); + +/** + * A [Utf8Codec] encodes strings to utf-8 code units (bytes) and decodes + * UTF-8 code units to strings. + */ +class Utf8Codec extends Encoding { + final bool _allowMalformed; + + /** + * Instantiates a new [Utf8Codec]. + * + * The optional [allowMalformed] argument defines how [decoder] (and [decode]) + * deal with invalid or unterminated character sequences. + * + * If it is `true` (and not overriden at the method invocation) [decode] and + * the [decoder] replace invalid (or unterminated) octet + * sequences with the Unicode Replacement character `U+FFFD` (�). Otherwise + * they throw a [FormatException]. + */ + const Utf8Codec({ bool allowMalformed: false }) + : _allowMalformed = allowMalformed; + + String get name => "utf-8"; + + /** + * Decodes the UTF-8 [codeUnits] (a list of unsigned 8-bit integers) to the + * corresponding string. + * + * If the [codeUnits] start with a leading [UNICODE_BOM_CHARACTER_RUNE] this + * character is discarded. + * + * If [allowMalformed] is `true` the decoder replaces invalid (or + * unterminated) character sequences with the Unicode Replacement character + * `U+FFFD` (�). Otherwise it throws a [FormatException]. + * + * If [allowMalformed] is not given, it defaults to the `allowMalformed` that + * was used to instantiate `this`. + */ + String decode(List codeUnits, { bool allowMalformed }) { + if (allowMalformed == null) allowMalformed = _allowMalformed; + return new Utf8Decoder(allowMalformed: allowMalformed).convert(codeUnits); + } + + Converter> get encoder => new Utf8Encoder(); + Converter, String> get decoder { + return new Utf8Decoder(allowMalformed: _allowMalformed); + } +} + +/** + * This class converts strings to their UTF-8 code units (a list of + * unsigned 8-bit integers). + */ +class Utf8Encoder extends Converter> { + + const Utf8Encoder(); + + /** + * Converts [string] to its UTF-8 code units (a list of + * unsigned 8-bit integers). + */ + List convert(String string) { + // Create a new encoder with a length that is guaranteed to be big enough. + // A single code unit uses at most 3 bytes. Two code units at most 4. + _Utf8Encoder encoder = new _Utf8Encoder.withBufferSize(string.length * 3); + int endPosition = encoder._fillBuffer(string, 0, string.length); + assert(endPosition >= string.length - 1); + if (endPosition != string.length) { + int lastCodeUnit = string.codeUnitAt(string.length - 1); + assert(_isLeadSurrogate(lastCodeUnit)); + // We use a non-surrogate as `nextUnit` so that _writeSurrogate just + // writes the lead-surrogate. + bool wasCombined = encoder._writeSurrogate(lastCodeUnit, 0); + assert(!wasCombined); + } + return encoder._buffer.sublist(0, encoder._bufferIndex); + } + + /** + * Starts a chunked conversion. + * + * The converter works more efficiently if the given [sink] is a + * [ByteConversionSink]. + */ + StringConversionSink startChunkedConversion(Sink> sink) { + if (sink is! ByteConversionSink) { + sink = new ByteConversionSink.from(sink); + } + return new _Utf8EncoderSink(sink); + } + + // Override the base-classes bind, to provide a better type. + Stream> bind(Stream stream) => super.bind(stream); +} + +/** + * This class encodes Strings to UTF-8 code units (unsigned 8 bit integers). + */ +// TODO(floitsch): make this class public. +class _Utf8Encoder { + int _carry = 0; + int _bufferIndex = 0; + final List _buffer; + + static const _DEFAULT_BYTE_BUFFER_SIZE = 1024; + + _Utf8Encoder() : this.withBufferSize(_DEFAULT_BYTE_BUFFER_SIZE); + + _Utf8Encoder.withBufferSize(int bufferSize) + : _buffer = _createBuffer(bufferSize); + + // TODO(11971): Always use Uint8List. + /** + * Allow an implementation to pick the most efficient way of storing bytes. + */ + external static List _createBuffer(int size); + + /** + * Tries to combine the given [leadingSurrogate] with the [nextCodeUnit] and + * writes it to [_buffer]. + * + * Returns true if the [nextCodeUnit] was combined with the + * [leadingSurrogate]. If it wasn't then nextCodeUnit was not a trailing + * surrogate and has not been written yet. + * + * It is safe to pass 0 for [nextCodeUnit] in which case only the leading + * surrogate is written. + */ + bool _writeSurrogate(int leadingSurrogate, int nextCodeUnit) { + if (_isTailSurrogate(nextCodeUnit)) { + int rune = _combineSurrogatePair(leadingSurrogate, nextCodeUnit); + // If the rune is encoded with 2 code-units then it must be encoded + // with 4 bytes in UTF-8. + assert(rune > _THREE_BYTE_LIMIT); + assert(rune <= _FOUR_BYTE_LIMIT); + _buffer[_bufferIndex++] = 0xF0 | (rune >> 18); + _buffer[_bufferIndex++] = 0x80 | ((rune >> 12) & 0x3f); + _buffer[_bufferIndex++] = 0x80 | ((rune >> 6) & 0x3f); + _buffer[_bufferIndex++] = 0x80 | (rune & 0x3f); + return true; + } else { + // TODO(floitsch): allow to throw on malformed strings. + // Encode the half-surrogate directly into UTF-8. This yields + // invalid UTF-8, but we started out with invalid UTF-16. + + // Surrogates are always encoded in 3 bytes in UTF-8. + _buffer[_bufferIndex++] = 0xE0 | (leadingSurrogate >> 12); + _buffer[_bufferIndex++] = 0x80 | ((leadingSurrogate >> 6) & 0x3f); + _buffer[_bufferIndex++] = 0x80 | (leadingSurrogate & 0x3f); + return false; + } + } + + /** + * Fills the [_buffer] with as many characters as possible. + * + * Does not encode any trailing lead-surrogate. This must be done by the + * caller. + * + * Returns the position in the string. The returned index points to the + * first code unit that hasn't been encoded. + */ + int _fillBuffer(String str, int start, int end) { + if (start != end && _isLeadSurrogate(str.codeUnitAt(end - 1))) { + // Don't handle a trailing lead-surrogate in this loop. The caller has + // to deal with those. + end--; + } + int stringIndex; + for (stringIndex = start; stringIndex < end; stringIndex++) { + int codeUnit = str.codeUnitAt(stringIndex); + // ASCII has the same representation in UTF-8 and UTF-16. + if (codeUnit <= _ONE_BYTE_LIMIT) { + if (_bufferIndex >= _buffer.length) break; + _buffer[_bufferIndex++] = codeUnit; + } else if (_isLeadSurrogate(codeUnit)) { + if (_bufferIndex + 3 >= _buffer.length) break; + // Note that it is safe to read the next code unit. We decremented + // [end] above when the last valid code unit was a leading surrogate. + int nextCodeUnit = str.codeUnitAt(stringIndex + 1); + bool wasCombined = _writeSurrogate(codeUnit, nextCodeUnit); + if (wasCombined) stringIndex++; + } else { + int rune = codeUnit; + if (rune <= _TWO_BYTE_LIMIT) { + if (_bufferIndex + 1 >= _buffer.length) break; + _buffer[_bufferIndex++] = 0xC0 | (rune >> 6); + _buffer[_bufferIndex++] = 0x80 | (rune & 0x3f); + } else { + assert(rune <= _THREE_BYTE_LIMIT); + if (_bufferIndex + 2 >= _buffer.length) break; + _buffer[_bufferIndex++] = 0xE0 | (rune >> 12); + _buffer[_bufferIndex++] = 0x80 | ((rune >> 6) & 0x3f); + _buffer[_bufferIndex++] = 0x80 | (rune & 0x3f); + } + } + } + return stringIndex; + } +} + +/** + * This class encodes chunked strings to UTF-8 code units (unsigned 8-bit + * integers). + */ +class _Utf8EncoderSink extends _Utf8Encoder with StringConversionSinkMixin { + + final ByteConversionSink _sink; + + _Utf8EncoderSink(this._sink); + + void close() { + if (_carry != 0) { + // addSlice will call close again, but then the carry must be equal to 0. + addSlice("", 0, 0, true); + return; + } + _sink.close(); + } + + void addSlice(String str, int start, int end, bool isLast) { + _bufferIndex = 0; + + if (start == end && !isLast) { + return; + } + + if (_carry != 0) { + int nextCodeUnit = 0; + if (start != end) { + nextCodeUnit = str.codeUnitAt(start); + } else { + assert(isLast); + } + bool wasCombined = _writeSurrogate(_carry, nextCodeUnit); + // Either we got a non-empty string, or we must not have been combined. + assert(!wasCombined || start != end ); + if (wasCombined) start++; + _carry = 0; + } + do { + start = _fillBuffer(str, start, end); + bool isLastSlice = isLast && (start == end); + if (start == end - 1 && _isLeadSurrogate(str.codeUnitAt(start))) { + if (isLast && _bufferIndex < _buffer.length - 3) { + // There is still space for the last incomplete surrogate. + // We use a non-surrogate as second argument. This way the + // function will just add the surrogate-half to the buffer. + bool hasBeenCombined = _writeSurrogate(str.codeUnitAt(start), 0); + assert(!hasBeenCombined); + } else { + // Otherwise store it in the carry. If isLast is true, then + // close will flush the last carry. + _carry = str.codeUnitAt(start); + } + start++; + } + _sink.addSlice(_buffer, 0, _bufferIndex, isLastSlice); + _bufferIndex = 0; + } while (start < end); + if (isLast) close(); + } + + // TODO(floitsch): implement asUtf8Sink. Sligthly complicated because it + // needs to deal with malformed input. +} + +/** + * This class converts UTF-8 code units (lists of unsigned 8-bit integers) + * to a string. + */ +class Utf8Decoder extends Converter, String> { + final bool _allowMalformed; + + /** + * Instantiates a new [Utf8Decoder]. + * + * The optional [allowMalformed] argument defines how [convert] deals + * with invalid or unterminated character sequences. + * + * If it is `true` [convert] replaces invalid (or unterminated) character + * sequences with the Unicode Replacement character `U+FFFD` (�). Otherwise + * it throws a [FormatException]. + */ + const Utf8Decoder({ bool allowMalformed: false }) + : this._allowMalformed = allowMalformed; + + /** + * Converts the UTF-8 [codeUnits] (a list of unsigned 8-bit integers) to the + * corresponding string. + * + * If the [codeUnits] start with a leading [UNICODE_BOM_CHARACTER_RUNE] this + * character is discarded. + */ + String convert(List codeUnits) { + StringBuffer buffer = new StringBuffer(); + _Utf8Decoder decoder = new _Utf8Decoder(buffer, _allowMalformed); + decoder.convert(codeUnits, 0, codeUnits.length); + decoder.close(); + return buffer.toString(); + } + + /** + * Starts a chunked conversion. + * + * The converter works more efficiently if the given [sink] is a + * [StringConversionSink]. + */ + ByteConversionSink startChunkedConversion(Sink sink) { + StringConversionSink stringSink; + if (sink is StringConversionSink) { + stringSink = sink; + } else { + stringSink = new StringConversionSink.from(sink); + } + return stringSink.asUtf8Sink(_allowMalformed); + } + + // Override the base-classes bind, to provide a better type. + Stream bind(Stream> stream) => super.bind(stream); +} + +// UTF-8 constants. +const int _ONE_BYTE_LIMIT = 0x7f; // 7 bits +const int _TWO_BYTE_LIMIT = 0x7ff; // 11 bits +const int _THREE_BYTE_LIMIT = 0xffff; // 16 bits +const int _FOUR_BYTE_LIMIT = 0x10ffff; // 21 bits, truncated to Unicode max. + +// UTF-16 constants. +const int _SURROGATE_MASK = 0xF800; +const int _SURROGATE_TAG_MASK = 0xFC00; +const int _SURROGATE_VALUE_MASK = 0x3FF; +const int _LEAD_SURROGATE_MIN = 0xD800; +const int _TAIL_SURROGATE_MIN = 0xDC00; + +bool _isSurrogate(int codeUnit) => + (codeUnit & _SURROGATE_MASK) == _LEAD_SURROGATE_MIN; +bool _isLeadSurrogate(int codeUnit) => + (codeUnit & _SURROGATE_TAG_MASK) == _LEAD_SURROGATE_MIN; +bool _isTailSurrogate(int codeUnit) => + (codeUnit & _SURROGATE_TAG_MASK) == _TAIL_SURROGATE_MIN; +int _combineSurrogatePair(int lead, int tail) => + 0x10000 + ((lead & _SURROGATE_VALUE_MASK) << 10) + | (tail & _SURROGATE_VALUE_MASK); + + +/** + * Decodes UTF-8. + * + * The decoder handles chunked input. + */ +// TODO(floitsch): make this class public. +class _Utf8Decoder { + final bool _allowMalformed; + final StringSink _stringSink; + bool _isFirstCharacter = true; + int _value = 0; + int _expectedUnits = 0; + int _extraUnits = 0; + + _Utf8Decoder(this._stringSink, this._allowMalformed); + + bool get hasPartialInput => _expectedUnits > 0; + + // Limits of one through four byte encodings. + static const List _LIMITS = const [ + _ONE_BYTE_LIMIT, + _TWO_BYTE_LIMIT, + _THREE_BYTE_LIMIT, + _FOUR_BYTE_LIMIT ]; + + void close() { + flush(); + } + + /** + * Flushes this decoder as if closed. + * + * This method throws if the input was partial and the decoder was + * constructed with `allowMalformed` set to `false`. + */ + void flush() { + if (hasPartialInput) { + if (!_allowMalformed) { + throw new FormatException("Unfinished UTF-8 octet sequence"); + } + _stringSink.writeCharCode(UNICODE_REPLACEMENT_CHARACTER_RUNE); + _value = 0; + _expectedUnits = 0; + _extraUnits = 0; + } + } + + void convert(List codeUnits, int startIndex, int endIndex) { + int value = _value; + int expectedUnits = _expectedUnits; + int extraUnits = _extraUnits; + int singleBytesCount = 0; + _value = 0; + _expectedUnits = 0; + _extraUnits = 0; + + void addSingleBytes(int from, int to) { + assert(singleBytesCount > 0); + assert(from >= startIndex && from <= endIndex); + assert(to >= startIndex && to <= endIndex); + if (from == 0 && to == codeUnits.length) { + _stringSink.write(new String.fromCharCodes(codeUnits)); + } else { + _stringSink.write( + new String.fromCharCodes(codeUnits.sublist(from, to))); + } + singleBytesCount = 0; + } + + int i = startIndex; + loop: while (true) { + multibyte: if (expectedUnits > 0) { + do { + if (i == endIndex) { + break loop; + } + int unit = codeUnits[i]; + if ((unit & 0xC0) != 0x80) { + expectedUnits = 0; + if (!_allowMalformed) { + throw new FormatException( + "Bad UTF-8 encoding 0x${unit.toRadixString(16)}"); + } + _isFirstCharacter = false; + _stringSink.writeCharCode(UNICODE_REPLACEMENT_CHARACTER_RUNE); + break multibyte; + } else { + value = (value << 6) | (unit & 0x3f); + expectedUnits--; + i++; + } + } while (expectedUnits > 0); + if (value <= _LIMITS[extraUnits - 1]) { + // Overly long encoding. The value could be encoded with a shorter + // encoding. + if (!_allowMalformed) { + throw new FormatException( + "Overlong encoding of 0x${value.toRadixString(16)}"); + } + expectedUnits = extraUnits = 0; + value = UNICODE_REPLACEMENT_CHARACTER_RUNE; + } + if (value > _FOUR_BYTE_LIMIT) { + if (!_allowMalformed) { + throw new FormatException("Character outside valid Unicode range: " + "0x${value.toRadixString(16)}"); + } + value = UNICODE_REPLACEMENT_CHARACTER_RUNE; + } + if (!_isFirstCharacter || value != UNICODE_BOM_CHARACTER_RUNE) { + _stringSink.writeCharCode(value); + } + _isFirstCharacter = false; + } + + while (i < endIndex) { + int unit = codeUnits[i++]; + // TODO(floitsch): the way we test we could potentially allow + // units that are too large, if they happen to have the + // right bit-pattern. (Same is true for the multibyte loop above). + // TODO(floitsch): optimize this loop. See: + // https://codereview.chromium.org/22929022/diff/1/sdk/lib/convert/utf.dart?column_width=80 + if (unit < 0) { + // TODO(floitsch): should this be unit <= 0 ? + if (singleBytesCount > 0) { + int to = i - 1; + addSingleBytes(to - singleBytesCount, to); + } + if (!_allowMalformed) { + throw new FormatException( + "Negative UTF-8 code unit: -0x${(-unit).toRadixString(16)}"); + } + _stringSink.writeCharCode(UNICODE_REPLACEMENT_CHARACTER_RUNE); + } else if (unit <= _ONE_BYTE_LIMIT) { + _isFirstCharacter = false; + singleBytesCount++; + } else { + if (singleBytesCount > 0) { + int to = i - 1; + addSingleBytes(to - singleBytesCount, to); + } + if ((unit & 0xE0) == 0xC0) { + value = unit & 0x1F; + expectedUnits = extraUnits = 1; + continue loop; + } + if ((unit & 0xF0) == 0xE0) { + value = unit & 0x0F; + expectedUnits = extraUnits = 2; + continue loop; + } + // 0xF5, 0xF6 ... 0xFF never appear in valid UTF-8 sequences. + if ((unit & 0xF8) == 0xF0 && unit < 0xF5) { + value = unit & 0x07; + expectedUnits = extraUnits = 3; + continue loop; + } + if (!_allowMalformed) { + throw new FormatException( + "Bad UTF-8 encoding 0x${unit.toRadixString(16)}"); + } + value = UNICODE_REPLACEMENT_CHARACTER_RUNE; + expectedUnits = extraUnits = 0; + _isFirstCharacter = false; + _stringSink.writeCharCode(value); + } + } + break loop; + } + if (singleBytesCount > 0) { + addSingleBytes(i - singleBytesCount, endIndex); + } + if (expectedUnits > 0) { + _value = value; + _expectedUnits = expectedUnits; + _extraUnits = extraUnits; + } + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/annotations.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/annotations.dart new file mode 100644 index 0000000..97eb11f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/annotations.dart @@ -0,0 +1,142 @@ +// Copyright (c) 2013, 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. + +part of dart.core; + +/** + * The annotation `@Deprecated('expires when')` marks a feature as deprecated. + * + * The annotation `@deprecated` is a shorthand for deprecating until + * an unspecified "next release". + * + * The intent of the `@Deprecated` annotation is to inform users of a feature + * that they should change their code, even if it is currently still working + * correctly. + * + * A deprecated feature is scheduled to be removed at a later time, possibly + * specified as the "expires" field of the annotation. + * This means that a deprecated feature should not be used, or code using it + * will break at some point in the future. If there is code using the feature, + * that code should be rewritten to not use the deprecated feature. + * + * A deprecated feature should document how the same effect can be achieved, + * so the programmer knows how to rewrite the code. + * + * The `@Deprecated` annotation applies to libraries, top-level declarations + * (variables, getters, setters, functions, classes and typedefs), + * class-level declarations (variables, getters, setters, methods, operators or + * constructors, whether static or not), named optional arguments and + * trailing optional positional parameters. + * + * Deprecation is transitive: + * + * - If a library is deprecated, so is every member of it. + * - If a class is deprecated, so is every member of it. + * - If a variable is deprecated, so are its implicit getter and setter. + * + * + * A tool that processes Dart source code may report when: + * + * - the code imports a deprecated library. + * - the code exports a deprecated library, or any deprecated member of + *  a non-deprecated library. + * - the code refers statically to a deprecated declaration. + * - the code dynamically uses a member of an object with a statically known + * type, where the member is deprecated on the static type of the object. + * - the code dynamically calls a method with an argument where the + * corresponding optional parameter is deprecated on the object's static type. + * + * + * If the deprecated use is inside a library, class or method which is itself + * deprecated, the tool should not bother the user about it. + * A deprecated feature is expected to use other deprecated features. + */ +class Deprecated { + /** + * A description of when the deprecated feature is expected to be retired. + */ + final String expires; + + /** + * Create a deprecation annotation which specifies the expiration of the + * annotated feature. + * + * The [expires] argument should be readable by programmers, and should state + * when an annotated feature is expected to be removed. + * This can be specified, for example, as a date, as a release number, or + * as relative to some other change (like "when bug 4418 is fixed"). + */ + const Deprecated(String expires) : this.expires = expires; + + String toString() => "Deprecated feature. Will be removed $expires"; +} + +class _Override { + const _Override(); +} + +/** + * Marks a feature as [Deprecated] until the next release. + */ +const Deprecated deprecated = const Deprecated("next release"); + +/* + * The annotation `@override` marks an instance member as overriding a + * superclass member with the same name. + * + * The annotation applies to instance methods, getters and setters, and to + * instance fields, where it means that the implicit getter and setter of the + * field is marked as overriding, but the field itself is not. + * + * The intent of the `@override` notation is to catch situations where a + * superclass renames a member, and an independent subclass which used to + * override the member, could silently continue working using the + * superclass implementation. + * + * The editor, or a similar tool aimed at the programmer, may report if no + * declaration of an annotated member is inherited by the class from either a + * superclass or an interface. + * + * Use the `@override` annotation judiciously and only for methods where + * the superclass is not under the programmer's control, the superclass is in a + * different library or package, and it is not considered stable. + * In any case, the use of `@override` is optional. + * + * For example, the annotation is intentionally not used in the Dart platform + * libraries, since they only depend on themselves. + */ +const Object override = const _Override(); + +class _Proxy { + const _Proxy(); +} + +/** + * The annotation `@proxy` marks a class as implementing interfaces and members + * dynamically through `noSuchMethod`. + * + * The annotation applies to any class. It is inherited by subclasses from both + * superclass and interfaces. + * + * If a class is annotated with `@proxy`, or it implements any class that is + * annotated, then the class is considered to implement any interface and + * any member with regard to static type analysis. As such, it is not a static + * type warning to assign the object to a variable of any type, and it is not + * a static type warning to access any member of the object. + * + * This only applies to static type warnings. The runtime type of the object + * is unaffected. It is not considered to implement any special interfaces at + * runtime, so assigning it to a typed variable may fail in checked mode, and + * testing it with the `is` operator will not work for any type except the + * ones it actually implements. + * + * Tools that understand `@proxy` should tell the user if a class using `@proxy` + * does not override the `noSuchMethod` declared on [Object]. + * + * The intent of the `@proxy` notation is to create objects that implement a + * type (or multiple types) that are not known at compile time. If the types + * are known at compile time, a class can be written that implements these + * types. + */ +const Object proxy = const _Proxy(); diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/bool.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/bool.dart new file mode 100644 index 0000000..8150219 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/bool.dart @@ -0,0 +1,43 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * The reserved words [:true:] and [:false:] denote objects that are the only + * instances of this class. + * + * It is a compile-time error for a class to attempt to extend or implement + * bool. + */ +class bool { + /** + * Returns the boolean value of the environment declaration [name]. + * + * The boolean value of the declaration is `true` if the declared value is + * the string `"true"`, and `false` if the value is `"false"`. + * + * In all other cases, including when there is no declaration for `name`, + * the result is the [defaultValue]. + * + * Example: + * + * const loggingFlag = const bool.fromEnvironment("logging"); + * + * If you want to use a different truth-string, you can use the + * [String.fromEnvironment] constructor directly: + * + * const isLoggingOn = (const String.fromEnvironment("logging") == "on"); + */ + external const factory bool.fromEnvironment(String name, + {bool defaultValue: false}); + + /** + * Returns [:"true":] if the receiver is [:true:], or [:"false":] if the + * receiver is [:false:]. + */ + String toString() { + return this ? "true" : "false"; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/comparable.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/comparable.dart new file mode 100644 index 0000000..7a22948 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/comparable.dart @@ -0,0 +1,94 @@ +// Copyright (c) 2011, 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. + +part of dart.core; + +/** + * The signature of a generic comparison function. + * + * A comparison function represents an ordering on a type of objects. + * A total ordering on a type means that for two values, either they + * are equal or one is greater than the other (and the latter must then be + * smaller than the former). + * + * A [Comparator] function represents such a total ordering by returning + * + * * a negative integer if [a] is smaller than [b], + * * zero if [a] is equal to [b], and + * * a positive integer if [a] is greater than [b]. + */ +typedef int Comparator(T a, T b); + +/** + * Interface used by types that have an intrinsic ordering. + * + * The [compareTo] operation defines a total ordering of objects, + * which can be used for ordering and sorting. + * + * The [Comparable] interface should be used for the natural ordering of a type. + * If a type can be ordered in more than one way, + * and none of them is the obvious natural ordering, + * then it might be better not to use the [Comparable] interface, + * and to provide separate [Comparator]s instead. + * + * It is recommended that the order of a [Comparable] agrees + * with its operator [==] equality (`a.compareTo(b) == 0` iff `a == b`), + * but this is not a requirement. + * For example, [double] and [DateTime] have `compareTo` methods + * that do not agree with operator [==]. + * For doubles the [compareTo] method is more precise than the equality, + * and for [DateTime] it is less precise. + * + * Examples: + * + * (0.0).compareTo(-0.0); // => 1 + * 0.0 == -0.0; // => true + * var dt = new DateTime.now(); + * var dt2 = dt.toUtc(); + * dt == dt2; // => false + * dt.compareTo(dt2); // => 0 + * + * The [Comparable] interface does not imply the existence + * of the comparison operators `<`, `<=`, `>` and `>=`. + * These should only be defined + * if the ordering is a less-than/greater-than ordering, + * that is, an ordering where you would naturally + * use the words "less than" about the order of two elements. + * + * If the equality operator and [compareTo] disagree, + * the comparison operators should follow the equality operator, + * and will likely also disagree with [compareTo]. + * Otherwise they should match the [compareTo] method, + * so that `a < b` iff `a.compareTo(b) < 0`. + * + * The [double] class defines comparison operators + * that are compatible with equality. + * The operators differ from `double.compareTo` on -0.0 and NaN. + * + * The [DateTime] class has no comparison operators, instead it has the more + * precisely named [DateTime.isBefore] and [DateTime.isAfter]. + */ +abstract class Comparable { + /** + * Compares this object to another [Comparable] + * + * Returns a value like a [Comparator] when comparing `this` to [other]. + * That is, it returns a negative integer if `this` is ordered before [other], + * a positive integer if `this` is ordered after [other], + * and zero if `this` and [other] are ordered together. + * + * The [other] argument must be a value that is comparable to this object. + */ + int compareTo(T other); + + /** + * A [Comparator] that compares one comparable to another. + * + * It returns the result of `a.compareTo(b)`. + * + * This utility function is used as the default comparator + * for ordering collections, for example in the [List] sort function. + */ + static int compare(Comparable a, Comparable b) => a.compareTo(b); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/core.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/core.dart new file mode 100644 index 0000000..4b11edc --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/core.dart @@ -0,0 +1,193 @@ +// Copyright (c) 2012, 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. + +/** + * + * Built-in types, collections, + * and other core functionality for every Dart program. + * + * This library is automatically imported. + * + * Some classes in this library, + * such as [String] and [num], + * support Dart's built-in data types. + * Other classes, such as [List] and [Map], provide data structures + * for managing collections of objects. + * And still other classes represent commonly used types of data + * such as URIs, dates and times, and errors. + * + * ## Numbers and booleans + * + * [int] and [double] provide support for Dart's built-in numerical data types: + * integers and double-precision floating point numbers, respectively. + * An object of type [bool] is either true or false. + * Variables of these types can be constructed from literals: + * + * int meaningOfLife = 42; + * double valueOfPi = 3.141592; + * bool visible = true; + * + * ## Strings and regular expressions + * + * A [String] is immutable and represents a sequence of characters. + * + * String shakespeareQuote = "All the world's a stage, ..."; + * + * [StringBuffer] provides a way to construct strings efficiently. + * + * StringBuffer moreShakespeare = new StringBuffer(); + * moreShakespeare.write('And all the men and women '); + * moreShakespeare.write('merely players; ...'); + * + * The String and StringBuffer classes implement string concatenation, + * interpolation, and other string manipulation features. + * + * String philosophy = 'Live on '; + * String get palindrome => philosophy + philosophy.split('').reversed.join(); + * + * [RegExp] implements Dart regular expressions, + * which provide a grammar for matching patterns within text. + * For example, here's a regular expression that matches + * a string of one or more digits: + * + * var numbers = new RegExp(r'\d+'); + * + * Dart regular expressions have the same syntax and semantics as + * JavaScript regular expressions. See + * + * for the specification of JavaScript regular expressions. + * + * ## Collections + * + * The dart:core library provides basic collections, + * such as [List], [Map], and [Set]. + * + * A List is an ordered collection of objects, with a length. + * Lists are sometimes called arrays. + * Use a List when you need to access objects by index. + * + * List superheroes = [ 'Batman', 'Superman', 'Harry Potter' ]; + * + * A Set is an unordered collection of unique objects. + * You cannot get an item by index (position). + * Adding a duplicate item has no effect. + * + * Set villains = new Set(); + * villains.add('Joker'); + * villains.addAll( ['Lex Luther', 'Voldemort'] ); + * + * A Map is an unordered collection of key-value pairs. + * Maps are sometimes called associative arrays because + * maps associate a key to some value for easy retrieval. + * Keys are unique. + * Use a Map when you need to access objects + * by a unique identifier. + * + * Map sidekicks = { 'Batman': 'Robin', + * 'Superman': 'Lois Lane', + * 'Harry Potter': 'Ron and Hermione' }; + * + * In addition to these classes, + * dart:core contains [Iterable], + * an interface that defines functionality + * common in collections of objects. + * Examples include the ability + * to run a function on each element in the collection, + * to apply a test to each element, + * to retrieve an object, and to determine length. + * + * Iterable is implemented by List and Set, + * and used by Map for its keys and values. + * + * For other kinds of collections, check out the + * [dart:collection](#dart-collection) library. + * + * ## Date and time + * + * Use [DateTime] to represent a point in time + * and [Duration] to represent a span of time. + * + * You can create DateTime objects with constructors + * or by parsing a correctly formatted string. + * + * DateTime now = new DateTime.now(); + * DateTime berlinWallFell = new DateTime(1989, 11, 9); + * DateTime moonLanding = DateTime.parse("1969-07-20"); + * + * Create a Duration object specifying the individual time units. + * + * Duration timeRemaining = new Duration(hours:56, minutes:14); + * + * In addition to DateTime and Duration, + * dart:core contains the [Stopwatch] class for measuring elapsed time. + * + * ## Uri + * + * A [Uri] object represents a uniform resource identifier, + * which identifies a resource on the web. + * + * Uri dartlang = Uri.parse('http://dartlang.org/'); + * + * ## Errors + * + * The [Error] class represents the occurrence of an error + * during runtime. + * Subclasses of this class represent specific kinds of errors. + * + * ## Other documentation + * + * For more information about how to use the built-in types, refer to + * [Built-in Types](http://www.dartlang.org/docs/dart-up-and-running/contents/ch02.html#built-in-types) + * in Chapter 2 of + * [Dart: Up and Running](http://www.dartlang.org/docs/dart-up-and-running/). + * + * Also, see + * [dart:core - Numbers, Collections, Strings, and More](http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html#ch03-dartcore---strings-collections-and-more) + * for more coverage of classes in this package. + * + * The + * [Dart Language Specification](http://www.dartlang.org/docs/spec/) + * provides technical details. + */ +library dart.core; + +import "dart:collection"; +import "dart:_internal" hide Symbol; +import "dart:_internal" as internal show Symbol; +import "dart:convert" show UTF8, LATIN1, Encoding; +import "dart:math" show Random; // Used by List.shuffle. + +part "annotations.dart"; +part "bool.dart"; +part "comparable.dart"; +part "date_time.dart"; +part "double.dart"; +part "duration.dart"; +part "errors.dart"; +part "exceptions.dart"; +part "expando.dart"; +part "function.dart"; +part "identical.dart"; +part "int.dart"; +part "invocation.dart"; +part "iterable.dart"; +part "iterator.dart"; +part "list.dart"; +part "map.dart"; +part "null.dart"; +part "num.dart"; +part "object.dart"; +part "pattern.dart"; +part "print.dart"; +part "regexp.dart"; +part "set.dart"; +part "sink.dart"; +part "stacktrace.dart"; +part "stopwatch.dart"; +part "string.dart"; +part "string_buffer.dart"; +part "string_sink.dart"; +part "symbol.dart"; +part "type.dart"; +part "uri.dart"; diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/date_time.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/date_time.dart new file mode 100644 index 0000000..2e93ad1 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/date_time.dart @@ -0,0 +1,631 @@ +// Copyright (c) 2011, 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. + +part of dart.core; + +/** + * An instant in time, such as July 20, 1969, 8:18pm GMT. + * + * Create a DateTime object by using one of the constructors + * or by parsing a correctly formatted string, + * which complies with a subset of ISO 8601. + * Note that hours are specified between 0 and 23, + * as in a 24-hour clock. + * For example: + * + * DateTime now = new DateTime.now(); + * DateTime berlinWallFell = new DateTime(1989, 11, 9); + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); // 8:18pm + * + * A DateTime object is anchored either in the UTC time zone + * or in the local time zone of the current computer + * when the object is created. + * + * Once created, neither the value nor the time zone + * of a DateTime object may be changed. + * + * You can use properties to get + * the individual units of a DateTime object. + * + * assert(berlinWallFell.month == 11); + * assert(moonLanding.hour == 20); + * + * For convenience and readability, + * the DateTime class provides a constant for each day and month + * name—for example, [AUGUST] and [FRIDAY]. + * You can use these constants to improve code readibility: + * + * DateTime berlinWallFell = new DateTime(1989, DateTime.NOVEMBER, 9); + * assert(berlinWallFell.weekday == DateTime.THURSDAY); + * + * Day and month values begin at 1, and the week starts on Monday. + * That is, the constants [JANUARY] and [MONDAY] are both 1. + * + * ## Working with UTC and local time + * + * A DateTime object is in the local time zone + * unless explicitly created in the UTC time zone. + * + * DateTime dDay = new DateTime.utc(1944, 6, 6); + * + * Use [isUtc] to determine whether a DateTime object is based in UTC. + * Use the methods [toLocal] and [toUtc] + * to get the equivalent date/time value specified in the other time zone. + * Use [timeZoneName] to get an abbreviated name of the time zone + * for the DateTime object. + * To find the difference + * between UTC and the time zone of a DateTime object + * call [timeZoneOffset]. + * + * ## Comparing DateTime objects + * + * The DateTime class contains several handy methods, + * such as [isAfter], [isBefore], and [isAtSameMomentAs], + * for comparing DateTime objects. + * + * assert(berlinWallFell.isAfter(moonLanding) == true); + * assert(berlinWallFell.isBefore(moonLanding) == false); + * + * ## Using DateTime with Duration + * + * Use the [add] and [subtract] methods with a [Duration] object + * to create a new DateTime object based on another. + * For example, to find the date that is sixty days after today, write: + * + * DateTime today = new DateTime.now(); + * DateTime sixtyDaysFromNow = today.add(new Duration(days: 60)); + * + * To find out how much time is between two DateTime objects use + * [difference], which returns a [Duration] object: + * + * Duration difference = berlinWallFell.difference(moonLanding) + * assert(difference.inDays == 7416); + * + * The difference between two dates in different time zones + * is just the number of nanoseconds between the two points in time. + * It doesn't take calendar days into account. + * That means that the difference between two midnights in local time may be + * less than 24 hours times the number of days between them, + * if there is a daylight saving change in between. + * If the difference above is calculated using Australian local time, the + * difference is 7415 days and 23 hours, which is only 7415 whole days as + * reported by `inDays`. + * + * ## Other resources + * + * See [Duration] to represent a span of time. + * See [Stopwatch] to measure timespans. + * + * The DateTime class does not provide internationalization. + * To internationalize your code, use + * the [intl](http://pub.dartlang.org/packages/intl) package. + * + */ +class DateTime implements Comparable { + // Weekday constants that are returned by [weekday] method: + static const int MONDAY = 1; + static const int TUESDAY = 2; + static const int WEDNESDAY = 3; + static const int THURSDAY = 4; + static const int FRIDAY = 5; + static const int SATURDAY = 6; + static const int SUNDAY = 7; + static const int DAYS_PER_WEEK = 7; + + // Month constants that are returned by the [month] getter. + static const int JANUARY = 1; + static const int FEBRUARY = 2; + static const int MARCH = 3; + static const int APRIL = 4; + static const int MAY = 5; + static const int JUNE = 6; + static const int JULY = 7; + static const int AUGUST = 8; + static const int SEPTEMBER = 9; + static const int OCTOBER = 10; + static const int NOVEMBER = 11; + static const int DECEMBER = 12; + static const int MONTHS_PER_YEAR = 12; + + /** + * The number of milliseconds since + * the "Unix epoch" 1970-01-01T00:00:00Z (UTC). + * + * This value is independent of the time zone. + * + * This value is at most + * 8,640,000,000,000,000ms (100,000,000 days) from the Unix epoch. + * In other words: [:millisecondsSinceEpoch.abs() <= 8640000000000000:]. + * + */ + final int millisecondsSinceEpoch; + + /** + * True if this [DateTime] is set to UTC time. + * + * DateTime dDay = new DateTime.utc(1944, 6, 6); + * assert(dDay.isUtc); + * + */ + final bool isUtc; + + /** + * Constructs a [DateTime] instance specified in the local time zone. + * + * For example, + * to create a new DateTime object representing April 29, 2014, 6:04am: + * + * DateTime annularEclipse = new DateTime(2014, DateTime.APRIL, 29, 6, 4); + */ + DateTime(int year, + [int month = 1, + int day = 1, + int hour = 0, + int minute = 0, + int second = 0, + int millisecond = 0]) + : this._internal( + year, month, day, hour, minute, second, millisecond, false); + + /** + * Constructs a [DateTime] instance specified in the UTC time zone. + * + * DateTime dDay = new DateTime.utc(1944, DateTime.JUNE, 6); + */ + DateTime.utc(int year, + [int month = 1, + int day = 1, + int hour = 0, + int minute = 0, + int second = 0, + int millisecond = 0]) + : this._internal( + year, month, day, hour, minute, second, millisecond, true); + + /** + * Constructs a [DateTime] instance with current date and time in the + * local time zone. + * + * DateTime thisInstant = new DateTime.now(); + * + */ + DateTime.now() : this._now(); + + /** + * Constructs a new [DateTime] instance based on [formattedString]. + * + * Throws a [FormatException] if the input cannot be parsed. + * + * The function parses a subset of ISO 8601 + * which includes the subset accepted by RFC 3339. + * + * The result is always in either local time or UTC. + * If a time zone offset other than UTC is specified, + * the time is converted to the equivalent UTC time. + * + * Examples of accepted strings: + * + * * `"2012-02-27 13:27:00"` + * * `"2012-02-27 13:27:00.123456z"` + * * `"20120227 13:27:00"` + * * `"20120227T132700"` + * * `"20120227"` + * * `"+20120227"` + * * `"2012-02-27T14Z"` + * * `"2012-02-27T14+00:00"` + * * `"-123450101 00:00:00 Z"`: in the year -12345. + * * `"2002-02-27T14:00:00-0500"`: Same as `"2002-02-27T19:00:00Z"` + */ + // TODO(floitsch): specify grammar. + // TODO(lrn): restrict incorrect values like 2003-02-29T50:70:80. + static DateTime parse(String formattedString) { + /* + * date ::= yeardate time_opt timezone_opt + * yeardate ::= year colon_opt month colon_opt day + * year ::= sign_opt digit{4,5} + * colon_opt :: | ':' + * sign ::= '+' | '-' + * sign_opt ::= | sign + * month ::= digit{2} + * day ::= digit{2} + * time_opt ::= | (' ' | 'T') hour minutes_opt + * minutes_opt ::= | ':' digit{2} seconds_opt + * seconds_opt ::= | ':' digit{2} millis_opt + * millis_opt ::= | '.' digit{1,6} + * timezone_opt ::= | space_opt timezone + * space_opt :: ' ' | + * timezone ::= 'z' | 'Z' | sign digit{2} timezonemins_opt + * timezonemins_opt ::= | colon_opt digit{2} + */ + final RegExp re = new RegExp( + r'^([+-]?\d{4,5})-?(\d\d)-?(\d\d)' // The day part. + r'(?:[ T](\d\d)(?::?(\d\d)(?::?(\d\d)(.\d{1,6})?)?)?' // The time part + r'( ?[zZ]| ?([-+])(\d\d)(?::?(\d\d))?)?)?$'); // The timezone part + + Match match = re.firstMatch(formattedString); + if (match != null) { + int parseIntOrZero(String matched) { + if (matched == null) return 0; + return int.parse(matched); + } + + double parseDoubleOrZero(String matched) { + if (matched == null) return 0.0; + return double.parse(matched); + } + + int years = int.parse(match[1]); + int month = int.parse(match[2]); + int day = int.parse(match[3]); + int hour = parseIntOrZero(match[4]); + int minute = parseIntOrZero(match[5]); + int second = parseIntOrZero(match[6]); + bool addOneMillisecond = false; + int millisecond = (parseDoubleOrZero(match[7]) * 1000).round(); + if (millisecond == 1000) { + addOneMillisecond = true; + millisecond = 999; + } + bool isUtc = false; + if (match[8] != null) { // timezone part + isUtc = true; + if (match[9] != null) { + // timezone other than 'Z' and 'z'. + int sign = (match[9] == '-') ? -1 : 1; + int hourDifference = int.parse(match[10]); + int minuteDifference = parseIntOrZero(match[11]); + minuteDifference += 60 * hourDifference; + minute -= sign * minuteDifference; + } + } + int millisecondsSinceEpoch = _brokenDownDateToMillisecondsSinceEpoch( + years, month, day, hour, minute, second, millisecond, isUtc); + if (millisecondsSinceEpoch == null) { + throw new FormatException(formattedString); + } + if (addOneMillisecond) millisecondsSinceEpoch++; + return new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, + isUtc: isUtc); + } else { + throw new FormatException(formattedString); + } + } + + static const int _MAX_MILLISECONDS_SINCE_EPOCH = 8640000000000000; + + /** + * Constructs a new [DateTime] instance + * with the given [millisecondsSinceEpoch]. + * + * If [isUtc] is false then the date is in the local time zone. + * + * The constructed [DateTime] represents + * 1970-01-01T00:00:00Z + [millisecondsSinceEpoch] ms in the given + * time zone (local or UTC). + */ + DateTime.fromMillisecondsSinceEpoch(int millisecondsSinceEpoch, + {bool isUtc: false}) + : this.millisecondsSinceEpoch = millisecondsSinceEpoch, + this.isUtc = isUtc { + if (millisecondsSinceEpoch.abs() > _MAX_MILLISECONDS_SINCE_EPOCH) { + throw new ArgumentError(millisecondsSinceEpoch); + } + if (isUtc == null) throw new ArgumentError(isUtc); + } + + /** + * Returns true if [other] is a [DateTime] at the same moment and in the + * same time zone (UTC or local). + * + * DateTime dDayUtc = new DateTime.utc(1944, DateTime.JUNE, 6); + * DateTime dDayLocal = new DateTime(1944, DateTime.JUNE, 6); + * + * assert(dDayUtc.isAtSameMomentAs(dDayLocal) == false); + * + * See [isAtSameMomentAs] for a comparison that adjusts for time zone. + */ + bool operator ==(other) { + if (!(other is DateTime)) return false; + return (millisecondsSinceEpoch == other.millisecondsSinceEpoch && + isUtc == other.isUtc); + } + + /** + * Returns true if [this] occurs before [other]. + * + * The comparison is independent + * of whether the time is in UTC or in the local time zone. + * + * DateTime berlinWallFell = new DateTime(1989, 11, 9); + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); + * + * assert(berlinWallFell.isBefore(moonLanding) == false); + * + */ + bool isBefore(DateTime other) { + return millisecondsSinceEpoch < other.millisecondsSinceEpoch; + } + + /** + * Returns true if [this] occurs after [other]. + * + * The comparison is independent + * of whether the time is in UTC or in the local time zone. + * + * DateTime berlinWallFell = new DateTime(1989, 11, 9); + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); + * + * assert(berlinWallFell.isAfter(moonLanding) == true); + * + */ + bool isAfter(DateTime other) { + return millisecondsSinceEpoch > other.millisecondsSinceEpoch; + } + + /** + * Returns true if [this] occurs at the same moment as [other]. + * + * The comparison is independent of whether the time is in UTC or in the local + * time zone. + * + * DateTime berlinWallFell = new DateTime(1989, 11, 9); + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); + * + * assert(berlinWallFell.isAtSameMomentAs(moonLanding) == false); + */ + bool isAtSameMomentAs(DateTime other) { + return millisecondsSinceEpoch == other.millisecondsSinceEpoch; + } + + /** + * Compares this DateTime object to [other], + * returning zero if the values are equal. + * + * This function returns a negative integer + * if this DateTime is smaller (earlier) than [other], + * or a positive integer if it is greater (later). + */ + int compareTo(DateTime other) + => millisecondsSinceEpoch.compareTo(other.millisecondsSinceEpoch); + + int get hashCode => millisecondsSinceEpoch; + + /** + * Returns this DateTime value in the local time zone. + * + * Returns [this] if it is already in the local time zone. + * Otherwise this method is equivalent to: + * + * new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, + * isUtc: false) + */ + DateTime toLocal() { + if (isUtc) { + return new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, + isUtc: false); + } + return this; + } + + /** + * Returns this DateTime value in the UTC time zone. + * + * Returns [this] if it is already in UTC. + * Otherwise this method is equivalent to: + * + * new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, + * isUtc: true) + */ + DateTime toUtc() { + if (isUtc) return this; + return new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, + isUtc: true); + } + + static String _fourDigits(int n) { + int absN = n.abs(); + String sign = n < 0 ? "-" : ""; + if (absN >= 1000) return "$n"; + if (absN >= 100) return "${sign}0$absN"; + if (absN >= 10) return "${sign}00$absN"; + return "${sign}000$absN"; + } + + static String _threeDigits(int n) { + if (n >= 100) return "${n}"; + if (n >= 10) return "0${n}"; + return "00${n}"; + } + + static String _twoDigits(int n) { + if (n >= 10) return "${n}"; + return "0${n}"; + } + + /** + * Returns a human-readable string for this instance. + * + * The returned string is constructed for the time zone of this instance. + * The `toString()` method provides a simply formatted string. + * It does not support internationalized strings. + * Use the [intl](http://pub.dartlang.org/packages/intl) package + * at the pub shared packages repo. + */ + String toString() { + String y = _fourDigits(year); + String m = _twoDigits(month); + String d = _twoDigits(day); + String h = _twoDigits(hour); + String min = _twoDigits(minute); + String sec = _twoDigits(second); + String ms = _threeDigits(millisecond); + if (isUtc) { + return "$y-$m-$d $h:$min:$sec.${ms}Z"; + } else { + return "$y-$m-$d $h:$min:$sec.$ms"; + } + } + + /** + * Returns an ISO-8601 full-precision extended format representation. + * + * The format is "YYYY-MM-DDTHH:mm:ss.sssZ" for UTC time, and + * "YYYY-MM-DDTHH:mm:ss.sss" (no trailing "Z") for local/non-UTC time. + */ + String toIso8601String() { + String y = _fourDigits(year); + String m = _twoDigits(month); + String d = _twoDigits(day); + String h = _twoDigits(hour); + String min = _twoDigits(minute); + String sec = _twoDigits(second); + String ms = _threeDigits(millisecond); + if (isUtc) { + return "$y-$m-${d}T$h:$min:$sec.${ms}Z"; + } else { + return "$y-$m-${d}T$h:$min:$sec.$ms"; + } + } + + /** + * Returns a new [DateTime] instance with [duration] added to [this]. + * + * DateTime today = new DateTime.now(); + * DateTime sixtyDaysFromNow = today.add(new Duration(days: 60)); + */ + DateTime add(Duration duration) { + int ms = millisecondsSinceEpoch; + return new DateTime.fromMillisecondsSinceEpoch( + ms + duration.inMilliseconds, isUtc: isUtc); + } + + /** + * Returns a new [DateTime] instance with [duration] subtracted from [this]. + * + * DateTime today = new DateTime.now(); + * DateTime sixtyDaysAgo = today.subtract(new Duration(days: 60)); + */ + DateTime subtract(Duration duration) { + int ms = millisecondsSinceEpoch; + return new DateTime.fromMillisecondsSinceEpoch( + ms - duration.inMilliseconds, isUtc: isUtc); + } + + /** + * Returns a [Duration] with the difference between [this] and [other]. + * + * DateTime berlinWallFell = new DateTime(1989, DateTime.NOVEMBER, 9); + * DateTime dDay = new DateTime(1944, DateTime.JUNE, 6); + * + * Duration difference = berlinWallFell.difference(dDay); + * assert(difference.inDays == 16592); + */ + + Duration difference(DateTime other) { + int ms = millisecondsSinceEpoch; + int otherMs = other.millisecondsSinceEpoch; + return new Duration(milliseconds: ms - otherMs); + } + + external DateTime._internal(int year, + int month, + int day, + int hour, + int minute, + int second, + int millisecond, + bool isUtc); + external DateTime._now(); + external static int _brokenDownDateToMillisecondsSinceEpoch( + int year, int month, int day, int hour, int minute, int second, + int millisecond, bool isUtc); + + /** + * The abbreviated time zone name—for example, + * [:"CET":] or [:"CEST":]. + */ + external String get timeZoneName; + + /** + * The time zone offset, which + * is the difference between local time and UTC. + * + * The offset is positive for time zones east of UTC. + * + * Note, that JavaScript, Python and C return the difference between UTC and + * local time. Java, C# and Ruby return the difference between local time and + * UTC. + */ + external Duration get timeZoneOffset; + + /** + * The year. + * + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); + * assert(moonLanding.year == 1969); + */ + external int get year; + + /** + * The month [1..12]. + * + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); + * assert(moonLanding.month == 7); + * assert(moonLanding.month == DateTime.JULY); + */ + external int get month; + + /** + * The day of the month [1..31]. + * + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); + * assert(moonLanding.day == 20); + */ + external int get day; + + /** + * The hour of the day, expressed as in a 24-hour clock [0..23]. + * + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); + * assert(moonLanding.hour == 20); + */ + external int get hour; + + /** + * The minute [0...59]. + * + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); + * assert(moonLanding.minute == 18); + */ + external int get minute; + + /** + * The second [0...59]. + * + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); + * assert(moonLanding.second == 0); + */ + external int get second; + + /** + * The millisecond [0...999]. + * + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); + * assert(moonLanding.millisecond == 0); + */ + external int get millisecond; + + /** + * The day of the week [MONDAY]..[SUNDAY]. + * + * In accordance with ISO 8601 + * a week starts with Monday, which has the value 1. + * + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); + * assert(moonLanding.weekday == 7); + * assert(moonLanding.weekday == DateTime.SUNDAY); + * + */ + external int get weekday; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/double.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/double.dart new file mode 100644 index 0000000..089bbd1 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/double.dart @@ -0,0 +1,205 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +// TODO: Convert this abstract class into a concrete class double +// that uses the patch class functionality to account for the +// different platform implementations. + +/** + * A double-precision floating point number. + * + * Representation of Dart doubles containing double specific constants + * and operations and specializations of operations inherited from + * [num]. Dart doubles are 64-bit floating-point numbers as specified in the + * IEEE 754 standard. + * + * The [double] type is contagious. Operations on [double]s return + * [double] results. + * + * It is a compile-time error for a class to attempt to extend or implement + * double. + */ +abstract class double extends num { + static const double NAN = 0.0 / 0.0; + static const double INFINITY = 1.0 / 0.0; + static const double NEGATIVE_INFINITY = -INFINITY; + static const double MIN_POSITIVE = 5e-324; + static const double MAX_FINITE = 1.7976931348623157e+308; + + double remainder(num other); + + /** Addition operator. */ + double operator +(num other); + + /** Subtraction operator. */ + double operator -(num other); + + /** Multiplication operator. */ + double operator *(num other); + + double operator %(num other); + + /** Division operator. */ + double operator /(num other); + + /** + * Truncating division operator. + * + * The result of the truncating division `a ~/ b` is equivalent to + * `(a / b).truncate()`. + */ + int operator ~/(num other); + + /** Negate operator. */ + double operator -(); + + /** Returns the absolute value of this [double]. */ + double abs(); + + /** + * Returns the sign of the double's numerical value. + * + * Returns -1.0 if the value is less than zero, + * +1.0 if the value is greater than zero, + * and the value itself if it is -0.0, 0.0 or NaN. + */ + double get sign; + + /** + * Returns the integer closest to `this`. + * + * Rounds away from zero when there is no closest integer: + * `(3.5).round() == 4` and `(-3.5).round() == -4`. + * + * If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. + */ + int round(); + + /** + * Returns the greatest integer no greater than `this`. + * + * If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. + */ + int floor(); + + /** + * Returns the least integer no smaller than `this`. + * + * If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. + */ + int ceil(); + + /** + * Returns the integer obtained by discarding any fractional + * digits from `this`. + * + * If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. + */ + int truncate(); + + /** + * Returns the integer double value closest to `this`. + * + * Rounds away from zero when there is no closest integer: + * `(3.5).roundToDouble() == 4` and `(-3.5).roundToDouble() == -4`. + * + * If this is already an integer valued double, including `-0.0`, or it is not + * a finite value, the value is returned unmodified. + * + * For the purpose of rounding, `-0.0` is considered to be below `0.0`, + * and `-0.0` is therefore considered closer to negative numbers than `0.0`. + * This means that for a value, `d` in the range `-0.5 < d < 0.0`, + * the result is `-0.0`. + */ + double roundToDouble(); + + /** + * Returns the greatest integer double value no greater than `this`. + * + * If this is already an integer valued double, including `-0.0`, or it is not + * a finite value, the value is returned unmodified. + * + * For the purpose of rounding, `-0.0` is considered to be below `0.0`. + * A number `d` in the range `0.0 < d < 1.0` will return `0.0`. + */ + double floorToDouble(); + + /** + * Returns the least integer double value no smaller than `this`. + * + * If this is already an integer valued double, including `-0.0`, or it is not + * a finite value, the value is returned unmodified. + * + * For the purpose of rounding, `-0.0` is considered to be below `0.0`. + * A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. + */ + double ceilToDouble(); + + /** + * Returns the integer double value obtained by discarding any fractional + * digits from `this`. + * + * If this is already an integer valued double, including `-0.0`, or it is not + * a finite value, the value is returned unmodified. + * + * For the purpose of rounding, `-0.0` is considered to be below `0.0`. + * A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and + * in the range `0.0 < d < 1.0` it will return 0.0. + */ + double truncateToDouble(); + + /** + * Provide a representation of this [double] value. + * + * The representation is a number literal such that the closest double value + * to the representation's mathematical value is this [double]. + * + * Returns "NaN" for the Not-a-Number value. + * Returns "Infinity" and "-Infinity" for positive and negative Infinity. + * Returns "-0.0" for negative zero. + * + * For all doubles, `d`, converting to a string and parsing the string back + * gives the same value again: `d == double.parse(d.toString())`. + */ + String toString(); + + /** + * Parse [source] as an double literal and return its value. + * + * Accepts an optional sign (`+` or `-`) followed by either the characters + * "Infinity", the characters "NaN" or a floating-point representation. + * A floating-point representation is composed of a mantissa and an optional + * exponent part. The mantissa is either a decimal point (`.`) followed by a + * sequence of (decimal) digits, or a sequence of digits + * optionally followed by a decimal point and optionally more digits. The + * (optional) exponent part consists of the character "e" or "E", an optional + * sign, and one or more digits. + * + * Leading and trailing whitespace is ignored. + * + * If the [source] is not a valid double literal, the [onError] + * is called with the [source] as argument, and its return value is + * used instead. If no `onError` is provided, a [FormatException] + * is thrown instead. + * + * The [onError] function is only invoked if [source] is a [String] with an + * invalid format. It is not invoked if the [source] is invalid for some + * other reason, for example by being `null`. + * + * Examples of accepted strings: + * + * "3.14" + * " 3.14 \xA0" + * "0." + * ".0" + * "-1.e3" + * "1234E+7" + * "+.12e-9" + * "-NaN" + */ + external static double parse(String source, + [double onError(String source)]); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/duration.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/duration.dart new file mode 100644 index 0000000..d174012 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/duration.dart @@ -0,0 +1,245 @@ +// Copyright (c) 2011, 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. + +part of dart.core; + +/** + * A span of time, such as 27 days, 4 hours, 12 minutes, and 3 seconds. + * + * To create a new Duration object, use this class's single constructor + * giving the appropriate arguments: + * + * Duration fastestMarathon = new Duration(hours:2, minutes:3, seconds:2); + * + * The Duration is the sum of all individual parts. + * This means that individual parts can be larger than the next-bigger unit. + * For example, [minutes] can be greater than 59. + * + * assert(fastestMarathon.inMinutes == 123); + * + * All individual parts are allowed to be negative. + * + * Use one of the properties, such as [inDays], + * to retrieve the integer value of the Duration in the specified time unit. + * Note that the returned value is rounded down. + * For example, + * + * Duration aLongWeekend = new Duration(hours:88); + * assert(aLongWeekend.inDays == 3); + * + * This class provides a collection of arithmetic + * and comparison operators, + * plus a set of constants useful for converting time units. + * + * See [DateTime] to represent a point in time. + * See [Stopwatch] to measure time-spans. + * + */ +class Duration implements Comparable { + static const int MICROSECONDS_PER_MILLISECOND = 1000; + static const int MILLISECONDS_PER_SECOND = 1000; + static const int SECONDS_PER_MINUTE = 60; + static const int MINUTES_PER_HOUR = 60; + static const int HOURS_PER_DAY = 24; + + static const int MICROSECONDS_PER_SECOND = + MICROSECONDS_PER_MILLISECOND * MILLISECONDS_PER_SECOND; + static const int MICROSECONDS_PER_MINUTE = + MICROSECONDS_PER_SECOND * SECONDS_PER_MINUTE; + static const int MICROSECONDS_PER_HOUR = + MICROSECONDS_PER_MINUTE * MINUTES_PER_HOUR; + static const int MICROSECONDS_PER_DAY = + MICROSECONDS_PER_HOUR * HOURS_PER_DAY; + + + static const int MILLISECONDS_PER_MINUTE = + MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE; + static const int MILLISECONDS_PER_HOUR = + MILLISECONDS_PER_MINUTE * MINUTES_PER_HOUR; + static const int MILLISECONDS_PER_DAY = + MILLISECONDS_PER_HOUR * HOURS_PER_DAY; + + static const int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR; + static const int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; + + static const int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY; + + static const Duration ZERO = const Duration(seconds: 0); + + /* + * The value of this Duration object in microseconds. + */ + final int _duration; + + /** + * Creates a new Duration object whose value + * is the sum of all individual parts. + * + * Individual parts can be larger than the next-bigger unit. + * For example, [hours] can be greater than 23. + * + * All individual parts are allowed to be negative. + * All arguments are 0 by default. + */ + const Duration({int days: 0, + int hours: 0, + int minutes: 0, + int seconds: 0, + int milliseconds: 0, + int microseconds: 0}) + : _duration = days * MICROSECONDS_PER_DAY + + hours * MICROSECONDS_PER_HOUR + + minutes * MICROSECONDS_PER_MINUTE + + seconds * MICROSECONDS_PER_SECOND + + milliseconds * MICROSECONDS_PER_MILLISECOND + + microseconds; + + /** + * Adds this Duration and [other] and + * returns the sum as a new Duration object. + */ + Duration operator +(Duration other) { + return new Duration(microseconds: _duration + other._duration); + } + + /** + * Subtracts [other] from this Duration and + * returns the difference as a new Duration object. + */ + Duration operator -(Duration other) { + return new Duration(microseconds: _duration - other._duration); + } + + /** + * Multiplies this Duration by the given [factor] and returns the result + * as a new Duration object. + * + * Note that when [factor] is a double, and the duration is greater than + * 53 bits, precision is lost because of double-precision arithmetic. + */ + Duration operator *(num factor) { + return new Duration(microseconds: (_duration * factor).round()); + } + + /** + * Divides this Duration by the given [quotient] and returns the truncated + * result as a new Duration object. + * + * Throws an [IntegerDivisionByZeroException] if [quotient] is `0`. + */ + Duration operator ~/(int quotient) { + // By doing the check here instead of relying on "~/" below we get the + // exception even with dart2js. + if (quotient == 0) throw new IntegerDivisionByZeroException(); + return new Duration(microseconds: _duration ~/ quotient); + } + + /** + * Returns `true` if the value of this Duration + * is less than the value of [other]. + */ + bool operator <(Duration other) => this._duration < other._duration; + + /** + * Returns `true` if the value of this Duration + * is greater than the value of [other]. + */ + bool operator >(Duration other) => this._duration > other._duration; + + /** + * Returns `true` if the value of this Duration + * is less than or equal to the value of [other]. + */ + bool operator <=(Duration other) => this._duration <= other._duration; + + /** + * Returns `true` if the value of this Duration + * is greater than or equal to the value of [other]. + */ + bool operator >=(Duration other) => this._duration >= other._duration; + + /** + * Returns the number of whole days spanned by this Duration. + */ + int get inDays => _duration ~/ Duration.MICROSECONDS_PER_DAY; + + /** + * Returns the number of whole hours spanned by this Duration. + * + * The returned value can be greater than 23. + */ + int get inHours => _duration ~/ Duration.MICROSECONDS_PER_HOUR; + + /** + * Returns the number of whole minutes spanned by this Duration. + * + * The returned value can be greater than 59. + */ + int get inMinutes => _duration ~/ Duration.MICROSECONDS_PER_MINUTE; + + /** + * Returns the number of whole seconds spanned by this Duration. + * + * The returned value can be greater than 59. + */ + int get inSeconds => _duration ~/ Duration.MICROSECONDS_PER_SECOND; + + /** + * Returns number of whole milliseconds spanned by this Duration. + * + * The returned value can be greater than 999. + */ + int get inMilliseconds => _duration ~/ Duration.MICROSECONDS_PER_MILLISECOND; + + /** + * Returns number of whole microseconds spanned by this Duration. + */ + int get inMicroseconds => _duration; + + /** + * Returns `true` if this Duration is the same object as [other]. + */ + bool operator ==(other) { + if (other is !Duration) return false; + return _duration == other._duration; + } + + int get hashCode => _duration.hashCode; + + /** + * Compares this Duration to [other], + * returning zero if the values are equal. + * + * This function returns a negative integer + * if this Duration is smaller than [other], + * or a positive integer if it is greater. + */ + int compareTo(Duration other) => _duration.compareTo(other._duration); + + String toString() { + String sixDigits(int n) { + if (n >= 100000) return "$n"; + if (n >= 10000) return "0$n"; + if (n >= 1000) return "00$n"; + if (n >= 100) return "000$n"; + if (n >= 10) return "0000$n"; + return "00000$n"; + } + String twoDigits(int n) { + if (n >= 10) return "$n"; + return "0$n"; + } + + if (inMicroseconds < 0) { + Duration duration = + new Duration(microseconds: -inMicroseconds); + return "-$duration"; + } + String twoDigitMinutes = twoDigits(inMinutes.remainder(MINUTES_PER_HOUR)); + String twoDigitSeconds = twoDigits(inSeconds.remainder(SECONDS_PER_MINUTE)); + String sixDigitUs = + sixDigits(inMicroseconds.remainder(MICROSECONDS_PER_SECOND)); + return "$inHours:$twoDigitMinutes:$twoDigitSeconds.$sixDigitUs"; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/errors.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/errors.dart new file mode 100644 index 0000000..905250c --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/errors.dart @@ -0,0 +1,367 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * Error objects thrown in the case of a program failure. + * + * An `Error` object represents a program failure that the programmer + * should have avoided. + * + * Examples include calling a function with invalid arguments, + * or even with the wrong number of arguments, + * or calling it at a time when it is not allowed. + * + * These are not errors that a caller should expect or catch - + * if they occur, the program is erroneous, + * and terminating the program may be the safest response. + * + * When deciding that a function throws an error, + * the conditions where it happens should be clearly described, + * and they should be detectable and predictable, + * so the programmer using the function can avoid triggering the error. + * + * Such descriptions often uses words like + * "must" or "must not" to describe the condition, + * and if you see words like that in a function's documentation, + * then not satisfying the requirement + * is very likely to cause an error to be thrown. + * + * Example (from [String.contains]): + * + * `startIndex` must not be negative or greater than `length`. + * + * In this case, an error will be thrown if `startIndex` is negative + * or too large. + * + * If the conditions are not detectable before calling a function, + * the called function should not throw an `Error`. + * It may still throw a value, + * but the caller will have to catch the thrown value, + * effectively making it an alternative result rather than an error. + * The thrown object can choose to implement [Exception] + * to document that it represents an exceptional, but not erroneous, occurrence, + * but it has no other effect than documentation. + * + * All non-`null` values can be thrown in Dart. + * Objects extending `Error` are handled specially: + * The first time they are thrown, + * the stack trace at the throw point is recorded + * and stored in the error object. + * It can be retrieved using the [stackTrace] getter. + * An error object that merely implements `Error`, and doesn't extend it, + * will not store the stack trace automatically. + * + * Error objects are also used for system wide failures + * like stack overflow or an out-of-memory situation. + * + * Since errors are not created to be caught, + * there is no need for subclasses to distinguish the errors. + * Instead subclasses have been created in order to make groups + * of related errors easy to create with consistent error messages. + * For example, the [String.contains] method will use a [RangeError] + * if its `startIndex` isn't in the range `0..length`, + * which is easily created by `new RangeError.range(startIndex, 0, length)`. + */ +class Error { + Error(); // Prevent use as mixin. + + /** + * Safely convert a value to a [String] description. + * + * The conversion is guaranteed to not throw, so it won't use the object's + * toString method. + */ + static String safeToString(Object object) { + if (object is num || object is bool || null == object) { + return object.toString(); + } + if (object is String) { + String string = object; + StringBuffer buffer = new StringBuffer('"'); + const int TAB = 0x09; + const int NEWLINE = 0x0a; + const int CARRIGE_RETURN = 0x0d; + const int BACKSLASH = 0x5c; + const int DOUBLE_QUOTE = 0x22; + const int DIGIT_ZERO = 0x30; + const int LOWERCASE_A = 0x61; + const int MAX_CONTROL = 0x1f; + for (int i = 0; i < string.length; i++) { + int codeUnit = string.codeUnitAt(i); + if (codeUnit <= MAX_CONTROL) { + if (codeUnit == NEWLINE) { + buffer.write(r"\n"); + } else if (codeUnit == CARRIGE_RETURN) { + buffer.write(r"\r"); + } else if (codeUnit == TAB) { + buffer.write(r"\t"); + } else { + buffer.write(r"\x"); + // Convert code in range 0x00 .. 0x1f to hex a two-digit hex string. + if (codeUnit < 0x10) { + buffer.write("0"); + } else { + buffer.write("1"); + codeUnit -= 0x10; + } + // Single digit to hex. + buffer.writeCharCode(codeUnit < 10 ? DIGIT_ZERO + codeUnit + : LOWERCASE_A - 10 + codeUnit); + } + } else if (codeUnit == BACKSLASH) { + buffer.write(r"\\"); + } else if (codeUnit == DOUBLE_QUOTE) { + buffer.write(r'\"'); + } else { + buffer.writeCharCode(codeUnit); + } + } + buffer.write('"'); + return buffer.toString(); + } + return _objectToString(object); + } + + external static String _objectToString(Object object); + + external StackTrace get stackTrace; +} + +/** + * Error thrown by the runtime system when an assert statement fails. + */ +class AssertionError extends Error { +} + +/** + * Error thrown by the runtime system when a type assertion fails. + */ +class TypeError extends AssertionError { +} + +/** + * Error thrown by the runtime system when a cast operation fails. + */ +class CastError extends Error { +} + +/** + * Error thrown when attempting to throw [:null:]. + */ +class NullThrownError extends Error { + String toString() => "Throw of null."; +} + +/** + * Error thrown when a function is passed an unacceptable argument. + */ +class ArgumentError extends Error { + final message; + + /** The [message] describes the erroneous argument. */ + ArgumentError([this.message]); + + String toString() { + if (message != null) { + return "Illegal argument(s): $message"; + } + return "Illegal argument(s)"; + } +} + +/** + * Error thrown due to an index being outside a valid range. + */ +class RangeError extends ArgumentError { + // TODO(lrn): This constructor should be called only with string values. + // It currently isn't in all cases. + /** + * Create a new [RangeError] with the given [message]. + */ + RangeError(var message) : super(message); + + /** Create a new [RangeError] with a message for the given [value]. */ + RangeError.value(num value) : super("value $value"); + + /** + * Create a new [RangeError] with a message for a value and a range. + * + * The allowed range is from [start] to [end], inclusive. + */ + RangeError.range(num value, num start, num end) + : super("value $value not in range $start..$end"); + + String toString() => "RangeError: $message"; +} + + +/** + * Error thrown when control reaches the end of a switch case. + * + * The Dart specification requires this error to be thrown when + * control reaches the end of a switch case (except the last case + * of a switch) without meeting a break or similar end of the control + * flow. + */ +class FallThroughError extends Error { + FallThroughError(); +} + +/** + * Error thrown when trying to instantiate an abstract class. + */ +class AbstractClassInstantiationError extends Error { + final String _className; + AbstractClassInstantiationError(String this._className); + String toString() => "Cannot instantiate abstract class: '$_className'"; +} + + +/** + * Error thrown by the default implementation of [:noSuchMethod:] on [Object]. + */ +class NoSuchMethodError extends Error { + final Object _receiver; + final Symbol _memberName; + final List _arguments; + final Map _namedArguments; + final List _existingArgumentNames; + + /** + * Create a [NoSuchMethodError] corresponding to a failed method call. + * + * The [receiver] is the receiver of the method call. + * That is, the object on which the method was attempted called. + * If the receiver is `null`, it is interpreted as a call to a top-level + * function of a library. + * + * The [memberName] is a [Symbol] representing the name of the called method + * or accessor. It should not be `null`. + * + * The [positionalArguments] is a list of the positional arguments that the + * method was called with. If `null`, it is considered equivalent to the + * empty list. + * + * The [namedArguments] is a map from [Symbol]s to the values of named + * arguments that the method was called with. + * + * The optional [exisitingArgumentNames] is the expected parameters of a + * method with the same name on the receiver, if available. This is + * the signature of the method that would have been called if the parameters + * had matched. + */ + NoSuchMethodError(Object receiver, + Symbol memberName, + List positionalArguments, + Map namedArguments, + [List existingArgumentNames = null]) + : _receiver = receiver, + _memberName = memberName, + _arguments = positionalArguments, + _namedArguments = namedArguments, + _existingArgumentNames = existingArgumentNames; + + external String toString(); +} + + +/** + * The operation was not allowed by the object. + * + * This [Error] is thrown when an instance cannot implement one of the methods + * in its signature. + */ +class UnsupportedError extends Error { + final String message; + UnsupportedError(this.message); + String toString() => "Unsupported operation: $message"; +} + + +/** + * Thrown by operations that have not been implemented yet. + * + * This [Error] is thrown by unfinished code that hasn't yet implemented + * all the features it needs. + * + * If a class is not intending to implement the feature, it should throw + * an [UnsupportedError] instead. This error is only intended for + * use during development. + */ +class UnimplementedError extends Error implements UnsupportedError { + final String message; + UnimplementedError([String this.message]); + String toString() => (this.message != null + ? "UnimplementedError: $message" + : "UnimplementedError"); +} + + +/** + * The operation was not allowed by the current state of the object. + * + * This is a generic error used for a variety of different erroneous + * actions. The message should be descriptive. + */ +class StateError extends Error { + final String message; + StateError(this.message); + String toString() => "Bad state: $message"; +} + + +/** + * Error occurring when a collection is modified during iteration. + * + * Some modifications may be allowed for some collections, so each collection + * ([Iterable] or similar collection of values) should declare which operations + * are allowed during an iteration. + */ +class ConcurrentModificationError extends Error { + /** The object that was modified in an incompatible way. */ + final Object modifiedObject; + + ConcurrentModificationError([this.modifiedObject]); + + String toString() { + if (modifiedObject == null) { + return "Concurrent modification during iteration."; + } + return "Concurrent modification during iteration: " + "${Error.safeToString(modifiedObject)}."; + } +} + + +class OutOfMemoryError implements Error { + const OutOfMemoryError(); + String toString() => "Out of Memory"; + + StackTrace get stackTrace => null; +} + + +class StackOverflowError implements Error { + const StackOverflowError(); + String toString() => "Stack Overflow"; + + StackTrace get stackTrace => null; +} + +/** + * Error thrown when a lazily initialized variable cannot be initialized. + * + * A static/library variable with an initializer expression is initialized + * the first time it is read. If evaluating the initializer expression causes + * another read of the variable, this error is thrown. + */ +class CyclicInitializationError extends Error { + final String variableName; + CyclicInitializationError([this.variableName]); + String toString() => variableName == null + ? "Reading static variable during its initialization" + : "Reading static variable '$variableName' during its initialization"; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/exceptions.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/exceptions.dart new file mode 100644 index 0000000..800a652 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/exceptions.dart @@ -0,0 +1,59 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +// Exceptions are thrown either by the VM or from Dart code. + +/** + * A marker interface implemented by all core library exceptions. + * + * An [Exception] is intended to convey information to the user about a failure, + * so that the error can be addressed programmatically. It is intended to be + * caught, and it should contain useful data fields. + * + * Creating instances of [Exception] directly with [:new Exception("message"):] + * is discouraged, and only included as a temporary measure during development, + * until the actual exceptions used by a library are done. + */ +abstract class Exception { + factory Exception([var message]) => new _ExceptionImplementation(message); +} + + +/** Default implementation of [Exception] which carries a message. */ +class _ExceptionImplementation implements Exception { + final message; + + _ExceptionImplementation([this.message]); + + String toString() { + if (message == null) return "Exception"; + return "Exception: $message"; + } +} + + +/** + * Exception thrown when a string or some other data does not have an expected + * format and cannot be parsed or processed. + */ +class FormatException implements Exception { + /** + * A message describing the format error. + */ + final String message; + + /** + * Creates a new FormatException with an optional error [message]. + */ + const FormatException([this.message = ""]); + + String toString() => "FormatException: $message"; +} + +class IntegerDivisionByZeroException implements Exception { + const IntegerDivisionByZeroException(); + String toString() => "IntegerDivisionByZeroException"; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/expando.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/expando.dart new file mode 100644 index 0000000..5b239de --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/expando.dart @@ -0,0 +1,45 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * An [Expando] allows adding new properties to objects. + */ +class Expando { + + /** + * The name of the this [Expando] as passed to the constructor. If + * no name was passed to the constructor, the name is [:null:]. + */ + final String name; + + /** + * Creates a new [Expando]. The optional name is only used for + * debugging purposes and creating two different [Expando]s with the + * same name yields two [Expando]s that work on different properties + * of the objects they are used on. + */ + external Expando([String name]); + + /** + * Expando toString method override. + */ + String toString() => "Expando:$name"; + + /** + * Gets the value of this [Expando]'s property on the given + * object. If the object hasn't been expanded, the method returns + * [:null:]. + */ + external T operator [](Object object); + + /** + * Sets the value of this [Expando]'s property on the given + * object. Properties can effectively be removed again by setting + * their value to null. + */ + external void operator []=(Object object, T value); + +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/function.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/function.dart new file mode 100644 index 0000000..3eebddc --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/function.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2011, 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. + +part of dart.core; + +/** + * The base class for all function types. + * + * A function value, or an instance of a class with a "call" method, is a + * subtype of a function type, and as such, a subtype of [Function]. + */ +abstract class Function { + /** + * Dynamically call [function] with the specified arguments. + * + * Acts the same as calling function with positional arguments + * corresponding to the elements of [positionalArguments] and + * named arguments corresponding to the elements of [namedArguments]. + * + * This includes giving the same errors if [function] isn't callable or + * if it expects different parameters. + * + * Example: + * Map namedArguments = new Map(); + * namedArguments[const Symbol("f")] = 4; + * namedArguments[const Symbol("g")] = 5; + * Function.apply(foo, [1,2,3], namedArguments); + * + * gives exactly the same result as + * foo(1, 2, 3, f: 4, g: 5). + * + * If [positionalArguments] is null, it's considered an empty list. + * If [namedArguments] is omitted or null, it is considered an empty map. + */ + external static apply(Function function, + List positionalArguments, + [Map namedArguments]); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/identical.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/identical.dart new file mode 100644 index 0000000..cc00c30 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/identical.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * Check whether two references are to the same object. + */ +external bool identical(Object a, Object b); + +/** + * Returns the identity hash code of `object`. + * + * Returns the same value as `object.hashCode` if [object] has not overridden + * [Object.hashCode]. Returns the value that [Object.hashCode] would return + * on this object, even if `hashCode` has been overridden. + * + * This hash code is compatible with [identical]. + */ +external int identityHashCode(Object object); diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/int.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/int.dart new file mode 100644 index 0000000..23eda31 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/int.dart @@ -0,0 +1,278 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * An arbitrarily large integer. + * + * **Note:** When compiling to JavaScript, integers are + * implemented as JavaScript numbers. When compiling to JavaScript, + * integers are therefore restricted to 53 significant bits because + * all JavaScript numbers are double-precision floating point + * values. The behavior of the operators and methods in the [int] + * class therefore sometimes differs between the Dart VM and Dart code + * compiled to JavaScript. + * + * It is a compile-time error for a class to attempt to extend or implement int. + */ +abstract class int extends num { + /** + * Returns the integer value of the given environment declaration [name]. + * + * The result is the same as would be returned by: + * + * int.parse(const String.fromEnvironment(name, defaultValue: ""), + * (_) => defaultValue) + * + * Example: + * + * const int.fromEnvironment("defaultPort", defaultValue: 80) + */ + external const factory int.fromEnvironment(String name, {int defaultValue}); + + /** + * Bit-wise and operator. + * + * Treating both `this` and [other] as sufficiently large two's component + * integers, the result is a number with only the bits set that are set in + * both `this` and [other] + * + * Of both operands are negative, the result is negative, otherwise + * the result is non-negative. + */ + int operator &(int other); + + /** + * Bit-wise or operator. + * + * Treating both `this` and [other] as sufficiently large two's component + * integers, the result is a number with the bits set that are set in either + * of `this` and [other] + * + * If both operands are non-negative, the result is non-negative, + * otherwise the result us negative. + */ + int operator |(int other); + + /** + * Bit-wise exclusive-or operator. + * + * Treating both `this` and [other] as sufficiently large two's component + * integers, the result is a number with the bits set that are set in one, + * but not both, of `this` and [other] + * + * If the operands have the same sign, the result is non-negative, + * otherwise the result is negative. + */ + int operator ^(int other); + + /** + * The bit-wise negate operator. + * + * Treating `this` as a sufficiently large two's component integer, + * the result is a number with the opposite bits set. + * + * This maps any integer `x` to `-x - 1`. + */ + int operator ~(); + + /** + * Shift the bits of this integer to the left by [shiftAmount]. + * + * Shifting to the left makes the number larger, effectively multiplying + * the number by `pow(2, shiftIndex)`. + * + * There is no limit on the size of the result. It may be relevant to + * limit intermediate values by using the "and" operator with a suitable + * mask. + * + * It is an error of [shiftAmount] is negative. + */ + int operator <<(int shiftAmount); + + /** + * Shift the bits of this integer to the right by [shiftAmount]. + * + * Shifting to the right makes the number smaller and drops the least + * significant bits, effectively doing an integer division by + *`pow(2, shiftIndex)`. + * + * It is an error of [shiftAmount] is negative. + */ + int operator >>(int shiftAmount); + + /** Returns true if and only if this integer is even. */ + bool get isEven; + + /** Returns true if and only if this integer is odd. */ + bool get isOdd; + + /** + * Returns the minimum number of bits required to store this integer. + * + * The number of bits excludes the sign bit, which gives the natural length + * for non-negative (unsigned) values. Negative values are complemented to + * return the bit position of the first bit that differs from the sign bit. + * + * To find the the number of bits needed to store the value as a signed value, + * add one, i.e. use `x.bitLength + 1`. + * + * x.bitLength == (-x-1).bitLength + * + * 3.bitLength == 2; // 00000011 + * 2.bitLength == 2; // 00000010 + * 1.bitLength == 1; // 00000001 + * 0.bitLength == 0; // 00000000 + * (-1).bitLength == 0; // 11111111 + * (-2).bitLength == 1; // 11111110 + * (-3).bitLength == 2; // 11111101 + * (-4).bitLength == 2; // 11111100 + */ + int get bitLength; + + /** + * Returns the least significant [width] bits of this integer as a + * non-negative number (i.e. unsigned representation). The returned value has + * zeros in all bit positions higher than [width]. + * + * (-1).toUnsigned(5) == 32 // 11111111 -> 00011111 + * + * This operation can be used to simulate arithmetic from low level languages. + * For example, to increment an 8 bit quantity: + * + * q = (q + 1).toUnsigned(8); + * + * `q` will count from `0` up to `255` and then wrap around to `0`. + * + * If the input fits in [width] bits without truncation, the result is the + * same as the input. The minimum width needed to avoid truncation of `x` is + * given by `x.bitLength`, i.e. + * + * x == x.toUnsigned(x.bitLength); + */ + int toUnsigned(int width); + + /** + * Returns the least significant [width] bits of this integer, extending the + * highest retained bit to the sign. This is the same as truncating the value + * to fit in [width] bits using an signed 2-s complement representation. The + * returned value has the same bit value in all positions higher than [width]. + * + * V--sign bit-V + * 16.toSigned(5) == -16 // 00010000 -> 11110000 + * 239.toSigned(5) == 15 // 11101111 -> 00001111 + * ^ ^ + * + * This operation can be used to simulate arithmetic from low level languages. + * For example, to increment an 8 bit signed quantity: + * + * q = (q + 1).toSigned(8); + * + * `q` will count from `0` up to `127`, wrap to `-128` and count back up to + * `127`. + * + * If the input value fits in [width] bits without truncation, the result is + * the same as the input. The minimum width needed to avoid truncation of `x` + * is `x.bitLength + 1`, i.e. + * + * x == x.toSigned(x.bitLength + 1); + */ + int toSigned(int width); + + /** + * Return the negative value of this integer. + * + * The result of negating an integer always has the opposite sign, except + * for zero, which is its own negation. + */ + int operator -(); + + /** + * Returns the absolute value of this integer. + * + * For any integer `x`, the result is the same as `x < 0 ? -x : x`. + */ + int abs(); + + /** + * Returns the sign of this integer. + * + * Returns 0 for zero, -1 for values less than zero and + * +1 for values greater than zero. + */ + int get sign; + + /** Returns `this`. */ + int round(); + + /** Returns `this`. */ + int floor(); + + /** Returns `this`. */ + int ceil(); + + /** Returns `this`. */ + int truncate(); + + /** Returns `this.toDouble()`. */ + double roundToDouble(); + + /** Returns `this.toDouble()`. */ + double floorToDouble(); + + /** Returns `this.toDouble()`. */ + double ceilToDouble(); + + /** Returns `this.toDouble()`. */ + double truncateToDouble(); + + /** + * Returns a String-representation of this integer. + * + * The returned string is parsable by [parse]. + * For any `int` [:i:], it is guaranteed that + * [:i == int.parse(i.toString()):]. + */ + String toString(); + + /** + * Converts [this] to a string representation in the given [radix]. + * + * In the string representation, lower-case letters are used for digits above + * '9'. + * + * The [radix] argument must be an integer in the range 2 to 36. + */ + String toRadixString(int radix); + + /** + * Parse [source] as an integer literal and return its value. + * + * The [radix] must be in the range 2..36. The digits used are + * first the decimal digits 0..9, and then the letters 'a'..'z'. + * Accepts capital letters as well. + * + * If no [radix] is given then it defaults to 10, unless the string starts + * with "0x", "-0x" or "+0x", in which case the radix is set to 16 and the + * "0x" is ignored. + * + * The [source] must be a non-empty sequence of base-[radix] digits, + * optionally prefixed with a minus or plus sign ('-' or '+'). + * + * It must always be the case for an int [:n:] and radix [:r:] that + * [:n == parseRadix(n.toRadixString(r), r):]. + * + * If the [source] is not a valid integer literal, optionally prefixed by a + * sign, the [onError] is called with the [source] as argument, and its return + * value is used instead. If no [onError] is provided, a [FormatException] + * is thrown. + * + * The [onError] function is only invoked if [source] is a [String]. It is + * not invoked if the [source] is, for example, `null`. + */ + external static int parse(String source, + { int radix, + int onError(String source) }); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/invocation.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/invocation.dart new file mode 100644 index 0000000..7deaeec --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/invocation.dart @@ -0,0 +1,52 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * Representation of the invocation of a member on an object. + * + * This is the type of objects passed to [Object.noSuchMethod] when + * an object doesn't support the member invocation that was attempted + * on it. + */ +abstract class Invocation { + /** The name of the invoked member. */ + Symbol get memberName; + + /** + * An unmodifiable view of the positional arguments of the call. + * + * If the member is a getter, the positional arguments is empty. + */ + List get positionalArguments; + + /** + * An unmodifiable view of the named arguments of the call. + * + * If the member is a getter, setter or operator, the named arguments + * is empty. + */ + Map get namedArguments; + + /** Whether the invocation was a method call. */ + bool get isMethod; + + /** + * Whether the invocation was a getter call. + * If so, both types of arguments is empty. + */ + bool get isGetter; + + /** + * Whether the invocation was a setter call. + * + * If so, [arguments] has exactly one positonal argument, + * and [namedArguments] is empty. + */ + bool get isSetter; + + /** Whether the invocation was a getter or a setter call. */ + bool get isAccessor => isGetter || isSetter; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/iterable.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/iterable.dart new file mode 100644 index 0000000..7d0ff16 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/iterable.dart @@ -0,0 +1,362 @@ +// Copyright (c) 2011, 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. + +part of dart.core; + +/** + * An object that uses an [Iterator] to serve objects one at a time. + * + * You can iterate over all objects served by an Iterable object + * using the for-in loop construct. + * For example, you can iterate over all of the keys in a [Map], + * because Map keys are iterable. + * + * Map kidsBooks = {'Matilda': 'Roald Dahl', + * 'Green Eggs and Ham': 'Dr Seuss', + * 'Where the Wild Things Are': 'Maurice Sendak'}; + * for (var book in kidsBooks.keys) { + * print('$book was written by ${kidsBooks[book]}'); + * } + * + * The [List] class and the [Set] class implement this interface, + * as do classes in the [dart:collection](#dart-collection) library. + * + * You can implement Iterable in your own class. + * If you do, then an instance of your Iterable class + * can be the right-hand side of a for-in construct. + * + * Some subclasss of [Iterable] can be modified. It is generally not allowed + * to modify such collections while they are being iterated. Doing so will break + * the iteration, which is typically signalled by throwing a + * [ConcurrentModificationError] when it is detected. + */ +abstract class Iterable { + const Iterable(); + + /** + * Creates an Iterable that generates its elements dynamically. + * + * The Iterators created by the Iterable count from + * zero to [:count - 1:] while iterating, and call [generator] + * with that index to create the next value. + * + * If [generator] is omitted, it defaults to an identity function + * on integers `(int x) => x`, so it should only be omitted if the type + * parameter allows integer values. + * + * As an Iterable, [:new Iterable.generate(n, generator)):] is equivalent to + * [:const [0, ..., n - 1].map(generator):] + */ + factory Iterable.generate(int count, [E generator(int index)]) { + if (count <= 0) return new EmptyIterable(); + return new _GeneratorIterable(count, generator); + } + + /** + * Returns an Iterator that iterates over this Iterable object. + */ + Iterator get iterator; + + /** + * Returns a lazy [Iterable] where each element [:e:] of `this` is replaced + * by the result of [:f(e):]. + * + * This method returns a view of the mapped elements. As long as the + * returned [Iterable] is not iterated over, the supplied function [f] will + * not be invoked. The transformed elements will not be cached. Iterating + * multiple times over the the returned [Iterable] will invoke the supplied + * function [f] multiple times on the same element. + */ + Iterable map(f(E element)); + + /** + * Returns a lazy [Iterable] with all elements that satisfy the + * predicate [test]. + * + * This method returns a view of the mapped elements. As long as the + * returned [Iterable] is not iterated over, the supplied function [test] will + * not be invoked. Iterating will not cache results, and thus iterating + * multiple times over the returned [Iterable] will invoke the supplied + * function [test] multiple times on the same element. + */ + Iterable where(bool test(E element)); + + /** + * Expands each element of this [Iterable] into zero or more elements. + * + * The resulting Iterable runs through the elements returned + * by [f] for each element of this, in order. + * + * The returned [Iterable] is lazy, and calls [f] for each element + * of this every time it's iterated. + */ + Iterable expand(Iterable f(E element)); + + /** + * Returns true if the collection contains an element equal to [element]. + */ + bool contains(Object element); + + /** + * Applies the function [f] to each element of this collection. + */ + void forEach(void f(E element)); + + /** + * Reduces a collection to a single value by iteratively combining elements + * of the collection using the provided function. + * + * Example of calculating the sum of an iterable: + * + * iterable.reduce((value, element) => value + element); + * + */ + E reduce(E combine(E value, E element)); + + /** + * Reduces a collection to a single value by iteratively combining each + * element of the collection with an existing value using the provided + * function. + * + * Use [initialValue] as the initial value, and the function [combine] to + * create a new value from the previous one and an element. + * + * Example of calculating the sum of an iterable: + * + * iterable.fold(0, (prev, element) => prev + element); + * + */ + dynamic fold(var initialValue, + dynamic combine(var previousValue, E element)); + + /** + * Returns true if every elements of this collection satisify the + * predicate [test]. Returns `false` otherwise. + */ + bool every(bool test(E element)); + + /** + * Converts each element to a [String] and concatenates the strings. + * + * Converts each element to a [String] by calling [Object.toString] on it. + * Then concatenates the strings, optionally separated by the [separator] + * string. + */ + String join([String separator = ""]) { + StringBuffer buffer = new StringBuffer(); + buffer.writeAll(this, separator); + return buffer.toString(); + } + + /** + * Returns true if one element of this collection satisfies the + * predicate [test]. Returns false otherwise. + */ + bool any(bool test(E element)); + + /** + * Creates a [List] containing the elements of this [Iterable]. + * + * The elements are in iteration order. The list is fixed-length + * if [growable] is false. + */ + List toList({ bool growable: true }); + + /** + * Creates a [Set] containing the elements of this [Iterable]. + */ + Set toSet(); + + /** + * Returns the number of elements in [this]. + * + * Counting all elements may be involve running through all elements and can + * therefore be slow. + */ + int get length; + + /** + * Returns true if there is no element in this collection. + */ + bool get isEmpty; + + /** + * Returns true if there is at least one element in this collection. + */ + bool get isNotEmpty; + + /** + * Returns an [Iterable] with at most [n] elements. + * + * The returned [Iterable] may contain fewer than [n] elements, if `this` + * contains fewer than [n] elements. + * + * It is an error if [n] is negative. + */ + Iterable take(int n); + + /** + * Returns an Iterable that stops once [test] is not satisfied anymore. + * + * The filtering happens lazily. Every new Iterator of the returned + * Iterable starts iterating over the elements of `this`. + * + * When the iterator encounters an element `e` that does not satisfy [test], + * it discards `e` and moves into the finished state. That is, it does not + * get or provide any more elements. + */ + Iterable takeWhile(bool test(E value)); + + /** + * Returns an Iterable that skips the first [n] elements. + * + * If `this` has fewer than [n] elements, then the resulting Iterable is + * empty. + * + * It is an error if [n] is negative. + */ + Iterable skip(int n); + + /** + * Returns an Iterable that skips elements while [test] is satisfied. + * + * The filtering happens lazily. Every new Iterator of the returned + * Iterable iterates over all elements of `this`. + * + * As long as the iterator's elements satisfy [test] they are + * discarded. Once an element does not satisfy the [test] the iterator stops + * testing and uses every later element unconditionally. That is, the elements + * of the returned Iterable are the elements of `this` starting from the + * first element that does not satisfy [test]. + */ + Iterable skipWhile(bool test(E value)); + + /** + * Returns the first element. + * + * If `this` is empty throws a [StateError]. Otherwise this method is + * equivalent to [:this.elementAt(0):] + */ + E get first; + + /** + * Returns the last element. + * + * If `this` is empty throws a [StateError]. + */ + E get last; + + /** + * Returns the single element in `this`. + * + * If `this` is empty or has more than one element throws a [StateError]. + */ + E get single; + + /** + * Returns the first element that satisfies the given predicate [test]. + * + * If none matches, the result of invoking the [orElse] function is + * returned. By default, when [orElse] is `null`, a [StateError] is + * thrown. + */ + E firstWhere(bool test(E element), { E orElse() }); + + /** + * Returns the last element that satisfies the given predicate [test]. + * + * If none matches, the result of invoking the [orElse] function is + * returned. By default, when [orElse] is `null`, a [StateError] is + * thrown. + */ + E lastWhere(bool test(E element), {E orElse()}); + + /** + * Returns the single element that satisfies [test]. If no or more than one + * element match then a [StateError] is thrown. + */ + E singleWhere(bool test(E element)); + + /** + * Returns the [index]th element. + * + * The [index] must be non-negative and less than [length]. + * + * Note: if `this` does not have a deterministic iteration order then the + * function may simply return any element without any iteration if there are + * at least [index] elements in `this`. + */ + E elementAt(int index); +} + +typedef E _Generator(int index); + +class _GeneratorIterable extends IterableBase + implements EfficientLength { + final int _start; + final int _end; + final _Generator _generator; + _GeneratorIterable(this._end, E generator(int n)) + : _start = 0, + _generator = (generator != null) ? generator : _id; + + _GeneratorIterable.slice(this._start, this._end, this._generator); + + Iterator get iterator => + new _GeneratorIterator(_start, _end, _generator); + int get length => _end - _start; + + Iterable skip(int n) { + if (n < 0) throw new RangeError.value(n); + if (n == 0) return this; + int newStart = _start + n; + if (newStart >= _end) return new EmptyIterable(); + return new _GeneratorIterable.slice(newStart, _end, _generator); + } + + Iterable take(int n) { + if (n < 0) throw new RangeError.value(n); + if (n == 0) return new EmptyIterable(); + int newEnd = _start + n; + if (newEnd >= _end) return this; + return new _GeneratorIterable.slice(_start, newEnd, _generator); + } + + static int _id(int n) => n; +} + +class _GeneratorIterator implements Iterator { + final int _end; + final _Generator _generator; + int _index; + E _current; + + _GeneratorIterator(this._index, this._end, this._generator); + + bool moveNext() { + if (_index < _end) { + _current = _generator(_index); + _index++; + return true; + } else { + _current = null; + return false; + } + } + + E get current => _current; +} + +/** + * An Iterator that allows moving backwards as well as forwards. + */ +abstract class BidirectionalIterator implements Iterator { + /** + * Move back to the previous element. + * + * Returns true and updates [current] if successful. Returns false + * and sets [current] to null if there is no previous element. + */ + bool movePrevious(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/iterator.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/iterator.dart new file mode 100644 index 0000000..ed7aca9 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/iterator.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * An interface for getting items, one at a time, from an object. + * + * The for-in construct transparently uses Iterator to test for the end + * of the iteration, and to get each item (or _element_). + * + * If the object iterated over is changed during the iteration, the + * behavior is unspecified. + * + * The Iterator is initially positioned before the first element. Before + * accessing the first element the iterator must thus be advanced ([moveNext]) + * to point to the first element. If no element is left, then [moveNext] + * returns false. + * + * A typical usage of an Iterator looks as follows: + * + * var it = obj.iterator; + * while (it.moveNext()) { + * use(it.current); + * } + * + * **See also:** [Iteration] + * (http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html#ch03-iteration) + * in the [library tour] + * (http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html) + */ +abstract class Iterator { + /** + * Moves to the next element. Returns true if [current] contains the next + * element. Returns false, if no element was left. + * + * It is safe to invoke [moveNext] even when the iterator is already + * positioned after the last element. In this case [moveNext] has no effect. + */ + bool moveNext(); + + /** + * Returns the current element. + * + * Return [:null:] if the iterator has not yet been moved to the first + * element, or if the iterator has been moved after the last element of the + * [Iterable]. + */ + E get current; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/list.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/list.dart new file mode 100644 index 0000000..0b55b8e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/list.dart @@ -0,0 +1,463 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * An indexable collection of objects with a length. + * + * Subclasses of this class implement different kinds of lists. + * The most common kinds of lists are: + * + * * Fixed-length list. + * An error occurs when attempting to use operations + * that can change the length of the list. + * + * * Growable list. Full implementation of the API defined in this class. + * + * The default growable list, as returned by `new List()` or `[]`, keeps + * an internal buffer, and grows that buffer when necessary. This guarantees + * that a sequence of [add] operations will each execute in amortized constant + * time. Setting the length directly may take time proportional to the new + * length, and may change the internal capacity so that a following add + * operation will need to immediately increase the buffer capacity. + * Other list implementations may have different performance behavior. + * + * The following code illustrates that some List implementations support + * only a subset of the API. + * + * List fixedLengthList = new List(5); + * fixedLengthList.length = 0; // Error + * fixedLengthList.add(499); // Error + * fixedLengthList[0] = 87; + * List growableList = [1, 2]; + * growableList.length = 0; + * growableList.add(499); + * growableList[0] = 87; + * + * Lists are [Iterable]. Iteration occurs over values in index order. Changing + * the values does not affect iteration, but changing the valid + * indices—that is, changing the list's length—between iteration + * steps causes a [ConcurrentModificationError]. This means that only growable + * lists can throw ConcurrentModificationError. If the length changes + * temporarily and is restored before continuing the iteration, the iterator + * does not detect it. + * + * It is generally not allowed to modify the list's length (adding or removing + * elements) while an operation on the list is being performed, + * for example during a call to [forEach] or [sort]. + * Changing the list's length while it is being iterated, either by iterating it + * directly or through iterating an [Iterable] that is backed by the list, will + * break the iteration. + */ +abstract class List implements Iterable, EfficientLength { + /** + * Creates a list of the given length. + * + * The created list is fixed-length if [length] is provided. + * + * List fixedLengthList = new List(3); + * fixedLengthList.length; // 3 + * fixedLengthList.length = 1; // Error + * + * + * The list has length 0 and is growable if [length] is omitted. + * + * List growableList = new List(); + * growableList.length; // 0; + * growableList.length = 3; + * + * The [length] must not be negative or null, if it is provided. + */ + external factory List([int length]); + + /** + * Creates a fixed-length list of the given length, and initializes the + * value at each position with [fill]: + * + * new List.filled(3, 0); // [0, 0, 0] + * + * The [length] must not be negative or null. + */ + external factory List.filled(int length, E fill); + + /** + * Creates a list and initializes it using the contents of [other]. + * + * The [Iterator] of [other] provides the order of the objects. + * + * This constructor returns a growable list if [growable] is true; + * otherwise, it returns a fixed-length list. + */ + factory List.from(Iterable other, { bool growable: true }) { + List list = new List(); + for (E e in other) { + list.add(e); + } + if (growable) return list; + return makeListFixedLength(list); + } + + /** + * Generates a list of values. + * + * Creates a list with [length] positions and fills it with values created by + * calling [generator] for each index in the range `0` .. `length - 1` + * in increasing order. + * + * new List.generate(3, (int index) => index * index); // [0, 1, 4] + * + * The created list is fixed-length unless [growable] is true. + */ + factory List.generate(int length, E generator(int index), + { bool growable: true }) { + List result; + if (growable) { + result = []..length = length; + } else { + result = new List(length); + } + for (int i = 0; i < length; i++) { + result[i] = generator(i); + } + return result; + } + + /** + * Returns the object at the given [index] in the list + * or throws a [RangeError] if [index] is out of bounds. + */ + E operator [](int index); + + /** + * Sets the value at the given [index] in the list to [value] + * or throws a [RangeError] if [index] is out of bounds. + */ + void operator []=(int index, E value); + + /** + * Returns the number of objects in this list. + * + * The valid indices for a list are `0` through `length - 1`. + */ + int get length; + + /** + * Changes the length of this list. + * + * If [newLength] is greater than + * the current length, entries are initialized to [:null:]. + * + * Throws an [UnsupportedError] if the list is fixed-length. + */ + void set length(int newLength); + + /** + * Adds [value] to the end of this list, + * extending the length by one. + * + * Throws an [UnsupportedError] if the list is fixed-length. + */ + void add(E value); + + /** + * Appends all objects of [iterable] to the end of this list. + * + * Extends the length of the list by the number of objects in [iterable]. + * Throws an [UnsupportedError] if this list is fixed-length. + */ + void addAll(Iterable iterable); + + /** + * Returns an [Iterable] of the objects in this list in reverse order. + */ + Iterable get reversed; + + /** + * Sorts this list according to the order specified by the [compare] function. + * + * The [compare] function must act as a [Comparator]. + + * List numbers = ['one', 'two', 'three', 'four']; + * // Sort from shortest to longest. + * numbers.sort((x, y) => x.length.compareTo(y.length)); + * numbers.join(', '); // 'one, two, four, three' + * + * The default List implementations use [Comparable.compare] if + * [compare] is omitted. + * + * List nums = [13, 2, -11]; + * nums.sort(); + nums.join(', '); // '-11, 2, 13' + */ + void sort([int compare(E a, E b)]); + + /** + * Shuffles the elements of this list randomly. + */ + void shuffle([Random random]); + + /** + * Returns the first index of [element] in this list. + * + * Searches the list from index [start] to the end of the list. + * The first time an object [:o:] is encountered so that [:o == element:], + * the index of [:o:] is returned. + * + * List notes = ['do', 're', 'mi', 're']; + * notes.indexOf('re'); // 1 + * notes.indexOf('re', 2); // 3 + * + * Returns -1 if [element] is not found. + * + * notes.indexOf('fa'); // -1 + */ + int indexOf(E element, [int start = 0]); + + /** + * Returns the last index of [element] in this list. + * + * Searches the list backwards from index [start] to 0. + * + * The first time an object [:o:] is encountered so that [:o == element:], + * the index of [:o:] is returned. + * + * List notes = ['do', 're', 'mi', 're']; + * notes.lastIndexOf('re', 2); // 1 + * + * If [start] is not provided, this method searches from the end of the + * list./Returns + * + * notes.lastIndexOf('re'); // 3 + * + * Returns -1 if [element] is not found. + * + * notes.lastIndexOf('fa'); // -1 + */ + int lastIndexOf(E element, [int start]); + + /** + * Removes all objects from this list; + * the length of the list becomes zero. + * + * Throws an [UnsupportedError], and retains all objects, if this + * is a fixed-length list. + */ + void clear(); + + /** + * Inserts the object at position [index] in this list. + * + * This increases the length of the list by one and shifts all objects + * at or after the index towards the end of the list. + * + * An error occurs if the [index] is less than 0 or greater than length. + * An [UnsupportedError] occurs if the list is fixed-length. + */ + void insert(int index, E element); + + /** + * Inserts all objects of [iterable] at position [index] in this list. + * + * This increases the length of the list by the length of [iterable] and + * shifts all later objects towards the end of the list. + * + * An error occurs if the [index] is less than 0 or greater than length. + * An [UnsupportedError] occurs if the list is fixed-length. + */ + void insertAll(int index, Iterable iterable); + + /** + * Overwrites objects of `this` with the objects of [iterable], starting + * at position [index] in this list. + * + * List list = ['a', 'b', 'c']; + * list.setAll(1, ['bee', 'sea']); + * list.join(', '); // 'a, bee, sea' + * + * This operation does not increase the length of `this`. + * + * The [index] must be non-negative and no greater than [length]. + * + * The [iterable] must not have more elements than what can fit from [index] + * to [length]. + * + * If `iterable` is based on this list, its values may change /during/ the + * `setAll` operation. + */ + void setAll(int index, Iterable iterable); + + /** + * Removes the first occurence of [value] from this list. + * + * Returns true if [value] was in the list, false otherwise. + * + * List parts = ['head', 'shoulders', 'knees', 'toes']; + * parts.remove('head'); // true + * parts.join(', '); // 'shoulders, knees, toes' + * + * The method has no effect if [value] was not in the list. + * + * // Note: 'head' has already been removed. + * parts.remove('head'); // false + * parts.join(', '); // 'shoulders, knees, toes' + * + * An [UnsupportedError] occurs if the list is fixed-length. + */ + bool remove(Object value); + + /** + * Removes the object at position [index] from this list. + * + * This method reduces the length of `this` by one and moves all later objects + * down by one position. + * + * Returns the removed object. + * + * The [index] must be in the range `0 ≤ index < length`. + * + * Throws an [UnsupportedError] if this is a fixed-length list. In that case + * the list is not modified. + */ + E removeAt(int index); + + /** + * Pops and returns the last object in this list. + * + * Throws an [UnsupportedError] if this is a fixed-length list. + */ + E removeLast(); + + /** + * Removes all objects from this list that satisfy [test]. + * + * An object [:o:] satisfies [test] if [:test(o):] is true. + * + * List numbers = ['one', 'two', 'three', 'four']; + * numbers.removeWhere((item) => item.length == 3); + * numbers.join(', '); // 'three, four' + * + * Throws an [UnsupportedError] if this is a fixed-length list. + */ + void removeWhere(bool test(E element)); + + /** + * Removes all objects from this list that fail to satisfy [test]. + * + * An object [:o:] satisfies [test] if [:test(o):] is true. + * + * List numbers = ['one', 'two', 'three', 'four']; + * numbers.retainWhere((item) => item.length == 3); + * numbers.join(', '); // 'one, two' + * + * Throws an [UnsupportedError] if this is a fixed-length list. + */ + void retainWhere(bool test(E element)); + + /** + * Returns a new list containing the objects from [start] inclusive to [end] + * exclusive. + * + * List colors = ['red', 'green', 'blue', 'orange', 'pink']; + * colors.sublist(1, 3); // ['green', 'blue'] + * + * If [end] is omitted, the [length] of `this` is used. + * + * colors.sublist(1); // ['green', 'blue', 'orange', 'pink'] + * + * An error occurs if [start] is outside the range `0` .. `length` or if + * [end] is outside the range `start` .. `length`. + */ + List sublist(int start, [int end]); + + /** + * Returns an [Iterable] that iterates over the objects in the range + * [start] inclusive to [end] exclusive. + * + * An error occurs if [end] is before [start]. + * + * An error occurs if the [start] and [end] are not valid ranges at the time + * of the call to this method. The returned [Iterable] behaves like + * `skip(start).take(end - start)`. That is, it does not throw exceptions + * if `this` changes size. + * + * List colors = ['red', 'green', 'blue', 'orange', 'pink']; + * Iterable range = colors.getRange(1, 4); + * range.join(', '); // 'green, blue, orange' + * colors.length = 3; + * range.join(', '); // 'green, blue' + */ + Iterable getRange(int start, int end); + + /** + * Copies the objects of [iterable], skipping [skipCount] objects first, + * into the range [start], inclusive, to [end], exclusive, of the list. + * + * List list1 = [1, 2, 3, 4]; + * List list2 = [5, 6, 7, 8, 9]; + * // Copies the 4th and 5th items in list2 as the 2nd and 3rd items + * // of list1. + * list1.setRange(1, 3, list2, 3); + * list1.join(', '); // '1, 8, 9, 4' + * + * The [start] and [end] indices must satisfy `0 ≤ start ≤ end ≤ length`. + * If [start] equals [end], this method has no effect. + * + * The [iterable] must have enough objects to fill the range from `start` + * to `end` after skipping [skipCount] objects. + * + * If `iterable` is this list, the operation will copy the elements originally + * in the range from `skipCount` to `skipCount + (end - start)` to the + * range `start` to `end`, even if the two ranges overlap. + * + * If `iterable` depends on this list in some other way, no guarantees are + * made. + */ + void setRange(int start, int end, Iterable iterable, [int skipCount = 0]); + + /** + * Removes the objects in the range [start] inclusive to [end] exclusive. + * + * The [start] and [end] indices must be in the range + * `0 ≤ index ≤ length`, and `start ≤ end`. + * + * Throws an [UnsupportedError] if this is a fixed-length list. In that case + * the list is not modified. + */ + void removeRange(int start, int end); + + /** + * Sets the objects in the range [start] inclusive to [end] exclusive + * to the given [fillValue]. + * + * An error occurs if [start]..[end] is not a valid range for `this`. + */ + void fillRange(int start, int end, [E fillValue]); + + /** + * Removes the objects in the range [start] inclusive to [end] exclusive + * and inserts the contents of [replacement] in its place. + * + * List list = [1, 2, 3, 4, 5]; + * list.replaceRange(1, 4, [6, 7]); + * list.join(', '); // '1, 6, 7, 5' + * + * An error occurs if [start]..[end] is not a valid range for `this`. + */ + void replaceRange(int start, int end, Iterable replacement); + + /** + * Returns an unmodifiable [Map] view of `this`. + * + * The map uses the indices of this list as keys and the corresponding objects + * as values. The `Map.keys` [Iterable] iterates the indices of this list + * in numerical order. + * + * List words = ['fee', 'fi', 'fo', 'fum']; + * Map map = words.asMap(); + * map[0] + map[1]; // 'feefi'; + * map.keys.toList(); // [0, 1, 2, 3] + */ + Map asMap(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/map.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/map.dart new file mode 100644 index 0000000..db6d7bc --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/map.dart @@ -0,0 +1,234 @@ +// Copyright (c) 2011, 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. + +part of dart.core; + +/** + * An collection of key-value pairs, from which you retrieve a value + * by using its associated key. + * + * Each key can occur at most once in a map. + * + * Maps, and their keys and values, can be iterated. + * The order of iteration is defined by the individual type of map. + * Examples: + * + * * The plain [HashMap] is unordered (no order is guaranteed), + * * the [LinkedHashMap] iterates in key insertion order, + * * and a sorted map like [SplayTreeMap] iterates the keys in sorted order. + * + * It is generally not allowed to modify the map (add or remove keys) while + * an operation is being performed on the map, for example in functions called + * during a [forEach] or [putIfAbsent] call. + * Modifying the map while iterating the keys or values + * may also break the iteration. + */ +abstract class Map { + /** + * Creates a Map instance with the default implementation, [LinkedHashMap]. + * + * This constructor is equivalent to the non-const map literal `{}`. + * + * A `LinkedHashMap` requires the keys to implement compatible + * `operator==` and `hashCode`, and it allows null as a key. + * It iterates in key insertion order. + */ + factory Map() = LinkedHashMap; + + /** + * Creates a [LinkedHashMap] instance that contains all key-value pairs of + * [other]. + * + * A `LinkedHashMap` requires the keys to implement compatible + * `operator==` and `hashCode`, and it allows null as a key. + * It iterates in key insertion order. + */ + factory Map.from(Map other) = LinkedHashMap.from; + + /** + * Creates an identity map with the default implementation, [LinkedHashMap]. + * + * The returned map allows `null` as a key. + * It iterates in key insertion order. + */ + factory Map.identity() = LinkedHashMap.identity; + + /** + * Creates a Map instance in which the keys and values are computed from the + * [iterable]. + * + * The created map is a [LinkedHashMap]. + * A `LinkedHashMap` requires the keys to implement compatible + * `operator==` and `hashCode`, and it allows null as a key. + * It iterates in key insertion order. + * + * For each element of the [iterable] this constructor computes a key-value + * pair, by applying [key] and [value] respectively. + * + * The example below creates a new Map from a List. The keys of `map` are + * `list` values converted to strings, and the values of the `map` are the + * squares of the `list` values: + * + * List list = [1, 2, 3]; + * Map map = new Map.fromIterable(list, + * key: (item) => item.toString(), + * value: (item) => item * item)); + * + * map['1'] + map['2']; // 1 + 4 + * map['3'] - map['2']; // 9 - 4 + * + * If no values are specified for [key] and [value] the default is the + * identity function. + * + * In the following example, the keys and corresponding values of `map` + * are `list` values: + * + * map = new Map.fromIterable(list); + * map[1] + map[2]; // 1 + 2 + * map[3] - map[2]; // 3 - 2 + * + * The keys computed by the source [iterable] do not need to be unique. The + * last occurrence of a key will simply overwrite any previous value. + */ + factory Map.fromIterable(Iterable iterable, + {K key(element), V value(element)}) = LinkedHashMap.fromIterable; + + /** + * Creates a Map instance associating the given [keys] to [values]. + * + * The created map is a [LinkedHashMap]. + * A `LinkedHashMap` requires the keys to implement compatible + * `operator==` and `hashCode`, and it allows null as a key. + * It iterates in key insertion order. + * + * This constructor iterates over [keys] and [values] and maps each element of + * [keys] to the corresponding element of [values]. + * + * List letters = ['b', 'c']; + * List words = ['bad', 'cat']; + * Map map = new Map.fromIterables(letters, words); + * map['b'] + map['c']; // badcat + * + * If [keys] contains the same object multiple times, the last occurrence + * overwrites the previous value. + * + * The two [Iterable]s must have the same length. + */ + factory Map.fromIterables(Iterable keys, Iterable values) + = LinkedHashMap.fromIterables; + + /** + * Returns true if this map contains the given value. + */ + bool containsValue(Object value); + + /** + * Returns true if this map contains the given key. + */ + bool containsKey(Object key); + + /** + * Returns the value for the given [key] or null if [key] is not + * in the map. Because null values are supported, one should either + * use [containsKey] to distinguish between an absent key and a null + * value, or use the [putIfAbsent] method. + */ + V operator [](Object key); + + /** + * Associates the [key] with the given [value]. + * + * If the key was already in the map, its associated value is changed. + * Otherwise the key-value pair is added to the map. + */ + void operator []=(K key, V value); + + /** + * If [key] is not associated to a value, calls [ifAbsent] and + * updates the map by mapping [key] to the value returned by + * [ifAbsent]. Returns the value in the map. + * + * Map scores = {'Bob': 36}; + * for (var key in ['Bob', 'Rohan', 'Sophena']) { + * scores.putIfAbsent(key, () => key.length); + * } + * scores['Bob']; // 36 + * scores['Rohan']; // 5 + * scores['Sophena']; // 7 + * + * The code that [ifAbsent] executes must not add or remove keys. + */ + V putIfAbsent(K key, V ifAbsent()); + + /** + * Adds all key-value pairs of [other] to this map. + * + * If a key of [other] is already in this map, its value is overwritten. + * + * The operation is equivalent to doing `this[key] = value` for each key + * and associated value in other. It iterates over [other], which must + * therefore not change during the iteration. + */ + void addAll(Map other); + + /** + * Removes the association for the given [key]. Returns the value for + * [key] in the map or null if [key] is not in the map. Note that values + * can be null and a returned null value does not always imply that the + * key is absent. + */ + V remove(Object key); + + /** + * Removes all pairs from the map. + * + * After this, the map is empty. + */ + void clear(); + + /** + * Applies [f] to each {key, value} pair of the map. + * + * Adding or removing keys from the map during iteration is not allowed. + */ + void forEach(void f(K key, V value)); + + /** + * The keys of [this]. + * + * The returned iterable has efficient `length` and `contains` operations, + * based on [length] and [containsKey] of the map. + * + * The order of iteration is defined by the individual `Map` implementation, + * but must be consistent between changes to the map. + */ + Iterable get keys; + + /** + * The values of [this]. + * + * The values are iterated in the order of their corresponding keys. + * This means that iterating [keys] and [values] in parrallel will + * provided matching pairs of keys and values. + * + * The returned iterable has an efficient `length` method based on the + * [length] of the map. + */ + Iterable get values; + + /** + * The number of key-value pairs in the map. + */ + int get length; + + /** + * Returns true if there is no key-value pair in the map. + */ + bool get isEmpty; + + /** + * Returns true if there is at least one key-value pair in the map. + */ + bool get isNotEmpty; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/null.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/null.dart new file mode 100644 index 0000000..dd914a3 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/null.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2013, 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. + +part of dart.core; + +/** + * The reserved word [:null:] denotes an object that is the sole instance of + * this class. + * + * It is a compile-time error for a class to attempt to extend or implement + * Null. + */ +class Null { + factory Null._uninstantiable() { + throw new UnsupportedError('class Null cannot be instantiated'); + } + + /** Returns the string `"null"`. */ + String toString() => "null"; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/num.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/num.dart new file mode 100644 index 0000000..825dba0 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/num.dart @@ -0,0 +1,447 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * An integer or floating-point number. + * + * It is a compile-time error for any type other than [int] or [double] + * to attempt to extend or implement num. + */ +abstract class num implements Comparable { + /** + * Test whether this value is numerically equal to `other`. + * + * If both operands are doubles, they are equal if they have the same + * representation, except that: + * * zero and minus zero (0.0 and -0.0) are considered equal. They + * both have the numerical value zero. + * * NaN is not equal to anything, including NaN. If either operand is + * NaN, the result is always false. + * + * If one operand is a double and the other is an int, they are equal if + * the double has an integer value (finite with no fractional part) and + * `identical(doubleValue.toInt(), intValue)`. + * + * If both operands are integers, they are equal if they have the same value. + * + * Returns false if `other` is not a [num]. + * + * Notice that the behavior for NaN is non-reflexive. This means that + * equality of double values is not a proper equality relation, as is + * otherwise required of `operator==`. Using NaN in, e.g., a [HashSet] + * will fail to work. The behavior is the standard IEEE-754 equality of + * doubles. + * + * If you can avoid NaN values, the remaining doubles do have a proper eqality + * relation, and can be used safely. + * + * Use [compareTo] for a comparison that distinguishes zero and minus zero, + * and that considers NaN values as equal. + */ + bool operator==(Object other); + + /** + * Returns a hash code for a numerical value. + * + * The hash code is compatible with equality. It returns the same value + * for an [int] and a [double] with the same numerical value, and therefore + * the same value for the doubles zero and minus zero. + * + * No guarantees are made about the hash code of NaN. + */ + int get hashCode; + + /** + * Compares this to `other`. + * + * Returns a negative number if `this` is less than `other`, zero if they are + * equal, and a positive number if `this` is greater than `other`. + * + * The orderding represented by this method is a total ordering of [num] + * values. All distinct doubles are non-equal, as are all distinct integers, + * but integers are equal to doubles if they have the same numerical + * value. + * + * For ordering, the double NaN value is considered equal to itself, and + * greater than any numeric value (unlike its behavior in `operator==`). + * + * The double value -0.0 is considered less than 0.0 (and the integer 0), but + * greater than any non-zero negative value. + * + * Positive infinity is greater than any finite value (any value apart from + * itself and NaN), and negative infinity is less than any other value. + * + * All other values are compared using their numeric value. + */ + int compareTo(num other); + + /** Addition operator. */ + num operator +(num other); + + /** Subtraction operator. */ + num operator -(num other); + + /** Multiplication operator. */ + num operator *(num other); + + /** + * Euclidean modulo operator. + * + * Returns the remainder of the euclidean division. The euclidean division of + * two integers `a` and `b` yields two integers `q` and `r` such that + * `a == b * q + r` and `0 <= r < b.abs()`. + * + * The euclidean division is only defined for integers, but can be easily + * extended to work with doubles. In that case `r` may have a non-integer + * value, but it still verifies `0 <= r < |b|`. + * + * The sign of the returned value `r` is always positive. + * + * See [remainder] for the remainder of the truncating division. + */ + num operator %(num other); + + /** Division operator. */ + double operator /(num other); + + /** + * Truncating division operator. + * + * If either operand is a [double] then the result of the truncating division + * `a ~/ b` is equivalent to `(a / b).truncate().toInt()`. + * + * If both operands are [int]s then `a ~/ b` performs the truncating + * integer division. + */ + int operator ~/(num other); + + /** Negate operator. */ + num operator -(); + + /** + * Returns the remainder of the truncating division of `this` by [other]. + * + * The result `r` of this operation satisfies: + * `this == (this ~/ other) * other + r`. + * As a consequence the remainder `r` has the same sign as the divider `this`. + */ + num remainder(num other); + + /** Relational less than operator. */ + bool operator <(num other); + + /** Relational less than or equal operator. */ + bool operator <=(num other); + + /** Relational greater than operator. */ + bool operator >(num other); + + /** Relational greater than or equal operator. */ + bool operator >=(num other); + + /** True if the number is the double Not-a-Number value; otherwise, false. */ + bool get isNaN; + + /** + * True if the number is negative; otherwise, false. + * + * Negative numbers are those less than zero, and the double `-0.0`. + */ + bool get isNegative; + + /** + * True if the number is positive infinity or negative infinity; otherwise, + * false. + */ + bool get isInfinite; + + /** + * True if the number is finite; otherwise, false. + * + * The only non-finite numbers are NaN, positive infinitity and + * negative infinity. + */ + bool get isFinite; + + /** Returns the absolute value of this [num]. */ + num abs(); + + /** + * Returns minus one, zero or plus one depending on the sign and + * numerical value of the number. + * + * Returns minus one if the number is less than zero, + * plus one if the number is greater than zero, + * and zero if the number is equal to zero. + * + * Returns NaN if the number is the double NaN value. + * + * Returns a number of the same type as this number. + * For doubles, `-0.0.sign == -0.0`. + + * The result satisfies: + * + * n == n.sign * n.abs() + * + * for all numbers `n` (except NaN, because NaN isn't `==` to itself). + */ + num get sign; + + /** + * Returns the integer closest to `this`. + * + * Rounds away from zero when there is no closest integer: + * `(3.5).round() == 4` and `(-3.5).round() == -4`. + * + * If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. + */ + int round(); + + /** + * Returns the greatest integer no greater than `this`. + * + * If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. + */ + int floor(); + + /** + * Returns the least integer no smaller than `this`. + * + * If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. + */ + int ceil(); + + /** + * Returns the integer obtained by discarding any fractional + * digits from `this`. + * + * If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. + */ + int truncate(); + + /** + * Returns the double integer value closest to `this`. + * + * Rounds away from zero when there is no closest integer: + * `(3.5).roundToDouble() == 4` and `(-3.5).roundToDouble() == -4`. + * + * If this is already an integer valued double, including `-0.0`, or it is a + * non-finite double value, the value is returned unmodified. + * + * For the purpose of rounding, `-0.0` is considered to be below `0.0`, + * and `-0.0` is therefore considered closer to negative numbers than `0.0`. + * This means that for a value, `d` in the range `-0.5 < d < 0.0`, + * the result is `-0.0`. + * + * The result is always a double. + * If this is a numerically large integer, the result may be an infinite + * double. + */ + double roundToDouble(); + + /** + * Returns the greatest double integer value no greater than `this`. + * + * If this is already an integer valued double, including `-0.0`, or it is a + * non-finite double value, the value is returned unmodified. + * + * For the purpose of rounding, `-0.0` is considered to be below `0.0`. + * A number `d` in the range `0.0 < d < 1.0` will return `0.0`. + * + * The result is always a double. + * If this is a numerically large integer, the result may be an infinite + * double. + */ + double floorToDouble(); + + /** + * Returns the least double integer value no smaller than `this`. + * + * If this is already an integer valued double, including `-0.0`, or it is a + * non-finite double value, the value is returned unmodified. + * + * For the purpose of rounding, `-0.0` is considered to be below `0.0`. + * A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. + * + * The result is always a double. + * If this is a numerically large integer, the result may be an infinite + * double. + */ + double ceilToDouble(); + + /** + * Returns the double integer value obtained by discarding any fractional + * digits from the double value of `this`. + * + * If this is already an integer valued double, including `-0.0`, or it is a + * non-finite double value, the value is returned unmodified. + * + * For the purpose of rounding, `-0.0` is considered to be below `0.0`. + * A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and + * in the range `0.0 < d < 1.0` it will return 0.0. + * + * The result is always a double. + * If this is a numerically large integer, the result may be an infinite + * double. + */ + double truncateToDouble(); + + /** + * Clamps [this] to be in the range [lowerLimit]-[upperLimit]. The comparison + * is done using [compareTo] and therefore takes `-0.0` into account. + * It also implies that [double.NAN] is treated as the maximal double value. + */ + num clamp(num lowerLimit, num upperLimit); + + /** Truncates this [num] to an integer and returns the result as an [int]. */ + int toInt(); + + /** + * Return this [num] as a [double]. + * + * If the number is not representable as a [double], an + * approximation is returned. For numerically large integers, the + * approximation may be infinite. + */ + double toDouble(); + + /** + * Returns a decimal-point string-representation of `this`. + * + * Converts `this` to a [double] before computing the string representation. + * + * If the absolute value of `this` is greater or equal to `10^21` then this + * methods returns an exponential representation computed by + * `this.toStringAsExponential()`. Otherwise the result + * is the closest string representation with exactly [fractionDigits] digits + * after the decimal point. If [fractionDigits] equals 0 then the decimal + * point is omitted. + * + * The parameter [fractionDigits] must be an integer satisfying: + * `0 <= fractionDigits <= 20`. + * + * Examples: + * + * 1.toStringAsFixed(3); // 1.000 + * (4321.12345678).toStringAsFixed(3); // 4321.123 + * (4321.12345678).toStringAsFixed(5); // 4321.12346 + * 123456789012345678901.toStringAsFixed(3); // 123456789012345683968.000 + * 1000000000000000000000.toStringAsFixed(3); // 1e+21 + * 5.25.toStringAsFixed(0); // 5 + */ + String toStringAsFixed(int fractionDigits); + + /** + * Returns an exponential string-representation of `this`. + * + * Converts `this` to a [double] before computing the string representation. + * + * If [fractionDigits] is given then it must be an integer satisfying: + * `0 <= fractionDigits <= 20`. In this case the string contains exactly + * [fractionDigits] after the decimal point. Otherwise, without the parameter, + * the returned string uses the shortest number of digits that accurately + * represent [this]. + * + * If [fractionDigits] equals 0 then the decimal point is omitted. + * Examples: + * + * 1.toStringAsExponential(); // 1e+0 + * 1.toStringAsExponential(3); // 1.000e+0 + * 123456.toStringAsExponential(); // 1.23456e+5 + * 123456.toStringAsExponential(3); // 1.235e+5 + * 123.toStringAsExponential(0); // 1e+2 + */ + String toStringAsExponential([int fractionDigits]); + + /** + * Converts `this` to a double and returns a string representation with + * exactly [precision] significant digits. + * + * The parameter [precision] must be an integer satisfying: + * `1 <= precision <= 21`. + * + * Examples: + * + * 1.toStringAsPrecision(2); // 1.0 + * 1e15.toStringAsPrecision(3); // 1.00+15 + * 1234567.toStringAsPrecision(3); // 1.23e+6 + * 1234567.toStringAsPrecision(9); // 1234567.00 + * 12345678901234567890.toStringAsPrecision(20); // 12345678901234567168 + * 12345678901234567890.toStringAsPrecision(14); // 1.2345678901235e+19 + * 0.00000012345.toPrecision(15); // 1.23450000000000e-7 + * 0.0000012345.toPrecision(15); // 0.00000123450000000000 + */ + String toStringAsPrecision(int precision); + + /** + * Returns the shortest string that correctly represent the input number. + * + * All [double]s in the range `10^-6` (inclusive) to `10^21` (exclusive) + * are converted to their decimal representation with at least one digit + * after the decimal point. For all other doubles, + * except for special values like `NaN` or `Infinity`, this method returns an + * exponential representation (see [toStringAsExponential]). + * + * Returns `"NaN"` for [double.NAN], `"Infinity"` for [double.INFINITY], and + * `"-Infinity"` for [double.MINUS_INFINITY]. + * + * An [int] is converted to a decimal representation with no decimal point. + * + * Examples: + * + * (0.000001).toString(); // "0.000001" + * (0.0000001).toString(); // "1e-7" + * (111111111111111111111.0).toString(); // "111111111111111110000.0" + * (100000000000000000000.0).toString(); // "100000000000000000000.0" + * (1000000000000000000000.0).toString(); // "1e+21" + * (1111111111111111111111.0).toString(); // "1.1111111111111111e+21" + * 1.toString(); // "1" + * 111111111111111111111.toString(); // "111111111111111110000" + * 100000000000000000000.toString(); // "100000000000000000000" + * 1000000000000000000000.toString(); // "1000000000000000000000" + * 1111111111111111111111.toString(); // "1111111111111111111111" + * 1.234e5.toString(); // 123400 + * 1234.5e6.toString(); // 1234500000 + * 12.345e67.toString(); // 1.2345e+68 + * + * Note: the conversion may round the output if the returned string + * is accurate enough to uniquely identify the input-number. + * For example the most precise representation of the [double] `9e59` equals + * `"899999999999999918767229449717619953810131273674690656206848"`, but + * this method returns the shorter (but still uniquely identifying) `"9e59"`. + * + */ + String toString(); + + /** + * Parses a string containing a number literal into a number. + * + * The method first tries to read the [input] as integer (similar to + * [int.parse] without a radix). + * If that fails, it tries to parse the [input] as a double (similar to + * [double.parse]). + * If that fails, too, it invokes [onError] with [input]. + * + * If no [onError] is supplied, it defaults to a function that throws a + * [FormatException]. + * + * For any number `n`, this function satisfies + * `identical(n, num.parse(n.toString()))`. + */ + static num parse(String input, [num onError(String input)]) { + String source = input.trim(); + // TODO(lrn): Optimize to detect format and result type in one check. + num result = int.parse(source, onError: _returnNull); + if (result != null) return result; + result = double.parse(source, _returnNull); + if (result != null) return result; + if (onError == null) throw new FormatException(input); + return onError(input); + } + + /** Helper function for [parse]. */ + static _returnNull(_) => null; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/object.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/object.dart new file mode 100644 index 0000000..2951a73 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/object.dart @@ -0,0 +1,96 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * The base class for all Dart objects. + * + * Because Object is the root of the Dart class hierarchy, + * every other Dart class is a subclass of Object. + * + * When you define a class, you should override [toString] + * to return a string describing an instance of that class. + * You might also need to define [hashCode] and [==], as described in the + * [Implementing map keys] + * (http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html#ch03-implementing-map-keys) + * section of the [library tour] + * (http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html). + */ +class Object { + /** + * Creates a new [Object] instance. + * + * [Object] instances have no meaningful state, and are only useful + * through their identity. An [Object] instance is equal to itself + * only. + */ + const Object(); + + /** + * The equality operator. + * + * The default behavior for all [Object]s is to return true if and + * only if [:this:] and [other] are the same object. + * + * Override this method to specify a different equality relation on + * a class. The overriding method must still be an equivalence relation. + * That is, it must be: + * + * * Total: It must return a boolean for all arguments. It should never throw + * or return `null`. + * + * * Reflexive: For all objects `o`, `o == o` must be true. + * + * * Symmetric: For all objects `o1` and `o2`, `o1 == o2` and `o2 == o1` must + * either both be true, or both be false. + * + * * Transitive: For all objects `o1`, `o2`, and `o3`, if `o1 == o2` and + * `o2 == o3` are true, then `o1 == o3` must be true. + * + * The method should also be consistent over time, so equality of two objects + * should not change over time, or at least only change if one of the objects + * was modified. + * + * If a subclass overrides the equality operator it should override + * the [hashCode] method as well to maintain consistency. + */ + bool operator ==(other) => identical(this, other); + + /** + * Get a hash code for this object. + * + * All objects have hash codes. Hash codes are guaranteed to be the + * same for objects that are equal when compared using the equality + * operator [:==:]. Other than that there are no guarantees about + * the hash codes. They will not be consistent between runs and + * there are no distribution guarantees. + * + * If a subclass overrides [hashCode] it should override the + * equality operator as well to maintain consistency. + */ + external int get hashCode; + + /** + * Returns a string representation of this object. + */ + external String toString(); + + /** + * [noSuchMethod] is invoked when users invoke a non-existent method + * on an object. The name of the method and the arguments of the + * invocation are passed to [noSuchMethod] in an [Invocation]. + * If [noSuchMethod] returns a value, that value becomes the result of + * the original invocation. + * + * The default behavior of [noSuchMethod] is to throw a + * [NoSuchMethodError]. + */ + external dynamic noSuchMethod(Invocation invocation); + + /** + * A representation of the runtime type of the object. + */ + external Type get runtimeType; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/pattern.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/pattern.dart new file mode 100644 index 0000000..92ac07e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/pattern.dart @@ -0,0 +1,35 @@ +// Copyright (c) 2011, 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. + +part of dart.core; + +/** + * An interface for basic searches within strings. + */ +abstract class Pattern { + /** + * Match this pattern against the string repeatedly. + * + * The iterable will contain all the non-overlapping matches of the + * pattern on the string, ordered by start index. + * + * The matches are found by repeatedly finding the first match + * of the pattern on the string, starting from the end of the previous + * match, and initially starting from index zero. + * + * If the pattern matches the empty string at some point, the next + * match is found by starting at the previous match's end plus one. + */ + Iterable allMatches(String str); + + /** + * Match this pattern against the start of string. + * + * If [start] is provided, it must be an integer in the range `0` .. + * `string.length`. In that case, this patten is tested against the + * string at the [start] position. That is, a match is returned if the + * pattern can match a part of the string starting from position [start]. + */ + Match matchAsPrefix(String string, [int start = 0]); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/print.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/print.dart new file mode 100644 index 0000000..42c7d1c --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/print.dart @@ -0,0 +1,15 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/// Prints a string representation of the object to the console. +void print(Object object) { + String line = "$object"; + if (printToZone == null) { + printToConsole(line); + } else { + printToZone(line); + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/regexp.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/regexp.dart new file mode 100644 index 0000000..840754e --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/regexp.dart @@ -0,0 +1,173 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * A result from searching within a string. + * + * A Match or an [Iterable] of Match objects is returned from [Pattern] + * matching methods. + * + * The following example finds all matches of a [RegExp] in a [String] + * and iterates through the returned iterable of Match objects. + * + * RegExp exp = new RegExp(r"(\w+)"); + * String str = "Parse my string"; + * Iterable matches = exp.allMatches(str); + * for (Match m in matches) { + * String match = m.group(0); + * print(match); + * } + * + * The output of the example is: + * + * Parse + * my + * string + * + * Some patterns, regular expressions in particular, may record subtrings + * that were part of the matching. These are called _groups_ in the Match + * object. Some patterns may never have any groups, and their matches always + * have zero [groupCount]. + */ +abstract class Match { + /** + * Returns the index in the string where the match starts. + */ + int get start; + + /** + * Returns the index in the string after the last character of the + * match. + */ + int get end; + + /** + * Returns the string matched by the given [group]. + * + * If [group] is 0, returns the match of the pattern. + * + * The result may be `null` if the pattern didn't assign a value to it + * as part of this match. + */ + String group(int group); + + /** + * Returns the string matched by the given [group]. + * + * If [group] is 0, returns the match of the pattern. + * + * Short alias for [Match.group]. + */ + String operator [](int group); + + /** + * Returns a list of the groups with the given indices. + * + * The list contains the strings returned by [group] for each index in + * [groupIndices]. + */ + List groups(List groupIndices); + + /** + * Returns the number of captured groups in the match. + * + * Some patterns may capture parts of the input that was used to + * compute the full match. This is the number of captured groups, + * which is also the maximal allowed argument to the [group] method. + */ + int get groupCount; + + /** + * The string on which this match was computed. + */ + String get input; + + /** + * The pattern used to search in [input]. + */ + Pattern get pattern; +} + + +/** + * A regular expression pattern. + * + * Regular expressions are [Pattern]s, and can as such be used to match strings + * or parts of strings. + * + * Dart regular expressions have the same syntax and semantics as + * JavaScript regular expressions. See + * + * for the specification of JavaScript regular expressions. + * + * [firstMatch] is the main implementation method that applies a regular + * expression to a string and returns the first [Match]. All + * other methods in [RegExp] can build on it. + * + * Use [allMatches] to look for all matches of a regular expression in + * a string. + * + * The following example finds all matches of a regular expression in + * a string. + * + * RegExp exp = new RegExp(r"(\w+)"); + * String str = "Parse my string"; + * Iterable matches = exp.allMatches(str); + */ +abstract class RegExp implements Pattern { + /** + * Constructs a regular expression. + * + * Throws a [FormatException] if [source] is not valid regular + * expression syntax. + */ + external factory RegExp(String source, {bool multiLine: false, + bool caseSensitive: true}); + + /** + * Searches for the first match of the regular expression + * in the string [input]. Returns `null` if there is no match. + */ + Match firstMatch(String input); + + /** + * Returns an iterable of the matches of the regular expression on [input]. + */ + Iterable allMatches(String input); + + /** + * Returns whether the regular expression has a match in the string [input]. + */ + bool hasMatch(String input); + + /** + * Returns the first substring match of this regular expression in [input]. + */ + String stringMatch(String input); + + /** + * The pattern of this regular expression. + */ + String get pattern; + + /** + * Whether this regular expression matches multiple lines. + * + * If the regexp does match multiple lines, the "^" and "$" characters + * match the beginning and end of lines. If not, the character match the + * beginning and end of the input. + */ + bool get isMultiLine; + + /** + * Whether this regular expression is case sensitive. + * + * If the regular expression is not case sensitive, it will match an input + * letter with a pattern letter even if the two letters are different case + * versions of the same letter. + */ + bool get isCaseSensitive; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/set.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/set.dart new file mode 100644 index 0000000..271b389 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/set.dart @@ -0,0 +1,169 @@ +// Copyright (c) 2011, 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. + +part of dart.core; + +/** + * A collection of objects in which each object can occur only once. + * + * That is, for each object of the element type, the object is either considered + * to be in the set, or to _not_ be in the set. + * + * Set implementations may consider some elements indistinguishable. These + * elements are treated as being the same for any operation on the set. + * + * The default [Set] implementation, [LinkedHashSet], considers objects + * indistinguishable if they are equal with regard to + * operator [Object.==]. + * + * Iterating over elements of a set may be either unordered + * or ordered in some way. Examples: + * + * * A [HashSet] is unordered, which means that its iteration order is + * uspecified, + * * [LinkedHashSet] iterates in the insertion order of its elements, and + * * a sorted set like [SplayTreeSet] iterates the elements in sorted order. + * + * It is generally not allowed to modify the set (add or remove elements) while + * an operation on the set is being performed, for example during a call to + * [forEach] or [containsAll]. Nor is it allowed to modify the set while + * iterating either the set itself or any [Iterable] that is backed by the set, + * such as the ones returned by methods like [where] and [map]. + */ +abstract class Set extends IterableBase implements EfficientLength { + /** + * Creates an empty [Set]. + * + * The created [Set] is a plain [LinkedHashSet]. + * As such, it considers elements that are equal (using [==]) to be + * indistinguishable, and requires them to have a compatible + * [Object.hashCode] implementation. + * + * The set is equivalent to one created by `new LinkedHashSet()`. + */ + factory Set() = LinkedHashSet; + + /** + * Creates an empty identity [Set]. + * + * The created [Set] is a [LinkedHashSet] that uses identity as equality + * relation. + * + * The set is equivalent to one created by `new LinkedHashSet.identity()`. + */ + factory Set.identity() = LinkedHashSet.identity; + + /** + * Creates a [Set] that contains all elements of [other]. + * + * The created [Set] is a [LinkedHashSet]. As such, it considers elements that + * are equal (using [==]) to be undistinguishable, and requires them to + * have a compatible [Object.hashCode] implementation. + * + * The set is equivalent to one created by `new LinkedHashSet.from(other)`. + */ + factory Set.from(Iterable other) = LinkedHashSet.from; + + /** + * Provides an iterator that iterates over the elements of this set. + * + * The order of iteration is defined by the individual `Set` implementation, + * but must be consistent between changes to the set. + */ + Iterator get iterator; + + /** + * Returns true if [value] is in the set. + */ + bool contains(Object value); + + /** + * Adds [value] into the set. Returns `true` if [value] was added to the set. + * + * If [value] already exists, the set is not changed and `false` is returned. + */ + bool add(E value); + + /** + * Adds all of [elements] to this Set. + * + * Equivalent to adding each element in [elements] using [add], + * but some collections may be able to optimize it. + */ + void addAll(Iterable elements); + + /** + * Removes [value] from the set. Returns true if [value] was + * in the set. Returns false otherwise. The method has no effect + * if [value] value was not in the set. + */ + bool remove(Object value); + + /** + * If an object equal to [object] is in the set, return it. + * + * Checks if there is an object in the set that is equal to [object]. + * If so, that object is returned, otherwise returns null. + */ + E lookup(Object object); + + /** + * Removes each element of [elements] from this set. + */ + void removeAll(Iterable elements); + + /** + * Removes all elements of this set that are not elements in [elements]. + * + * Checks for each element of [elements] whether there is an element in this + * set that is equal to it (according to `this.contains`), and if so, the + * equal element in this set is retained, and elements that are not equal + * to any element in `elements` are removed. + */ + void retainAll(Iterable elements); + + /** + * Removes all elements of this set that satisfy [test]. + */ + void removeWhere(bool test(E element)); + + /** + * Removes all elements of this set that fail to satisfy [test]. + */ + void retainWhere(bool test(E element)); + + /** + * Returns whether this Set contains all the elements of [other]. + */ + bool containsAll(Iterable other); + + /** + * Returns a new set which is the intersection between this set and [other]. + * + * That is, the returned set contains all the elements of this [Set] that + * are also elements of [other] according to `other.contains`. + */ + Set intersection(Set other); + + /** + * Returns a new set which contains all the elements of this set and [other]. + * + * That is, the returned set contains all the elements of this [Set] and + * all the elements of [other]. + */ + Set union(Set other); + + /** + * Returns a new set with the the elements of this that are not in [other]. + * + * That is, the returned set contains all the elements of this [Set] that + * are not elements of [other] according to `other.contains`. + */ + Set difference(Set other); + + /** + * Removes all elements in the set. + */ + void clear(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/sink.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/sink.dart new file mode 100644 index 0000000..0d8f2ef --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/sink.dart @@ -0,0 +1,31 @@ +// Copyright (c) 2014, 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. + +part of dart.core; + +/** + * A generic destination for data. + * + * Multiple data values can be put into a sink, and when no more data is + * available, the sink should be closed. + * + * This is a generic interface that other data receivers can implement. + */ +abstract class Sink { + /** + * Put the data into the sink. + * + * Must not be called after a call to [close]. + */ + void add(T data); + + /** + * Tell the sink that no further data will be added. + * + * Calling this method more than once is allowed, but does nothing. + * + * The [add] method must not be called after this method. + */ + void close(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/stacktrace.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/stacktrace.dart new file mode 100644 index 0000000..ef8cc4d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/stacktrace.dart @@ -0,0 +1,27 @@ +// Copyright (c) 2013, 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. + +part of dart.core; + +/** + * An interface implemented by all stack trace objects. + * + * A [StackTrace] is intended to convey information to the user about the call + * sequence that triggered an exception. + * + * These objects are created by the runtime, it is not possible to create + * them programmatically. + */ +abstract class StackTrace { + /** + * Returns a [String] representation of the stack trace. + * + * The string represents the full stack trace starting from + * the point where a throw ocurred to the top of the current call sequence. + * + * The exact format of the string representation is not final. + */ + String toString(); +} + diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/stopwatch.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/stopwatch.dart new file mode 100644 index 0000000..9650a57 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/stopwatch.dart @@ -0,0 +1,132 @@ +// Copyright (c) 2011, 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. + +part of dart.core; + +/** + * A simple stopwatch interface to measure elapsed time. + */ +class Stopwatch { + /** + * Frequency of the elapsed counter in Hz. + */ + final int frequency = _frequency(); + + // The _start and _stop fields capture the time when [start] and [stop] + // are called respectively. + // If _start is null, then the [Stopwatch] has not been started yet. + // If _stop is null, then the [Stopwatch] has not been stopped yet, + // or is running. + int _start; + int _stop; + + /** + * Creates a [Stopwatch] in stopped state with a zero elapsed count. + * + * The following example shows how to start a [Stopwatch] + * immediately after allocation. + * + * Stopwatch stopwatch = new Stopwatch()..start(); + */ + Stopwatch(); + + /** + * Starts the [Stopwatch]. + * + * The [elapsed] count is increasing monotonically. If the [Stopwatch] has + * been stopped, then calling start again restarts it without resetting the + * [elapsed] count. + * + * If the [Stopwatch] is currently running, then calling start does nothing. + */ + void start() { + if (isRunning) return; + if (_start == null) { + // This stopwatch has never been started. + _start = _now(); + } else { + // Restart this stopwatch. Prepend the elapsed time to the current + // start time. + _start = _now() - (_stop - _start); + _stop = null; + } + } + + /** + * Stops the [Stopwatch]. + * + * The [elapsedTicks] count stops increasing after this call. If the + * [Stopwatch] is currently not running, then calling this method has no + * effect. + */ + void stop() { + if (!isRunning) return; + _stop = _now(); + } + + /** + * Resets the [elapsed] count to zero. + * + * This method does not stop or start the [Stopwatch]. + */ + void reset() { + if (_start == null) return; + // If [_start] is not null, then the stopwatch had already been started. It + // may running right now. + _start = _now(); + if (_stop != null) { + // The watch is not running. So simply set the [_stop] to [_start] thus + // having an elapsed time of 0. + _stop = _start; + } + } + + /** + * Returns the elapsed number of clock ticks since calling [start] while the + * [Stopwatch] is running. + * + * Returns the elapsed number of clock ticks between calling [start] and + * calling [stop]. + * + * Returns 0 if the [Stopwatch] has never been started. + * + * The elapsed number of clock ticks increases by [frequency] every second. + */ + int get elapsedTicks { + if (_start == null) { + return 0; + } + return (_stop == null) ? (_now() - _start) : (_stop - _start); + } + + /** + * Returns the [elapsedTicks] counter converted to a [Duration]. + */ + Duration get elapsed { + return new Duration(microseconds: elapsedMicroseconds); + } + + /** + * Returns the [elapsedTicks] counter converted to microseconds. + */ + int get elapsedMicroseconds { + return (elapsedTicks * 1000000) ~/ frequency; + } + + /** + * Returns the [elapsedTicks] counter converted to milliseconds. + */ + int get elapsedMilliseconds { + return (elapsedTicks * 1000) ~/ frequency; + } + + + /** + * Returns wether the [StopWatch] is currently running. + */ + bool get isRunning => _start != null && _stop == null; + + external static int _frequency(); + external static int _now(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/string.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/string.dart new file mode 100644 index 0000000..5488a2f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/string.dart @@ -0,0 +1,740 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * A sequence of characters. + * + * A string can be either single or multiline. Single line strings are + * written using matching single or double quotes, and multiline strings are + * written using triple quotes. The following are all valid Dart strings: + * + * 'Single quotes'; + * "Double quotes"; + * 'Double quotes in "single" quotes'; + * "Single quotes in 'double' quotes"; + * + * '''A + * multiline + * string'''; + * + * """ + * Another + * multiline + * string"""; + * + * Strings are immutable. Although you cannot change a string, you can perform + * an operation on a string and assign the result to a new string: + * + * var string = 'Dart is fun'; + * var newString = string.substring(0, 5); + * + * You can use the plus (`+`) operator to concatenate strings: + * + * 'Dart ' + 'is ' + 'fun!'; // 'Dart is fun!' + * + * You can also use adjacent string literals for concatenation: + * + * 'Dart ' 'is ' 'fun!'; // 'Dart is fun!' + * + * You can use `${}` to interpolate the value of Dart expressions + * within strings. The curly braces can be omitted when evaluating identifiers: + * + * string = 'dartlang'; + * '$string has ${string.length} letters'; // 'dartlang has 8 letters' + * + * A string is represented by a sequence of Unicode UTF-16 code units + * accessible through the [codeUnitAt] or the [codeUnits] members: + * + * string = 'Dart'; + * string.codeUnitAt(0); // 68 + * string.codeUnits; // [68, 97, 114, 116] + * + * The string representation of code units is accessible through the index + * operator: + * + * string[0]; // 'D' + * + * The characters of a string are encoded in UTF-16. Decoding UTF-16, which + * combines surrogate pairs, yields Unicode code points. Following a similar + * terminology to Go, we use the name 'rune' for an integer representing a + * Unicode code point. Use the [runes] property to get the runes of a string: + * + * string.runes.toList(); // [68, 97, 114, 116] + * + * For a character outside the Basic Multilingual Plane (plane 0) that is + * composed of a surrogate pair, [runes] combines the pair and returns a + * single integer. For example, the Unicode character for a + * musical G-clef ('𝄞') with rune value 0x1D11E consists of a UTF-16 surrogate + * pair: `0xD834` and `0xDD1E`. Using [codeUnits] returns the surrogate pair, + * and using `runes` returns their combined value: + * + * var clef = '\u{1D11E}'; + * clef.codeUnits; // [0xD834, 0xDD1E] + * clef.runes.toList(); // [0x1D11E] + * + * The String class can not be extended or implemented. Attempting to do so + * yields a compile-time error. + * + * ## Other resources + * + * See [StringBuffer] to efficiently build a string incrementally. See + * [RegExp] to work with regular expressions. + * + * Also see: + + * * [Dart Cookbook](https://www.dartlang.org/docs/cookbook/#strings) + * for String examples and recipes. + * * [Dart Up and Running] + * (https://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html#ch03-strings-and-regular-expressions) + */ +abstract class String implements Comparable, Pattern { + /** + * Allocates a new String for the specified [charCodes]. + * + * The [charCodes] can be UTF-16 code units or runes. If a char-code value is + * 16-bit, it is copied verbatim: + * + * new String.fromCharCodes([68]); // 'D' + * + * If a char-code value is greater than 16-bits, it is decomposed into a + * surrogate pair: + * + * var clef = new String.fromCharCodes([0x1D11E]); + * clef.codeUnitAt(0); // 0xD834 + * clef.codeUnitAt(1); // 0xDD1E + */ + external factory String.fromCharCodes(Iterable charCodes); + + /** + * Allocates a new String for the specified [charCode]. + * + * If the [charCode] can be represented by a single UTF-16 code unit, the new + * string contains a single code unit. Otherwise, the [length] is 2 and + * the code units form a surrogate pair. See documentation for + * [fromCharCodes]. + * + * Creating a String with half of a surrogate pair is legal but generally + * discouraged. + */ + factory String.fromCharCode(int charCode) { + List charCodes = new List.filled(1, charCode); + return new String.fromCharCodes(charCodes); + } + + /** + * Returns the string value of the environment declaration [name]. + * + * Environment declarations are provided by the surrounding system compiling + * or running the Dart program. Declarations map a string key to a string + * value. + * + * If [name] is not declared in the environment, the result is instead + * [defaultValue]. + * + * Example of getting a value: + * + * const String.fromEnvironment("defaultFloo", defaultValue: "no floo") + * + * Example of checking whether a declaration is there at all: + * + * var isDeclared = const String.fromEnvironment("maybeDeclared") != null; + */ + external const factory String.fromEnvironment(String name, + {String defaultValue}); + + /** + * Gets the character (as a single-code-unit [String]) at the given [index]. + * + * The returned string represents exactly one UTF-16 code unit, which may be + * half of a surrogate pair. A single member of a surrogate pair is an + * invalid UTF-16 string: + * + * var clef = '\u{1D11E}'; + * // These represent invalid UTF-16 strings. + * clef[0].codeUnits; // [0xD834] + * clef[1].codeUnits; // [0xDD1E] + * + * This method is equivalent to + * `new String.fromCharCode(this.codeUnitAt(index))`. + */ + String operator [](int index); + + /** + * Returns the 16-bit UTF-16 code unit at the given [index]. + */ + int codeUnitAt(int index); + + /** + * The length of the string. + * + * Returns the number of UTF-16 code units in this string. The number + * of [runes] might be fewer, if the string contains characters outside + * the Basic Multilingual Plane (plane 0): + * + * 'Dart'.length; // 4 + * 'Dart'.runes.length; // 4 + * + * var clef = '\u{1D11E}'; + * clef.length; // 2 + * clef.runes.length; // 1 + */ + int get length; + + /** + * Returns true if the two strings are equal. False, otherwise. + * + * This method compares each individual code unit of the strings. + * It does not check for Unicode equivalence. + * For example, both the following strings represent the string 'Amélie', + * but due to their different encoding, are not equal: + * + * 'Am\xe9lie' == 'Ame\u{301}lie'; // false + * + * The first string encodes 'é' as a single unicode code unit (also + * a single rune), whereas the second string encodes it as 'e' with the + * combining accent character '◌́'. + */ + bool operator ==(var other); + + /** + * Returns true if this string ends with [other]. For example: + * + * 'Dart'.endsWith('t'); // true + */ + bool endsWith(String other); + + /** + * Returns true if this string starts with a match of [pattern]. + * + * var string = 'Dart'; + * string.startsWith('D'); // true + * string.startsWith(new RegExp(r'[A-Z][a-z]')); // true + * + * If [index] is provided, this method checks if the substring starting + * at that index starts with a match of [pattern]: + * + * string.startsWith('art', 1); // true + * string.startsWith(new RegExp(r'\w{3}')); // true + * + * [index] must not be negative or greater than [length]. + * + * A [RegExp] containing '^' does not match if the [index] is greater than + * zero. The pattern works on the string as a whole, and does not extract + * a substring starting at [index] first: + * + * string.startsWith(new RegExp(r'^art'), 1); // false + * string.startsWith(new RegExp(r'art'), 1); // true + */ + bool startsWith(Pattern pattern, [int index = 0]); + + /** + * Returns the position of the first match of [pattern] in this string, + * starting at [start], inclusive: + * + * var string = 'Dartisans'; + * string.indexOf('art'); // 1 + * string.indexOf(new RegExp(r'[A-Z][a-z]')); // 0 + * + * Returns -1 if no match is found: + * + * string.indexOf(new RegExp(r'dart')); // -1 + * + * [start] must not be negative or greater than [length]. + */ + int indexOf(Pattern pattern, [int start]); + + /** + * Returns the position of the last match [pattern] in this string, searching + * backward starting at [start], inclusive: + * + * var string = 'Dartisans'; + * string.lastIndexOf('a'); // 6 + * string.lastIndexOf(new RegExp(r'a(r|n)')); // 6 + * + * Returns -1 if [other] could not be found. + * + * string.lastIndexOf(new RegExp(r'DART')); // -1 + * + * [start] must not be negative or greater than [length]. + */ + int lastIndexOf(Pattern pattern, [int start]); + + /** + * Returns true if this string is empty. + */ + bool get isEmpty; + + /** + * Returns true if this string is not empty. + */ + bool get isNotEmpty; + + /** + * Creates a new string by concatenating this string with [other]. + * + * 'dart' + 'lang'; // 'dartlang' + */ + String operator +(String other); + + /** + * Returns the substring of this string that extends from [startIndex], + * inclusive, to [endIndex], exclusive. + * + * var string = 'dartlang'; + * string.substring(1); // 'artlang' + * string.substring(1, 4); // 'art' + */ + String substring(int startIndex, [int endIndex]); + + /** + * Returns the string without any leading and trailing whitespace. + * + * If the string contains leading or trailing whitespace, a new string with no + * leading and no trailing whitespace is returned: + * + * '\tDart is fun\n'.trim(); // 'Dart is fun' + * + * Otherwise, the original string itself is returned: + * + * var str1 = 'Dart'; + * var str2 = str1.trim(); + * identical(str1, str2); // true + * + * Whitespace is defined by the Unicode White_Space property (as defined in + * version 6.2 or later) and the BOM character, 0xFEFF. + * + * Here is the list of trimmed characters (following version 6.2): + * + * 0009..000D ; White_Space # Cc .. + * 0020 ; White_Space # Zs SPACE + * 0085 ; White_Space # Cc + * 00A0 ; White_Space # Zs NO-BREAK SPACE + * 1680 ; White_Space # Zs OGHAM SPACE MARK + * 180E ; White_Space # Zs MONGOLIAN VOWEL SEPARATOR + * 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE + * 2028 ; White_Space # Zl LINE SEPARATOR + * 2029 ; White_Space # Zp PARAGRAPH SEPARATOR + * 202F ; White_Space # Zs NARROW NO-BREAK SPACE + * 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE + * 3000 ; White_Space # Zs IDEOGRAPHIC SPACE + * + * FEFF ; BOM ZERO WIDTH NO_BREAK SPACE + */ + String trim(); + + /** + * Returns the string without any leading whitespace. + * + * As [trim], but only removes leading whitespace. + */ + String trimLeft(); + + /** + * Returns the string without any trailing whitespace. + * + * As [trim], but only removes trailing whitespace. + */ + String trimRight(); + + /** + * Creates a new string by concatenating this string with itself a number + * of times. + * + * The result of `str * n` is equivalent to + * `str + str + ...`(n times)`... + str`. + * + * Returns an empty string if [times] is zero or negative. + */ + String operator *(int times); + + /** + * Pads this string on the left if it is shorther than [width]. + * + * Return a new string that prepends [padding] onto this string + * one time for each position the length is less than [width]. + * + * If [width] is already smaller than or equal to `this.length`, + * no padding is added. A negative `width` is treated as zero. + * + * If [padding] has length different from 1, the result will not + * have length `width`. This may be useful for cases where the + * padding is a longer string representing a single character, like + * `" "` or `"\u{10002}`". + * In that case, the user should make sure that `this.length` is + * the correct measure of the strings length. + */ + String padLeft(int width, [String padding = ' ']); + + /** + * Pads this string on the right if it is shorther than [width]. + * + * Return a new string that appends [padding] after this string + * one time for each position the length is less than [width]. + * + * If [width] is already smaller than or equal to `this.length`, + * no padding is added. A negative `width` is treated as zero. + * + * If [padding] has length different from 1, the result will not + * have length `width`. This may be useful for cases where the + * padding is a longer string representing a single character, like + * `" "` or `"\u{10002}`". + * In that case, the user should make sure that `this.length` is + * the correct measure of the strings length. + */ + String padRight(int width, [String padding = ' ']); + + /** + * Returns true if this string contains a match of [other]: + * + * var string = 'Dart strings'; + * string.contains('D'); // true + * string.contains(new RegExp(r'[A-Z]')); // true + * + * If [startIndex] is provided, this method matches only at or after that + * index: + * + * string.contains('X', 1); // false + * string.contains(new RegExp(r'[A-Z]'), 1); // false + * + * [startIndex] must not be negative or greater than [length]. + */ + bool contains(Pattern other, [int startIndex = 0]); + + /** + * Returns a new string in which the first occurence of [from] in this string + * is replaced with [to]: + * + * '0.0001'.replaceFirst(new RegExp(r'0'), ''); // '.0001' + */ + String replaceFirst(Pattern from, String to); + + /** + * Replaces all substrings that match [from] with [replace]. + * + * Returns a new string in which the non-overlapping substrings matching + * [from] (the ones iterated by `from.allMatches(thisString)`) are replaced + * by the literal string [replace]. + * + * 'resume'.replaceAll(new RegExp(r'e'), 'é'); // 'résumé' + * + * Notice that the [replace] string is not interpreted. If the replacement + * depends on the match (for example on a [RegExp]'s capture groups), use + * the [replaceAllMapped] method instead. + */ + String replaceAll(Pattern from, String replace); + + /** + * Replace all substrings that match [from] by a string computed from the + * match. + * + * Returns a new string in which the non-overlapping substrings that match + * [from] (the ones iterated by `from.allMatches(thisString)`) are replaced + * by the result of calling [replace] on the corresponding [Match] object. + * + * This can be used to replace matches with new content that depends on the + * match, unlike [replaceAll] where the replacement string is always the same. + * + * The [replace] function is called with the [Match] generated + * by the pattern, and its result is used as replacement. + * + * The function defined below converts each word in a string to simplified + * 'pig latin' using [replaceAllMapped]: + * + * pigLatin(String words) => words.replaceAllMapped( + * new RegExp(r'\b(\w*?)([aeiou]\w*)', caseSensitive: false), + * (Match m) => "${m[2]}${m[1]}${m[1].isEmpty ? 'way' : 'ay'}"); + * + * pigLatin('I have a secret now!'); // 'Iway avehay away ecretsay ownay!' + */ + String replaceAllMapped(Pattern from, String replace(Match match)); + + /** + * Splits the string at matches of [pattern]. Returns + * a list of substrings. + * + * Splitting with an empty string pattern (`''`) splits at UTF-16 code unit + * boundaries and not at rune boundaries: + * + * var string = 'Pub'; + * string.split(''); // ['P', 'u', 'b'] + * + * string.codeUnits.map((unit) { + * return new String.fromCharCode(unit); + * }).toList(); // ['P', 'u', 'b'] + * + * // String made up of two code units, but one rune. + * string = '\u{1D11E}'; + * string.split('').length; // 2 + * + * You should [map] the runes unless you are certain that the string is in + * the basic multilingual plane (meaning that each code unit represents a + * rune): + * + * string.runes.map((rune) => new String.fromCharCode(rune)); + */ + List split(Pattern pattern); + + /** + * Splits the string, converts its parts, and combines them into a new + * string. + * + * [pattern] is used to split the string into parts and separating matches. + * + * Each match is converted to a string by calling [onMatch]. If [onMatch] + * is omitted, the matched string is used. + * + * Each non-matched part is converted by a call to [onNonMatch]. If + * [onNonMatch] is omitted, the non-matching part is used. + * + * Then all the converted parts are combined into the resulting string. + * + * 'Eats shoots leaves'.splitMapJoin((new RegExp(r'shoots')), + * onMatch: (m) => '${m.group(0)}', + * onNonMatch: (n) => '*'); // *shoots* + */ + String splitMapJoin(Pattern pattern, + {String onMatch(Match match), + String onNonMatch(String nonMatch)}); + + /** + * Returns an unmodifiable list of the UTF-16 code units of this string. + */ + List get codeUnits; + + /** + * Returns an [Iterable] of Unicode code-points of this string. + * + * If the string contains surrogate pairs, they are combined and returned + * as one integer by this iterator. Unmatched surrogate halves are treated + * like valid 16-bit code-units. + */ + Runes get runes; + + /** + * Converts all characters in this string to lower case. + * If the string is already in all lower case, this method returns [:this:]. + * + * 'ALPHABET'.toLowerCase(); // 'alphabet' + * 'abc'.toLowerCase(); // 'abc' + * + * This function uses the language independent Unicode mapping and thus only + * works in some languages. + */ + // TODO(floitsch): document better. (See EcmaScript for description). + String toLowerCase(); + + /** + * Converts all characters in this string to upper case. + * If the string is already in all upper case, this method returns [:this:]. + * + * 'alphabet'.toUpperCase(); // 'ALPHABET' + * 'ABC'.toUpperCase(); // 'ABC' + * + * This function uses the language independent Unicode mapping and thus only + * works in some languages. + */ + // TODO(floitsch): document better. (See EcmaScript for description). + String toUpperCase(); +} + +/** + * The runes (integer Unicode code points) of a [String]. + */ +class Runes extends IterableBase { + final String string; + Runes(this.string); + + RuneIterator get iterator => new RuneIterator(string); + + int get last { + if (string.length == 0) { + throw new StateError('No elements.'); + } + int length = string.length; + int code = string.codeUnitAt(length - 1); + if (_isTrailSurrogate(code) && string.length > 1) { + int previousCode = string.codeUnitAt(length - 2); + if (_isLeadSurrogate(previousCode)) { + return _combineSurrogatePair(previousCode, code); + } + } + return code; + } + +} + +// Is then code (a 16-bit unsigned integer) a UTF-16 lead surrogate. +bool _isLeadSurrogate(int code) => (code & 0xFC00) == 0xD800; + +// Is then code (a 16-bit unsigned integer) a UTF-16 trail surrogate. +bool _isTrailSurrogate(int code) => (code & 0xFC00) == 0xDC00; + +// Combine a lead and a trail surrogate value into a single code point. +int _combineSurrogatePair(int start, int end) { + return 0x10000 + ((start & 0x3FF) << 10) + (end & 0x3FF); +} + +/** [Iterator] for reading runes (integer Unicode code points) out of a Dart + * string. + */ +class RuneIterator implements BidirectionalIterator { + /** String being iterated. */ + final String string; + /** Position before the current code point. */ + int _position; + /** Position after the current code point. */ + int _nextPosition; + /** + * Current code point. + * + * If the iterator has hit either end, the [_currentCodePoint] is null + * and [: _position == _nextPosition :]. + */ + int _currentCodePoint; + + /** Create an iterator positioned at the beginning of the string. */ + RuneIterator(String string) + : this.string = string, _position = 0, _nextPosition = 0; + + /** + * Create an iterator positioned before the [index]th code unit of the string. + * + * When created, there is no [current] value. + * A [moveNext] will use the rune starting at [index] the current value, + * and a [movePrevious] will use the rune ending just before [index] as the + * the current value. + * + * It is an error if the [index] position is in the middle of a surrogate + * pair. + */ + RuneIterator.at(String string, int index) + : string = string, _position = index, _nextPosition = index { + if (index < 0 || index > string.length) { + throw new RangeError.range(index, 0, string.length); + } + _checkSplitSurrogate(index); + } + + /** Throw an error if the index is in the middle of a surrogate pair. */ + void _checkSplitSurrogate(int index) { + if (index > 0 && index < string.length && + _isLeadSurrogate(string.codeUnitAt(index - 1)) && + _isTrailSurrogate(string.codeUnitAt(index))) { + throw new ArgumentError('Index inside surrogate pair: $index'); + } + } + + /** + * Returns the starting position of the current rune in the string. + * + * Returns null if the [current] rune is null. + */ + int get rawIndex => (_position != _nextPosition) ? _position : null; + + /** + * Resets the iterator to the rune at the specified index of the string. + * + * Setting a negative [rawIndex], or one greater than or equal to + * [:string.length:], + * is an error. So is setting it in the middle of a surrogate pair. + * + * Setting the position to the end of then string will set [current] to null. + */ + void set rawIndex(int rawIndex) { + if (rawIndex >= string.length) { + throw new RangeError.range(rawIndex, 0, string.length - 1); + } + reset(rawIndex); + moveNext(); + } + + /** + * Resets the iterator to the given index into the string. + * + * After this the [current] value is unset. + * You must call [moveNext] make the rune at the position current, + * or [movePrevious] for the last rune before the position. + * + * Setting a negative [rawIndex], or one greater than [:string.length:], + * is an error. So is setting it in the middle of a surrogate pair. + */ + void reset([int rawIndex = 0]) { + if (rawIndex < 0 || rawIndex > string.length) { + throw new RangeError.range(rawIndex, 0, string.length); + } + _checkSplitSurrogate(rawIndex); + _position = _nextPosition = rawIndex; + _currentCodePoint = null; + } + + /** The rune (integer Unicode code point) starting at the current position in + * the string. + */ + int get current => _currentCodePoint; + + /** + * The number of code units comprising the current rune. + * + * Returns zero if there is no current rune ([current] is null). + */ + int get currentSize => _nextPosition - _position; + + /** + * A string containing the current rune. + * + * For runes outside the basic multilingual plane, this will be + * a String of length 2, containing two code units. + * + * Returns null if [current] is null. + */ + String get currentAsString { + if (_position == _nextPosition) return null; + if (_position + 1 == _nextPosition) return string[_position]; + return string.substring(_position, _nextPosition); + } + + bool moveNext() { + _position = _nextPosition; + if (_position == string.length) { + _currentCodePoint = null; + return false; + } + int codeUnit = string.codeUnitAt(_position); + int nextPosition = _position + 1; + if (_isLeadSurrogate(codeUnit) && nextPosition < string.length) { + int nextCodeUnit = string.codeUnitAt(nextPosition); + if (_isTrailSurrogate(nextCodeUnit)) { + _nextPosition = nextPosition + 1; + _currentCodePoint = _combineSurrogatePair(codeUnit, nextCodeUnit); + return true; + } + } + _nextPosition = nextPosition; + _currentCodePoint = codeUnit; + return true; + } + + bool movePrevious() { + _nextPosition = _position; + if (_position == 0) { + _currentCodePoint = null; + return false; + } + int position = _position - 1; + int codeUnit = string.codeUnitAt(position); + if (_isTrailSurrogate(codeUnit) && position > 0) { + int prevCodeUnit = string.codeUnitAt(position - 1); + if (_isLeadSurrogate(prevCodeUnit)) { + _position = position - 1; + _currentCodePoint = _combineSurrogatePair(prevCodeUnit, codeUnit); + return true; + } + } + _position = position; + _currentCodePoint = codeUnit; + return true; + } +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/string_buffer.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/string_buffer.dart new file mode 100644 index 0000000..e8bc72d --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/string_buffer.dart @@ -0,0 +1,68 @@ +// Copyright (c) 2011, 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. + +part of dart.core; + +/** + * A class for concatenating strings efficiently. + * + * Allows for the incremental building of a string using write*() methods. + * The strings are concatenated to a single string only when [toString] is + * called. + */ +class StringBuffer implements StringSink { + + /** Creates the string buffer with an initial content. */ + external StringBuffer([Object content = ""]); + + /** + * Returns the length of the content that has been accumulated so far. + * This is a constant-time operation. + */ + external int get length; + + /** Returns whether the buffer is empty. This is a constant-time operation. */ + bool get isEmpty => length == 0; + + /** + * Returns whether the buffer is not empty. This is a constant-time + * operation. + */ + bool get isNotEmpty => !isEmpty; + + /// Adds the contents of [obj], converted to a string, to the buffer. + external void write(Object obj); + + /// Adds the string representation of [charCode] to the buffer. + external void writeCharCode(int charCode); + + void writeAll(Iterable objects, [String separator = ""]) { + Iterator iterator = objects.iterator; + if (!iterator.moveNext()) return; + if (separator.isEmpty) { + do { + write(iterator.current); + } while (iterator.moveNext()); + } else { + write(iterator.current); + while (iterator.moveNext()) { + write(separator); + write(iterator.current); + } + } + } + + void writeln([Object obj = ""]) { + write(obj); + write("\n"); + } + + /** + * Clears the string buffer. + */ + external void clear(); + + /// Returns the contents of buffer as a concatenated string. + external String toString(); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/string_sink.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/string_sink.dart new file mode 100644 index 0000000..5fdd0f2 --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/string_sink.dart @@ -0,0 +1,32 @@ +// Copyright (c) 2013, 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. + +part of dart.core; + +abstract class StringSink { + + /** + * Converts [obj] to a String by invoking [Object.toString] and + * adds the result to `this`. + */ + void write(Object obj); + + /** + * Iterates over the given [objects] and [write]s them in sequence. + */ + void writeAll(Iterable objects, [String separator = ""]); + + /** + * Converts [obj] to a String by invoking [Object.toString] and + * adds the result to `this`, followed by a newline. + */ + void writeln([Object obj = ""]); + + /** + * Writes the [charCode] to `this`. + * + * This method is equivalent to `write(new String.fromCharCode(charCode))`. + */ + void writeCharCode(int charCode); +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/symbol.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/symbol.dart new file mode 100644 index 0000000..9f33e3a --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/symbol.dart @@ -0,0 +1,38 @@ +// Copyright (c) 2013, 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. + +part of dart.core; + +/// Opaque name used by mirrors, invocations and [Function.apply]. +class Symbol { + /** + * Constructs a new Symbol. + * + * The name must be a valid public Dart member name, + * public constructor name, or library name, optionally qualified. + * + * A qualified name is a valid name preceded by a public identifier name + * and a '`.`', e.g., `foo.bar.baz=` is a qualified version of `baz=`. + * That means that the content of the [name] String must be either + * + * * a valid public Dart identifier + * (that is, an identifier not starting with "`_`"), + * * such an identifier followed by "=" (a setter name), + * * the name of a declarable operator + * (one of "`+`", "`-`", "`*`", "`/`", "`%`", "`~/`", "`&`", "`|`", + * "`^`", "`~`", "`<<`", "`>>`", "`<`", "`<=`", "`>`", "`>=`", "`==`", + * "`[]`", "`[]=`", or "`unary-`"), + * * any of the above preceeded by any number of qualifiers, + * where a qualifier is a non-private identifier followed by '`.`', + * * or the empty string (the default name of a library with not library + * name declaration). + * + * The following text is non-normative: + * + * Creating non-const Symbol instances may result in larger output. If + * possible, use [MirrorsUsed] in "dart:mirrors" to specify which names might + * be passed to this constructor. + */ + const factory Symbol(String name) = internal.Symbol; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/type.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/type.dart new file mode 100644 index 0000000..d2e107b --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/type.dart @@ -0,0 +1,10 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * Runtime representation of a type. + */ +abstract class Type {} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/uri.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/uri.dart new file mode 100644 index 0000000..7787a1f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/core/uri.dart @@ -0,0 +1,1881 @@ +// Copyright (c) 2012, 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. + +part of dart.core; + +/** + * A parsed URI, such as a URL. + * + * **See also:** + * + * * [URIs][uris] in the [library tour][libtour] + * * [RFC-3986](http://tools.ietf.org/html/rfc3986) + * + * [uris]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html#ch03-uri + * [libtour]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html + */ +class Uri { + final String _host; + int _port; + String _path; + + /** + * Returns the scheme component. + * + * Returns the empty string if there is no scheme component. + */ + final String scheme; + + /** + * Returns the authority component. + * + * The authority is formatted from the [userInfo], [host] and [port] + * parts. + * + * Returns the empty string if there is no authority component. + */ + String get authority { + if (!hasAuthority) return ""; + var sb = new StringBuffer(); + _writeAuthority(sb); + return sb.toString(); + } + + /** + * Returns the user info part of the authority component. + * + * Returns the empty string if there is no user info in the + * authority component. + */ + final String userInfo; + + /** + * Returns the host part of the authority component. + * + * Returns the empty string if there is no authority component and + * hence no host. + * + * If the host is an IP version 6 address, the surrounding `[` and `]` is + * removed. + */ + String get host { + if (_host != null && _host.startsWith('[')) { + return _host.substring(1, _host.length - 1); + } + return _host; + } + + /** + * Returns the port part of the authority component. + * + * Returns 0 if there is no port in the authority component. + */ + int get port { + if (_port == 0) { + if (scheme == "http") return 80; + if (scheme == "https") return 443; + } + return _port; + } + + /** + * Returns the path component. + * + * The returned path is encoded. To get direct access to the decoded + * path use [pathSegments]. + * + * Returns the empty string if there is no path component. + */ + String get path => _path; + + /** + * Returns the query component. The returned query is encoded. To get + * direct access to the decoded query use [queryParameters]. + * + * Returns the empty string if there is no query component. + */ + final String query; + + /** + * Returns the fragment identifier component. + * + * Returns the empty string if there is no fragment identifier + * component. + */ + final String fragment; + + /** + * Cache the computed return value of [pathSegements]. + */ + List _pathSegments; + + /** + * Cache the computed return value of [queryParameters]. + */ + Map _queryParameters; + + /** + * Creates a new URI object by parsing a URI string. + */ + static Uri parse(String uri) { + // This parsing will not validate percent-encoding, IPv6, etc. When done + // it will call `new Uri(...)` which will perform these validations. + // This is purely splitting up the URI string into components. + // + // Important parts of the RFC 3986 used here: + // URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + // + // hier-part = "//" authority path-abempty + // / path-absolute + // / path-rootless + // / path-empty + // + // URI-reference = URI / relative-ref + // + // absolute-URI = scheme ":" hier-part [ "?" query ] + // + // relative-ref = relative-part [ "?" query ] [ "#" fragment ] + // + // relative-part = "//" authority path-abempty + // / path-absolute + // / path-noscheme + // / path-empty + // + // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + // + // authority = [ userinfo "@" ] host [ ":" port ] + // userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + // host = IP-literal / IPv4address / reg-name + // port = *DIGIT + // reg-name = *( unreserved / pct-encoded / sub-delims ) + // + // path = path-abempty ; begins with "/" or is empty + // / path-absolute ; begins with "/" but not "//" + // / path-noscheme ; begins with a non-colon segment + // / path-rootless ; begins with a segment + // / path-empty ; zero characters + // + // path-abempty = *( "/" segment ) + // path-absolute = "/" [ segment-nz *( "/" segment ) ] + // path-noscheme = segment-nz-nc *( "/" segment ) + // path-rootless = segment-nz *( "/" segment ) + // path-empty = 0 + // + // segment = *pchar + // segment-nz = 1*pchar + // segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) + // ; non-zero-length segment without any colon ":" + // + // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + // + // query = *( pchar / "/" / "?" ) + // + // fragment = *( pchar / "/" / "?" ) + bool isRegName(int ch) { + return ch < 128 && ((_regNameTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); + } + + int ipV6Address(int index) { + // IPv6. Skip to ']'. + index = uri.indexOf(']', index); + if (index == -1) { + throw new FormatException("Bad end of IPv6 host"); + } + return index + 1; + } + + int length = uri.length; + int index = 0; + + int schemeEndIndex = 0; + + if (length == 0) { + return new Uri(); + } + + if (uri.codeUnitAt(0) != _SLASH) { + // Can be scheme. + while (index < length) { + // Look for ':'. If found, continue from the post of ':'. If not (end + // reached or invalid scheme char found) back up one char, and continue + // to path. + // Note that scheme-chars is contained in path-chars. + int codeUnit = uri.codeUnitAt(index++); + if (!_isSchemeCharacter(codeUnit)) { + if (codeUnit == _COLON) { + schemeEndIndex = index; + } else { + // Back up one char, since we met an invalid scheme char. + index--; + } + break; + } + } + } + + int userInfoEndIndex = -1; + int portIndex = -1; + int authorityEndIndex = schemeEndIndex; + // If we see '//', there must be an authority. + if (authorityEndIndex == index && + authorityEndIndex + 1 < length && + uri.codeUnitAt(authorityEndIndex) == _SLASH && + uri.codeUnitAt(authorityEndIndex + 1) == _SLASH) { + // Skip '//'. + authorityEndIndex += 2; + // It can both be host and userInfo. + while (authorityEndIndex < length) { + int codeUnit = uri.codeUnitAt(authorityEndIndex++); + if (!isRegName(codeUnit)) { + if (codeUnit == _LEFT_BRACKET) { + authorityEndIndex = ipV6Address(authorityEndIndex); + } else if (portIndex == -1 && codeUnit == _COLON) { + // First time ':'. + portIndex = authorityEndIndex; + } else if (codeUnit == _AT_SIGN || codeUnit == _COLON) { + // Second time ':' or first '@'. Must be userInfo. + userInfoEndIndex = uri.indexOf('@', authorityEndIndex - 1); + // Not found. Must be path then. + if (userInfoEndIndex == -1) { + authorityEndIndex = index; + break; + } + portIndex = -1; + authorityEndIndex = userInfoEndIndex + 1; + // Now it can only be host:port. + while (authorityEndIndex < length) { + int codeUnit = uri.codeUnitAt(authorityEndIndex++); + if (!isRegName(codeUnit)) { + if (codeUnit == _LEFT_BRACKET) { + authorityEndIndex = ipV6Address(authorityEndIndex); + } else if (codeUnit == _COLON) { + if (portIndex != -1) { + throw new FormatException("Double port in host"); + } + portIndex = authorityEndIndex; + } else { + authorityEndIndex--; + break; + } + } + } + break; + } else { + authorityEndIndex--; + break; + } + } + } + } else { + authorityEndIndex = schemeEndIndex; + } + + // At path now. + int pathEndIndex = authorityEndIndex; + while (pathEndIndex < length) { + int codeUnit = uri.codeUnitAt(pathEndIndex++); + if (codeUnit == _QUESTION || codeUnit == _NUMBER_SIGN) { + pathEndIndex--; + break; + } + } + + // Maybe query. + int queryEndIndex = pathEndIndex; + if (queryEndIndex < length && uri.codeUnitAt(queryEndIndex) == _QUESTION) { + while (queryEndIndex < length) { + int codeUnit = uri.codeUnitAt(queryEndIndex++); + if (codeUnit == _NUMBER_SIGN) { + queryEndIndex--; + break; + } + } + } + + var scheme = null; + if (schemeEndIndex > 0) { + scheme = uri.substring(0, schemeEndIndex - 1); + } + + var host = ""; + var userInfo = ""; + var port = 0; + if (schemeEndIndex != authorityEndIndex) { + int startIndex = schemeEndIndex + 2; + if (userInfoEndIndex > 0) { + userInfo = uri.substring(startIndex, userInfoEndIndex); + startIndex = userInfoEndIndex + 1; + } + if (portIndex > 0) { + var portStr = uri.substring(portIndex, authorityEndIndex); + try { + port = int.parse(portStr); + } catch (_) { + throw new FormatException("Invalid port: '$portStr'"); + } + host = uri.substring(startIndex, portIndex - 1); + } else { + host = uri.substring(startIndex, authorityEndIndex); + } + } + + var path = uri.substring(authorityEndIndex, pathEndIndex); + var query = ""; + if (pathEndIndex < queryEndIndex) { + query = uri.substring(pathEndIndex + 1, queryEndIndex); + } + var fragment = ""; + // If queryEndIndex is not at end (length), there is a fragment. + if (queryEndIndex < length) { + fragment = uri.substring(queryEndIndex + 1, length); + } + + return new Uri(scheme: scheme, + userInfo: userInfo, + host: host, + port: port, + path: path, + query: query, + fragment: fragment); + } + + /** + * Creates a new URI from its components. + * + * Each component is set through a named argument. Any number of + * components can be provided. The default value for the components + * not provided is the empry string, except for [port] which has a + * default value of 0. The [path] and [query] components can be set + * using two different named arguments. + * + * The scheme component is set through [scheme]. The scheme is + * normalized to all lowercase letters. + * + * The user info part of the authority component is set through + * [userInfo]. + * + * The host part of the authority component is set through + * [host]. The host can either be a hostname, an IPv4 address or an + * IPv6 address, contained in '[' and ']'. If the host contains a + * ':' character, the '[' and ']' are added if not already provided. + * + * The port part of the authority component is set through + * [port]. The port is normalized for scheme http and https where + * port 80 and port 443 respectively is set. + * + * The path component is set through either [path] or + * [pathSegments]. When [path] is used, the provided string is + * expected to be fully percent-encoded, and is used in its literal + * form. When [pathSegments] is used, each of the provided segments + * is percent-encoded and joined using the forward slash + * separator. The percent-encoding of the path segments encodes all + * characters except for the unreserved characters and the following + * list of characters: `!$&'()*+,;=:@`. If the other components + * calls for an absolute path a leading slash `/` is prepended if + * not already there. + * + * The query component is set through either [query] or + * [queryParameters]. When [query] is used the provided string is + * expected to be fully percent-encoded and is used in its literal + * form. When [queryParameters] is used the query is built from the + * provided map. Each key and value in the map is percent-encoded + * and joined using equal and ampersand characters. The + * percent-encoding of the keys and values encodes all characters + * except for the unreserved characters. + * + * The fragment component is set through [fragment]. + */ + Uri({String scheme, + this.userInfo: "", + String host: "", + port: 0, + String path, + Iterable pathSegments, + String query, + Map queryParameters, + fragment: ""}) : + scheme = _makeScheme(scheme), + _host = _makeHost(host), + query = _makeQuery(query, queryParameters), + fragment = _makeFragment(fragment) { + // Perform scheme specific normalization. + if (scheme == "http" && port == 80) { + _port = 0; + } else if (scheme == "https" && port == 443) { + _port = 0; + } else { + _port = port; + } + // Fill the path. + _path = _makePath(path, pathSegments); + } + + /** + * Creates a new `http` URI from authority, path and query. + * + * Examples: + * + * // Create the URI http://example.org/path?q=abc. + * new Uri.http("google.com", "/search", { "q" : "dart" });http://example.org/path?q=abc. + * new Uri.http("user:pass@localhost:8080, ""); // http://user:pass@localhost:8080/ + * new Uri.http("example.org, "a b"); // http://example.org/a%20b + * new Uri.http("example.org, "/a%2F"); // http://example.org/a%25%2F + * + * The `scheme` is always set to `http`. + * + * The `userInfo`, `host` and `port` components are set from the + * [authority] argument. + * + * The `path` component is set from the [unencodedPath] + * argument. The path passed must not be encoded as this constructor + * encodes the path. + * + * The `query` component is set from the optional [queryParameters] + * argument. + */ + factory Uri.http(String authority, + String unencodedPath, + [Map queryParameters]) { + return _makeHttpUri("http", authority, unencodedPath, queryParameters); + } + + /** + * Creates a new `https` URI from authority, path and query. + * + * This constructor is the same as [Uri.http] except for the scheme + * which is set to `https`. + */ + factory Uri.https(String authority, + String unencodedPath, + [Map queryParameters]) { + return _makeHttpUri("https", authority, unencodedPath, queryParameters); + } + + static Uri _makeHttpUri(String scheme, + String authority, + String unencodedPath, + Map queryParameters) { + var userInfo = ""; + var host = ""; + var port = 0; + + var hostStart = 0; + // Split off the user info. + bool hasUserInfo = false; + for (int i = 0; i < authority.length; i++) { + if (authority.codeUnitAt(i) == _AT_SIGN) { + hasUserInfo = true; + userInfo = authority.substring(0, i); + hostStart = i + 1; + break; + } + } + var hostEnd = hostStart; + if (hostStart < authority.length && + authority.codeUnitAt(hostStart) == _LEFT_BRACKET) { + // IPv6 host. + for (; hostEnd < authority.length; hostEnd++) { + if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break; + } + if (hostEnd == authority.length) { + throw new FormatException("Invalid IPv6 host entry."); + } + parseIPv6Address(authority.substring(hostStart + 1, hostEnd)); + hostEnd++; // Skip the closing bracket. + if (hostEnd != authority.length && + authority.codeUnitAt(hostEnd) != _COLON) { + throw new FormatException("Invalid end of authority"); + } + } + // Split host and port. + bool hasPort = false; + for (; hostEnd < authority.length; hostEnd++) { + if (authority.codeUnitAt(hostEnd) == _COLON) { + var portString = authority.substring(hostEnd + 1); + // We allow the empty port - falling back to initial value. + if (portString.isNotEmpty) port = int.parse(portString); + break; + } + } + host = authority.substring(hostStart, hostEnd); + + return new Uri(scheme: scheme, + userInfo: userInfo, + host: host, + port: port, + pathSegments: unencodedPath.split("/"), + queryParameters: queryParameters); + } + + /** + * Creates a new file URI from an absolute or relative file path. + * + * The file path is passed in [path]. + * + * This path is interpreted using either Windows or non-Windows + * semantics. + * + * With non-Windows semantics the slash ("/") is used to separate + * path segments. + * + * With Windows semantics, backslash ("\") and forward-slash ("/") + * are used to separate path segments, except if the path starts + * with "\\?\" in which case, only backslash ("\") separates path + * segments. + * + * If the path starts with a path separator an absolute URI is + * created. Otherwise a relative URI is created. One exception from + * this rule is that when Windows semantics is used and the path + * starts with a drive letter followed by a colon (":") and a + * path separator then an absolute URI is created. + * + * The default for whether to use Windows or non-Windows semantics + * determined from the platform Dart is running on. When running in + * the standalone VM this is detected by the VM based on the + * operating system. When running in a browser non-Windows semantics + * is always used. + * + * To override the automatic detection of which semantics to use pass + * a value for [windows]. Passing `true` will use Windows + * semantics and passing `false` will use non-Windows semantics. + * + * Examples using non-Windows semantics (resulting URI in comment): + * + * new Uri.file("xxx/yyy"); // xxx/yyy + * new Uri.file("xxx/yyy/"); // xxx/yyy/ + * new Uri.file("/xxx/yyy"); // file:///xxx/yyy + * new Uri.file("/xxx/yyy/"); // file:///xxx/yyy/ + * new Uri.file("C:"); // C: + * + * Examples using Windows semantics (resulting URI in comment): + * + * new Uri.file(r"xxx\yyy"); // xxx/yyy + * new Uri.file(r"xxx\yyy\"); // xxx/yyy/ + * new Uri.file(r"\xxx\yyy"); // file:///xxx/yyy + * new Uri.file(r"\xxx\yyy/"); // file:///xxx/yyy/ + * new Uri.file(r"C:\xxx\yyy"); // file:///C:/xxx/yyy + * new Uri.file(r"C:xxx\yyy"); // Throws as path with drive letter + * // is not absolute. + * new Uri.file(r"\\server\share\file"); // file://server/share/file + * new Uri.file(r"C:"); // Throws as path with drive letter + * // is not absolute. + * + * If the path passed is not a legal file path [ArgumentError] is thrown. + */ + factory Uri.file(String path, {bool windows}) { + windows = windows == null ? Uri._isWindows : windows; + return windows ? _makeWindowsFileUrl(path) : _makeFileUri(path); + } + + /** + * Returns the natural base URI for the current platform. + * + * When running in a browser this is the current URL (from + * `window.location.href`). + * + * When not running in a browser this is the file URI referencing + * the current working directory. + */ + external static Uri get base; + + external static bool get _isWindows; + + static _checkNonWindowsPathReservedCharacters(List segments, + bool argumentError) { + segments.forEach((segment) { + if (segment.contains("/")) { + if (argumentError) { + throw new ArgumentError("Illegal path character $segment"); + } else { + throw new UnsupportedError("Illegal path character $segment"); + } + } + }); + } + + static _checkWindowsPathReservedCharacters(List segments, + bool argumentError, + [int firstSegment = 0]) { + segments.skip(firstSegment).forEach((segment) { + if (segment.contains(new RegExp(r'["*/:<>?\\|]'))) { + if (argumentError) { + throw new ArgumentError("Illegal character in path"); + } else { + throw new UnsupportedError("Illegal character in path"); + } + } + }); + } + + static _checkWindowsDriveLetter(int charCode, bool argumentError) { + if ((_UPPER_CASE_A <= charCode && charCode <= _UPPER_CASE_Z) || + (_LOWER_CASE_A <= charCode && charCode <= _LOWER_CASE_Z)) { + return; + } + if (argumentError) { + throw new ArgumentError("Illegal drive letter " + + new String.fromCharCode(charCode)); + } else { + throw new UnsupportedError("Illegal drive letter " + + new String.fromCharCode(charCode)); + } + } + + static _makeFileUri(String path) { + String sep = "/"; + if (path.length > 0 && path[0] == sep) { + // Absolute file:// URI. + return new Uri(scheme: "file", pathSegments: path.split(sep)); + } else { + // Relative URI. + return new Uri(pathSegments: path.split(sep)); + } + } + + static _makeWindowsFileUrl(String path) { + if (path.startsWith("\\\\?\\")) { + if (path.startsWith("\\\\?\\UNC\\")) { + path = "\\${path.substring(7)}"; + } else { + path = path.substring(4); + if (path.length < 3 || + path.codeUnitAt(1) != _COLON || + path.codeUnitAt(2) != _BACKSLASH) { + throw new ArgumentError( + "Windows paths with \\\\?\\ prefix must be absolute"); + } + } + } else { + path = path.replaceAll("/", "\\"); + } + String sep = "\\"; + if (path.length > 1 && path[1] == ":") { + _checkWindowsDriveLetter(path.codeUnitAt(0), true); + if (path.length == 2 || path.codeUnitAt(2) != _BACKSLASH) { + throw new ArgumentError( + "Windows paths with drive letter must be absolute"); + } + // Absolute file://C:/ URI. + var pathSegments = path.split(sep); + _checkWindowsPathReservedCharacters(pathSegments, true, 1); + return new Uri(scheme: "file", pathSegments: pathSegments); + } + + if (path.length > 0 && path[0] == sep) { + if (path.length > 1 && path[1] == sep) { + // Absolute file:// URI with host. + int pathStart = path.indexOf("\\", 2); + String hostPart = + pathStart == -1 ? path.substring(2) : path.substring(2, pathStart); + String pathPart = + pathStart == -1 ? "" : path.substring(pathStart + 1); + var pathSegments = pathPart.split(sep); + _checkWindowsPathReservedCharacters(pathSegments, true); + return new Uri( + scheme: "file", host: hostPart, pathSegments: pathSegments); + } else { + // Absolute file:// URI. + var pathSegments = path.split(sep); + _checkWindowsPathReservedCharacters(pathSegments, true); + return new Uri(scheme: "file", pathSegments: pathSegments); + } + } else { + // Relative URI. + var pathSegments = path.split(sep); + _checkWindowsPathReservedCharacters(pathSegments, true); + return new Uri(pathSegments: pathSegments); + } + } + + /** + * Returns the URI path split into its segments. Each of the + * segments in the returned list have been decoded. If the path is + * empty the empty list will be returned. A leading slash `/` does + * not affect the segments returned. + * + * The returned list is unmodifiable and will throw [UnsupportedError] on any + * calls that would mutate it. + */ + List get pathSegments { + if (_pathSegments == null) { + var pathToSplit = !path.isEmpty && path.codeUnitAt(0) == _SLASH + ? path.substring(1) + : path; + _pathSegments = new UnmodifiableListView( + pathToSplit == "" ? const[] + : pathToSplit.split("/") + .map(Uri.decodeComponent) + .toList(growable: false)); + } + return _pathSegments; + } + + /** + * Returns the URI query split into a map according to the rules + * specified for FORM post in the [HTML 4.01 specification section 17.13.4] + * (http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 + * "HTML 4.01 section 17.13.4"). Each key and value in the returned map + * has been decoded. If there is no query the empty map is returned. + * + * Keys in the query string that have no value are mapped to the + * empty string. + * + * The returned map is unmodifiable and will throw [UnsupportedError] on any + * calls that would mutate it. + */ + Map get queryParameters { + if (_queryParameters == null) { + _queryParameters = new _UnmodifiableMap(splitQueryString(query)); + } + return _queryParameters; + } + + static String _makeHost(String host) { + if (host == null || host.isEmpty) return host; + if (host.codeUnitAt(0) == _LEFT_BRACKET) { + if (host.codeUnitAt(host.length - 1) != _RIGHT_BRACKET) { + throw new FormatException('Missing end `]` to match `[` in host'); + } + parseIPv6Address(host.substring(1, host.length - 1)); + return host; + } + for (int i = 0; i < host.length; i++) { + if (host.codeUnitAt(i) == _COLON) { + parseIPv6Address(host); + return '[$host]'; + } + } + return host; + } + + static String _makeScheme(String scheme) { + bool isSchemeLowerCharacter(int ch) { + return ch < 128 && + ((_schemeLowerTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); + } + + if (scheme == null) return ""; + bool allLowercase = true; + int length = scheme.length; + for (int i = 0; i < length; i++) { + int codeUnit = scheme.codeUnitAt(i); + if (i == 0 && !_isAlphabeticCharacter(codeUnit)) { + // First code unit must be an alphabetic character. + throw new ArgumentError('Illegal scheme: $scheme'); + } + if (!isSchemeLowerCharacter(codeUnit)) { + if (_isSchemeCharacter(codeUnit)) { + allLowercase = false; + } else { + throw new ArgumentError('Illegal scheme: $scheme'); + } + } + } + + return allLowercase ? scheme : scheme.toLowerCase(); + } + + String _makePath(String path, Iterable pathSegments) { + if (path == null && pathSegments == null) return ""; + if (path != null && pathSegments != null) { + throw new ArgumentError('Both path and pathSegments specified'); + } + var result; + if (path != null) { + result = _normalize(path); + } else { + result = pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/"); + } + if ((hasAuthority || (scheme == "file")) && + result.isNotEmpty && !result.startsWith("/")) { + return "/$result"; + } + return result; + } + + static String _makeQuery(String query, Map queryParameters) { + if (query == null && queryParameters == null) return ""; + if (query != null && queryParameters != null) { + throw new ArgumentError('Both query and queryParameters specified'); + } + if (query != null) return _normalize(query); + + var result = new StringBuffer(); + var first = true; + queryParameters.forEach((key, value) { + if (!first) { + result.write("&"); + } + first = false; + result.write(Uri.encodeQueryComponent(key)); + if (value != null && !value.isEmpty) { + result.write("="); + result.write(Uri.encodeQueryComponent(value)); + } + }); + return result.toString(); + } + + static String _makeFragment(String fragment) { + if (fragment == null) return ""; + return _normalize(fragment); + } + + static String _normalize(String component) { + int index = component.indexOf('%'); + if (index < 0) return component; + + bool isNormalizedHexDigit(int digit) { + return (_ZERO <= digit && digit <= _NINE) || + (_UPPER_CASE_A <= digit && digit <= _UPPER_CASE_F); + } + + bool isLowerCaseHexDigit(int digit) { + return _LOWER_CASE_A <= digit && digit <= _LOWER_CASE_F; + } + + bool isUnreserved(int ch) { + return ch < 128 && + ((_unreservedTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); + } + + int normalizeHexDigit(int index) { + var codeUnit = component.codeUnitAt(index); + if (isLowerCaseHexDigit(codeUnit)) { + return codeUnit - 0x20; + } else if (!isNormalizedHexDigit(codeUnit)) { + throw new ArgumentError("Invalid URI component: $component"); + } else { + return codeUnit; + } + } + + int decodeHexDigitPair(int index) { + int byte = 0; + for (int i = 0; i < 2; i++) { + var codeUnit = component.codeUnitAt(index + i); + if (_ZERO <= codeUnit && codeUnit <= _NINE) { + byte = byte * 16 + codeUnit - _ZERO; + } else { + // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66). + codeUnit |= 0x20; + if (_LOWER_CASE_A <= codeUnit && + codeUnit <= _LOWER_CASE_F) { + byte = byte * 16 + codeUnit - _LOWER_CASE_A + 10; + } else { + throw new ArgumentError( + "Invalid percent-encoding in URI component: $component"); + } + } + } + return byte; + } + + // Start building the normalized component string. + StringBuffer result; + int length = component.length; + int prevIndex = 0; + + // Copy a part of the component string to the result. + void fillResult() { + if (result == null) { + assert(prevIndex == 0); + result = new StringBuffer(component.substring(prevIndex, index)); + } else { + result.write(component.substring(prevIndex, index)); + } + } + + while (index < length) { + // Normalize percent-encoding to uppercase and don't encode + // unreserved characters. + assert(component.codeUnitAt(index) == _PERCENT); + if (length < index + 2) { + throw new ArgumentError( + "Invalid percent-encoding in URI component: $component"); + } + + var codeUnit1 = component.codeUnitAt(index + 1); + var codeUnit2 = component.codeUnitAt(index + 2); + var decodedCodeUnit = decodeHexDigitPair(index + 1); + if (isNormalizedHexDigit(codeUnit1) && + isNormalizedHexDigit(codeUnit2) && + !isUnreserved(decodedCodeUnit)) { + index += 3; + } else { + fillResult(); + if (isUnreserved(decodedCodeUnit)) { + result.writeCharCode(decodedCodeUnit); + } else { + result.write("%"); + result.writeCharCode(normalizeHexDigit(index + 1)); + result.writeCharCode(normalizeHexDigit(index + 2)); + } + index += 3; + prevIndex = index; + } + int next = component.indexOf('%', index); + if (next >= index) { + index = next; + } else { + index = length; + } + } + if (result == null) return component; + + if (result != null && prevIndex != index) fillResult(); + assert(index == length); + + return result.toString(); + } + + static bool _isSchemeCharacter(int ch) { + return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); + } + + + /** + * Returns whether the URI is absolute. + */ + bool get isAbsolute => scheme != "" && fragment == ""; + + String _merge(String base, String reference) { + if (base == "") return "/$reference"; + return "${base.substring(0, base.lastIndexOf("/") + 1)}$reference"; + } + + bool _hasDotSegments(String path) { + if (path.length > 0 && path.codeUnitAt(0) == _COLON) return true; + int index = path.indexOf("/."); + return index != -1; + } + + String _removeDotSegments(String path) { + if (!_hasDotSegments(path)) return path; + List output = []; + bool appendSlash = false; + for (String segment in path.split("/")) { + appendSlash = false; + if (segment == "..") { + if (!output.isEmpty && + ((output.length != 1) || (output[0] != ""))) output.removeLast(); + appendSlash = true; + } else if ("." == segment) { + appendSlash = true; + } else { + output.add(segment); + } + } + if (appendSlash) output.add(""); + return output.join("/"); + } + + /** + * Resolve [reference] as an URI relative to `this`. + * + * First turn [reference] into a URI using [Uri.parse]. Then resolve the + * resulting URI relative to `this`. + * + * Returns the resolved URI. + * + * See [resolveUri] for details. + */ + Uri resolve(String reference) { + return resolveUri(Uri.parse(reference)); + } + + /** + * Resolve [reference] as an URI relative to `this`. + * + * Returns the resolved URI. + * + * The algorithm for resolving a reference is described in + * [RFC-3986 Section 5] + * (http://tools.ietf.org/html/rfc3986#section-5 "RFC-1123"). + */ + Uri resolveUri(Uri reference) { + // From RFC 3986. + String targetScheme; + String targetUserInfo; + String targetHost; + int targetPort; + String targetPath; + String targetQuery; + if (reference.scheme != "") { + targetScheme = reference.scheme; + targetUserInfo = reference.userInfo; + targetHost = reference.host; + targetPort = reference.port; + targetPath = _removeDotSegments(reference.path); + targetQuery = reference.query; + } else { + if (reference.hasAuthority) { + targetUserInfo = reference.userInfo; + targetHost = reference.host; + targetPort = reference.port; + targetPath = _removeDotSegments(reference.path); + targetQuery = reference.query; + } else { + if (reference.path == "") { + targetPath = this.path; + if (reference.query != "") { + targetQuery = reference.query; + } else { + targetQuery = this.query; + } + } else { + if (reference.path.startsWith("/")) { + targetPath = _removeDotSegments(reference.path); + } else { + targetPath = _removeDotSegments(_merge(this.path, reference.path)); + } + targetQuery = reference.query; + } + targetUserInfo = this.userInfo; + targetHost = this.host; + targetPort = this.port; + } + targetScheme = this.scheme; + } + return new Uri(scheme: targetScheme, + userInfo: targetUserInfo, + host: targetHost, + port: targetPort, + path: targetPath, + query: targetQuery, + fragment: reference.fragment); + } + + /** + * Returns whether the URI has an [authority] component. + */ + bool get hasAuthority => host != ""; + + /** + * Returns the origin of the URI in the form scheme://host:port for the + * schemes http and https. + * + * It is an error if the scheme is not "http" or "https". + * + * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin + */ + String get origin { + if (scheme == "" || _host == null || _host == "") { + throw new StateError("Cannot use origin without a scheme: $this"); + } + if (scheme != "http" && scheme != "https") { + throw new StateError( + "Origin is only applicable schemes http and https: $this"); + } + if (_port == 0) return "$scheme://$_host"; + return "$scheme://$_host:$_port"; + } + + /** + * Returns the file path from a file URI. + * + * The returned path has either Windows or non-Windows + * semantics. + * + * For non-Windows semantics the slash ("/") is used to separate + * path segments. + * + * For Windows semantics the backslash ("\") separator is used to + * separate path segments. + * + * If the URI is absolute the path starts with a path separator + * unless Windows semantics is used and the first path segment is a + * drive letter. When Windows semantics is used a host component in + * the uri in interpreted as a file server and a UNC path is + * returned. + * + * The default for whether to use Windows or non-Windows semantics + * determined from the platform Dart is running on. When running in + * the standalone VM this is detected by the VM based on the + * operating system. When running in a browser non-Windows semantics + * is always used. + * + * To override the automatic detection of which semantics to use pass + * a value for [windows]. Passing `true` will use Windows + * semantics and passing `false` will use non-Windows semantics. + * + * If the URI ends with a slash (i.e. the last path component is + * empty) the returned file path will also end with a slash. + * + * With Windows semantics URIs starting with a drive letter cannot + * be relative to the current drive on the designated drive. That is + * for the URI `file:///c:abc` calling `toFilePath` will throw as a + * path segment cannot contain colon on Windows. + * + * Examples using non-Windows semantics (resulting of calling + * toFilePath in comment): + * + * Uri.parse("xxx/yyy"); // xxx/yyy + * Uri.parse("xxx/yyy/"); // xxx/yyy/ + * Uri.parse("file:///xxx/yyy"); // /xxx/yyy + * Uri.parse("file:///xxx/yyy/"); // /xxx/yyy/ + * Uri.parse("file:///C:"); // /C: + * Uri.parse("file:///C:a"); // /C:a + * + * Examples using Windows semantics (resulting URI in comment): + * + * Uri.parse("xxx/yyy"); // xxx\yyy + * Uri.parse("xxx/yyy/"); // xxx\yyy\ + * Uri.parse("file:///xxx/yyy"); // \xxx\yyy + * Uri.parse("file:///xxx/yyy/"); // \xxx\yyy/ + * Uri.parse("file:///C:/xxx/yyy"); // C:\xxx\yyy + * Uri.parse("file:C:xxx/yyy"); // Throws as a path segment + * // cannot contain colon on Windows. + * Uri.parse("file://server/share/file"); // \\server\share\file + * + * If the URI is not a file URI calling this throws + * [UnsupportedError]. + * + * If the URI cannot be converted to a file path calling this throws + * [UnsupportedError]. + */ + String toFilePath({bool windows}) { + if (scheme != "" && scheme != "file") { + throw new UnsupportedError( + "Cannot extract a file path from a $scheme URI"); + } + if (query != "") { + throw new UnsupportedError( + "Cannot extract a file path from a URI with a query component"); + } + if (fragment != "") { + throw new UnsupportedError( + "Cannot extract a file path from a URI with a fragment component"); + } + if (windows == null) windows = _isWindows; + return windows ? _toWindowsFilePath() : _toFilePath(); + } + + String _toFilePath() { + if (host != "") { + throw new UnsupportedError( + "Cannot extract a non-Windows file path from a file URI " + "with an authority"); + } + _checkNonWindowsPathReservedCharacters(pathSegments, false); + var result = new StringBuffer(); + if (_isPathAbsolute) result.write("/"); + result.writeAll(pathSegments, "/"); + return result.toString(); + } + + String _toWindowsFilePath() { + bool hasDriveLetter = false; + var segments = pathSegments; + if (segments.length > 0 && + segments[0].length == 2 && + segments[0].codeUnitAt(1) == _COLON) { + _checkWindowsDriveLetter(segments[0].codeUnitAt(0), false); + _checkWindowsPathReservedCharacters(segments, false, 1); + hasDriveLetter = true; + } else { + _checkWindowsPathReservedCharacters(segments, false); + } + var result = new StringBuffer(); + if (_isPathAbsolute && !hasDriveLetter) result.write("\\"); + if (host != "") { + result.write("\\"); + result.write(host); + result.write("\\"); + } + result.writeAll(segments, "\\"); + if (hasDriveLetter && segments.length == 1) result.write("\\"); + return result.toString(); + } + + bool get _isPathAbsolute { + if (path == null || path.isEmpty) return false; + return path.startsWith('/'); + } + + void _writeAuthority(StringSink ss) { + _addIfNonEmpty(ss, userInfo, userInfo, "@"); + ss.write(_host == null ? "null" : _host); + if (_port != 0) { + ss.write(":"); + ss.write(_port.toString()); + } + } + + String toString() { + StringBuffer sb = new StringBuffer(); + _addIfNonEmpty(sb, scheme, scheme, ':'); + if (hasAuthority || (scheme == "file")) { + sb.write("//"); + _writeAuthority(sb); + } + sb.write(path); + _addIfNonEmpty(sb, query, "?", query); + _addIfNonEmpty(sb, fragment, "#", fragment); + return sb.toString(); + } + + bool operator==(other) { + if (other is! Uri) return false; + Uri uri = other; + return scheme == uri.scheme && + userInfo == uri.userInfo && + host == uri.host && + port == uri.port && + path == uri.path && + query == uri.query && + fragment == uri.fragment; + } + + int get hashCode { + int combine(part, current) { + // The sum is truncated to 30 bits to make sure it fits into a Smi. + return (current * 31 + part.hashCode) & 0x3FFFFFFF; + } + return combine(scheme, combine(userInfo, combine(host, combine(port, + combine(path, combine(query, combine(fragment, 1))))))); + } + + static void _addIfNonEmpty(StringBuffer sb, String test, + String first, String second) { + if ("" != test) { + sb.write(first); + sb.write(second); + } + } + + /** + * Encode the string [component] using percent-encoding to make it + * safe for literal use as a URI component. + * + * All characters except uppercase and lowercase letters, digits and + * the characters `-_.!~*'()` are percent-encoded. This is the + * set of characters specified in RFC 2396 and the which is + * specified for the encodeUriComponent in ECMA-262 version 5.1. + * + * When manually encoding path segments or query components remember + * to encode each part separately before building the path or query + * string. + * + * For encoding the query part consider using + * [encodeQueryComponent]. + * + * To avoid the need for explicitly encoding use the [pathSegments] + * and [queryParameters] optional named arguments when constructing + * a [Uri]. + */ + static String encodeComponent(String component) { + return _uriEncode(_unreserved2396Table, component); + } + + /** + * Encode the string [component] according to the HTML 4.01 rules + * for encoding the posting of a HTML form as a query string + * component. + * + * Encode the string [component] according to the HTML 4.01 rules + * for encoding the posting of a HTML form as a query string + * component. + + * The component is first encoded to bytes using [encoding]. + * The default is to use [UTF8] encoding, which preserves all + * the characters that don't need encoding. + + * Then the resulting bytes are "percent-encoded". This transforms + * spaces (U+0020) to a plus sign ('+') and all bytes that are not + * the ASCII decimal digits, letters or one of '-._~' are written as + * a percent sign '%' followed by the two-digit hexadecimal + * representation of the byte. + + * Note that the set of characters which are percent-encoded is a + * superset of what HTML 4.01 requires, since it refers to RFC 1738 + * for reserved characters. + * + * When manually encoding query components remember to encode each + * part separately before building the query string. + * + * To avoid the need for explicitly encoding the query use the + * [queryParameters] optional named arguments when constructing a + * [Uri]. + * + * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more + * details. + */ + static String encodeQueryComponent(String component, + {Encoding encoding: UTF8}) { + return _uriEncode( + _unreservedTable, component, encoding: encoding, spaceToPlus: true); + } + + /** + * Decodes the percent-encoding in [encodedComponent]. + * + * Note that decoding a URI component might change its meaning as + * some of the decoded characters could be characters with are + * delimiters for a given URI componene type. Always split a URI + * component using the delimiters for the component before decoding + * the individual parts. + * + * For handling the [path] and [query] components consider using + * [pathSegments] and [queryParameters] to get the separated and + * decoded component. + */ + static String decodeComponent(String encodedComponent) { + return _uriDecode(encodedComponent); + } + + /** + * Decodes the percent-encoding in [encodedComponent], converting + * pluses to spaces. + * + * It will create a byte-list of the decoded characters, and then use + * [encoding] to decode the byte-list to a String. The default encoding is + * UTF-8. + */ + static String decodeQueryComponent( + String encodedComponent, + {Encoding encoding: UTF8}) { + return _uriDecode(encodedComponent, plusToSpace: true, encoding: encoding); + } + + /** + * Encode the string [uri] using percent-encoding to make it + * safe for literal use as a full URI. + * + * All characters except uppercase and lowercase letters, digits and + * the characters `!#$&'()*+,-./:;=?@_~` are percent-encoded. This + * is the set of characters specified in in ECMA-262 version 5.1 for + * the encodeURI function . + */ + static String encodeFull(String uri) { + return _uriEncode(_encodeFullTable, uri); + } + + /** + * Decodes the percent-encoding in [uri]. + * + * Note that decoding a full URI might change its meaning as some of + * the decoded characters could be reserved characters. In most + * cases an encoded URI should be parsed into components using + * [Uri.parse] before decoding the separate components. + */ + static String decodeFull(String uri) { + return _uriDecode(uri); + } + + /** + * Returns the [query] split into a map according to the rules + * specified for FORM post in the + * [HTML 4.01 specification section 17.13.4] + * (http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 + * "HTML 4.01 section 17.13.4"). Each key and value in the returned + * map has been decoded. If the [query] + * is the empty string an empty map is returned. + * + * Keys in the query string that have no value are mapped to the + * empty string. + * + * Each query component will be decoded using [encoding]. The default encoding + * is UTF-8. + */ + static Map splitQueryString(String query, + {Encoding encoding: UTF8}) { + return query.split("&").fold({}, (map, element) { + int index = element.indexOf("="); + if (index == -1) { + if (element != "") { + map[decodeQueryComponent(element, encoding: encoding)] = ""; + } + } else if (index != 0) { + var key = element.substring(0, index); + var value = element.substring(index + 1); + map[Uri.decodeQueryComponent(key, encoding: encoding)] = + decodeQueryComponent(value, encoding: encoding); + } + return map; + }); + } + + /** + * Parse the [host] as an IP version 4 (IPv4) address, returning the address + * as a list of 4 bytes in network byte order (big endian). + * + * Throws a [FormatException] if [host] is not a valid IPv4 address + * representation. + */ + static List parseIPv4Address(String host) { + void error(String msg) { + throw new FormatException('Illegal IPv4 address, $msg'); + } + var bytes = host.split('.'); + if (bytes.length != 4) { + error('IPv4 address should contain exactly 4 parts'); + } + // TODO(ajohnsen): Consider using Uint8List. + return bytes + .map((byteString) { + int byte = int.parse(byteString); + if (byte < 0 || byte > 255) { + error('each part must be in the range of `0..255`'); + } + return byte; + }) + .toList(); + } + + /** + * Parse the [host] as an IP version 6 (IPv6) address, returning the address + * as a list of 16 bytes in network byte order (big endian). + * + * Throws a [FormatException] if [host] is not a valid IPv6 address + * representation. + * + * Some examples of IPv6 addresses: + * * ::1 + * * FEDC:BA98:7654:3210:FEDC:BA98:7654:3210 + * * 3ffe:2a00:100:7031::1 + * * ::FFFF:129.144.52.38 + * * 2010:836B:4179::836B:4179 + */ + static List parseIPv6Address(String host) { + // An IPv6 address consists of exactly 8 parts of 1-4 hex digits, seperated + // by `:`'s, with the following exceptions: + // + // - One (and only one) wildcard (`::`) may be present, representing a fill + // of 0's. The IPv6 `::` is thus 16 bytes of `0`. + // - The last two parts may be replaced by an IPv4 address. + void error(String msg) { + throw new FormatException('Illegal IPv6 address, $msg'); + } + int parseHex(int start, int end) { + if (end - start > 4) { + error('an IPv6 part can only contain a maximum of 4 hex digits'); + } + int value = int.parse(host.substring(start, end), radix: 16); + if (value < 0 || value > (1 << 16) - 1) { + error('each part must be in the range of `0x0..0xFFFF`'); + } + return value; + } + if (host.length < 2) error('address is too short'); + List parts = []; + bool wildcardSeen = false; + int partStart = 0; + // Parse all parts, except a potential last one. + for (int i = 0; i < host.length; i++) { + if (host.codeUnitAt(i) == _COLON) { + if (i == 0) { + // If we see a `:` in the beginning, expect wildcard. + i++; + if (host.codeUnitAt(i) != _COLON) { + error('invalid start colon.'); + } + partStart = i; + } + if (i == partStart) { + // Wildcard. We only allow one. + if (wildcardSeen) { + error('only one wildcard `::` is allowed'); + } + wildcardSeen = true; + parts.add(-1); + } else { + // Found a single colon. Parse [partStart..i] as a hex entry. + parts.add(parseHex(partStart, i)); + } + partStart = i + 1; + } + } + if (parts.length == 0) error('too few parts'); + bool atEnd = partStart == host.length; + bool isLastWildcard = parts.last == -1; + if (atEnd && !isLastWildcard) { + error('expected a part after last `:`'); + } + if (!atEnd) { + try { + parts.add(parseHex(partStart, host.length)); + } catch (e) { + // Failed to parse the last chunk as hex. Try IPv4. + try { + List last = parseIPv4Address(host.substring(partStart)); + parts.add(last[0] << 8 | last[1]); + parts.add(last[2] << 8 | last[3]); + } catch (e) { + error('invalid end of IPv6 address.'); + } + } + } + if (wildcardSeen) { + if (parts.length > 7) { + error('an address with a wildcard must have less than 7 parts'); + } + } else if (parts.length != 8) { + error('an address without a wildcard must contain exactly 8 parts'); + } + // TODO(ajohnsen): Consider using Uint8List. + return parts + .expand((value) { + if (value == -1) { + return new List.filled((9 - parts.length) * 2, 0); + } else { + return [(value >> 8) & 0xFF, value & 0xFF]; + } + }) + .toList(); + } + + // Frequently used character codes. + static const int _SPACE = 0x20; + static const int _DOUBLE_QUOTE = 0x22; + static const int _NUMBER_SIGN = 0x23; + static const int _PERCENT = 0x25; + static const int _ASTERISK = 0x2A; + static const int _PLUS = 0x2B; + static const int _SLASH = 0x2F; + static const int _ZERO = 0x30; + static const int _NINE = 0x39; + static const int _COLON = 0x3A; + static const int _LESS = 0x3C; + static const int _GREATER = 0x3E; + static const int _QUESTION = 0x3F; + static const int _AT_SIGN = 0x40; + static const int _UPPER_CASE_A = 0x41; + static const int _UPPER_CASE_F = 0x46; + static const int _UPPER_CASE_Z = 0x5A; + static const int _LEFT_BRACKET = 0x5B; + static const int _BACKSLASH = 0x5C; + static const int _RIGHT_BRACKET = 0x5D; + static const int _LOWER_CASE_A = 0x61; + static const int _LOWER_CASE_F = 0x66; + static const int _LOWER_CASE_Z = 0x7A; + static const int _BAR = 0x7C; + + /** + * This is the internal implementation of JavaScript's encodeURI function. + * It encodes all characters in the string [text] except for those + * that appear in [canonicalTable], and returns the escaped string. + */ + static String _uriEncode(List canonicalTable, + String text, + {Encoding encoding: UTF8, + bool spaceToPlus: false}) { + byteToHex(byte, buffer) { + const String hex = '0123456789ABCDEF'; + buffer.writeCharCode(hex.codeUnitAt(byte >> 4)); + buffer.writeCharCode(hex.codeUnitAt(byte & 0x0f)); + } + + // Encode the string into bytes then generate an ASCII only string + // by percent encoding selected bytes. + StringBuffer result = new StringBuffer(); + var bytes = encoding.encode(text); + for (int i = 0; i < bytes.length; i++) { + int byte = bytes[i]; + if (byte < 128 && + ((canonicalTable[byte >> 4] & (1 << (byte & 0x0f))) != 0)) { + result.writeCharCode(byte); + } else if (spaceToPlus && byte == _SPACE) { + result.writeCharCode(_PLUS); + } else { + result.writeCharCode(_PERCENT); + byteToHex(byte, result); + } + } + return result.toString(); + } + + /** + * Convert a byte (2 character hex sequence) in string [s] starting + * at position [pos] to its ordinal value + */ + static int _hexCharPairToByte(String s, int pos) { + int byte = 0; + for (int i = 0; i < 2; i++) { + var charCode = s.codeUnitAt(pos + i); + if (0x30 <= charCode && charCode <= 0x39) { + byte = byte * 16 + charCode - 0x30; + } else { + // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66). + charCode |= 0x20; + if (0x61 <= charCode && charCode <= 0x66) { + byte = byte * 16 + charCode - 0x57; + } else { + throw new ArgumentError("Invalid URL encoding"); + } + } + } + return byte; + } + + /** + * Uri-decode a percent-encoded string. + * + * It unescapes the string [text] and returns the unescaped string. + * + * This function is similar to the JavaScript-function `decodeURI`. + * + * If [plusToSpace] is `true`, plus characters will be converted to spaces. + * + * The decoder will create a byte-list of the percent-encoded parts, and then + * decode the byte-list using [encoding]. The default encodingis UTF-8. + */ + static String _uriDecode(String text, + {bool plusToSpace: false, + Encoding encoding: UTF8}) { + // First check whether there is any characters which need special handling. + bool simple = true; + for (int i = 0; i < text.length && simple; i++) { + var codeUnit = text.codeUnitAt(i); + simple = codeUnit != _PERCENT && codeUnit != _PLUS; + } + List bytes; + if (simple) { + if (encoding == UTF8 || encoding == LATIN1) { + return text; + } else { + bytes = text.codeUnits; + } + } else { + bytes = new List(); + for (int i = 0; i < text.length; i++) { + var codeUnit = text.codeUnitAt(i); + if (codeUnit > 127) { + throw new ArgumentError("Illegal percent encoding in URI"); + } + if (codeUnit == _PERCENT) { + if (i + 3 > text.length) { + throw new ArgumentError('Truncated URI'); + } + bytes.add(_hexCharPairToByte(text, i + 1)); + i += 2; + } else if (plusToSpace && codeUnit == _PLUS) { + bytes.add(_SPACE); + } else { + bytes.add(codeUnit); + } + } + } + return encoding.decode(bytes); + } + + static bool _isAlphabeticCharacter(int codeUnit) + => (codeUnit >= _LOWER_CASE_A && codeUnit <= _LOWER_CASE_Z) || + (codeUnit >= _UPPER_CASE_A && codeUnit <= _UPPER_CASE_Z); + + // Tables of char-codes organized as a bit vector of 128 bits where + // each bit indicate whether a character code on the 0-127 needs to + // be escaped or not. + + // The unreserved characters of RFC 3986. + static const _unreservedTable = const [ + // LSB MSB + // | | + 0x0000, // 0x00 - 0x0f 0000000000000000 + 0x0000, // 0x10 - 0x1f 0000000000000000 + // -. + 0x6000, // 0x20 - 0x2f 0000000000000110 + // 0123456789 + 0x03ff, // 0x30 - 0x3f 1111111111000000 + // ABCDEFGHIJKLMNO + 0xfffe, // 0x40 - 0x4f 0111111111111111 + // PQRSTUVWXYZ _ + 0x87ff, // 0x50 - 0x5f 1111111111100001 + // abcdefghijklmno + 0xfffe, // 0x60 - 0x6f 0111111111111111 + // pqrstuvwxyz ~ + 0x47ff]; // 0x70 - 0x7f 1111111111100010 + + // The unreserved characters of RFC 2396. + static const _unreserved2396Table = const [ + // LSB MSB + // | | + 0x0000, // 0x00 - 0x0f 0000000000000000 + 0x0000, // 0x10 - 0x1f 0000000000000000 + // ! '()* -. + 0x6782, // 0x20 - 0x2f 0100000111100110 + // 0123456789 + 0x03ff, // 0x30 - 0x3f 1111111111000000 + // ABCDEFGHIJKLMNO + 0xfffe, // 0x40 - 0x4f 0111111111111111 + // PQRSTUVWXYZ _ + 0x87ff, // 0x50 - 0x5f 1111111111100001 + // abcdefghijklmno + 0xfffe, // 0x60 - 0x6f 0111111111111111 + // pqrstuvwxyz ~ + 0x47ff]; // 0x70 - 0x7f 1111111111100010 + + // Table of reserved characters specified by ECMAScript 5. + static const _encodeFullTable = const [ + // LSB MSB + // | | + 0x0000, // 0x00 - 0x0f 0000000000000000 + 0x0000, // 0x10 - 0x1f 0000000000000000 + // ! #$ &'()*+,-./ + 0xf7da, // 0x20 - 0x2f 0101101111101111 + // 0123456789:; = ? + 0xafff, // 0x30 - 0x3f 1111111111110101 + // @ABCDEFGHIJKLMNO + 0xffff, // 0x40 - 0x4f 1111111111111111 + // PQRSTUVWXYZ _ + 0x87ff, // 0x50 - 0x5f 1111111111100001 + // abcdefghijklmno + 0xfffe, // 0x60 - 0x6f 0111111111111111 + // pqrstuvwxyz ~ + 0x47ff]; // 0x70 - 0x7f 1111111111100010 + + // Characters allowed in the scheme. + static const _schemeTable = const [ + // LSB MSB + // | | + 0x0000, // 0x00 - 0x0f 0000000000000000 + 0x0000, // 0x10 - 0x1f 0000000000000000 + // + -. + 0x6800, // 0x20 - 0x2f 0000000000010110 + // 0123456789 + 0x03ff, // 0x30 - 0x3f 1111111111000000 + // ABCDEFGHIJKLMNO + 0xfffe, // 0x40 - 0x4f 0111111111111111 + // PQRSTUVWXYZ + 0x07ff, // 0x50 - 0x5f 1111111111100001 + // abcdefghijklmno + 0xfffe, // 0x60 - 0x6f 0111111111111111 + // pqrstuvwxyz + 0x07ff]; // 0x70 - 0x7f 1111111111100010 + + // Characters allowed in scheme except for upper case letters. + static const _schemeLowerTable = const [ + // LSB MSB + // | | + 0x0000, // 0x00 - 0x0f 0000000000000000 + 0x0000, // 0x10 - 0x1f 0000000000000000 + // + -. + 0x6800, // 0x20 - 0x2f 0000000000010110 + // 0123456789 + 0x03ff, // 0x30 - 0x3f 1111111111000000 + // + 0x0000, // 0x40 - 0x4f 0111111111111111 + // + 0x0000, // 0x50 - 0x5f 1111111111100001 + // abcdefghijklmno + 0xfffe, // 0x60 - 0x6f 0111111111111111 + // pqrstuvwxyz + 0x07ff]; // 0x70 - 0x7f 1111111111100010 + + // Sub delimiter characters combined with unreserved as of 3986. + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + // / "*" / "+" / "," / ";" / "=" + // RFC 3986 section 2.3. + // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + static const _subDelimitersTable = const [ + // LSB MSB + // | | + 0x0000, // 0x00 - 0x0f 0000000000000000 + 0x0000, // 0x10 - 0x1f 0000000000000000 + // ! $ &'()*+,-. + 0x7fd2, // 0x20 - 0x2f 0100101111111110 + // 0123456789 ; = + 0x2bff, // 0x30 - 0x3f 1111111111010100 + // ABCDEFGHIJKLMNO + 0xfffe, // 0x40 - 0x4f 0111111111111111 + // PQRSTUVWXYZ _ + 0x87ff, // 0x50 - 0x5f 1111111111100001 + // abcdefghijklmno + 0xfffe, // 0x60 - 0x6f 0111111111111111 + // pqrstuvwxyz ~ + 0x47ff]; // 0x70 - 0x7f 1111111111100010 + + // Characters allowed in the reg-name as of RFC 3986. + // RFC 3986 Apendix A + // reg-name = *( unreserved / pct-encoded / sub-delims ) + static const _regNameTable = const [ + // LSB MSB + // | | + 0x0000, // 0x00 - 0x0f 0000000000000000 + 0x0000, // 0x10 - 0x1f 0000000000000000 + // ! $%&'()*+,-. + 0x7ff2, // 0x20 - 0x2f 0100111111111110 + // 0123456789 ; = + 0x2bff, // 0x30 - 0x3f 1111111111010100 + // ABCDEFGHIJKLMNO + 0xfffe, // 0x40 - 0x4f 0111111111111111 + // PQRSTUVWXYZ _ + 0x87ff, // 0x50 - 0x5f 1111111111100001 + // abcdefghijklmno + 0xfffe, // 0x60 - 0x6f 0111111111111111 + // pqrstuvwxyz ~ + 0x47ff]; // 0x70 - 0x7f 1111111111100010 + + // Characters allowed in the path as of RFC 3986. + // RFC 3986 section 3.3. + // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + static const _pathCharTable = const [ + // LSB MSB + // | | + 0x0000, // 0x00 - 0x0f 0000000000000000 + 0x0000, // 0x10 - 0x1f 0000000000000000 + // ! $ &'()*+,-. + 0x7fd2, // 0x20 - 0x2f 0100101111111110 + // 0123456789:; = + 0x2fff, // 0x30 - 0x3f 1111111111110100 + // @ABCDEFGHIJKLMNO + 0xffff, // 0x40 - 0x4f 1111111111111111 + // PQRSTUVWXYZ _ + 0x87ff, // 0x50 - 0x5f 1111111111100001 + // abcdefghijklmno + 0xfffe, // 0x60 - 0x6f 0111111111111111 + // pqrstuvwxyz ~ + 0x47ff]; // 0x70 - 0x7f 1111111111100010 + + // Characters allowed in the query as of RFC 3986. + // RFC 3986 section 3.4. + // query = *( pchar / "/" / "?" ) + static const _queryCharTable = const [ + // LSB MSB + // | | + 0x0000, // 0x00 - 0x0f 0000000000000000 + 0x0000, // 0x10 - 0x1f 0000000000000000 + // ! $ &'()*+,-./ + 0xffd2, // 0x20 - 0x2f 0100101111111111 + // 0123456789:; = ? + 0xafff, // 0x30 - 0x3f 1111111111110101 + // @ABCDEFGHIJKLMNO + 0xffff, // 0x40 - 0x4f 1111111111111111 + // PQRSTUVWXYZ _ + 0x87ff, // 0x50 - 0x5f 1111111111100001 + // abcdefghijklmno + 0xfffe, // 0x60 - 0x6f 0111111111111111 + // pqrstuvwxyz ~ + 0x47ff]; // 0x70 - 0x7f 1111111111100010 +} + +class _UnmodifiableMap implements Map { + final Map _map; + const _UnmodifiableMap(this._map); + + bool containsValue(Object value) => _map.containsValue(value); + bool containsKey(Object key) => _map.containsKey(key); + V operator [](Object key) => _map[key]; + void operator []=(K key, V value) { + throw new UnsupportedError("Cannot modify an unmodifiable map"); + } + V putIfAbsent(K key, V ifAbsent()) { + throw new UnsupportedError("Cannot modify an unmodifiable map"); + } + addAll(Map other) { + throw new UnsupportedError("Cannot modify an unmodifiable map"); + } + V remove(Object key) { + throw new UnsupportedError("Cannot modify an unmodifiable map"); + } + void clear() { + throw new UnsupportedError("Cannot modify an unmodifiable map"); + } + void forEach(void f(K key, V value)) => _map.forEach(f); + Iterable get keys => _map.keys; + Iterable get values => _map.values; + int get length => _map.length; + bool get isEmpty => _map.isEmpty; + bool get isNotEmpty => _map.isNotEmpty; +} diff --git a/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/html/dart2js/html_dart2js.dart b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/html/dart2js/html_dart2js.dart new file mode 100644 index 0000000..654e26f --- /dev/null +++ b/Chapter 1/bank_terminal/build/web/packages/$sdk/lib/html/dart2js/html_dart2js.dart @@ -0,0 +1,36043 @@ +/** + * HTML elements and other resources for web-based applications that need to + * interact with the browser and the DOM (Document Object Model). + * + * This library includes DOM element types, CSS styling, local storage, + * media, speech, events, and more. + * To get started, + * check out the [Element] class, the base class for many of the HTML + * DOM types. + * + * ## Other resources + * + * * If you've never written a web app before, try our + * tutorials—[A Game of Darts](http://dartlang.org/docs/tutorials). + * + * * To see some web-based Dart apps in action and to play with the code, + * download + * [Dart Editor](http://www.dartlang.org/#get-started) + * and run its built-in examples. + * + * * For even more examples, see + * [Dart HTML5 Samples](https://github.com/dart-lang/dart-html5-samples) + * on Github. + */ +library dart.dom.html; + +import 'dart:async'; +import 'dart:collection'; +import 'dart:_internal' hide Symbol, deprecated; +import 'dart:html_common'; +import 'dart:indexed_db'; +import 'dart:isolate'; +import "dart:convert"; +import 'dart:math'; +import 'dart:_native_typed_data'; +import 'dart:typed_data'; +import 'dart:svg' as svg; +import 'dart:svg' show Matrix; +import 'dart:svg' show SvgSvgElement; +import 'dart:web_audio' as web_audio; +import 'dart:web_gl' as gl; +import 'dart:web_sql'; +import 'dart:_isolate_helper' show IsolateNatives; +import 'dart:_foreign_helper' show JS, JS_INTERCEPTOR_CONSTANT; +// Copyright (c) 2012, 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. + +// DO NOT EDIT - unless you are editing documentation as per: +// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation +// Auto-generated dart:html library. + + +// Not actually used, but imported since dart:html can generate these objects. +import 'dart:_js_helper' show + convertDartClosureToJS, Creates, JavaScriptIndexingBehavior, + JSName, Null, Returns, + findDispatchTagForInterceptorClass, setNativeSubclassDispatchRecord, + makeLeafDispatchRecord; +import 'dart:_interceptors' show + Interceptor, JSExtendableArray, findInterceptorConstructorForType, + findConstructorForNativeSubclassType, getNativeInterceptor, + setDispatchProperty; + +export 'dart:math' show Rectangle, Point; + + + + + +/** + * Top-level container for a web page, which is usually a browser tab or window. + * + * Each web page loaded in the browser has its own [Window], which is a + * container for the web page. + * + * If the web page has any `
+ + + + + + +
+
+ + +
+
+

+
+
+
+
+
There are no items in your cart.
+ + +
+
+ +
+

+ + Submit an image

+
+ +
+
+

Cover Image

+

Submit an image, and if we use it you’ll get $100. To submit your images Click Here

+
+
+
+
+ + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/1865EN_Oracle Siebel CRM 8-2 Developers Guide.jpg b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/1865EN_Oracle Siebel CRM 8-2 Developers Guide.jpg new file mode 100644 index 0000000..e2921a7 Binary files /dev/null and b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/1865EN_Oracle Siebel CRM 8-2 Developers Guide.jpg differ diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/300lo.json b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/300lo.json new file mode 100644 index 0000000..21ec0f9 --- /dev/null +++ b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/300lo.json @@ -0,0 +1 @@ +_ate.track.hsr({"loc":"MDAwMDBFVUJFMDAyMzA4MTg0MzAwMDAwMDAwVg=="}); \ No newline at end of file diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/682094929-postmessagerelay.js b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/682094929-postmessagerelay.js new file mode 100644 index 0000000..c6562c9 --- /dev/null +++ b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/682094929-postmessagerelay.js @@ -0,0 +1,12 @@ +var f=this,g=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null"; +else if("function"==b&&"undefined"==typeof a.call)return"object";return b};Math.random();var h=function(a,b){var c=a.split("."),d=f;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b};Math.random();var l;i:{var m=f.navigator;if(m){var n=m.userAgent;if(n){l=n;break i}}l=""};var p=-1!=l.indexOf("Opera")||-1!=l.indexOf("OPR"),r=-1!=l.indexOf("Trident")||-1!=l.indexOf("MSIE"),s=-1!=l.indexOf("Gecko")&&-1==l.toLowerCase().indexOf("webkit")&&!(-1!=l.indexOf("Trident")||-1!=l.indexOf("MSIE")),t=-1!=l.toLowerCase().indexOf("webkit"),u=t&&-1!=l.indexOf("Mobile"); +(function(){var a="",b;if(p&&f.opera)return a=f.opera.version,"function"==g(a)?a():a;s?b=/rv\:([^\);]+)(\)|;)/:r?b=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:t&&(b=/WebKit\/(\S+)/);b&&(a=(a=b.exec(l))?a[1]:"");return r&&(b=(b=f.document)?b.documentMode:void 0,b>parseFloat(a))?String(b):a})();var v;v=!1;var x=l;x&&(-1!=x.indexOf("Firefox")||-1!=x.indexOf("Camino")||-1!=x.indexOf("iPhone")||-1!=x.indexOf("iPod")||-1!=x.indexOf("iPad")||-1!=x.indexOf("Chrome")||-1!=x.indexOf("Android")||-1!=x.indexOf("Safari")&&(v=!0));var y=v;var z=function(a){var b=window;if(u&&y&&b){b.focus();var c=0,d=null,d=b.setInterval(function(){a.closed||5==c?b.clearInterval(d):(a.close(),c++)},150)}else a.close()};var B,C=function(a){a=a||[];for(var b=[],c=0,d=a.length;c#hoverNav{left:0;}#frameHoverNav{z-index:10;margin-left:auto;margin-right:auto;width:20%;position:relative;top:-60px;}#imageData>#frameHoverNav{left:0;}#hoverNav a,#frameHoverNav a{outline:none;}#prevLink,#nextLink{width:49%;height:100%;background:transparent url(/sites/all/themes/packt_new/css/lightbox2/images/blank.gif) no-repeat; display:block;}#prevLink,#framePrevLink{left:0;float:left;}#nextLink,#frameNextLink{right:0;float:right;}#prevLink:hover,#prevLink:visited:hover,#prevLink.force_show_nav,#framePrevLink{background:url(/sites/all/themes/packt_new/css/lightbox2/images/prev.gif) left 15% no-repeat;}#nextLink:hover,#nextLink:visited:hover,#nextLink.force_show_nav,#frameNextLink{background:url(/sites/all/themes/packt_new/css/lightbox2/images/next.gif) right 15% no-repeat;}#prevLink:hover.force_show_nav,#prevLink:visited:hover.force_show_nav,#framePrevLink:hover,#framePrevLink:visited:hover{background:url(/sites/all/themes/packt_new/css/lightbox2/images/prev_hover.gif) left 15% no-repeat;}#nextLink:hover.force_show_nav,#nextLink:visited:hover.force_show_nav,#frameNextLink:hover,#frameNextLink:visited:hover{background:url(/sites/all/themes/packt_new/css/lightbox2/images/next_hover.gif) right 15% no-repeat;}#framePrevLink,#frameNextLink{width:45px;height:45px;display:block;margin-bottom:-60px;position:relative;top:60px;}#imageDataContainer{font:10px Verdana,Helvetica,sans-serif;background-color:#fff;margin:0 auto;line-height:1.4em;min-width:240px;}#imageData{padding:0 10px;}#imageData #imageDetails{width:70%;float:left;text-align:left;}#imageData #caption{font-weight:bold;}#imageData #numberDisplay{display:block;clear:left;padding-bottom:1.0em;}#imageData #node_link_text{display:block;padding-bottom:1.0em;}#imageData #bottomNav{height:66px;}#overlay{position:absolute;top:0;left:0;z-index:90;width:100%;height:500px;background-color:#000;}#overlay_default{opacity:0.6;}#overlay_macff2{background:transparent url(/sites/all/themes/packt_new/css/lightbox2/images/overlay.png) repeat;}.clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden;}* html>body .clearfix{display:inline;width:100%;}* html .clearfix{/* Hides from IE-mac \*/ height:1%;}#bottomNavClose{display:block;background:url(/sites/all/themes/packt_new/css/lightbox2/images/close.gif) left no-repeat;margin-top:33px;float:right;padding-top:0.7em;height:26px;width:26px;}#bottomNavClose:hover{background-position:right;}#loadingLink{display:block;background:url(/sites/all/themes/packt_new/css/lightbox2/images/loading.gif) no-repeat;width:32px;height:32px;}#bottomNavZoom{display:none;background:url(/sites/all/themes/packt_new/css/lightbox2/images/expand.gif) no-repeat;width:34px;height:34px;position:relative;left:30px;float:right;}#bottomNavZoomOut{display:none;background:url(/sites/all/themes/packt_new/css/lightbox2/images/contract.gif) no-repeat;width:34px;height:34px;position:relative;left:30px;float:right;}#lightshowPlay{margin-top:42px;float:right;margin-right:5px;margin-bottom:1px;height:20px;width:20px;background:url(/sites/all/themes/packt_new/css/lightbox2/images/play.png) no-repeat;}#lightshowPause{margin-top:42px;float:right;margin-right:5px;margin-bottom:1px;height:20px;width:20px;background:url(/sites/all/themes/packt_new/css/lightbox2/images/pause.png) no-repeat;}.lightbox_hide_image{display:none;}div.toboggan-container{text-align:center;}div.toboggan-login-link-container{text-align:left;}div.toboggan-login-link-container a{text-decoration:none;}div.toboggan-block-message{text-align:left;}div.user-login-block{text-align:left;}div.user-login-block a{text-align:left;}.block-nice_menus{line-height:normal;z-index:10;}ul.nice-menu,ul.nice-menu ul{z-index:5;position:relative;}ul.nice-menu li{position:relative;}ul.nice-menu a{display:block;}ul.nice-menu ul,#header-region ul.nice-menu ul{position:absolute;visibility:hidden;}ul.nice-menu li.over ul{visibility:visible;}ul.nice-menu ul li{display:block;}ul.nice-menu:after{content:".";display:block;height:0;clear:both;visibility:hidden;}ul.nice-menu li:hover ul,ul.nice-menu li.menuparent li:hover ul,ul.nice-menu li.menuparent li.menuparent li:hover ul,ul.nice-menu li.menuparent li.menuparent li.menuparent li:hover ul,ul.nice-menu li.over ul,ul.nice-menu li.menuparent li.over ul,ul.nice-menu li.menuparent li.menuparent li.over ul,ul.nice-menu li.menuparent li.menuparent li.menuparent li.over ul,#header-region ul.nice-menu li:hover ul,#header-region ul.nice-menu li.menuparent li:hover ul,#header-region ul.nice-menu li.menuparent li.menuparent li:hover ul,#header-region ul.nice-menu li.over ul,#header-region ul.nice-menu li.menuparent li.over ul,#header-region ul.nice-menu li.menuparent li.menuparent li.over ul{visibility:visible;}ul.nice-menu li:hover ul ul,ul.nice-menu li:hover ul ul ul,ul.nice-menu li:hover li:hover ul ul,ul.nice-menu li:hover li:hover ul ul ul,ul.nice-menu li:hover li:hover li:hover ul ul,ul.nice-menu li:hover li:hover li:hover ul ul ul,ul.nice-menu li.over ul ul,ul.nice-menu li.over ul ul ul,ul.nice-menu li.over li.over ul ul,ul.nice-menu li.over li.over ul ul ul,ul.nice-menu li.over li.over li.over ul ul,ul.nice-menu li.over li.over li.over ul ul ul,#header-region ul.nice-menu li:hover ul ul,#header-region ul.nice-menu li:hover ul ul ul,#header-region ul.nice-menu li:hover li:hover ul ul,#header-region ul.nice-menu li:hover li:hover ul ul ul,#header-region ul.nice-menu li:hover li:hover li:hover ul ul,#header-region ul.nice-menu li:hover li:hover li:hover ul ul ul,#header-region ul.nice-menu li.over ul ul,#header-region ul.nice-menu li.over ul ul ul,#header-region ul.nice-menu li.over li.over ul ul,#header-region ul.nice-menu li.over li.over ul ul ul,#header-region ul.nice-menu li.over li.over li.over ul ul,#header-region ul.nice-menu li.over li.over li.over ul ul ul{visibility:hidden;}ul.nice-menu li.menuparent ul,#header-region ul.nice-menu li.menuparent ul{overflow:visible !important;}ul.nice-menu li.menuparent ul iframe,#header-region ul.nice-menu li.menuparent ul iframe{display:none;display/**/:block;position:absolute;top:0;left:0;z-index:-1;filter:mask();width:20px;height:20px;}ul.nice-menu,ul.nice-menu ul{list-style:none;padding:0;margin:0;border-top:1px solid #ccc;}ul.nice-menu li{border:1px solid #ccc;border-top:0;float:left;background-color:#eee; margin:0;padding-left:0;background-image:none;}#header-region ul.nice-menu li{margin:0; padding-top:0.1em;padding-bottom:0.1em;background:#eee;}ul.nice-menu a{padding:0.3em 5px 0.3em 5px;}ul.nice-menu ul,#header-region ul.nice-menu ul{top:1.8em;left:-1px;border:0;border-top:1px solid #ccc;margin-right:0;}#header-region ul.nice-menu ul{top:1.7em;}ul.nice-menu ul li{width:12.5em;}ul.nice-menu-right,ul.nice-menu-left,ul.nice-menu-right li,ul.nice-menu-left li{width:12.5em;}ul.nice-menu-right ul{width:12.5em;left:12.5em;top:-1px;}ul.nice-menu-right ul ul{width:12.5em;left:12.5em;top:-1px;}ul.nice-menu-right li.menuparent,ul.nice-menu-right li li.menuparent{background:#eee url(/sites/all/themes/packt_new/css/nice_menus/arrow-right.png) right center no-repeat;}ul.nice-menu-right li.menuparent:hover,ul.nice-menu-right li.over,ul.nice-menu-right li li.menuparent:hover,ul.nice-menu-right li li.over{background:#ccc url(/sites/all/themes/packt_new/css/nice_menus/arrow-right.png) right center no-repeat;}ul.nice-menu-left li ul{width:12.5em;left:-12.65em;top:-1px;}ul.nice-menu-left li ul li ul{width:12.5em;left:-12.65em;top:-1px;}ul.nice-menu-left li.menuparent,ul.nice-menu-left li li.menuparent{background:#eee url(/sites/all/themes/packt_new/css/nice_menus/arrow-left.png) left center no-repeat;}ul.nice-menu-left li.menuparent:hover,ul.nice-menu-left li.over,ul.nice-menu-left li li.menuparent:hover,ul.nice-menu-left li li.over{background:#ccc url(/sites/all/themes/packt_new/css/nice_menus/arrow-left.png) left center no-repeat;}ul.nice-menu-left a,ul.nice-menu-left ul a{padding-left:14px;}ul.nice-menu-down{float:left;border:0;}ul.nice-menu-down li{border-top:1px solid #ccc;}ul.nice-menu-down li li{border-top:0;}ul.nice-menu-down ul{left:0;}ul.nice-menu-down ul li{clear:both;}ul.nice-menu-down li ul li ul,#header-region ul.nice-menu-down li ul li ul{left:12.5em;top:-1px;}ul.nice-menu-down .menuparent a{padding-right:15px;}ul.nice-menu-down li.menuparent,#header-region ul.nice-menu-down li.menuparent{background:#eee url(/sites/all/themes/packt_new/css/nice_menus/arrow-down.png) right center no-repeat;}ul.nice-menu-down li.menuparent:hover,ul.nice-menu-down li.over,#header-region ul.nice-menu-down li.menuparent:hover,#header-region ul.nice-menu-down li.over{background:#ccc url(/sites/all/themes/packt_new/css/nice_menus/arrow-down.png) right center no-repeat;}ul.nice-menu-down li li.menuparent,#header-region ul.nice-menu-down li li.menuparent{background:#eee url(/sites/all/themes/packt_new/css/nice_menus/arrow-right.png) right center no-repeat;}ul.nice-menu-down li li.menuparent:hover,ul.nice-menu-down li li.over,#header-region ul.nice-menu-down li li.menuparent:hover,#header-region ul.nice-menu-down li li.over{background:#ccc url(/sites/all/themes/packt_new/css/nice_menus/arrow-right.png) right center no-repeat;}table.packtlib-table.margins{margin:10px 0px 20px;}div.profile form div.form-item{margin:0.5em 0px;}div.affiliate form div.form-item label,div.affiliate form div.form-item select,div.affiliate form div.form-item input,div.affiliate form div.form-item textarea,div.affiliate form div.form-item div.description{float:left;}div.affiliate form div.form-item select,div.affiliate form div.form-item input,div.affiliate form div.form-item textarea{width:250px;}div.affiliate form div.form-item label{width:200px;}div.affiliate form div.form-item div.description{margin-left:10px;}div.affiliate form div.form-item{clear:both;}div#affiliate_link_preview{margin:15px 0px;border:1px solid gray;width:100%;text-align:left;font-size:2.5em;background-color:white;font-weight:bold;line-height:1.25em;word-wrap:break-word;}div#affiliate_link_preview div.inner{margin:20px 10px;}div.profile a img{position:relative;top:2px;}table tr td.noborder{border:0px;background-color:transparent;}p.account-indent{margin-left:1em;}input.form-submit.disabled{background:url("/sites/all/themes/packt_new/button.gif") repeat-x scroll center center #FFFFFF;cursor:default;}fieldset#affiliate_credit-pane{width:260px;float:right;}div#special_offers div#special_offers_centre{width:100%;margin-top:0px;}span.largeplus{line-height:160px;font-size:8em;}div.partner-offer{width:100%;}div.partner-offer div.inner{margin:0px;}div.partner-offer h2{font-weight:bold !important;font-size:1.6em !important;text-align:left;color:#F68C23;}.partner-offer .partner-offer-left{float:left;}.partner-offer span.largefont{line-height:50px;font-size:4em;font-weight:bold;}.partner-offer span.mediumfont{line-height:45px;font-size:2.5em;font-weight:bold;}div.partner-offer-right{float:right;width:390px;}div.offer-inner-left{float:left;margin-top:35px;}div.offer-inner-right{float:right;margin-top:70px;}.partner-offer .partner-offer-left .offer-inner-left{float:left;}div.partner-offer .partner-offer-text{margin-top:10px;}div.partner-offer div.partner-offer-left a{text-decoration:none;}div.offer-inner-right div.partner-offer-price{width:130px;}body.main-chromeless.page-books div.category-page-block{margin:10px 0px;}body.main-chromeless.page-books div#category_description div.thumb-image{height:80px;width:120px;float:left;text-align:center;}body.main-chromeless.page-books div#category_description div.description{margin-left:20px;border:1px;float:left;width:600px;}body.main-chromeless.page-books div.category-page-block{margin:10px 0px;}body.main-chromeless.page-books div#category_description div.thumb-image{height:80px;width:120px;float:left;text-align:center;}body.main-chromeless.page-books div#category_description div.description{margin-left:20px;border:1px;float:left;width:600px;}body.main-chromeless.page-books #views-exposed-form-taxonomy-book-list-default{padding:0em 1em 1em 1em;}body.main-chromeless.page-books #views-exposed-form-taxonomy-book-list-default #edit-submit-taxonomy-book-list{background:url('/sites/all/themes/packt_new/images/pp/go-button.png');position:relative;height:31px;margin-left:250px;margin-top:-1px;width:31px;border:none;}body.main-chromeless.page-books #book_taxonomy_view .views-exposed-widget{height:30px;line-height:30px;margin-right:20px;}.solr-search-result{width:100%;float:left;height:150px;margin-left:5px;}.solr-search-result a{font-size:14px;}.solr-search-result img{margin-right:5px;}.solr-result-exc-text{overflow:hidden;}.solr-search-result em{font-weight:700;font-style:italic;}.title-image{float:left;}#packt-categorypages-cat-search-form #edit-keys-wrapper input{margin:0px;width:320px;}#packt-categorypages-cat-search-form #edit-keys-wrapper,#packt-categorypages-cat-search-form #edit-types-wrapper,#packt-categorypages-cat-search-form #edit-count-wrapper{margin:0px;margin-right:15px;height:auto;width:auto;float:left;}#packt-categorypages-cat-search-form #edit-types-wrapper,#packt-categorypages-cat-search-form #edit-count-wrapper{margin-top:18px;float:right;}#packt-categorypages-cat-search-form label{font-size:14px;}#packt-categorypages-cat-search-form label.option{font-size:inherit;margin:0;}#packt-categorypages-cat-search-form .solr-book-options .form-item{margin:0;}#packt-categorypages-cat-search-form .solr-book-options{width:100px;height:99px;margin-left:345px;}#packt-categorypages-cat-search-form #edit-types{margin-bottom:10px;}#packt-categorypages-cat-search-form input[type=submit]{margin:0;margin-right:15px;margin-top:20px;height:30px;width:auto;float:left;}#packt-categorypages-cat-search-form #edit-forthcoming-wrapper{padding-bottom:10px;padding-top:5px;}#packt-categorypages-cat-search-form #edit-forthcoming-wrapper,#packt-categorypages-cat-search-form #edit-available-wrapper{width:auto;height:auto;float:left;}.solr-book-options .form-item{display:none;}body.main-chromeless.page-books #views-exposed-form-taxonomy-book-list-default{padding:0em 1em 1em 1em;}body.main-chromeless.page-books #views-exposed-form-taxonomy-book-list-default #edit-submit-taxonomy-book-list{background:url('/sites/all/themes/packt_new/images/pp/go-button.png');position:relative;height:31px;margin-left:250px;margin-top:-1px;width:31px;border:none;}body.main-chromeless.page-books #book_taxonomy_view .views-exposed-widget{height:30px;line-height:30px;margin-right:20px;}#packt-categorypages-cat-search-form #edit-submit{position:relative;height:35px;width:45px;border:1px solid #000;color:#F68C23;text-shadow:none;font-size:16px;padding:0px;text-align:center;background:#000 url(/sites/all/themes/packt_new/images/sprite-horizontal-gradients.png) repeat-x center;background-position:0px -995px;font-weight:normal;}.view-display-id-panel_pane_1 .view-content .views-field-title span a{font-weight:bold;}div#cookie-outer-outer,div#webapp-cookie-outer-outer{position:fixed;z-index:10000000;bottom:0px;right:0px;width:345px;margin-bottom:20px;margin-right:20px;display:none;-webkit-transform-style:preserve-3d;}div.cookie-outer{display:block;padding:10px;border:8px solid #888;background-color:#ddd;border-radius:8px;padding:10px;}div.cookie-inner{padding:15px;}#footer .block div.cookie-outer p{text-align:left;font-size:1.2em;}#footer .block div.cookie-outer form{text-align:left;}#footer .block div.cookie-outer a,#footer .block div.cookie-outer a:hover{color:#F68C23;}.webapp-popup-tablet-only div#webapp-cookie-outer-outer{bottom:auto;right:auto;margin:0;}.webapp-popup-tablet-only{z-index:10000000;}div#footer div.block.block-packt_cookie.modal{height:100% !important;background-image:url('/sites/all/themes/packt_new/images/pp/background-opacity-50.png');position:fixed;top:0px;left:0px;z-index:100;width:100%;height:100%;}body div.block.block-packt_cookie.modal div.cookie-outer,body div.block.block-packt_cookie.modal div.cookie-inner{margin:0;padding:0;background:transparent;border:0;}body.mobile-view{margin-top:0px !important;background:#323335 !important;}div.hadoop-offer-register{font-size:15px;font-weight:normal;padding:5px 0px 9px 5px;background-color:white;background-repeat:repeat-x;color:rgb(246,140,35);border:none;font-size:1em;width:440px;margin:0 auto;margin-top:30px;}.hadoop-banner{margin:0 auto;}.hadoop-banner img{margin:0 auto;display:block;border:1px solid black;}.hadoop-checkout-login{font-size:15px;font-weight:normal;padding:5px 0px 9px 5px;background-color:white;background-repeat:repeat-x;color:rgb(246,140,35);border:none;font-size:1em;width:445px;margin:0 auto;}#packt-hadoopoffer-register-special-form .form-item label,#packt-hadoopoffer-register-login-form .form-item label{display:inline-block;margin-right:0px;text-align:right;width:120px;}#packt-hadoopoffer-register-special-form .form-item label.option{display:inline-block;margin-left:170px;text-align:left;width:270px;}#packt-hadoopoffer-register-special-form input,#packt-hadoopoffer-register-login-form input{float:right;}#packt-hadoopoffer-register-special-form input#edit-newsletter{float:left;}#packt-hadoopoffer-register-special-form input[type=submit]{float:right;margin:0 auto;display:block;}#packt-hadoopoffer-register-login-form > div{text-align:center;}#packt-hadoopoffer-register-login-form input[type=submit]{display:block;margin:0 auto;}#packt-hadoopoffer-register-login-form h2{text-align:left;}#packt-hadoopoffer-register-login-form .form-item label{float:left;}#packt-hadoopoffer-register-login-form div{height:16px;}#packt-hadoopoffer-register-login-form .no-height{height:auto;}#packt-hadoopoffer-register-login-form .hadoop-offer-copy{color:black;height:60px;text-align:left;}#packt-hadoopoffer-register-login-form #edit-booklist{margin-right:15px;}.hadoop-offer-wrapper{width:100%;margin:0 auto;display:block;padding-left:30px;}.hadoop-offer-wrapper .hadoop-offer-book{padding-left:32px;width:268px;float:left;margin-bottom:20px;}.hadoop-offer-wrapper .hadoop-offer-book .book-info-inner{height:215px;}.hadoop-offer-divider{height:30px;}.hadoop-offer-wrapper .authors{width:240px;}.hadoop-offer-wrapper .pubdate{margin-top:5px;}.hadoop-offer-wrapper .add-to-cart-button{margin-top:9px;}.hadoop-checkout-login p{color:black;}div.content-inner{margin:10px;}table.packt-table tr td,table.packt-table tr th{height:23px;border:1px solid #777;border-right-width:0px;border-bottom-width:0px;padding:4px 10px;}table.packt-table tr.headers th{background:black url('/sites/all/themes/packt_new/images/pp/content-header-gradient.jpg') repeat scroll 0 0;background-color:#5C5C5C;background-repeat:repeat-x;color:white;font-size:15px;font-weight:normal;}table.packt-table tr.last td,table.packt-table tr.last th{border-bottom-width:1px;}table.packt-table td.last,table.packt-table th.last{border-right-width:1px;}a.addthis_button_google_plusone div{width:52px !important;float:left;}th.download-header-title{width:220px;}th.download-header-downloads{width:110px;}th.download-header-share{width:136px;}div.float-left.bimg{width:41px;}div.float-left.btitle{width:165px;}div#send_to_kindle{display:none;width:400px;height:400px;padding:20px;}div.email-suffix div.form-item{float:left;width:200px;}div.email-suffix div.form-item input{width:190px;}div.email-suffix div.suffix{height:58px;line-height:73px;}.packt-table-videos .form-submit{width:129px;background-image:url(/sites/all/themes/packt_new/images/sprite-horizontal-gradients.png);background-repeat:repeat-x;border-radius:5px;background-position:0 -904px;border-color:#000;font-size:1.3em;color:#F68C23;text-shadow:none;padding-bottom:5px;margin-bottom:1px;cursor:pointer;font-weight:bold;border:1px solid #888;padding-left:10px;padding-right:10px;display:block;position:relative;margin-left:38px;height:18px;padding-top:7px;text-decoration:none;text-align:center;margin:0 auto;}.packt-table-videos a{text-decoration:none;}.boxy-download-close{text-align:right;height:30%;font-size:1.6em;}.boxy-download-text{text-align:center;height:70%;}.boxy-download-info{font-weight:bold;font-size:2.8em;}.boxy-download-help{font-size:1.6em;}div.oracle-offer-register{font-size:15px;font-weight:normal;padding:5px 0px 9px 5px;background-color:white;background-repeat:repeat-x;color:rgb(246,140,35);border:none;font-size:1em;width:440px;margin:0 auto;margin-top:30px;}.oracle-banner{margin:0 auto;}.oracle-banner img{margin:0 auto;display:block;border:1px solid black;}.oracle-checkout-login{font-size:15px;font-weight:normal;padding:5px 0px 9px 5px;background-color:white;background-repeat:repeat-x;color:rgb(246,140,35);border:none;font-size:1em;width:445px;margin:0 auto;}#packt-oracleoffer-register-special-form .form-item label,#packt-oracleoffer-register-login-form .form-item label{display:inline-block;margin-right:0px;text-align:right;width:120px;}#packt-oracleoffer-register-special-form .form-item label.option{display:inline-block;margin-left:170px;text-align:left;width:270px;}#packt-oracleoffer-register-special-form input,#packt-oracleoffer-register-login-form input{float:right;}#packt-oracleoffer-register-special-form input#edit-newsletter{float:left;}#packt-oracleoffer-register-special-form input[type=submit]{float:right;margin:0 auto;display:block;}#packt-oracleoffer-register-login-form > div{text-align:center;}#packt-oracleoffer-register-login-form input[type=submit]{display:block;margin:0 auto;}#packt-oracleoffer-register-login-form h2{text-align:left;}#packt-oracleoffer-register-login-form .form-item label{float:left;}#packt-oracleoffer-register-login-form div{height:16px;}#packt-oracleoffer-register-login-form .no-height{height:auto;}#packt-oracleoffer-register-login-form .oracle-offer-copy{color:black;height:60px;text-align:left;}#packt-oracleoffer-register-login-form #edit-booklist{margin-right:15px;}.oracle-offer-wrapper{width:100%;margin:0 auto;display:block;padding-left:30px;}.oracle-offer-wrapper .oracle-offer-book{width:300px;float:left;margin-bottom:20px;}.oracle-offer-wrapper .oracle-offer-book .book-info-inner{height:215px;}.oracle-offer-divider{height:30px;}.oracle-offer-wrapper .authors{width:240px;}.oracle-offer-wrapper .pubdate{margin-top:5px;}.oracle-offer-wrapper .add-to-cart-button{margin-top:9px;}.oracle-checkout-login p{color:black;}body.raw{font-family:Helvetica;font-size:62.5%;}body.raw div#rawpage{font-size:1.4em;}body.raw div#downloads{margin:20px auto;background:#CCCCCC;overflow:hidden;border:1px solid #777;width:640px;}body.raw div#downloads .inner{margin:15px;}body.raw div.book-item{margin-bottom:15px;}body.raw div#downloads div.title,body.raw div#downloads div.links{width:50%;}body.raw span.link{margin-right:10px;font-size:1.2em;}body.raw div.message{margin:0px auto;text-align:center;}body.raw div.hidden{display:none;}body.raw div.spinner-logo{background-image:url(/sites/all/themes/packt_new/images/spinner.gif);margin:20px auto;background-repeat:no-repeat;width:40px;height:40px;}body.raw div.message.large{font-size:1.8em;}body.raw div#error{color:#FF0000;font-weight:bold;}body.raw div.links{margin-top:1em;}#partner-credit-card-form #edit-expiry-month-wrapper{float:left;}#partner-credit-card-form #edit-expiry-year-wrapper{float:left;width:90%;margin-top:24px;}a.nounderline{text-decoration:none;}div.staf{float:right;margin:0px 10px 10px 10px;}div.staf div.accordion{width:180px;}div.staf div.inner{border:1px solid gray;overflow:auto;}div.staf div.header{width:100%;height:32px;color:white;font-size:1.25em;background-position:0 -1150px;background-repeat:repeat-x;line-height:32px;border-bottom:1px solid #aaa;text-align:center;cursor:pointer;}div.staf div.accordion div.content{display:none;background-position:0 -1182px;background-color:#ffffff;background-repeat:repeat-x;height:250px;margin:0px;padding:0px;overflow:hidden;}div.staf div.accordion div.content.active{display:block;}div.staf div.accordion div.content div.inner{margin:10px 5px;border:0px;overflow:hidden;}div.staf div.accordion div.content div.inner input.form-text{width:160px;}div.ajax-forms{display:none;}div#staf_send{width:640px;height:auto;}div#staf_save{width:300px;height:auto;}div#staf_send input.form-text{width:180px;margin-right:10px;}div.staf-form div#edit-user-first-name-wrapper,div.staf-form div#edit-user-last-name-wrapper,div.staf-form div#edit-user-email-wrapper,div.staf-form div#edit-friend-first-name-wrapper,div.staf-form div#edit-friend-last-name-wrapper,div.staf-form div#edit-friend-email-wrapper,div.staf-form div#edit-newsletter-signup-wrapper,div.staf-form div#newsletter-signup-label{float:left;}div.staf-form div#edit-newsletter-signup-wrapper{width:20px;clear:left;}div.staf-form div#edit-message-wrapper,div.staf-form div#friend-details-label{clear:both;}p#send_again_message{font-weight:bold;display:none;}div#edit-kindle-address-wrapper{display:none;}.hidden{display:none;}td.content-header{background:black url('/sites/all/themes/packt_new/images/pp/content-header-gradient.jpg') repeat scroll 0 0;background-color:#5C5C5C;background-repeat:repeat-x;color:white;font-size:15px;padding-left:10px;}fieldset.newsletters-fieldset{border:none;margin:0px;margin-left:1px;padding:0px;}fieldset.newsletters-fieldset legend{display:block;float:left;width:100%;margin:0px 0px 10px 0px;}fieldset.newsletters-fieldset .form-item{margin:10px 0px;}fieldset.newsletters-fieldset input{float:left;margin:10px 20px 10px 5px;}fieldset.captcha{border:none;margin:0px;margin-left:1px;padding:0px;}fieldset.captcha img{}fieldset.captcha div.form-item{width:190px;float:left;}div.newsletter-unselected{margin:5px 0;background-image:url(/sites/all/themes/packt_new/css/packt_subscribe/cross.png);background-repeat:no-repeat;background-position-y:center;}div.newsletter-unsub{margin:5px 0;background-image:url(/sites/all/themes/packt_new/css/packt_subscribe/unsub-bullet.png);background-repeat:no-repeat;background-position-y:center;}div.newsletter-inner{margin-left:30px;}div.newsletter-selected{margin:5px 0;background-image:url(/sites/all/themes/packt_new/css/packt_subscribe/tick.png);background-repeat:no-repeat;background-position-y:center;}div#block-packt_subscribe-1.block{border:1px solid #F9CA8A;color:white;font-size:12px;height:206px;margin-left:770px;margin-top:25px;position:absolute;text-align:left;width:207px;clear:both;}#footer div#block-packt_subscribe-1.block div.block-content-inner,#footer div#block-packt_subscribe-1.block div.block-content-inner,#footer div#block-packt_subscribe-1.block div.block-content-inner{margin:0px 5px;float:left;}#footer div#block-packt_subscribe-1.block div.block-inner h2.block-title,#footer div#block-packt_subscribe-1.block div.block-inner h2.block-title,#footer div#block-packt_subscribe-1.block div.block-inner h2.block-title{text-align:left;margin-left:5px;}#footer div#block-packt_subscribe-1.block div.block-content-inner div#edit-first-0-wrapper,#footer div#block-packt_subscribe-1.block div.block-content-inner div#edit-first-1-wrapper,#footer div#block-packt_subscribe-1.block div.block-content-inner div#edit-last-0-wrapper,#footer div#block-packt_subscribe-1.block div.block-content-inner div#edit-last-1-wrapper,#footer div#block-packt_subscribe-1.block div.block-content-inner div#edit-last-2-wrapper,#footer div#block-packt_subscribe-1.block div.block-content-inner div#edit-first-2-wrapper{clear:both;width:187px;margin:7px 0px;float:left;}#footer div#block-packt_subscribe-1.block div.block-content-inner div#edit-mail-1-wrapper,#footer div#block-packt_subscribe-1.block div.block-content-inner div#edit-mail-2-wrapper{clear:none;width:146px;margin:7px 0px;float:left;}#footer div#block-packt_subscribe-1.block div.block-content-inner div#edit-mail-1-wrapper input,#footer div#block-packt_subscribe-1.block div.block-content-inner div#edit-mail-2-wrapper input{width:146px;margin-left:2px;}#footer div#block-packt_subscribe-1.block div.block-content-inner input.form-text{width:187px;margin-left:2px;}#footer div#block-packt_subscribe-1.block div.block-content-inner input#edit-subscribe-1.form-submit,#footer div#block-packt_subscribe-1.block div.block-content-inner input#edit-subscribe-2.form-submit{float:left;margin:0px}div#block-packt_subscribe-0.block div.block-content-inner input#edit-subscribe.form-submit,div#block-packt_subscribe-0.block div.block-content-inner input#edit-subscribe-0.form-submit,div#block-packt_subscribe-0.block div.block-content-inner input#edit-subscribe-1.form-submit,div#block-packt_subscribe-0.block div.block-content-inner input#edit-subscribe-2.form-submit,div#block-packt_subscribe-1.block div.block-content-inner input#edit-subscribe.form-submit,div#block-packt_subscribe-1.block div.block-content-inner input#edit-subscribe-0.form-submit,div#block-packt_subscribe-1.block div.block-content-inner input#edit-subscribe-1.form-submit,div#block-packt_subscribe-1.block div.block-content-inner input#edit-subscribe-2.form-submit{width:31px;border:none; background-color:transparent;}#footer #block-menu-menu-footer-links.block li{width:132px;}a.newsletter-block-sub-link{color:#000;}body.page-newsletters div#main input.form-text{width:250px;}.subscribe-form-submit.form-submit{width:100%;}.view.view-taxonomy-book-list.view-id-taxonomy_book_list.view-display-id-page_1.view-dom-id-1 .views-field-view-node .form-submit{width:133px;background-image:url(/sites/all/themes/packt_new/images/sprite-horizontal-gradients.png);background-repeat:repeat-x;border-radius:5px;background-position:0 -904px;border-color:#000;font-size:1.3em;color:#F68C23;text-shadow:none;padding-top:3px;padding-bottom:3px;margin-bottom:1px;cursor:pointer;font-weight:bold;border:1px solid #888;padding-left:10px;padding-right:10px;display:block;position:relative;margin-left:38px;height:18px;padding-top:7px;text-decoration:none;}.view.view-taxonomy-book-list.view-id-taxonomy_book_list.view-display-id-page_1.view-dom-id-1 .views-field-view-node{width:133px;top:12px;left:50px;}.view.view-taxonomy-book-list.view-id-taxonomy_book_list.view-display-id-page_1.view-dom-id-1 .views-field-view-node a{text-decoration:none;}.view.view-taxonomy-book-list.view-id-taxonomy_book_list.view-display-id-page_1.view-dom-id-1 .view-content{padding-left:15px;word-wrap:break-word;}#block-block-147{border:none!important;background-color:white!important;}#block-block-147 .block-content{background-color:white!important;}#block-block-147 .content-inner{background-color:white!important;}html.streaming-video-page{overflow-x:hidden;}html.streaming-video-page #admin-menu{display:none;}html.streaming-video-page #page{min-width:auto !important;}html.streaming-video-page #main{width:100%;min-width:100% !important;max-width:100% !important;}html.streaming-video-page div#main-container{margin-right:15px;}html.streaming-video-page div#content-area{overflow:hidden;}html.streaming-video-page .sidebar-left div#content div#content-inner-inner,html.streaming-video-page .sidebar-left div#content div#content-inner{width:auto;}div.chapter-menu-left{margin-top:20px;overflow:hidden;float:left;}div.chapter-menu-item{color:#f7921e;float:left;width:225px;overflow:hidden;cursor:default;float:left;}div#chapter-scroll-overlay{float:left;margin-left:15px;}div.chapter-menu-left div.chapter-menu-item-inner{min-height:32px;width:225px;}div.chapter-menu-left div.chapter-menu-item h2{width:180px;margin-top:5px;float:left;}div.chapter-menu-left div.chapter-menu-item div.sections-list-container{width:205px !important;overflow:hidden;padding-bottom:10px;}div.chapter-menu-item a.chapter-link,div.chapter-menu-item a.section-link{cursor:pointer;}html.streaming-video-page div.collapse-menu{padding-right:10px;background:url('/sites/all/modules/packt_video/bg-dark.png') repeat;max-width:235px;position:relative;cursor:pointer;width:235px;}div.collapse-menu.hide{right:0px;}div.collapse-menu.show{right:0px;}html.streaming-video-page div#collapse-menu{float:left;height:156px;width:10px;background-color:red;margin-top:20px;}div.chapter-menu-left div.chapter-menu-item div.sections-list-container.show{height:auto;padding:10px;padding-top:0px;}div.chapter-menu-left div.chapter-menu-item div.sections-list-container.hide{height:0px;overflow:hidden;padding:0px 10px;}div.chapter-menu-left div.chapter-menu-item div.sections-list-container div.section-container.current a{position:relative;}div.chapter-menu-left div.chapter-menu-item.current div.sections-list-container div.section-container.current a:before{background-color:transparent;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px;padding:0px;border:7px solid transparent;border-right:0px solid transparent;border-left:10px solid #56AC56;width:0px;position:relative;right:4px;}div.chapter-menu-left div.chapter-menu-item div.sections-list-container div.section-container{margin-left:10px;padding:5px;}div.chapter-menu-left div.chapter-menu-item div.sections-list-container div.section-container a:before{position:relative;content:'';padding:5px;border-radius:10px;-webkit-border-radius:10px;-moz-border-radius:10px;background-color:#FA9400;float:left;top:3px;right:5px;}div.chapter-menu-left div.chapter-menu-item div.chapter-menu-item-top{padding:10px;cursor:pointer;}div.chapter-menu-left div.chapter-menu-item div.sections-list-container div p{margin:0px;}div.expand-chapter-btn{color:#999;width:20px;height:20px;line-height:20px;font-size:20px;font-weight:normal;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border:1px solid #aaa;float:right;text-align:center;cursor:pointer;}div.chapter-close-button{background-image:url('/sites/all/modules/packt_video/packt-video-sprite.png?s');background-position:-0px -127px;background-size:52px 290px;float:right;padding:10px;top:50%;position:absolute;color:white;right:2px;margin-top:-9px;}div.chapter-close-button.close{background-position:-0px -127px;}div.chapter-close-button.open{background-position:-27px -127px;}p.section-description{color:white;}html.streaming-video-page .drk-grey-gradient{background-color:#414141;background-image:-moz-linear-gradient(top,#414141,#0B0B0B);background-image:-ms-linear-gradient(top,#414141,#0B0B0B);background-image:-webkit-gradient(linear,0 0,0 100%,from(#414141),to(#0B0B0B));background-image:-webkit-linear-gradient(top,#414141,#0B0B0B);background-image:-o-linear-gradient(top,#414141,#0B0B0B);background-image:linear-gradient(top,#414141,#0B0B0B);background-repeat:repeat-x;border-color:#0B0B0B #0B0B0B #0B0B0B;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);text-shadow:0 -1px 0 rgba(0,0,0,0.25);}div.video-player-container{margin-top:20px;float:right;width:663px;max-width:885px;margin-left:20px;margin-right:15px;margin-bottom:15px;}div.video-overview-container{background:url('/sites/all/modules/packt_video/bg-dark.png') repeat;color:white;}div.video-overview-container h2{padding:20px 0px 0px 10px;}div.video-overview-container div.video-description-container{padding:10px 10px;}div.video-overview-container div.video-navigation-container{width:150px;padding:10px;float:right;margin:10px;background-color:#333;}div.video-overview-container div.video-navigation-container div{margin-bottom:10px;}div.video-overview-container div.video-description-container div.section-description.hide{display:none;}div.video-overview-container div.video-description-container div.section-description.show{display:block;}html.streaming-video-page div.blue-btn,html.streaming-video-page div.green-btn{height:20px;border-radius:5px;width:150px;float:right;text-align:center;padding-top:5px;-webkit-border-radius:5px;-moz-border-radius:5px;color:white;cursor:pointer;}html.streaming-video-page div.blue-btn.last{margin-bottom:0px;}html.streaming-video-page div.blue-btn a,html.streaming-video-page div.green-btn a{color:white;}html.streaming-video-page div.blue-btn a:hover,html.streaming-video-page div.green-btn a:hover{text-decoration:none;}html.streaming-video-page div.blue-btn:hover{background-position:0px -15px;background-color:rgb(0,85,204);transition:background-position 0.1s linear 0s;}html.streaming-video-page div.blue-btn{background-color:#0074cc;background-image:-moz-linear-gradient(top,#0088cc,#0055cc);background-image:-ms-linear-gradient(top,#0088cc,#0055cc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#0088cc),to(#0055cc));background-image:-webkit-linear-gradient(top,#0088cc,#0055cc);background-image:-o-linear-gradient(top,#0088cc,#0055cc);background-image:linear-gradient(top,#0088cc,#0055cc);background-repeat:repeat-x;border-color:#0055cc #0055cc #003580;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);text-shadow:0 -1px 0 rgba(0,0,0,0.25);}html.streaming-video-page div.green-btn:hover{background-position:0px -15px;background-color:rgb(81,163,81);transition:background-position 0.1s linear 0s;}html.streaming-video-page div.green-btn{background-color:#0074cc;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-ms-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(top,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #003580;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);text-shadow:0 -1px 0 rgba(0,0,0,0.25);}html.streaming-video-page div.ios-right-scroll{display:none;}div.mobile-section-menu-container{display:none;}div#section-scroll-overlay{display:none;}html.streaming-video-page div.gradient{display:block;}html.streaming-video-page div.right-placeholder,html.streaming-video-page div.left-placeholder{display:none;height:52px;}@media (max-width:600px){div.video-player-container{width:95%;max-width:none;}div.collapse-menu{padding-right:0px;max-width:none;background:none;cursor:default;margin-left:20px;max-width:none;margin-left:20px;padding-left:0px;}div.chapter-menu-left{margin-top:0px;float:none;max-width:none;}div.chapter-menu-left div.chapter-menu-item{padding-bottom:1px;}}@media (max-width:980px) {html.streaming-video-page div#breadcrumb{display:none;}html.streaming-video-page{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-transform:none!important;}html.streaming-video-page body{margin:0!important;padding:0!important;}html.streaming-video-page div#main-container{margin:0!important;width:100%;}html.streaming-video-page div#shadow-left,html.streaming-video-page div#shadow-bottom,html.streaming-video-page div#shadow-right{max-width:100%;width:100%;padding:0!important;}div.chapter-menu-left.collapse-menu.hide{margin-top:15px!important;margin-left:40px!important;margin-right:40px!important;}div.chapter-close-button{display:none;}div.chapter-menu-left.collapse-menu{max-width:100%;padding:0!important; background:none;min-width:auto;}div#chapter-scroll-overlay div.chapter-menu-item.playing div.expand-chapter-btn{content:'';width:0px;height:0px;border:13px solid transparent;border-left:20px solid #56AC56;overflow:hidden;border-right:0px solid transparent;display:block;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px;}html.streaming-video-page div.mobile-section-menu-overlay{height:73px;overflow-y:hidden;float:left;}html.streaming-video-page div.chapter-menu-scroll-container{height:58px;overflow-y:hidden;float:left;}html.streaming-video-page div.video-player-container{margin-right:20px;max-width:980px;}html.streaming-video-page div#shadow-bottom{display:none;}div.ios-right-scroll{display:block;position:fixed;right:0;margin-right:20px;line-height:53px;font-size:40px;}div#chapter-scroll-overlay{overflow-x:scroll;overflow-y:hidden;max-width:903px;}div.chapter-menu-left div.chapter-menu-item div.sections-list-container{overflow:auto;}div.video-navigation-container{margin-top:-30px!important;max-width:none;}div.sections-list.hide{display:none;}div.section-inner{display:none;float:left;background-color:#3E3E3E;margin-left:20px;line-height:52px;}div.section-inner-content{float:left;width:225px;}div#section-scroll-overlay{display:block;}html.streaming-video-page a.section-link{padding:10px 10px 0px 0px;float:left;}div#chapter-scroll-overlay div.chapter-menu-left.collapse-menu{margin-left:0px !important;margin-top:0px !important;max-width:none;float:left;height:48px;}div#chapter-scroll-overlay{ margin-left:44px;margin-right:46px;margin-top:15px;}div#chapter-scroll-overlay div.chapter-menu-item{border-top:1px solid #333;border-right:1px solid #ccc;padding-bottom:1px;border-bottom:1px solid black;background-image:none;background-color:#333333;cursor:pointer;}div#chapter-scroll-overlay div.chapter-menu-item div.chapter-menu-item-top{padding-top:5px;height:30px;}div#chapter-scroll-overlay div.left-placeholder,div#chapter-scroll-overlay div.right-placeholder{position:absolute;height:52px;}div#chapter-scroll-overlay div.left-placeholder{position:absolute;height:52px;}div#chapter-scroll-overlay div.sections-list-container{width:auto !important;overflow-x:scroll;overflow-y:hidden;display:none;}div#chapter-scroll-overlay div.sections-list-container div.section-container a:before{content:'';padding:0px;background:none;float:none;}div.mobile-section-menu-container{ margin-left:44px;margin-right:45px;display:block;overflow-x:scroll;overflow-y:hidden;max-width:903px;}div.mobile-section-menu .section-container{min-width:225px;float:left;background-color:;padding:0px 10px;margin:0px;height:71px;background-color:white;border-color:#0B0B0B #0B0B0B #0B0B0B;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);text-shadow:0 -1px 0 rgba(0,0,0,0.25);border-right:1px solid #ccc;border-top:1px solid #ccc;border-bottom:1px solid #ccc;cursor:pointer;}div.mobile-section-menu div.section-container p{max-width:200px;color:#B4B4B4;font-size:13px;text-shadow:none;}div#chapter-scroll-overlay div.gradient{height:52px;margin-top:15px;z-index:999;}div#chapter-scroll-overlay div.left-placeholder{height:43px;}html.streaming-video-page div.gradient{position:absolute;width:10px;height:80px;background-image:-webkit-gradient(linear,right top,left top,from(transparent),to(#000));background-image:-moz-linear-gradient(right,transparent,#000);background-image:-ms-linear-gradient(right,transparent,#000);background-image:-o-linear-gradient(right,transparent,#000);background-image:linear-gradient(right,transparent,#000);pointer-events:none;}html.streaming-video-page div.gradient.right{background-image:-webkit-gradient(linear,left top,right top,from(transparent),to(#000));background-image:-moz-linear-gradient(left,transparent,#000);background-image:-ms-linear-gradient(left,transparent,#000);background-image:-o-linear-gradient(left,transparent,#000);background-image:linear-gradient(left,transparent,#000);right:1px;left:0px;left:auto;}html.streaming-video-page div.left-placeholder{font-size:20px;cursor:pointer;position:absolute;left:20px;width:15px;z-index:9999;float:left;padding:5px;height:50px;display:block;background-image:url('/sites/all/modules/packt_video/packt-video-sprite.png?S');}html.streaming-video-page div.right-placeholder{position:absolute;right:20px;width:15px;font-size:20px;cursor:pointer;z-index:9999;padding:5px;float:right;height:50px;display:block;background-image:url('/sites/all/modules/packt_video/packt-video-sprite.png?s');}div#chapter-scroll-overlay div.right-placeholder{height:34px;background-position:-24px -1px;}div.mobile-section-menu-container div.right-placeholder{height:63px;background-position:-24px -50px;}div#chapter-scroll-overlay div.left-placeholder{height:34px;width:15px;background-position:0px -2px;}div.mobile-section-menu-container div.left-placeholder{height:63px;background-position:0px -50px;}div#chapter-scroll-overlay div.expand-chapter-btn{display:none;}div#chapter-scroll-overlay div.chapter-menu-item.current{background-image:url('/sites/all/modules/packt_video/packt-video-sprite.png?s');background-position:0px -163px;background-size:60px 310px;background-repeat:repeat-x;border-color:#0B0B0B #0B0B0B #0B0B0B;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-right:1px solid #CCC;border-bottom:1px solid #CCC;border-top:1px solid #CCC;text-shadow:none;}div#chapter-scroll-overlay div.chapter-menu-item h2{width:205px;font-weight:normal;font-size:1.4em;margin:0px;}div.mobile-section-menu-container div.section-container.current{background-repeat:repeat-x;background-image:url('/sites/all/modules/packt_video/packt-video-sprite.png?s');background-position:0px -220px;background-size:60px 310px;background-repeat:repeat-x;border-color:#0B0B0B #0B0B0B #0B0B0B;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-right:1px solid #CCC; border-top:1px solid #CCC;text-shadow:none;}div.mobile-section-menu-container div.section-container a.section-link{font-size:16px;color:#B4B4B4;text-shadow:none;}div.mobile-section-menu-container div.section-container.current a.section-link,div.mobile-section-menu-container div.section-container.current p.section-description{color:#656565;}}@media (max-width:801px) {html.streaming-video-page div#main{width:100%;}html.streaming-video-page div#page{width:100%;}div#shadow-left,div#shadow-bottom,div#shadow-right{width:100%;padding:0!important;}div.video-player-container{}div#chapter-scroll-overlay{ overflow-x:scroll;overflow-y:hidden;}div#chapter-scroll-overlay{}div#chapter-scroll-overlay div.expand-chapter-btn{display:none;}div#chapter-scroll-overlay div.chapter-menu-left{height:54px;}div#chapter-scroll-overlay div.chapter-menu-item{padding-bottom:1px;border-right:1px solid #ccc;}div.mobile-section-menu-container{}div.mobile-section-menu-container div.section-container{border-right:1px solid #ccc;}}@media (max-width:769px) {html.streaming-video-page div#main,html.streaming-video-page div#main-container{ width:100%;}html.streaming-video-page div#page{ min-width:100% !important;width:100%;}div#shadow-left,div#shadow-bottom,div#shadow-right{width:100%;padding:0!important;}div.video-player-container{}div#chapter-scroll-overlay div.chapter-menu-left.collapse-menu{margin-left:0px !important;max-width:none;}div#chapter-scroll-overlay{ overflow-x:scroll;overflow-y:hidden;}div#chapter-scroll-overlay div.chapter-menu-item{border-right:1px solid #ccc;padding-bottom:1px;}div#chapter-scroll-overlay div.expand-chapter-btn{display:none;}div.mobile-section-menu-container{}div.mobile-section-menu-container div.section-container{border-right:1px solid #ccc;}}@media (max-width:601px) {html.streaming-video-page div#main,html.streaming-video-page div#main-container{ width:100%;}html.streaming-video-page div#page{ width:100%;}div#shadow-left,div#shadow-bottom,div#shadow-right{width:100% !important;}div#chapter-scroll-overlay{ overflow-x:scroll;overflow-y:hidden;}div#chapter-scroll-overlay div.chapter-menu-left{height:54px;}div#chapter-scroll-overlay div.chapter-menu-item{border-right:1px solid #ccc;padding-bottom:1px;}div.mobile-section-menu-container{}div.mobile-section-menu-container div.section-container{border-right:1px solid #ccc;}div#chapter-scroll-overlay div.expand-chapter-btn{display:none;}div.video-player-container{}}@media (max-width:569px) {html.streaming-video-page div#main,html.streaming-video-page div#main-container{ width:100%;}html.streaming-video-page div#page{ width:100%;min-width:100% !important;margin:0px;}div#shadow-left,div#shadow-bottom,div#shadow-right{width:100% !important;}div.video-player-container{}div#chapter-scroll-overlay{ overflow-x:scroll;overflow-y:hidden;}div#chapter-scroll-overlay div.chapter-menu-left{height:54px;}div#chapter-scroll-overlay div.chapter-menu-item{padding-bottom:1px;border-right:1px solid #ccc;}div#chapter-scroll-overlay div.expand-chapter-btn{display:none;}div.mobile-section-menu-container{}div.mobile-section-menu-container div.section-container{border-right:1px solid #ccc;}}@media (max-width:481px) {html.streaming-video-page div#main,html.streaming-video-page div#main-container{}html.streaming-video-page div#page{ width:100%;min-width:100% !important;margin:0px;}div#shadow-left,div#shadow-bottom,div#shadow-right{width:100% !important;}div.video-player-container{}div#chapter-scroll-overlay div.chapter-menu-left{height:54px;}div#chapter-scroll-overlay{ overflow-x:scroll;overflow-y:hidden;}div#chapter-scroll-overlay div.expand-chapter-btn{display:none;}div#chapter-scroll-overlay div.chapter-menu-left div.chapter-menu-item{width:220px;}div.mobile-section-menu-container div.mobile-section-menu div.section-container{width:220px;}}@media (max-width:321px) {html.streaming-video-page div#page-background div.inner,div#breadcrumb{display:none;}html.streaming-video-page #page #shadow-right{width:100%!important;}html.streaming-video-page div#main,html.streaming-video-page div#main-container{width:100% !important;}div#page{width:100% !important; min-width:100% !important;margin:0px;}div#shadow-left,div#shadow-bottom,div#shadow-right{width:100% !important;}div#chapter-scroll-overlay{width:270px;overflow-x:scroll;overflow-y:hidden;margin-left:25px;margin-right:25px;}div.chapter-menu-left.collapse-menu{height:54px;border-bottom:1px solid black;}div.mobile-section-menu-container{width:270px;margin-left:25px;margin-right:25px;}div.mobile-section-menu-container div.section-container{border-right:1px solid #ccc;width:249px !important;}html.streaming-video-page div.video-player-container{margin:5px;margin-top:20px;margin-right:5px;width:100%;}div#chapter-scroll-overlay div.expand-chapter-btn{display:none;}div#chapter-scroll-overlay div.left-placeholder,div.mobile-section-menu-container div.left-placeholder{left:5px;}div#chapter-scroll-overlay div.right-placeholder,div.mobile-section-menu-container div.right-placeholder{right:5px;}div#chapter-scroll-overlay div.chapter-menu-item{width:269px !important;border-right:1px solid #ccc;padding-bottom:1px;}div.video-player-container div.video-overview-container{min-height:150px;}html.streaming-video-page div.blue-btn,html.streaming-video-page div.green-btn{width:100px;}div.video-overview-container div.video-navigation-container{width:100px;margin:5px;margin-right:0px;}}div.panel-pane div.admin-links{font-size:xx-small;margin-right:1em;}div.panel-pane div.admin-links li a{color:#ccc;}div.panel-pane div.admin-links li{padding-bottom:2px;background:white;z-index:201;}div.panel-pane div.admin-links:hover a,div.panel-pane div.admin-links-hover a{color:#000;}div.panel-pane div.admin-links a:before{content:"[";}div.panel-pane div.admin-links a:after{content:"]";}div.panel-pane div.panel-hide{display:none;}div.panel-pane div.panel-hide-hover,div.panel-pane:hover div.panel-hide{display:block;position:absolute;z-index:200;margin-top:-1.5em;}div.panel-pane div.node{margin:0;padding:0;}div.panel-pane div.feed a{float:right;}.panel-1col{}.panel-2col .panel-col-first .inside{margin:0;}.panel-1col .panel-col{}#panels-edit-display .panel-pane,#panels-edit-display .helperclass{margin:.5em;}.panel-2col .panel-separator{margin:0 0 1em 0;}.simplenews-subscription-filter .form-item{clear:both;line-height:1.75em;margin:0pt 1em 0pt 0pt;}.simplenews-subscription-filter .form-item label{float:left;width:12em;}.simplenews-subscription-filter .spacer{margin-left:12em;}.simplenews-subscription-filter .form-select,.simplenews-subscription-filter .form-text{width:14em;}.block-simplenews .issues-link,.block-simplenews .issues-list{margin-top:1em;}.block-simplenews .issues-list .newsletter-created{display:none;}#switchtheme-switch-form div{width:100%;}#switchtheme-switch-form div.form-item{float:left;width:auto;margin:0;padding:0;}#switchtheme-switch-form #edit-submit{float:left;width:auto;margin-left:3px;}.wrapper.tagadelic{text-align:justify;margin-right:1em;}.tagadelic.level1{font-size:1em;}.tagadelic.level2{font-size:1.2em;}.tagadelic.level3{font-size:1.4em;}.tagadelic.level4{font-size:1.6em;}.tagadelic.level5{font-size:1.8em;}.tagadelic.level6{font-size:2em;}.tagadelic.level7{font-size:2.2em;}.tagadelic.level8{font-size:2.4em;}.tagadelic.level9{font-size:2.6em;}.tagadelic.level10{font-size:2.8em;}.form-item .description{ white-space:normal;}.solid-border#quote{margin-top:1em;}.quote-button{cursor:pointer;font-weight:bold;color:rgb(0,0,125);}.quote-error{display:inline;}.quote-notes{margin-left:25px;}.node-form .product-shipping .form-text{display:inline;width:auto;}.cart-block-icon-full,.cart-block-icon-empty{display:inline;padding:4px 12px 0;height:7px;}.cart-block-icon-full{background:transparent url(/sites/all/themes/packt_new/css/ubercart/uc_cart/images/cart-full.png) no-repeat left center;}.cart-block-icon-empty{background:transparent url(/sites/all/themes/packt_new/css/ubercart/uc_cart/images/cart-empty.png) no-repeat left center;}.cart-block-title-bar{display:inline;}.cart-block-toggle{cursor:pointer;}.cart-block-arrow{display:inline;padding:4px 12px 0;height:7px;}.cart-block-title-bar .arrow-up{background:transparent url(/sites/all/themes/packt_new/css/ubercart/uc_cart/images/bullet-arrow-up.gif) no-repeat center center;}.cart-block-title-bar .arrow-down{background:transparent url(/sites/all/themes/packt_new/css/ubercart/uc_cart/images/bullet-arrow-down.gif) no-repeat center center;}.cart-help-text{font-size:x-small;position:relative;top:-5px;}#cart-block-contents ul.product-description{margin:0pt;padding:0pt 0pt 0.25em 1em;}#cart-block-contents .product-description li{margin:0pt;padding-top:0pt;padding-bottom:0pt;font-size:.8em;}.cart-block-items{border-bottom:2px;padding:2px;}.cart-block-items tbody{border-top:0px;}.cart-block-items tr{vertical-align:top;}.cart-block-items tr.odd,.cart-block-items tr.even{background-color:inherit;border:none;}.cart-block-item-title{width:100%;}.cart-block-item-price{text-align:right;white-space:nowrap;}.cart-block-summary{padding:2px;}.cart-block-summary tbody{border-top:0px;}.cart-block-summary tr{vertical-align:top;}.cart-block-summary-items{white-space:nowrap;}.cart-block-summary-total{width:auto;text-align:right;}.cart-block-summary-total label{font-weight:bold;}.cart-block-summary-total .uc-price{display:inline;}.cart-block-summary-links{text-align:right;}.cart-block-summary-links ul.links li{padding:0em 1em 0em .75em;border-right:solid 1px;}.cart-block-summary-links ul.links li.last{padding-right:0em;border-right:none;}.order-overview-form{float:left;padding:0em 2em 0em 0em;}.order-overview-form .form-item{margin-bottom:0em;}.order-overview-form #uc-order-select-form,.order-overview-form #uc-order-admin-sort-form{margin-bottom:0em;}.uc-orders-table{width:100%;clear:both;}.uc-orders-table thead th{white-space:nowrap;}.uc-orders-table tr.odd:hover,.uc-orders-table tr.even:hover{background-color:#ddd;}.uc-orders-table img{float:left;margin-right:.5em;}.order-admin-icons{margin-left:2px;}.order-admin-icons img{position:relative;top:3px;padding:0px 3px;}.order-pane{border-style:solid;border-color:#bbb;border-width:1px;padding:.5em;margin:.5em;;width:auto;line-height:1.1em;}.order-pane thead{background-color:#ddd;}.order-pane tbody{border-top:0px;}.order-pane-title{font-weight:bold;padding-bottom:.5em;}.pos-left{float:left;}.abs-left{ clear:left;}.pos-right{float:right;}.abs-right{float:right;clear:right;}.text-right{text-align:right;}.text-center{text-align:center;}.full-width{width:100%;}.order-pane-table{width:100%;}.order-pane-table thead th{font-weight:bold;background-color:#ddd;border-style:solid;border-width:0px 0px 2px 0px;border-color:#bbb;padding:4px 8px;}.order-pane-table tr{border-style:solid;border-width:0px 0px 1px 0px;border-color:#bbb;}.order-pane-table tr.odd td,.order-pane-table tr.even td{padding:4px 8px;}.order-edit-table{width:auto;}.order-edit-table .oet-label{text-align:right;font-weight:bold;}.order-edit-table .form-item{display:inline;}.address-select-box{background-color:#ddd;border:solid 1px #999;width:auto;padding-left:1em;padding-bottom:1em;margin-bottom:1em;}.customer-select-box{background-color:#ddd;border:solid 1px #999;width:auto;padding:1em;margin-top:1em;}.product-select-box{background-color:#ddd;border:solid 1px #999;width:auto;margin-right:auto;margin-bottom:1em;}.product-select-box2{background-color:#ddd;border:solid 1px #999;width:auto;margin-right:auto;margin-bottom:1em;padding-left:1em;padding-bottom:1em;}.line-item-table{width:100%;}.line-item-table td{padding:2px;}.line-item-table .li-title{width:100%;font-weight:bold;text-align:right;}.line-item-table .li-amount{text-align:right;}.line-item-table .form-item{display:inline;}.order-review-table tbody{border-top:0px;}.order-pane-table .product-description{font-size:.7em;}.order-pane #uc-credit-order-view-form{margin-top:.5em;margin-bottom:0em;}#uc-credit-order-view-form .form-submit{margin:0em;}.order-pane #uc-order-view-update-form{margin-bottom:1em;}.update-controls{padding-top:1em;}.update-controls div{padding-right:1em;}.update-controls div,.update-controls label{display:inline;}.update-controls .form-submit{margin:0em;}.address-select-box #uc-order-address-book-form{margin-bottom:0em;}.order-pane.abs-left .form-submit{margin:0.5em 0.5em 0.5em 0em;}.order-pane #customer-select form{margin-bottom:0em;}.product-image{float:right;clear:right;text-align:center;padding-top:4px;padding-left:4px;margin-left:4px;}.uc-price-display{float:right;clear:right;width:100px;text-align:center;font-size:1.3em;font-weight:bold;padding-bottom:4px;padding-left:4px;}.display-price{float:right;clear:right;width:100px;text-align:center;font-size:1.3em;font-weight:bold;padding-bottom:4px;padding-left:4px;}.model{display:inline;margin-right:2em;font-weight:bold;}h3.title{margin-top:1em;}.node-form .product-field table{width:auto;margin:0;}.node-form .product-field td{padding:3px 6px 0 0;}.node-form tbody{border:none;margin:0;}.node-form .product-field table .form-item{margin-top:0;margin-bottom:0;}.node-form .product-field .form-text{width:auto;display:inline;}#products-selector table{margin:0em 0em;}#products-selector td{padding:0em .5em;}div#products-selector form{margin-bottom:0em;}#products-selector div.form-item{margin-bottom:.5em;margin-top:.5em;}table.product-list{clear:both;}.add-feature div{padding-right:1em;}.add-feature div,.add-feature label{display:inline;}.uc-price{white-space:nowrap;}.uc-store-admin-table{margin:1em auto;border:1px dashed #bbb;}.uc-store-admin-table tbody{border-top:0px;}.uc-store-admin-panel{margin:1em;padding:1em;}.uc-store-admin-panel .uc-store-icon{float:left;margin-right:1em;}.uc-store-admin-panel .panel-title{padding-top:4px;font-size:1.5em;width:100%;}.uc-store-admin-panel .panel-show-link a{cursor:pointer;}.uc-customer-table .uc-store-icon{float:left;margin-right:.5em;}.uc-customer-table tr.odd:hover,.uc-customer-table tr.even:hover{background-color:#ddd;}.uc-cust-orders-table tr.odd:hover,.uc-cust-orders-table tr.even:hover{background-color:#ddd;}.uc-cust-orders-table .uc-store-icon{float:left;margin-right:.5em;}#store-footer{width:100%;text-align:center;font-size:x-small;clear:both;}table.section-items{width:auto;}table.section-items td{padding:.25em;}#uc-country-import-form .form-item,#uc-country-import-form .form-item label{display:inline;float:left;margin-right:1em;}#uc-country-import-form .form-submit{float:left;margin-top:1em;}#uc-country-import-form table{clear:left;}.summary-overview{padding:.5em;border:1px dashed #bbb;margin-bottom:-1px;}.summary-title{font-weight:bold;}.summaryOnclick-processed{cursor:pointer;}.summaryOnclick-processed:hover{background-color:#ddd;}.summary-edit-icon{float:left;margin-right:.5em;position:relative;top:2px;}.summaryOnclick-processed .item-list{margin-left:1.75em;}.summaryOnclick-processed .summary-link{display:none;}.ubercart-throbber{background-image:url(/sites/all/themes/packt_new/css/ubercart/uc_store/images/throbber.gif);background-repeat:no-repeat;background-position:100% -20px;}.jcarousel-container{position:relative;}.jcarousel-clip{z-index:2;padding:0;margin:0;overflow:hidden;position:relative;}.jcarousel-list{z-index:1;overflow:hidden;position:relative;top:0;left:0;margin:0;padding:0;}.jcarousel-list li,.jcarousel-item{float:left;list-style:none; width:75px;height:75px;}.jcarousel-next{z-index:3;display:none;}.jcarousel-prev{z-index:3;display:none;}.jcarousel-skin-packt .jcarousel-container{background:url(/sites/all/themes/packt_new/css/viewscarousel/skins/packt/grad.png);background-color:#9d9d9d;background-repeat:repeat-x;border:1px solid #346F97;margin-left:auto;margin-right:auto;}.jcarousel-skin-packt .jcarousel-container-horizontal{width:245px;padding:20px 40px;}.jcarousel-skin-packt .jcarousel-container-vertical{width:75px;height:245px;padding:40px 20px;}.jcarousel-skin-packt .jcarousel-clip-horizontal{width:245px;height:75px;}.jcarousel-skin-packt .jcarousel-clip-vertical{width:75px;height:245px;}.jcarousel-skin-packt .jcarousel-item{width:75px;height:75px;}.jcarousel-skin-packt .jcarousel-item-horizontal{margin-right:10px;}.jcarousel-skin-packt .jcarousel-item-vertical{margin-bottom:10px;}.jcarousel-skin-packt .jcarousel-item-placeholder{background:#fff;color:#000;}.jcarousel-skin-packt .jcarousel-next-horizontal{position:absolute;top:43px;right:5px;width:32px;height:32px;cursor:pointer;background:transparent url(/sites/all/themes/packt_new/css/viewscarousel/skins/packt/next-horizontal.png) no-repeat 0 0;}.jcarousel-skin-packt .jcarousel-prev-horizontal{position:absolute;top:43px;left:5px;width:32px;height:32px;cursor:pointer;background:transparent url(/sites/all/themes/packt_new/css/viewscarousel/skins/packt/prev-horizontal.png) no-repeat 0 0;}.jcarousel-skin-packt .jcarousel-next-vertical{position:absolute;bottom:5px;left:43px;width:32px;height:32px;cursor:pointer;background:transparent url(/sites/all/themes/packt_new/css/viewscarousel/skins/packt/next-vertical.png) no-repeat 0 0;}.jcarousel-skin-packt .jcarousel-next-vertical:hover{background-position:0 -32px;}.jcarousel-skin-packt .jcarousel-next-vertical:active{background-position:0 -64px;}.jcarousel-skin-packt .jcarousel-next-disabled-vertical,.jcarousel-skin-packt .jcarousel-next-disabled-vertical:hover,.jcarousel-skin-packt .jcarousel-next-disabled-vertical:active{cursor:default;background-position:0 -96px;}.jcarousel-skin-packt .jcarousel-prev-vertical{position:absolute;top:5px;left:43px;width:32px;height:32px;cursor:pointer;background:transparent url(/sites/all/themes/packt_new/css/viewscarousel/skins/packt/prev-vertical.png) no-repeat 0 0;}.jcarousel-skin-packt .jcarousel-prev-vertical:hover{background-position:0 -32px;}.jcarousel-skin-packt .jcarousel-prev-vertical:active{background-position:0 -64px;}.jcarousel-skin-packt .jcarousel-prev-disabled-vertical,.jcarousel-skin-packt .jcarousel-prev-disabled-vertical:hover,.jcarousel-skin-packt .jcarousel-prev-disabled-vertical:active{cursor:default;background-position:0 -96px;}.jcarousel_dp{display:block;width:auto;padding:10px;margin-left:auto;margin-right:auto;min-height:200px; -moz-border-radius:20px;-webkit-border-radius:20px;overflow:none;}#block-views-carousel-block_1 div.jcarousel_dp ul li{list-style-image:url(/sites/default/files/color/pixture_reloaded-3b4047af/liststyle.png);}div.jcarousel_dp_title,div.jcarousel_dp_title a,div.jcarousel_dp_title a:link,div.jcarousel_dp_title a:active,div.jcarousel_dp_title a:visited{font-size:16px;color:white;font-weight:bold;margin-bottom:4px;}.jcarousel_dp_author{font-size:14px;font-weight:bold;color:black;margin-bottom:5px;}.jcarousel_dp_features{padding-left:10px;}#block-views-carousel-block_1 .jcarousel-container-horizontal{width:auto;}#block-views-carousel-block_1 .jcarousel-clip-horizontal{width:auto;height:100px;}#block-views-carousel-block_1 .jcarousel-item{height:90px;}#block-views-carousel-block_1 .item-list ul li{list-style-type:none;list-style-image:none;}html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block;}body{line-height:1;}ol,ul{list-style:none;}blockquote,q{quotes:none;}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}table{border-collapse:collapse;border-spacing:0;}body,.comment,#logo,#branding,#site-name,#content-inner,li,.item-list ul,ul.images li h3,#sidebar-left .block,#sidebar-right .block,#forum td.forum,#forum .name a,#forum .description,div.admin-panel,div.item-list ul.pager li,ul.pager li,.tabs ul,div.page-links a.page-previous,div.page-links a.page-up,div.page-links a.page-next,ul.images li h3,#header-blocks .block,#content-top .block,#content-bottom .block,#footer .block .content{padding:0;margin:0;}#page{margin:0 auto;padding:0px;max-width:1600px;min-width:800px;}#skip-to-content{height:30px;margin:-30px 0 0 0 !important;font-size:0.8em;}#header{margin:0 0 0 0;padding:0;height:120px;}#logo{padding-left:20px;padding-top:10px;float:left;overflow:hidden;}#head-elements{}h1#site-name,div#site-name{margin:0;padding:10px 0 0 0;font-size:2.6em;line-height:1.3em;}#site-slogan{padding-top:35px;margin:0;}#site-slogan b{font-weight:bold;font-size:medium;margin-left:40px;}#site-slogan em{font-style:normal;margin-left:40px;}#header-blocks,#content-top,#content-bottom{clear:both; text-align:center;}#header-blocks .block-inner,#content-top .block-inner,#content-bottom .block-inner{margin:0 auto;}#header-blocks{border-bottom:1px solid #DDDDDD;}#main{position:relative;padding:0 15px 10px 15px;}#main-inner{}#content{float:left;width:100%; }.two-sidebars #content-inner{padding-left:235px; padding-right:235px;}.sidebar-left #content-inner{padding-left:235px; padding-right:0;}.sidebar-right #content-inner{padding-left:0;padding-right:235px;}.node-full-view.two-sidebars #content-inner{padding-left:245px; padding-right:245px;}.node-full-view.sidebar-left #content-inner{padding-left:245px; padding-right:0;}.node-full-view.sidebar-right #content-inner{padding-left:0;padding-right:245px;}#search-box{float:right;margin:15px 10px 0pt 0pt; padding:0;}div#search{margin:0 10px 0 0;}#primary,#superfish{padding:0;}#primary-inner{margin:6px 0 0 15px;}#superfish-inner{margin:0 0 0 15px;}#primary ul{margin:0;}#primary ul li.first{padding-left:0;}#sidebar-left{float:left;width:220px;margin-left:0;margin-right:-220px; padding:0;}#sidebar-right{float:right;width:220px;margin-left:-220px; margin-right:0;padding:0;}#footer{min-height:70px;clear:both;}#footer-message{padding:0 0 3px 0;}#closure-blocks{}#header,#content,#navbar,#sidebar-left,#sidebar-right,#footer,#closure-blocks{overflow:visible;}.clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden;}* html .clearfix{height:1%;}.clearfix{display:block;}.clear{clear:both;}div#header-breadcrumbs{width:auto;float:left;line-height:40px;margin-left:15px;}div#secure-payments{width:220px;height:40px;line-height:40px;float:left;vertical-align:middle;font-weight:bold;margin-left:15px;}div#secure-payments img{position:relative;margin-right:10px;top:5px;}div.clear{clear:both;}#primary-inner ul.links{display:table;width:100%;table-layout:fixed;}#primary-inner ul.links li{display:table-cell;}div.views-exposed-form input,div.views-exposed-form select{width:170px;}div.comment-folded{margin-bottom:10px;}div.comment-folded .subject{font-weight:bold;}div.packtcart-topright{float:right;width:240px;margin-right:20px;}div.packtcart-topright .details{text-align:right;}div.packtcart-topright .label.right{float:right;width:100px;}div.packtcart-topright .label.double{width:230px;}div.packtcart-topright .label{float:left;width:120px;}#page .block .block-content{padding:0;}div.packtcart-topright .logo{float:left;}div.packtcart-main-block{width:auto;margin-left:75px;}div.packtcart-trolley-block{width:75px;height:100px;float:left;position:absolute;}div.packtcart-main-block .login-block{width:50%;float:left;}div.packtcart-main-block .login-block label{width:200px;display:inline-block;margin-right:5px;text-align:right;vertical-align:top;}.block-inline{display:inline-block;}div.packtcart-main-block div.stage{font-size:18px;}div.stage .current{font-weight:bold;}div.packtcart-main-block .under-link{margin-left:205px;}div.packtcart-main-block input.submit{float:right;}div.packtcart-main-block div.packtcart-cart-bottom-block-right{float:right;width:200px;}div.packtcart-main-block div.packtcart-cart-bottom-block-right *{margin-bottom:5px;width:200px;}div.packtcart-main-block div.packtcart-cart-bottom-block-left{float:left;}div.packtcart-main-block div.code-input{float:left;margin-right:10px;}html{font-size:100%;}body{background-color:#fff;}#pixture-reloaded{font-family:"Helvetica neue",Helvetica,Arial,Verdana,sans-serif;font-size:12px;color:#000;}#page{line-height:1.5em;text-align:left;}#content{text-align:left;}h1,h2,h3,h4,h5,h6{margin-bottom:.3em;}h1{font-size:1.6em;margin:.3em 0 .6em;}h2{font-size:1.2em;}h3,h4,h5,h6{font-size:1.1em;}p{margin-top:.4em;margin-bottom:.8em;}em{text-decoration:none;font-weight:400;}.form-item label{font-size:1em;font-weight:400;color:#191919;}blockquote{margin:4px 10px;padding:10px 15px;background:#c5c7c8;}abbr,acronym{border-bottom:none;}#mission,.node .content,.comment .content{line-height:1.5em;}.breadcrumb{padding:0;padding-bottom:2px;}ul{margin:0;padding:0 0 0 1em;}li{line-height:1.6em;margin-left:25px;}div.view.view-latest-articles div.item-list ul li{line-height:1em;margin:0 0 10px;}li a,li a:visited,li a:active,li a:hover{font-weight:400;}.item-list ul{}.item-list ul li{font-size:12px;list-style:none;list-style-image:none;margin-left:1.5em;}.item-list .title{font-size:1em;}ul.links li{margin:0;padding:0 2px;}.comment .links{margin-bottom:0;}.submitted{font-size:.9em;color:#191919;}.links{color:#191919;}.links a{font-weight:400;}li.leaf{list-style-image:url(/sites/all/themes/packt_new/bullet-round.png);}li.collapsed{list-style-image:url(/sites/all/themes/packt_new/bullet-sm-arrow-right.png);}li.expanded{list-style-image:url(/sites/all/themes/packt_new/bullet-sm-arrow-down.png);}table,tr,td,thead,tbody{border-spacing:0;border-collapse:collapse;border-width:0;}table{ font-size:1em;width:100%;margin:0 0 10px;}tr.odd td,tr.even td{padding:.4em;}tr.odd{background:#e7e8e9;}tr.even{background:#fff;}div#header{}#site-name{font-family:Georgia,"Times New Roman",Times,serif;text-align:left;overflow:hidden;}#site-name a{color:#fff;}#site-name a:hover{text-decoration:none;}#site-slogan{color:#000;font-size:1em;line-height:1.5em;text-align:left;margin-left:30px;}#primary{color:#fff;font-size:12px;}#primary ul.links li a{font-weight:700;color:#fff;padding:8px 15px 4px;}#primary ul.links li a:hover{background:#010101;text-decoration:underline;color:orange;}#superfish-inner .menu{float:left;margin:0 0 0 -12px;}#superfish-inner .menu a{padding:6px 12px 7px;text-decoration:none;}#superfish-inner .menu a,#superfish-inner .menu a:visited{color:#fff;}#superfish-inner .menu li{background:#3e3e3e;}#superfish-inner .menu li li{background:#3e3e3e;}#superfish-inner .menu li li li{background:#3e3e3e;}#superfish-inner .menu li:hover,#superfish-inner .menu li.sfHover,#superfish-inner .menu a:focus,#superfish-inner .menu a:hover,#superfish-inner .menu a:active{outline:0;}div#search{text-align:right;font-size:11px;}#search .form-text{font-size:12px;width:10em;padding:2px;border:solid 1px #868686;}div#search label{display:none;}#footer{font-size:11px;text-align:center;color:#000;border:none;margin-top:10px;}#footer a{text-decoration:none;font-weight:400;color:blue;}#footer a:hover,#footer a.active:hover{text-decoration:underline;color:#2d71f4;}#footer .block{padding:15px 0 0;background:#fef3cc;border:medium orange solid;width:55em;margin:0 auto;text-align:center;padding-left:2em;}#block-block-16 .block-content{padding:0;}#block-block-16 table{margin:0;}.block .links{font-size:11px;}.block .block-content{padding:0 0 10px;}div.panel-flexible div.panel-flexible-sidebars div.panel-sidebar-middle{ padding-left:15px;}#sidebar-left,#sidebar-right,#panelsidebar-left{background:#fff;}#sidebar-left .block,#sidebar-right .block{margin-bottom:10px;width:220px;}#sidebar-left .block-content-inner,#sidebar-right .block-content-inner,#panelsidebar-left .block-content-inner{margin:0;padding-left:15px;padding-right:15px;padding-top:10px;padding-bottom:5px;}.block h2.block-title{color:#FFF;font-size:12px;font-weight:700;height:22px;margin:0;padding:5px 0 0;text-align:center;}.block h3{padding:0 3px;font-size:12px;}.block ul.links{margin:0;padding:0 0 5px;}.block ul.links li{font-size:10px;}.block .node h2.title{font-size:13px;}.block .node{padding:5px 5px 0;}.block .node,.block .node .node-inner-0,.block .node .node-inner-1,.block .node .node-inner-2,.block .node .node-inner-3.block .sticky,.block .sticky .node-inner-0,.block .sticky .node-inner-1,.block .sticky .node-inner-2,.block .sticky .node-inner-3{background:#fff;}.poll .bar{background-color:#e3e3e3;border:solid 1px #868686;}.poll .bar .foreground{}#content-area .node{margin:0 0 15px;padding:0;background:#fff;}.node-full-view .node h1.title,.node h2.title{margin:0;padding-top:0;padding-bottom:2px;border-bottom:solid 1px #999;}.node h2.title{font-size:18px;}.node h2.title a:hover{text-decoration:none;color:#1850d4;}.node .picture{border-style:none;float:right;margin:.5em;}.node-full-view .node .submitted{color:#191919;margin:0;padding:0;}.node-full-view .node .content{clear:both;margin:1em 0 .5em;}.node .taxonomy{font-size:.9em;}.node-full-view .node .taxonomy{margin:0;padding:0;}.node-full-view .node .taxonomy li{padding:1px 5px;}.node-full-view .node .actions ul li{margin:0;padding:0;}.node .actions{text-align:right;}#content-area .node-teaser{padding:0;border:medium;overflow:hidden;margin-left:0;margin-right:0;margin-top:0;margin-bottom:15px;}#content-area .node-teaser .node-inner-0{margin:0;overflow:hidden;padding-left:0;padding-right:0;padding-top:10px;padding-bottom:0;}#content-area .node-teaser .node-inner-1{margin:0;overflow:hidden;padding-left:10px;padding-right:0;padding-top:0;padding-bottom:0;}#content-area .node-teaser .node-inner-2{margin:0;overflow:hidden;padding-left:0;padding-right:10px;padding-top:0;padding-bottom:10px;}#content-area .node-teaser h2.title{margin:5px 10px;padding:0 0 3px;}#content-area .node-teaser .submitted{margin:0 10px;padding:0;}#content-area .node-teaser .taxonomy{margin:0 10px;padding:0;}#content-area .node-teaser .content{margin:10px;padding:0;}#content-area .node-teaser .actions ul li{margin:0 10px 0 0;padding:0;}#content-area .node-teaser.sticky{padding:0;margin-left:0;margin-right:0;margin-top:0;margin-bottom:15px;}#content-area .node-teaser.sticky .node-inner-0{padding:0;margin:0;}#content-area .node-teaser.sticky .node-inner-1{padding:0;margin:0;}#content-area .node-teaser.sticky .node-inner-2{padding:0;margin:0;}#content-area .node-teaser.sticky .node-inner-3{padding:0;margin:0;}#content-area .node-teaser.sticky h2.title{margin:0 20px 5px;padding:10px 0 3px;}#content-area .node-teaser.sticky .submitted{margin:0 20px;padding:0;}#content-area .node-teaser.sticky .taxonomy{margin:0 20px;padding:0;}#content-area .node-teaser.sticky .content{margin:10px 20px;padding:0;}#content-area .node-teaser.sticky .actions{margin:0 10px;padding:0 0 10px;}#content-area .node-teaser.sticky .actions ul li{margin:0 10px 10px 0;padding:0;}.comment{border-style:none;background:#fff;padding:0;margin:0 0 10px;}.comment h3.title{}.comment .title a{font-size:1.2em;font-weight:700;}.comment .new{padding-right:10px;text-align:right;font-weight:700;font-size:.8em;float:right;color:red;}.comment .picture{border-style:none;float:left;margin:15px 0 0 20px;}.comment .comment-id{font-size:16px;font-weight:700;padding-right:10px;}.comment .submitted{color:#191919;font-size:11px;}.comment div.links{text-align:right;}#comments .odd{padding:0;margin:0;}#comments .odd .comment-inner-0{padding:0;margin:0;}#comments .odd .comment-inner-1{padding:0;margin:0;}#comments .odd .comment-inner-2{padding:0;margin:0;}#comments .odd .comment-inner-3{padding:0;margin:0;}.comment h3.title{margin:0 20px 5px;padding:10px 0 3px;}.comment .submitted{margin:0 20px;padding:0;}.comment .content{margin:10px 20px;padding:0;}.comment .content.with-picture{margin-left:115px;}.comment div.links{margin:0;padding:0 0 10px;}.comment div.links ul li{margin:0 20px 10px;padding:0;}#aggregator .feed-source{background-color:#e3e3e3;border:1px solid #afafaf;padding:1em;margin:1em 0;}#aggregator .news-item .categories,#aggregator .source,#aggregator .age{font-style:italic;font-size:.9em;}#aggregator .title{margin-bottom:.5em;font-size:1em;}#aggregator h3{margin-top:1em;}#forum{margin:15px 0;}#forum td.container{background:#c8c8c8;}#forum thead,#forum tbody{border:0 solid #fff;}td.icon{text-align:center;}#forum td.forum{}#forum td.last-reply{background:none;}#forum .name a{}#forum .description{}.block-forum h3{margin-bottom:.5em;}div.forum-topic-navigation a.topic-next{text-align:right;}div.forum-topic-navigation a.topic-previous{text-align:left;}#profile .profile{clear:both;border:1px solid #868686;padding:.5em;margin:1em 0;}#profile .profile .name{padding-bottom:.5em;}.block-forum h3{margin-bottom:.5em;}div.admin,div.admin .left,div.admin .right{margin:0;padding:5px;overflow:hidden;}div.admin-panel{margin-bottom:15px;background:#e7e8e9;}div.admin-panel a{font-weight:400;overflow:hidden;}div.admin-panel .description{}div.admin-panel .body{font-size:11px;}div.admin-panel h3{padding:5px 8px;margin:0;background:#494c50;color:#fff;}div.help{margin:0;padding:2px 5px;margin-top:5px;}div.item-list ul.pager li{}ul.pager{padding:5px 0;margin:0;}ul.pager li a,ul.pager li a:visited,ul.pager li a:active,ul.pager li a:hover{font-weight:700;}div.item-list ul.pager li.pager-current{background:#494c50;padding:3px;}.pager a{background:#c9dcff;padding:3px;}.pager a:hover{background:#a0bceb;padding:3px;}.tabs{font-size:12px;}.tabs a{font-weight:400;line-height:2.2em;}.tabs ul{padding-left:5px; border-style:none;margin-bottom:10px;}.tabs ul li{}.tabs ul li a{margin:0;padding:4px 10px;background:#fff;border:solid 1px #e3e3e3;}.tabs ul li a:hover{text-decoration:underline;background:#cbc1dc;border:solid 1px #868686;}.tabs ul li a.active,.tabs ul li.active a{color:#fff;border:1px solid #494c50;}.tabs ul li a.active:hover{text-decoration:underline;border:1px solid #528fff;}ul.secondary{}ul.secondary li{font-size:11px;border:solid 0 #fff;}ul.secondary li a{margin:0 2px 0 0;padding:2px 4px;border:solid 1px #edeaf3;}.sidebar .block-archive .block-content{padding:10px 15px 10px 5px;}.sidebar .block-archive .block-content tr{background:none;border:solid 0 #fff;}.sidebar .block-archive .block-content table td{border:solid 1px #adadad; padding:1px 2px;text-align:center;}#mission{margin:0 0 15px;padding:5px;font-size:1.1em;line-height:1.25em;font-weight:700;border-bottom:solid 1px #CCC;}.box{padding:5px;padding:10px;}.box h2{padding-bottom:2px;border-bottom:solid 1px #AAA;}.book-navigation{padding-bottom:5px;}.book-navigation ul.menu{padding-top:5px;padding-bottom:5px;}div.page-links a.page-previous,div.page-links a.page-next{width:40%;}div.page-links a.page-up{width:19%;}ul.galleries li{border:solid 1px #AAA;background:#FFF;}ul.galleries li h3 a{font-size:1.1em;font-weight:700;}ul.images{border:solid 1px #AAA;overflow:hidden;}ul.images li{text-align:center;}ul.images li h3{}button,.pushbutton,.form-submit{margin-bottom:1px;cursor:pointer;color:#666;font-weight:400;font-size:12px;border:1px solid #888;padding-left:10px;padding-right:10px;padding-top:2px;padding-bottom:2px;background:#fff url(/sites/all/themes/packt_new/images/button.gif) repeat-x center;}button:hover,.pushbutton:hover,.form-submit:hover{color:#000;border:1px solid #666;}input[type=submit].disabled,input[type=submit].disabled:hover{color:#666;}#edit-autocomplete-node-finder-submit,#edit-autocomplete-node-finder-submit-1,#block-views--exp-test_search_view .form-submit{cursor:pointer;background:url(/sites/all/themes/packt_new/go-button.png) no-repeat;border-style:none;display:inline;font-weight:700;font-size:12px;padding-top:4px;padding-bottom:4px;padding-left:0;padding-right:0;}#block-views--exp-test_search_view .form-submit{margin-top:0;margin-bottom:0;width:25px;}#block-views--exp-test_search_view .views-exposed-form .views-exposed-widget{float:left;padding:.5em .3em 0 0;}#edit-autocomplete-node-finder-submit:hover{background:url(/sites/all/themes/packt_new/go-button.png) no-repeat;border-style:none;display:inline;}pre{ line-height:1.5em;font-family:"Lucida Console",Monaco,"Courier New",Courier,monospace;background-color:#FAFAFA;border:1px solid #D1D7DC;color:#060;font-family:Courier,'Courier New',sans-serif;}div.messages{font-weight:700;margin-bottom:1.5em;padding:10px 10px 10px 52px;}#help{font-size:.9em;margin-bottom:1em;}.status{color:#264409;background:#E6EFC2 url(/sites/all/themes/packt_new/images/large-info.png) no-repeat 12px center;border:1px solid #C6D880;padding-left:10px;padding-right:10px;padding-top:5px;padding-bottom:5px;}div.error{background:#FBE3E4 url(/sites/all/themes/packt_new/images/large-stop.png) no-repeat 12px center;}div.notice{color:#514721;background:#FFF6BF url(/sites/all/themes/packt_new/images/large-info.png) no-repeat 12px center;border-color:#FFD324;}.error a{color:#8a1f11;text-decoration:underline;}.notice a{color:#514721;text-decoration:underline;}.success a{color:#264409;text-decoration:underline;}span.admin-disabled{color:#DC1000;}div.messages.success{color:#55872E;background:#F1F8EB;border:1px solid #7AC142;}div.messages ul{margin-bottom:0;}div.node h4.unpublished,div.comment h4.unpublished{text-align:left;color:#FCC;background:#FFF4F4 url(/sites/all/themes/packt_new/large-alert.png) no-repeat 12px center;margin:5px 0;border:medium none;font-style:normal;font-variant:normal;font-weight:700;font-size:2em;font-family:Arial,Helvetica,sans-serif;padding-left:52px;padding-right:0;padding-top:5px;padding-bottom:5px;}.center_price_block{padding-left:200px;padding-top:0;}.availability_bookpage{color:red;font-weight:700;}ul.quicktabs_tabs li{margin-left:0;}.scale-bookview-cover{border-width:0;width:73px;height:auto;}.custom-title{font-size:1.1em;font-weight:700;display:inline;}.custom-subtitle{font-size:1.1em;color:gray;display:inline;}#edit-autocomplete-node-finder-keywords-1-wrapper,#edit-autocomplete-node-finder-keywords-0-wrapper,#edit-autocomplete-node-finder-keywords-2-wrapper{width:30px;display:inline;}#block-autocomplete_node_finder-1 .block-content-inner,#block-autocomplete_node_finder-0 .block-content-inner{margin:0;}#custom-cumulus-id h2.title{background:#000 none no-repeat;color:#fff;font-size:12px;font-weight:700;height:22px;margin:0;padding:5px 0 0;text-align:center;}#block-tagadelic-5 .block-content-inner,#block-views-highest_rated_articlesblock_1 .block-content-inner{background-color:#dcdcdc;background-repeat:no-repeat;margin:0;padding-left:10px;padding-top:5px;padding-bottom:5px;}#block-tagadelic-4 .block-content-inner{background-color:#dcdcdc;background-repeat:no-repeat;margin:0;padding-left:10px;padding-top:5px;padding-bottom:5px;height:130px;}#block-views--exp-test_search_view .block-content-inner{margin:0;}#block-views--exp-test_search_view .views-exposed-widgets{margin:0;}.views-exposed-widget{display:inline-block;margin-left:5px;}#block-views-books_by_author-block_1 h2.block-title{color:#000;font-size:1.3em;font-weight:700;margin-bottom:5px;text-align:left;text-decoration:none;}.tor{padding:10px;}.clear{font-size:1px;height:1px;}#helpblock h2{padding-top:0;margin:0;}#helpblock p{padding-top:0;margin:0;padding-bottom:0;}#block-views-latest_articlesblock_1 h2{padding:0;margin:0;}#block-views--exp-browse_books_search_view .block-content-inner{padding-left:0;}#main{margin-top:10px;}#usergroup .views-exposed-widget .form-submit{margin-top:1.3em;}#field-title a:hover,a.active:hover{color:blue;}.gmap-map{border:thin #000 solid;}#usergroup div.panel-row-1 div.panel-col-1{float:left;}#usergroup div.panel-row-1 div.panel-col-2{float:right;overflow:hidden;}hr{color:#000;height:1px;background-color:#000;border:0 none;}#edit-field-replaced-by-nid{width:420px;}#views-exposed-form-support-page-block-1 .form-submit{cursor:pointer;background:url(/sites/all/themes/packt_new/go-button.png) no-repeat;border-style:none;display:inline;font-weight:700;font-size:12px;padding-top:4px;padding-bottom:4px;padding-left:0;padding-right:0;margin-top:1.3em;width:25px;}#block-views--exp-test_search_view .form-select{width:100px;}#block-views--exp-test_search_view .form-text{width:210px;}table.cart-block-summary tr td{padding:3px 6px 1px 0;}fieldset#delivery-pane{float:left;width:400px;}fieldset#billing-pane{float:left;width:400px;}fieldset#customer-pane{float:left;}fieldset#cart-pane{clear:both;}input#edit-panes-billing-copy-address{margin:5px 2px;}td.products,th.products{width:400px;}td.qty,th.qty{text-align:center;}td.price,th.price{text-align:center;}#block-block-21{width:70px;}.cart h1{color:gray;}.cart span.selected{color:#000;}form#uc-cart-checkout-form fieldset{border:0;}form#uc-cart-checkout-form fieldset legend{display:none;}form#uc-cart-checkout-form .description{font-weight:700;margin-bottom:10px;}tr.even,tr.odd{background-color:#fff;border:0;}tr.subtotal.even,tr.subtotal.odd{background-color:#fffc9e;}td.subtotal{background-color:#fffc9e;}td.subtotal.final{text-align:left;}div#cart-form-products table{margin:0;}div.right{float:right;}div.checkout-login{width:375px;margin-right:30px;float:left;}div.checkout-login label{display:inline-block;width:180px;text-align:right;}div.checkout-login div.description{margin-top:5px;margin-left:180px;}div.checkout-login .go_button{float:right;}div.checkout-login .dd{display:inline-block;float:left;width:190px;font-size:10px;}div#edit-panes-customer-new-account-newsletter-wrapper{float:left;width:180px;vertical-align:top;margin-top:0;}div#edit-panes-customer-new-account-newsletter-wrapper *{float:right;}div#edit-panes-customer-new-account-newsletter-wrapper input{margin-left:10px;margin-right:10px;}#book-ad table{margin:0;}#book-ad .content .node-teaser .node .node-full-view{padding:0;margin:0;}div.ebook_box{border:1px solid #d0d0d0;background-image:url(/sites/default/files/ebook_box_grad.png);background-repeat:repeat-x;margin-bottom:20px;}div.ebook_box_inner{margin:10px 20px;}div.ebook_box_inner table{width:100%;}div.ebook_box hr{border-top:1px solid #d0d0d0;border-bottom:0;}ul{padding:0;}li{margin-left:15px;}li.no-bullet{list-style-type:none;}div#edit-qty-1-wrapper{float:left;margin:5px 5px 5px 0;}#edit-submit-cart{margin-top:10px;}div.view-taxonomy-book-list .views-field-field-image-fid{float:left;margin-right:7px;height:100%;width:50px;overflow:none;}div.view-taxonomy-book-list .views-field-title{font-size:15px;}div.view-taxonomy-book-list .views-field-buyitnowbutton{float:right;position:relative;top:-50px;}div.view-taxonomy-book-list td{height:75px;padding:0 20px 20px;overflow:none;width:50%;}div.view-recent-books-carousel-small div.jcarousel_dp{display:none;}div.view-recent-books-carousel-small div.jcarousel-container,div.view-recent-books-carousel-small div.jcarousel-clip{width:auto;}.jcarousel-skin-packt .jcarousel-item{list-style:none;height:90px;}.jcarousel-skin-packt .jcarousel-clip-horizontal{height:90px;}.jcarousel-skin-packt .item-list ul li{height:90px;}div.user-profile-packt-block{float:left;padding-bottom:10px;}div.user-profile-packt-block h2{margin-top:0;}h3.noline{border:0;}span.larger{font-size:14px;}div#exclusive_offers_block .inner{padding:10px;}div#exclusive_offers_block .inner table{}div#exclusive_offers_block .left-float,div#exclusive_offers_block .right-float{margin-bottom:10px;}div#exclusive_offers_block .block{}div.user-profile-packt-block .block-inner h3{text-align:center;color:#FFF;background-color:#555;height:25px;line-height:25px;margin-bottom:0;}div#clear{clear:both;float:none;}img.imagecache-cart{width:50px;overflow:hidden;}td.ex_large{font-size:16px;}td.exx_large{font-size:16px;}div#downloads_block{width:49%;margin-right:1%;}div#orders_block{width:55%;}div#downloads_block .inner,div#orders_block .inner{margin:10px;margin-top:0;}div.full_order_block{width:100%;margin:10px 0 0;padding-bottom:10px;}div.menubar{background-color:#777;color:#fff;height:24px;}div.menubar label{color:#fff;}div.menubar a{color:#fff;}div.full_order_block h2{text-align:left;padding-left:15px;color:#FFF;background-color:#555;height:25px;line-height:25px;margin-bottom:0;-moz-border-radius-topleft:15px;-moz-border-radius-topright:15px;-webkit-border-radius-topleft:15px;-webkit-border-radius-topright:15px;}div.menubar ul{display:block;}div.menubar ul li{display:inline-block;list-style-type:none;line-height:2em;}div.full_order_block div.menubar ul li.right{float:right;line-height:1em;}div.full_order_block div.menubar ul li.right *{display:inline-block;margin-top:1px;margin-bottom:0;}div.full_order_block div.menubar ul li.right option{display:block;}div.full_order_block div.inner{margin:10px;}div.packt_reg_left{float:left;width:46%;margin-right:3%;}div.packt_reg_right{float:left;width:46%;margin-left:3%;}form#packt-login-form label,form#packt-registration-form label{display:inline-block;width:200px;}form#packt-login-form input,form#packt-registration-form input{width:200px;}form#packt-login-form input.form-image,form#packt-registration-form input.form-image{display:block;margin-top:10px;}form#packt-login-form input.form-submit,form#packt-registration-form input.form-submit{display:block;clear:both;}form#packt-registration-form .form-checkboxes{display:inline-block;width:200px;}form#packt-registration-form .nl_wrapper label{vertical-align:top;}div.form-checkboxes{margin-top:0;vertical-align:top;}span.small{font-size:.8em;}form#views-exposed-form-support-page-block-1 select{width:100%;}div.centre{text-align:center;}table.order-review-table{width:800px;}#node-1613,#node-1615,#node-1616,#node-1617,#node-1618,#node-1620,#node-1621{text-align:left;}#node-1613 .form-item label,#node-1616 .form-item label,#node-1617 .form-item label,#node-1618 .form-item label,#node-1620 .form-item label,#node-1621 .form-item label{display:inline;}#block-webformblock-1613 h2.block-title,#block-webformblock-1615 h2.block-title,#block-webformblock-1616 h2.block-title,#block-webformblock-1617 h2.block-title,#block-webformblock-1618 h2.block-title{color:#000;font-size:1.4em;font-weight:700;height:22px;margin:0;padding:5px 0 0;text-align:left;}table.autowidth{width:auto;}table.autowidth td{padding-right:15px;}.dialogue-label{font-weight:700;float:left;}.dialogue-label-small{display:block;font-weight:400;font-size:smaller;margin-top:-5px;}.dialogue-select{float:right;}.dialogue-addtocart{text-align:center;margin-top:10px;}.buynow-dialogue-bottom{background:transparent url(/sites/default/files/dialoguebottom.gif) no-repeat 170px;margin-top:-1px;width:100%;height:43px;}.buynow-dialogue-outer{display:none;position:absolute;top:-195px;left:-159px;z-index:101;}.buynow-dialogue{z-index:100;padding:10px 25px;width:195px;background:#fff;border:1px solid #333;-moz-border-radius:40px;}.dialogue-text{font-weight:700;margin-top:5px;margin-bottom:10px;}.margin15{margin-bottom:5px;}#branding{width:670px;}#book_taxonomy_view .panel-col.panel-col-1.panel-col-first{background:transparent url(/sites/all/themes/packt_new/images/browse-book-background.png) no-repeat;margin-top:12px;}#book_taxonomy_view .panel-col.panel-col-1.panel-col-first h2{color:#fff;font-size:12px;font-weight:700;margin-left:60px;margin-top:6px;}#book_taxonomy_view .jcarousel-container-horizontal{-moz-border-radius-topleft:0;-moz-border-radius-topright:0;border-top:0;}#book_taxonomy_view .jcarousel-container-horizontal{-moz-border-radius-topleft:0;-moz-border-radius-topright:0;border-top:0;}#book_taxonomy_view .panel-col-2 h2.title{background-color:#494D51;-moz-border-radius-topleft:15px;-moz-border-radius-topright:15px;margin-bottom:0;font-size:12px;color:#FFF;padding-left:14px;padding-top:6px;}.panel-col.panel-col-1.panel-col-first input{#width:20px;}#book_taxonomy_view .panel-col.panel-col-1.panel-col-first #edit-tid{padding:2px;width:145px;}.panel-col.panel-col-1.panel-col-first #edit-submit-taxonomy-book-list{border:none;position:relative;margin:0;top:-57px;left:50px;background:transparent url(/sites/all/themes/packt_new/go-button.png) no-repeat scroll 0 0;font-size:12px;font-weight:700;height:25px;width:25px;}.panel-col.panel-col-1.panel-col-first #edit-title{margin-top:5px;width:140px;}.receipt-orderaddress{float:right;margin-left:20px;margin-right:10px;}.receipt-ordernum{float:left;margin-left:20px;}.receipt-action{padding:3px 0;background-color:#494d51;color:#fff;width:100%;margin-top:15px;margin-bottom:15px;}.receipt-action a{color:#fff;}.order-pane{border:none;line-height:1.1em;margin:0;padding:0;width:100%;}.receipt-outer{-moz-border-radius:5px;background-color:#eee;padding:0 0 20px;}.receipt-outer-ebook{width:75%;}.receipt-top{-moz-border-radius:5px;background-color:#2C2C2C;padding:5px 5px 5px 20px;font-weight:700;color:#fff;margin-bottom:20px;}.receipt-indent-20{margin:0 20px;}.receipt-indent-50{margin:20px 50px 0;}.order-pane thead,.order-pane-table tr,.order-pane-table thead th,tr.even,tr.odd{border:none;background-color:transparent;}.order-pane #sidebar-right,.order-pane #block-block-25{width:265px;}.order-pane .block h3{padding:0;margin-top:5px;}.order-pane #sidebar-right .block-content-inner{padding-bottom:20px;}div.feedback{position:relative;background-color:#fff;border:1px solid #BCBCBD;margin-top:10px;}div.feedback-inner{padding:5px 0 0 10px;height:27px;font-size:15px;color:#FFF;}div.feedback-form{margin:10px;}form#webform-client-form-1724 .form-item{margin:0;margin-bottom:5px;}form#webform-client-form-1724 div.form-radios{margin:0;}form#webform-client-form-1724 div.form-radios .form-item{display:inline-block;}div.view-support-page .views-field-title,div.view-support-page .views-field-field-author-nid,div.view-support-page .views-field-field-date-of-publication-value{position:relative;top:-95px;left:120px;width:400px;font-size:1.2em;}div.view-support-page h3{font-size:1.2em;}#code-download-form #edit-submit{float:left;}#code-download-form #edit-email-wrapper{width:auto;margin-top:0;margin-right:10px;}#code-download-form fieldset.captcha div.description{margin-bottom:10px;}div.view-support-page .views-field-title{font-size:1.4em;}div.view-support-page .views-label-phpcode{display:none;}div.errata{display:none;}div.checkbox-label{float:left;font-weight:700;}div.checkbox-body,div.checkbox-body .description,div.checkbox-body input{float:left;}div.checkbox-body .form-item{margin-top:0;}div.checkbox-body .form-checkbox .description{float:left;}div.books_pager .item-list{}#limit_label{margin-left:20px;}#sort_label{margin-left:20px;}div.books_pager ul li a{background:none;}div.jcarousel_dp{height:200px;overflow:hidden;}#primary table.links{width:100%;color:#fff;font-weight:700;}#primary table.links tr td{text-align:center;}#primary table.links tr td a{color:#fff;font-weight:700;}#primary table.links .active{background:none;}#sidebar-right .block-uc_cart .block-content-inner{clear:both;padding:0;}#sidebar-right .block-uc_cart div.links{width:100%;background-position:0 -561px;height:24px;}#sidebar-right .block-uc_cart div.links a{color:#fff;margin:5px;line-height:21px;font-size:13px;}#sidebar-right .block-uc_cart .block-content-inner{background-color:#f7ce8a;background-image:none;color:#fff;}table.cart-block-summary tr td{padding:0;}table.cart-block-summary{margin:0;}div.item-preview-uc_cart{float:left;padding-left:7px;margin-top:10px;}div.no-items{margin:0 5px;padding:5px 0;color:#000;}div.messages{margin:0 12px;}.qty-label-select{position:absolute;float:right;right:0;top:0;padding-right:118px;}header-breacrumbs{width:auto;float:left;}#secure-payments{width:220px;height:40px;line-height:40px;float:left;}#field-title a:visited,a.active:visited{font-size:1em;}body{margin-top:0px;}div.block-inner h2{font-weight:normal;font-size:15px;}div.lite-top-box div.block{margin-bottom:10px;}div.lite-top-box div.block div.block-inner .bol2 h2{margin:0px;}div.lite-top-box div.block div.block-inner .bol2{font-size:15px;font-weight:normal;margin:0px;padding:5px 0px 1px 17px;}#page div.lite-top-box div.block div.block-content{xbackground:white url(/sites/all/themes/packt_new/images/pp/content-gradient.jpg) repeat-x scroll 0px 0px;background-position:0 -1182px;background-repeat:repeat-x;border:1px solid #BCBCBD;border-top:0px;color:black;padding:12px;width:576px;}.discount-offer table,.discount-offer p{margin:0px;height:0px;}.discount-offer #table3{margin-left:10px;margin-right:20px;}.discount-offer #table3 td b span{font-size:40px !important;}.discount-offer #table2 #table4 td p{margin:4px 0px;height:auto;font-size:17px;font-weight:normal;color:#E5781F;}.discount-offer #table2 #table4 td span{font-size:17px !important;background-color:transparent !important;font-weight:normal !important;margin:0 0 0 10px;padding:5px 0px 9px 0px;}.discount-offer #table2 #table4 td{background-color:white !important;background-repeat:repeat-x;color:white;}#content-bottom h2.block-title{height:26px;}#content-bottom h2.block-title{font-weight:normal;}.front #content-header{display:none;}#edit-panes-billing-billing-country{width:170px;}#edit-panes-delivery-delivery-country{width:170px;}#content-area{background-position:0 -1182px;}#content-bottom{background-position:0 -1182px;background-repeat:repeat-x;color:black;border-left:1px solid #BCBCBD;border-right:1px solid #BCBCBD;border-bottom:1px solid #BCBCBD;margin-top:10px;}#content-bottom .block-content{padding:12px;}.front #content-area,.section-open-source-awards-hometesttest #content-area{border:none;background:transparent;}body.section-open-source-awards-hometesttest div.panel-pane h2.title{margin:0px;padding-left:10px;border:1px solid #BCBCBD;border-bottom:none;}.section-open-source-awards-hometesttest #main .panel-pane .content{border-color:#BCBCBD #BCBCBD;border-right:1px solid #BCBCBD;border-style:none solid solid;border-width:medium 1px 1px;border-color:#BCBCBD;}.section-open-source-awards-hometesttest #main .panel-pane .content h2.title{display:none;}.section-open-source-awards-hometesttest #main .panel-pane .content .content{border:none;padding:5px 10px 10px 10px;}#content-area .node{background-color:transparent;}#content-area .node-teaser,#content-area .node-teaser .node-inner-0,#content-area .node-teaser .node-inner-1,#content-area .node-teaser .node-inner-2,#content-area .node-teaser .node-inner-3{background-image:none;background:transparent;margin:0px;padding:0px;}#content-area .node-teaser h2.title{margin-top:0px;}#content-area .node-teaser h2.title,#content-area .node-teaser .taxonomy,#content-area .node-teaser .content{margin-left:0px;}.section-open-source-awards-hometesttest .panel-pane h2.title{font-size:15px;font-weight:normal;margin:0 0 0 10px;padding:5px 0px 9px 0px;}.section-open-source-awards-hometesttest #main h1.title{display:none;}#content-area hr span{width:472px;border:none;border-top:1px solid #898989;margin-top:14px;margin-bottom:14px;background:white;}#content-area-inner{padding:10px 0 20px 0;}.content-block h2{color:#FA9704;margin:0 0 10px;}.content-block{width:712px;border:1px solid #777777;background:white;color:#202020;margin-left:113px;clear:both;float:none;}.content-block .inner{padding:18px 25px;}div#primary-links img{padding:0px 0px 0px 25px;vertical-align:middle;}div#primary-links{text-align:center;vertical-align:middle;line-height:50px;height:60px;}div#primary-links a{color:#FFFFFF;font-size:15px;font-weight:normal;z-index:2;}div#new-logo{float:left;margin-left:20px;margin-top:-4px;}div#new-logo img{margin-left:20px;}div#primary-links #logo-image{margin:-4px 10px 0;vertical-align:top;}#footer .block{margin:0 auto;text-align:center;width:992px;border:none;padding:0px;background:transparent;}#head-elements{}#primary{background:transparent url('/sites/all/themes/packt_new/images/pp/top-sub-nav-gradient.jpg') no-repeat 0 0;position:absolute;z-index:4;margin-top:0px;margin-left:465px;color:#202020;float:none;font-size:12px;width:560px;height:40px;}#primary-inner{margin-left:2px;margin-top:5px;font-size:2px;}#primary a,#primary a:hover{color:white;font-size:13px;font-weight:normal;}#primary img{margin:0px 8px;vertical-align:text-bottom;}div#header{height:auto;}#login-box .forgot-password{}#login-box .register{clear:both;margin-left:138px;margin-top:-18px;position:relative;}#login-box #login-button{position:absolute;margin-left:209px;margin-top:-5px;clear:both;}div#login-box .inner{padding:10px;}div#login-box label{display:block;float:left;margin-left:8px;width:71px}div#block-block-39 div.block-content-inner{padding-left:8px;padding-right:8px;}div#block-block-39 div.book{float:left;}div#login-box input.form-text{}div#breadrumb a,div#breadcrumb a:visited,div#breadcrumb:active{}.two-sidebars #content-inner{padding-left:0px;}input.form-text{}div#content-area form input.form-text{color:black;}div#secure-box{border:none;width:320px;height:76px;margin-left:645px;text-align:center;color:#828282;margin-top:12px;position:absolute;}div#secure-box .secure-text{font-size:12px;margin-top:-8px;}div#secure-box .secure-text a{font-size:12px;margin-top:-8px;font-weight:normal;color:inherit;}div#payment-box{border:1px solid #F9CA8A;width:208px;height:158px;float:left;margin-left:11px;margin-top:25px;color:#fff;text-align:left;font-size:12px;}div#payment-box .inner{padding-left:10px;padding-top:6px;}div#payment-box img{margin-top:10px;margin-left:30px;}div#footer-links{border:1px solid #F9CA8A;width:526px;height:232px;float:left;margin-left:11px;margin-top:25px;color:#fff;font-size:12px;text-align:left;}div#footer-links .inner{padding-left:10px;padding-top:6px;}div#footer-links a,div#footer-links a:hover{color:#fff;}div#footer-links ul{list-style:none;padding:0px;margin-top:5px;}div#footer-links ul li{list-style:none;padding:0px;margin:0px;}div#footer-links .inner #looking-for,div#footer-links .inner #orders,div#footer-links .inner #contact,div#footer-links .inner #media{float:left;margin-left:15px;font-size:11px;}div#footer-links .inner #looking-for{width:135px;margin-left:0px;}div#footer-links .inner #orders{width:140px;}div#footer-links .inner #contact{width:65px;}div#footer-links .inner #media{width:75px;}div#updates-box{border:1px solid #F9CA8A;width:208px;height:158px;float:left;margin-left:11px;margin-top:25px;text-align:left;color:#fff;font-size:12px;position:relative;}div#updates-box .inner{padding-left:10px;padding-right:10px;padding-top:6px;}div#footer-quick-links{color:#fff;font-size:12px;margin-top:10px;}div#footer-quick-links a,div#footer-quick-links a:hover{color:#fff;}div#disclaimer{color:#fff;font-size:12px;margin-top:7px;}div#copyright{color:#fff;font-size:12px;margin-top:7px;}#sidebar-left,#sidebar-right,#panelsidebar-left{display:none;}#sidebar-left{padding-right:13px;}#sidebar-right{padding-left:13px;}.sidebar-left #content-inner{padding-left:0px;}.sidebar-left div#content-inner-inner{width:780px;}.sidebar-left #content-inner{width:775px;}.no-sidebars #content-inner-inner,.page-cart-checkout #content-inner-inner,.page-cart-checkout-worldpay-direct #content-inner-inner{width:965px;margin-left:0px;}.page-node #content-inner-inner{margin-left:0px;}#sidebar-left,#sidebar-right,#panelsidebar-left{display:block;}#sidebar-left a,#sidebar-right a{color:black;font-size:11px;font-weight:normal;}#sidebar-left p,#sidebar-right p{margin-bottom:5px;}#sidebar-left{margin-left:0px;width:167px;}#sidebar-right{width:167px;margin-right:-3px;}#sidebar-left h2.block-title,#sidebar-right h2.block-title,#panelsidebar-left h2.block-title{}#sidebar-left .block-content-inner,#sidebar-right .block-content-inner,#panelsidebar-left .block-content-inner{background:transparent;}#sidebar-left h2.block-title,#sidebar-right h2.block-title,#panelsidebar-left h2.block-title{}.section-login div#content-inner-inner{background:white; margin-left:0px;}#block-views-accordion-block_1 li,#block-views-accordion-block_3 li{list-style:none;margin:0px;clear:both;font-size:11px;line-height:14px;}#block-views-accordion-block_1 .views-field-field-image-fid,#block-views-accordion-block_3 .views-field-field-image-fid{float:left;height:40px;margin-bottom:10px;width:42px;margin-top:3px;}#block-views-accordion-block_1 .views-field-title,#block-views-accordion-block_3 .views-field-title{width:90px;margin-bottom:10px;overflow:hidden;}#block-views-test_view-block_1 .views-field-field-image-fid{float:left;width:42px;height:40px;margin-bottom:10px;margin-top:3px;}#block-views-test_view-block_1 .views-field-title{width:90px;overflow:hidden;margin-bottom:10px;line-height:14px;}#block-views-accordion-block_1 a,#block-views-accordion-block_3 a{clear:both;}span.category-count{color:#F68C23;font-size:11px;}#block-views-latest_news-block_1 li{margin:0px 0px 20px 0px;line-height:13px;list-style-image:none;list-style-type:none;}#block-views-latest_news-block_1 .more-link{text-align:left;}#block-block-15 .block-content-inner{padding-left:0px;}#block-block-15 img{margin-left:17px;}#block-block-34 .block-content-inner{text-align:center;}#block-nice_menus-1 .block-content-inner{padding:0px;}#block-nice_menus-1 .nice-menu li{border:0px;background-image:none;background-color:transparent;margin-left:10px;}#block-nice_menus-1 ul{border:0px;background-image:none;background-color:transparent;}#block-nice_menus-1 ul ul{margin-top:1px;margin-left:-50px;background-color:#e5e5e5;}.node-full-view.two-sidebars #content-inner{padding-left:185px;padding-right:185px;}.node-full-view.sidebar-left #content-inner{padding-left:0px;}.node-full-view .node-type-books .content,.node .node-type-books .content{margin:0px;}.tabs{margin-top:0px;}.tabs li{margin:0px;}.menu-sep{display:block;height:12px;margin-top:3px;background-color:black;width:1px;margin-left:5px;margin-right:5px;}#primary-links .menu-sep{margin:8px 21px 0 22px;height:32px;line-height:40px;display:block;float:left;background:#3E3D3B;width:1px;overflow:hidden;}.secure-image-text-orange a{position:absolute;color:#F68C23;left:120px;margin-top:26px;clear:both;}.secure-image-text a,.secure-image-text a:hover{font-weight:normal;color:rgb(130,130,130);position:absolute;left:112px;width:140px;line-height:16px;margin-top:11px;clear:both;}#updates-box{line-height:16px;}#updates-box strong{display:block;margin-bottom:3px;}#updates-box input.form-text{margin-top:8px;width:118px;}#updates-box .subscription-center{margin-top:4px;}#updates-box .subscription-center a,#updates-box .subscription-center a:hover{color:#000;}#updates-box div.submit-button{position:absolute;margin-left:135px;margin-top:-25px;}#page #shadow-top{width:999px;padding-top:6px;background:transparent;}#page #shadow-left{background-position:0 0;background-repeat:repeat-y;padding-left:4px;width:100%;}#page #shadow-right{background-position:-22px 0;background-repeat:repeat;padding-right:4px;width:992px;}#page #shadow-bottom{background-position:-10px 0;background-repeat:no-repeat;overflow:hidden;width:999px;margin-bottom:6px;height:9px;}div#header{background-image:none;background:transparent;}#footer-message{padding:0;display:none;}#page #page-background{background:#fff;}.book-accordion-wrapper{display:none;}#sidebar-left #block-block-9,#sidebar-right #block-block-9,#sidebar-left #block-block-47,#sidebar-right #block-block-47,#panelsidebar-left #block-block-9,#sidebar-left #block-block-42,#sidebar-right #block-block-42,#sidebar-left #block-block-8,#sidebar-right #block-block-8,#panelsidebar-left #block-block-8,#sidebar-left #block-block-3,#sidebar-right #block-block-3,#panelsidebar-left #block-block-3{border-color:#BCBCBD;border-style:solid;border-width:1px;}#sidebar-left #block-block-3 .block-content-inner,#sidebar-right #block-block-3 .block-content-inner,#sidebar-left #block-block-8 .block-content-inner,#sidebar-right #block-block-8 .block-content-inner,#sidebar-left #block-block-42 .block-content-inner,#sidebar-right #block-block-42 .block-content-inner,#sidebar-left #block-block-47 .block-content-inner,#sidebar-right #block-block-47 .block-content-inner,#sidebar-left #block-block-9 .block-content-inner,#sidebar-right #block-block-9 .block-content-inner{padding-top:0px;}#sidebar-left #block-block-8 .block-inner,#sidebar-right #block-block-8 .block-inner,#sidebar-left #block-block-42 .block-inner,#sidebar-right #block-block-42 .block-inner,#panelsidebar-left #block-block-8 .block-inner,#sidebar-left #block-block-3 .block-inner,#sidebar-right #block-block-3 .block-inner,#panelsidebar-left #block-block-3 .block-inner{background-position:0 -1182px;}#sidebar-left #block-block-9 .block-inner img,#sidebar-right #block-block-9 .block-inner img,#sidebar-left #block-block-42 .block-inner img,#sidebar-right #block-block-42 .block-inner img{margin-left:-5px;}#sidebar-left #block-block-9 .block-inner,#sidebar-right #block-block-9 .block-inner,#panelsidebar-left #block-block-9 .block-inner{background:white url(/sites/all/themes/packt_new/images/pp/did-you-know-background-open-source.png) no-repeat scroll 0 0;}#sidebar-left #block-block-47 .block-inner,#sidebar-right #block-block-47 .block-inner,#panelsidebar-left #block-block-47 .block-inner{background:white url(/sites/all/themes/packt_new/images/pp/epub-background.jpg) no-repeat scroll 0 0;}#sidebar-left #block-block-42 .block-inner,#sidebar-right #block-block-42 .block-inner{background:white url(/sites/all/themes/packt_new/images/pp/did-you-know-background-enterprise.png) no-repeat scroll 0 0;}#sidebar-left #block-block-3 h2.block-title,#sidebar-right #block-block-3 h2.block-title,#panelsidebar-left #block-block-3 h2.block-title,#sidebar-left #block-block-9 h2.block-title,#sidebar-right #block-block-9 h2.block-title,#sidebar-left #block-block-42 h2.block-title,#sidebar-right #block-block-42 h2.block-title,#sidebar-left #block-block-47 h2.block-title,#sidebar-right #block-block-47 h2.block-title,#panelsidebar-left #block-block-9 h2.block-title,#sidebar-left #block-block-8 h2.block-title,#sidebar-right #block-block-8 h2.block-title,#panelsidebar-left #block-block-8 h2.block-title{font-size:15px;text-align:center;width:165px;padding-left:0px;background:transparent;color:#E57112;}#page #sidebar-left #block-block-3.block-content,#page #sidebar-right #block-block-3 .block-content,#page #sidebar-left #block-block-9.block-content,#page #sidebar-right #block-block-9 .block-content,#page #sidebar-left #block-block-42.block-content,#page #sidebar-right #block-block-42 .block-content,#page #sidebar-left #block-block-47.block-content,#page #sidebar-right #block-block-47 .block-content,#page #sidebar-left #block-block-8.block-content,#page #sidebar-right #block-block-8 .block-content{background:transparent;}.login-form form#packt-login-form input,form#packt-registration-form .register-form input,form#packt-registration-form .register-form select{width:288px;}form#packt-registration-form .register-form select{border:1px solid #A8A8A8;color:#838383;}.login-form .forgot-password{margin-left:235px;}.login-form .submit-button{position:absolute;margin-left:350px;margin-top:-60px;}.register-form .submit-button{position:absolute;margin-left:350px;margin-top:-150px;}.login-form,.register-form{position:relative;}form#packt-login-form label,form#packt-registration-form label{display:inline-block;margin-right:20px;text-align:right;width:121px;}.login-form form#packt-login-form .submit-button input,form#packt-registration-form .register-form .submit-button input{width:84px;padding:5px 0;}form#packt-registration-form .register-form .option input{width:auto;float:left;margin-bottom:70px;margin-right:10px;}form#packt-registration-form .register-form label.option{text-align:left;width:410px;}form#packt-login-form input.form-submit,form#packt-registration-form input.form-submit{margin-left:120px;background-image:none;background-color:#0285d5;box-shadow:inset 0px 7px 10px rgba(255,255,255,0.3);color:white;}#main #footer{margin-left:-12px;margin-top:12px;}#main{padding:0px;}.page-login #content{margin-bottom:20px;}.checkout-register-form{position:absolute;top:110px;left:343px;}.page-cart #block-block-21{display:none;}.page-cart div#content,.page-cart div#content-inner,.page-cart div#content-inner-inner{margin-left:0px;}.page-cart #cart-form-products,.page-cart thead th,.page-cart .odd,.page-cart .even{border:none;}.page-cart thead.tableHeader-processed th{border:1px solid #898989;border-right:none;height:24px;}.page-cart thead.tableHeader-processed th.qty,.page-cart thead.tableHeader-processed th.avail,.page-cart thead.tableHeader-processed th.price,.page-cart thead.tableHeader-processed th.remove,.page-cart thead.tableHeader-processed th.products{border-left:none;}.page-cart thead.tableHeader-processed th.tprice{border:1px solid #898989;border-left:none;}.cart-block-items{clear:both;}.cart h1{color:white;}#payment2-pane{clear:both;}.page-index #content-area,body.section-open-source-awards-hometesttest #content-area{padding:0px;}body.node-type-books #content-area{padding-bottom:0px;}#block-views-carousel-block_1 .jcarousel-item,.section-open-source-awards-hometesttest #main .panel-pane .content .jcarousel-item{height:auto;}#block-views-carousel-block_1 .jcarousel-clip-horizontal,.section-open-source-awards-hometesttest #main .panel-pane .content .jcarousel-clip-horizontal{height:172px;margin:0 10px;}#block-views-carousel-block_1 .item-list ul li,.section-open-source-awards-hometesttest #main .panel-pane .content .item-list ul li{width:95px;}div.homepage-block h2.title,.page-index .bol2,body.section-open-source-awards-hometesttest .bol2{font-size:15px;text-align:left;font-weight:normal;color:white;height:20px;padding:7px 0 0 8px;}.page-index .bol2,body.section-open-source-awards-hometesttest .bol2{height:25px;}.page-index .bor2,.page-index .tol2,.page-index .tor2,.page-index .bor2,body.section-open-source-awards-hometesttest .bor2,body.section-open-source-awards-hometesttest .tol2,body.section-open-source-awards-hometesttest .tor2,body.section-open-source-awards-hometesttest .bor2{background:transparent;padding:0px;}.page-index h2.collapsiblock,body.section-open-source-awards-hometesttest h2.collapsiblock{font-size:15px;font-weight:normal;background:transparent;}div.homepage-block,#block-views-latest_articlesblock_1 .block-inner,#block-views-latest_news_homepageblock_1 .block-inner{border:1px solid #BCBCBD;border-top:none;}div.homepage-block{background-position:0 -1182px;background-repeat:repeat-x;}div.homepage-block div.content{padding:15px;}div.homepage-block div.content div.content{padding:0px;}#block-views-latest_articlesblock_1 .block-inner .block-content-inner,#block-views-latest_news_homepageblock_1 .block-inner .block-content-inner{background-position:0 -1182px;background-repeat:repeat-x;}.block .node,.block .node .node-inner-0,.block .node .node-inner-1,.block .node .node-inner-2,.block .node .node-inner-3 .block .sticky,.block .sticky .node-inner-0,.block .sticky .node-inner-1,.block .sticky .node-inner-2,.block .sticky .node-inner-3{background:transparent;}.page-index #helpblock .tor,.page-index #helpblock .bor,.page-index #helpblock .tol,.page-index #helpblock .bol,body.section-open-source-awards-hometesttest #helpblock .tor,body.section-open-source-awards-hometesttest #helpblock .bor,body.section-open-source-awards-hometesttest #helpblock .tol,body.section-open-source-awards-hometesttest #helpblock .bol{background:transparent;}.page-index #helpblock{border:1px solid #BCBCBD;}.page-index #helpblock h2{color:#FF7C00;font-size:15px;font-weight:normal;margin-bottom:5px;}div#block-views-carousel-block_1{margin-top:0px;}div.jcarousel_dp{display:none;}#views-exposed-form-taxonomy-book-list-default{border:1px solid #BCBCBD;height:140px;background:white;margin-bottom:15px;}#views-exposed-form-taxonomy-book-list-default input{width:auto;}#views-exposed-form-taxonomy-book-list-default .form-checkboxes{clear:both;}#views-exposed-form-taxonomy-book-list-default .form-checkboxes .form-item{}.page-books h2.title{display:none;}img.imagecache-cart{width:auto;}div.view-forthcoming-books .views-field-field-image-fid,div.view-forthcoming-books .views-field-field-image-fid-1,div.view-latest-books-view .views-field-field-image-fid,div.view-latest-books-view .views-field-field-image-fid-1,div.view-taxonomy-subscription-list .views-field-field-image-cache-fid,div.view-taxonomy-book-list .views-field-field-image-fid{float:left;margin-right:7px;width:80px;height:90px;}div.view-forthcoming-books .views-field-buyitnowbutton,div.view-latest-books-view .views-field-buyitnowbutton,div.view-taxonomy-book-list .views-field-buyitnowbutton{float:none;position:relative;top:0px;margin-left:208px;margin-top:-23px;}div.view-forthcoming-books table.views-view-grid .views-field-view-node a,div.view-forthcoming-books table.views-view-grid .views-field-view-node a:hover,div.view-latest-books-view table.views-view-grid .views-field-view-node a,div.view-latest-books-view table.views-view-grid .views-field-view-node a:hover,div.view-taxonomy-book-list table.views-view-grid .views-field-view-node a,div.view-taxonomy-book-list table.views-view-grid .views-field-view-node a:hover{font-weight:normal;color:#FF7C00;}.rounded_corner{position:absolute;margin-top:40px;width:600px;margin-left:-12px;}.rounded_corner .t-edge,.rounded_corner .t-edge .l,.rounded_corner .t-edge .r,.rounded_corner .b-edge,.rounded_corner .b-edge .l,.rounded_corner .b-edge .r{background:transparent;}#book_taxonomy_view .wrap-corner .l,#book_taxonomy_view .wrap-corner .r,#book_taxonomy_view .wrap-corner .t-edge,#book_taxonomy_view .wrap-corner .l-edge,#book_taxonomy_view .wrap-corner .r-edge,#book_taxonomy_view .wrap-corner .b-edge{background:transparent;}#book_taxonomy_view .wrap-corner .t-edge,#book_taxonomy_view .wrap-corner .b-edge{display:none;height:0px;}#book_taxonomy_view .wrap-corner{border:1px solid #BCBCBD;}#book_taxonomy_view .wrap-corner h2{color:#FF7C00;font-size:15px;font-weight:normal;margin-bottom:5px;margin-top:10px;}#book_taxonomy_view .wrap-corner .r-edge{padding:1px 14px;}#block-views-latest_articles-block_2 .item-list ul li{font-size:12px;list-style-image:none;list-style-position:outside;list-style-type:none;margin:0px 0px 10px 0px;}#block-views-latest_articles-block_2 .view-latest-articles{padding:0px;}#block-views-test_view-block_1 .item-list ul li{font-size:12px;clear:both;list-style-image:none;list-style-position:outside;list-style-type:none;margin:0 0 15px;}#block-views-test_view-block_1 .view-latest-articles{padding:0px;}.clear{clear:both;}#block-block-13 .block-content-inner{padding:15px 10px 15px 5px;}.bold-orange{font-weight:bold;color:#F68C23;}.social-link{font-size:11px;line-height:11px;padding-bottom:6px;padding-left:8px;}.social-icon{}#book-view .book-zoom{height:151px;margin-top:20px;}#book-view .book-shadow-right{width:6px;height:140px;background:url('/sites/default/files/pp/book-view-shadow-right.gif');float:left;}#book-view .book-shadow-bottom{width:119px;height:7px;background:url('/sites/default/files/pp/book-view-shadow-bottom.gif');clear:both;}#book-view .book-image-container{height:147px;margin:8px 0 4px 0;width:auto;}.book-image-container .book-image img{margin-left:16px;}#book-view .label{float:left;width:150px;}#book-view .data{float:left;}#book-view .cover-price .label,#book-view .our-price .label{font-weight:bold;}#book-view .our-price .label{color:#ff0000;}#book-view .cover-price,#book-view .our-price{clear:both;}#book-view #edit-qty-wrapper{display:none;}#book-view{position:relative;}#book-view .book-cart .shipping-banner{margin-left:0px;margin-right:8px;position:relative;width:256px;margin:0px;width:auto;float:left;}#book-view .book-cart .add-to-cart{margin-left:260px;margin-top:-55px;position:relative;width:132px;overflow:hidden;margin:0px;width:auto;position:relative;margin-right:-5px;margin-top:-12px;float:right;}.book-cart .inner{padding:10px;margin:0px;}.clear{clear:both;}#book-view .book-content-details ul{margin-top:20px;}#book-view li{list-style-image:url('/sites/default/files/pp/bullet-point-grey.png');margin-bottom:8px;margin-top:8px;margin-left:40px;}.grey-button{background:url('/sites/default/files/pp/small-button.png');width:105px;height:29px;text-align:center;margin-bottom:7px;}#book-view h3{margin-top:0px;clear:both;}#book-view .grey-button a,#book-view .grey-button a:hover,#book-view .grey-button a:visited{color:#FFFFFF;font-size:11px;font-weight:normal;text-decoration:none;}.book-content-details{float:left;width:430px;}.book-content-buttons{float:left;width:100px;position:absolute;margin-left:465px;margin-top:20px;}.similar-books-arrow{margin:5px 0px 5px 180px;}h3.book-section{font-size:17px;font-weight:normal;color:#E5781F;}.detailed-information{width:360px;float:left;}#block-views-0cdcbfcdb875a5fc9a65cce33f6a0e2b h2{width:910px;font-size:15px;text-align:left;background:gray url(/sites/all/themes/packt_new/images/pp/content-header-gradient.jpg) repeat-x scroll 0 0;height:26px;color:#fff;font-weight:normal;padding:6px 12px 0px 12px;margin-bottom:0px;}div#content #block-views-0cdcbfcdb875a5fc9a65cce33f6a0e2b div.jcarousel-container-horizontal{width:840px;}#related_items_block h2,#exclusive_offers_block h2,#downloads_block h2,#orders_block h2{background:gray url(/sites/all/themes/packt_new/images/pp/content-header-gradient.jpg) repeat-x scroll 0 0;height:26px;color:#fff;font-weight:normal;-moz-border-radius:0px;padding:6px 0px 0px 10px;margin-bottom:0px;}#related_items_block .block-content,#exclusive_offers_block .block-content,#downloads_block .block-content,#orders_block .block-content{background-position:0 -1182px;background-repeat:repeat-x;border:1px solid #BCBCBD;border-top:none;-moz-border-radius:0px;}div.packtblock_system{border-top:none;padding:0px 10px;}div.packtblock_system.border_bg{border:1px solid #BCBCBD;padding:0px;}div#orders_block,div#downloads_block{background:transparent;margin-top:0px;}div.user-profile-packt-block,div#downloads_block h2,div#orders_block h2{-moz-border-radius:0px;}div#related_items_block,div#exclusive_offers_block,div#related_offers_block{margin-left:auto;margin-right:auto;width:97%;float:none;clear:both;}.book-links{border-color:#C0C0C0;border-style:none solid solid;border-width:medium 1px 1px;width:165px;float:right;margin-left:0px;margin-right:1px;}.book-links h2{background:gray url(/sites/all/themes/packt_new/images/pp/content-header-gradient.jpg) repeat-x scroll 0 0;height:27px;color:#fff;font-weight:normal;}.book-links div.block-inner h2{font-size:15px;font-weight:normal;margin:0 0 0;padding-left:5px;padding-top:4px;}.book-links .block-content{background-position:0 -1182px;background-repeat:repeat-x;}.book-links .block-content-inner{padding:15px 10px 15px 5px;}.discount-offer,.ebook-version{width:573px;overflow:hidden;margin-left:0px;border:1px solid #A8A8A8;margin-bottom:15px;margin-top:15px;}.ebook-logo,.ebook-pricing,.ebook-buy{float:left;}.ebook-buy{float:right;}.ebook-logo{width:125px;margin:10px 0 0 9px;}.ebook-pricing{width:240px;margin-left:19px;margin-top:12px;}.ebook-buy{width:145px;}.ebook-version .book-image-container{float:left;margin-left:15px;position:relative;}.ebook-inner{float:left;width:290px;font-size:11px;margin-left:12px;margin-right:10px;}#book-view .ebook-inner li{color:#FC891D;list-style-image:none;list-style-position:outside;list-style-type:disc;margin:0;margin-left:18px;}#book-view .ebook-inner li span{color:black;}#book-view .ebook-inner ul{margin-bottom:10px;}.ebook-user-guide{float:right;width:100px;margin-top:35px;}.pdf-overlay{margin-top:-80px;margin-left:60px;position:relative;}.book-in-detail{margin-top:15px;}#page .book-links a,#page .book-links a:hover,#page .book-links a:active,#page .book-links a:visited{margin-left:5px;font-size:11px;color:#000;font-weight:normal;}.book-links .content-inner{padding:15px 5px 15px 12px;}.book-links img{vertical-align:text-top;}.book-content-details{margin-top:15px;}#login-box #login-password-wrapper label{}#login-password-wrapper{clear:both;}#login-box #login-password{margin-top:3px;}#login-box{}#login-box .login-button{margin-left:215px;top:8px;clear:both;position:absolute;}.book-links-1{}.book-links-2{}#block-menu-menu-footer-links .block-inner h2.block-title,#footer #block-block-28 h2.block-title,#footer #block-block-29 h2.block-title{text-align:left;padding:5px 0px 0px 8px;}#block-menu-menu-footer-links ul.menu li{float:left;width:137px;list-style:none;}#block-menu-menu-footer-links ul.menu #dhtml_menu-6865:hover,#block-menu-menu-footer-links ul.menu #dhtml_menu-6866:hover,#block-menu-menu-footer-links ul.menu #dhtml_menu-6878:hover,#block-menu-menu-footer-links ul.menu #dhtml_menu-6884:hover{cursor:default;text-decoration:none;}#footer{background-position:0 -216px;border:medium none;height:110px;margin:0 auto;padding:0;text-align:center;width:992px;position:relative;clear:both;overflow:hidden;}#block-menu-menu-footer-links ul ul{height:200px;}#footer .block a,#footer .block a:hover{color:#fff;}#block-menu-menu-footer-links ul li.last{margin-left:-53px;width:100px;}#block-menu-menu-footer-links ul li ul li.last{margin-left:0.5em;width:137px;}#block-menu-menu-footer-links ul li a{font-weight:bold;margin-bottom:10px;}#block-menu-menu-footer-links ul li ul li a{font-weight:normal;margin-bottom:0px;}#block-menu-menu-footer-links ul.menu a{color:white;}#block-menu-menu-footer-links .block h2.block-title{float:left;}#footer #block-menu-menu-footer-links{border:1px solid #F9CA8A;width:525px;height:235px;position:absolute;margin-left:232px;margin-top:25px;height:235px;}#footer #block-block-28{border:1px solid #F9CA8A;color:#FFFFFF;position:absolute;font-size:12px;height:158px;margin-left:11px;margin-top:25px;text-align:center;width:208px;clear:both;}#footer #block-block-28 .block-inner{text-align:center;}#footer #block-block-29{border:1px solid #F9CA8A;color:#FFFFFF;font-size:12px;height:158px;margin-left:769px;margin-top:25px;position:absolute;text-align:left;width:208px;clear:both;}#packt-subscribe-form-footer #subscribe-name,#packt-subscribe-form-footer #subscribe-email,#packt-subscribe-form #subscribe-name,#packt-subscribe-form #subscribe-email{width:118px;margin-left:15px;}#packt-subscribe-form-footer #subscribe-name-wrapper,#packt-subscribe-form #subscribe-name-wrapper{margin-top:9px;}#packt-subscribe-form-footer #subscribe-email-wrapper,#packt-subscribe-form #subscribe-name-wrapper{margin-top:-2px;}#subscribe-name-wrapper input,#subscribe-email-wrapper input{width:318px;}.subscription-centre-text{margin-left:15px;line-height:15px;}.subscribe-button{position:relative;width:40px;margin-top:-40px;margin-left:140px;}#packt-subscribe-form-1 div.subscribe-button,#packt-subscribe-form-2 div.subscribe-button{margin-left:330px;}#footer .block .subscription-centre-link a,#footer .block .subscription-centre-link a:hover{color:#000;}.subscription-centre-link{margin-top:-8px;margin-left:15px;}#footer #block-block-30,#footer #block-block-57{clear:both;color:#FFFFFF; padding-top:9px;position:absolute;height:50px;}#footer #block-block-30 h2.block-title,#footer #block-block-57 h2.block-title{display:none;}#block-devel_node_access-0{clear:both;}.page-account #details_block,.page-account #subscriptions_block,.page-account .profile,.tabs .profile{margin-top:0px;padding-top:5px;}.page-account #subscriptions_block{width:350px;}.page-account h3.noline{margin-top:0px;}.page-account span.larger{font-size:12px;}.page-account div#details_block{margin-right:70px;}div.already_logged_in{margin:5px 10px 5px 10px;}div.login_account_links{margin:10px;}div.packtblock_system.border_bg{padding:0 10px 10px;border:none;}.views-exposed-form .books_pager{margin:-60px auto 0px auto;padding-bottom:40px;}.books_pager{text-align:center;}#page .books_pager,#page .books_pager div.item-list ul.pager li.pager-item a,#page .books_pager div.item-list ul.pager li.pager-next a,#page .books_pager div.item-list ul.pager li.pager-last a,#page .books_pager div.item-list ul.pager li.pager-first a,#page .books_pager div.item-list ul.pager li.pager-previous a{color:#aaa;text-decoration:none;background-color:transparent;font-weight:normal;}.books_pager ul.pager{margin:0px;}.books_pager div.item-list ul.pager li.pager-current{color:orange;background-color:transparent;font-size:1.5em;}#book_taxonomy_view label,#book_taxonomy_view .views-widget{float:left;width:auto;margin-left:10px;margin-right:10px;font-weight:normal;}#book_taxonomy_view .view-content label{margin:0px;margin-right:5px;}#book_taxonomy_view div.views-exposed-widget input.form-submit{margin:auto;float:left;margin-top:-53px;margin-left:320px;}#book_taxonomy_view #edit-keys{width:230px;}#book_taxonomy_view .views-exposed-widget{}#book_taxonomy_view .views-exposed-form{margin-top:20px;}#book_taxonomy_view .views-widget{margin:0px;}#books_limit{margin-top:-110px;margin-bottom:100px;margin-top:-50px;margin-bottom:50px;}#book_taxonomy_view .form-item{}#book_taxonomy_view select{float:left;margin-left:0px;}#books_limit label{color:black;line-height:25px;}div.view-forthcoming-books td,div.view-latest-books-view td,div.view-taxonomy-book-list td{height:75px;padding:0px 20px 20px;width:50%;padding-left:1px;padding-right:1px;line-height:14px;vertical-align:top;}div.view-forthcoming-books td.col-1,div.view-latest-books-view td.col-1,div.view-taxonomy-book-list td.col-1{padding-right:20px;}div.view-forthcoming-books td.col-2,div.view-latest-books-view td.col-2,div.view-taxonomy-book-list td.col-2{padding-left:20px;}div.view-forthcoming-books div.views-field-title,div.view-latest-books-view div.views-field-title,div.view-taxonomy-book-list div.views-field-title{line-height:14px;}#uc-worldpay-direct-auto-form .form-item{clear:both;}#uc-worldpay-direct-auto-form .form-item label{width:130px;float:left;}#uc-worldpay-direct-auto-form #edit-valid-from-wrapper{width:185px;float:left;clear:none;margin-top:0px;margin-bottom:1em;}#uc-worldpay-direct-auto-form #edit-valid-from-year-wrapper{margin-top:0px;margin-bottom:1em;clear:none;}#uc-worldpay-direct-auto-form #edit-valid-to-wrapper{width:185px;float:left;clear:left;margin-top:0px;margin-bottom:1em;}#uc-worldpay-direct-auto-form #edit-valid-to-year-wrapper{clear:none;margin-top:0px;margin-bottom:1em;}#uc-worldpay-direct-auto-form #edit-cvc-wrapper{clear:both;}#uc-worldpay-direct-auto-form #edit-issue-number-wrapper{clear:both;}#uc-worldpay-direct-auto-form .description{margin-left:130px;clear:both;}form#uc-cart-checkout-form fieldset#payment-pane{clear:both;}#bottomNavZoom{display:none;}#bottomNavClose{margin-top:0px;padding-top:0px;}.receipt-outer-ebook,.receipt-outer{width:760px;padding:10px;}.order-pane #sidebar-right{width:auto;margin-right:10px;margin-top:10px;margin-left:60px;margin-bottom:10px;}.order-pane #sidebar-right .block{margin:0px;}.receipt-orderaddress{margin-bottom:10px;}.receipt-indent-50{margin:0px;}.receipt-ordernum{margin:0px;}form#uc-cart-checkout-form #uc_discounts-pane .description{font-weight:normal;float:left;}div#edit-panes-packtcountry-vat-number-vat-number-wrapper div.description{font-weight:normal;float:none;clear:both;}form#uc-cart-checkout-form #uc_discounts-pane .form-item{float:left;margin:0px;}form#uc-cart-checkout-form #uc_discounts-pane input,div#edit-panes-uc-discounts-uc-discounts-codes-wrapper label{float:left;}div#edit-panes-uc-discounts-uc-discounts-codes-wrapper{width:276px;}div#edit-panes-uc-discounts-uc-discounts-codes-wrapper div.description{float:none;}div#edit-panes-uc-discounts-uc-discounts-codes-wrapper input.form-text,div#edit-panes-packtcountry-vat-number-vat-number-wrapper input.form-text{width:125px;}#edit-panes-uc-discounts-uc-discounts-button{margin:0px;}#edit-panes-uc-discounts-uc-discounts-codes{margin:0px 6px;width:100px;}fieldset#uc_discounts-pane input.form-submit,fieldset#packtcountry-vat-number-pane input.form-submit{float:right;width:180px;}form#uc-cart-checkout-form #packtcountry-vat-number-pane{float:right;margin-right:0px;}form#uc-cart-checkout-form #packtcountry-vat-number-pane .description{font-weight:normal;}form#uc-cart-checkout-form #packtcountry-vat-number-pane label{float:left;}form#uc-cart-checkout-form #packtcountry-vat-number-pane .form-item{float:left;margin:0px;}form#uc-cart-checkout-form #packtcountry-vat-number-pane input{float:left;}#edit-panes-packtcountry-vat-number-submit{margin:0px;float:left;clear:right;}#edit-panes-packtcountry-vat-number-vat-number{margin:0px 6px;width:100px;}fieldset#delivery-pane,div#edit-panes-delivery-copy-address-wrapper{margin-bottom:0px;padding-bottom:0px;}fieldset#payment2-pane{margin-top:0px;padding-top:0px;}#edit-panes-customer-login-pass-wrapper{margin-bottom:0px;}#edit-panes-customer-login-submit{margin-top:-30px;}#edit-panes-customer-login-pass-wrapper .description{background:transparent !important;border:0px !important;font-size:1em !important;width:173px;text-align:right;}#-checkout-form-bottom{clear:both;}#edit-nid-wrapper select#edit-nid{width:90%;}.section-support #views-exposed-form-support-page-block-1 .form-submit{background:url('/sites/all/themes/packt_new/images/pp/go-button.png'); height:31px;margin-left:518px;margin-top:-30px;width:31px;float:right;}#views-exposed-form-taxonomy-book-list-default{height:auto;}#views-exposed-form-taxonomy-book-list-default #edit-submit-taxonomy-book-list{background:url('/sites/all/themes/packt_new/images/pp/go-button.png');position:absolute;height:31px;margin-left:518px;margin-top:-33px;width:31px;border:none;}#books_limit{margin-left:255px;margin-top:-86px;position:absolute;}#book_taxonomy_view .views-exposed-form{margin-top:5px;}.buynow-dialogue-outer{left:-130px;top:-207px;}.views-field-buyitnowbutton{position:relative;}.page-account #orders_block #edit-submit{ padding:1px 7px;width:auto;height:26px;margin:0px 7px;position:relative;float:right;top:-30px;}.page-account #orders_block #edit-order-select-wrapper #edit-order-select{ position:relative;clear:both;}.page-account #recent-orders-form{height:45px;overflow:hidden;}.page-article-network #edit-submit-taxonomy-article-list{position:absolute;margin-left:10px;margin-top:-4px;background:url('/sites/all/themes/packt_new/images/pp/go-button.png'); width:31px;height:31px;border:none;}.page-authors #edit-autocomplete-node-finder-submit{background:url('/sites/all/themes/packt_new/images/pp/go-button.png'); width:31px;height:31px;border:none;margin-left:15px;margin-top:0px;position:absolute;}.page-authors .content div#content-header{width:760px;margin-left:-13px;margin-top:-12px;}.page-article-network #books_limit{margin-left:276px;margin-top:-73px;}.page-article-network .panel-pane div.books_pager{margin-top:-60px;text-align:center;width:570px;position:absolute;}.page-article-network .panel-pane #pager-bottom div.books_pager{margin-top:10px;margin-bottom:0px;text-align:center;width:570px;position:relative;}.checkout-register .form-item{margin-top:1em;margin-bottom:1em;}.checkout-register #edit-newsletter{float:right;margin-right:140px;}.checkout-register .go_button{float:right;margin-right:10px;margin-top:0px;}.checkout-register .captcha #edit-captcha-response{width:90px;}.checkout-register #edit-captcha-response-wrapper label{width:155px;}.checkout-register #edit-captcha-response-wrapper .description{display:none;}.page-article-network #views-exposed-form-taxonomy-article-list-default{height:95px;}.page-cart-checkout #payment2-pane{float:right;margin:0px;margin-right:8px;padding-top:20px;width:264px;}.page-cart-checkout #customer-pane{float:left;padding-right:0;width:670px;clear:both;}.not-logged-in #customer-pane{margin-left:0px;padding:0px;position:relative;width:940px;margin-bottom:190px;}.checkout-order-box-inner div{padding:10px;color:#000;}.page-cart-checkout .address-pane-table{background:#fff;}.page-login #block-uc_cart-0{display:none;}.page-login hr{margin:20px 55px;width:830px;}div.checkout-login,div.checkout-register{width:320px;display:block;float:left;}div.checkout-login,div.checkout-register,div.checkout-order-box-inner{font-size:15px;font-weight:normal;margin:0px 0px 0px 3px;padding:5px 0px 9px 5px; background-color:white;background-repeat:repeat-x;color:rgb(246,140,35);border:1px solid #BCBCBD;font-size:1em;}div.checkout-order-box-inner{margin-bottom:10px;}div.checkout-register{margin-left:20px;}div.checkout-login h2,div.checkout-register h2,div.checkout-order-box h2{font-weight:normal;color:rgb(246,140,35);margin-left:10px;font-size:15px;margin-top:5px;}div.checkout-login label,div.checkout-register label{width:115px;text-align:left;margin-left:10px;display:inline-block;}div.checkout-register label.option{width:176px;}.newsletter-text{margin-left:10px;margin-right:15px;clear:both;color:#000;}#edit-panes-customer-login-email-wrapper,#edit-panes-customer-new-account-email-wrapper{margin-top:10px;}div#edit-panes-customer-new-account-newsletter-wrapper{margin-left:6px;}div.checkout-login .forgot-password{margin-left:10px;}div.checkout-login #edit-panes-customer-login-submit{ margin-left:240px;margin-top:-25px;}div.checkout-register #edit-submit{margin-left:130px;}div.checkout-order-box{color:black;float:right;height:65px;margin:0px;padding:0px;}div.checkout-order-box-inner{width:230px;margin-left:18px;}#book_taxonomy_view .views-exposed-widget{}#books_limit{margin-left:274px;margin-top:-61px;clear:both;position:absolute;}.page-books .books_pager{position:relative;top:-55px;margin-bottom:-40px;}.page-books #pager-bottom .books_pager{top:-20px;margin-bottom:-20px;}.page-books #views-exposed-form-taxonomy-book-list-default{height:100px;}.page-books #views-exposed-form-taxonomy-book-list-default #edit-submit-taxonomy-book-list{}#downloads_block .border_bg,#orders_block .border_bg{border:1px solid #BCBCBD;border-top:none;padding:0px;margin:0px 10px 10px;}.relative{position:relative;}#sidebar-left{float:left;margin:0;width:167px;display:block;}#main{float:left;margin:0;margin-bottom:10px;padding-bottom:0;width:auto;display:block;}#sidebar-right{float:left;margin:0;width:167px;}.two-sidebars #content-inner{margin:0;padding:0;}#main-container{clear:both;margin-left:15px;margin-top:8px;}.panel-1col .panel-separator{margin:0 0 1em;}.node-full-view.two-sidebars #content-inner{padding:0;}#sidebar-right #block-uc_cart-0 .links span a{float:none;width:157px;background-position:123px -154px;display:block;margin:0 0 0 5px;}.page-support xmp{width:100%;white-space:pre-wrap;}.add-to-cart-ext{margin-left:435px;margin-top:20px;}.page-submit-errata select{width:385px;}.page-support #code-download-form #edit-submit{width:31px;height:31px;background:url('/sites/all/themes/packt_new/images/pp/go-button.png');border:none;margin-top:-4px;}.page-support #edit-signup-monthly-wrapper{clear:both;}.page-support #edit-signup-monthly-wrapper{clear:both;}.page-support #edit-signup-monthly-wrapper #edit-signup-monthly{float:left;margin-bottom:60px;}.page-support #edit-signup-monthly-wrapper .description{float:none;margin-right:10px;}.page-support #title-select-form input#edit-submit{background:url('/sites/all/themes/packt_v3/design/images/search-button.png');background-position:-12px -10px;}s#edit-panes-cart-continue-shopping{margin-right:10px;}.node-type-article pre{white-space:normal;}#block-views-books_by_author-block_1{}.pager a,.pager a:hover,div.item-list ul.pager li.pager-current{background:transparent;}#content-area .node{margin:0px;}#block-views-latest_articlesblock_1 .block-inner{}#block-views-latest_articlesblock_1 .block-inner .block-content-inner{border-bottom:1px solid white;}#edit-signup-monthly-wrapper p{line-height:12px;}.page-submit-errata #block-block-3 select{width:90%;}#book-view .book-zoom-container-outer{position:relative;width:150px;height:187px;}#book-view .book-zoom-container{background:#FFFFFF none repeat scroll 0 0;border:1px solid #A8A8A8;height:181px;width:150px;}img.zoom-button{border:1px solid #A8A8A8;border-top:none;}#book-view .book-zoom-container img{padding:0px;}ul.nice-menu a{margin-bottom:5px;padding:0px;}.page-news-center .content h1,.page-news-published-last-month .content h1,.page-news-published-two-months-back .content h1,.page-news-published-three-months-back .content h1,.page-latest-news .content h1{margin-top:30px;}form#uc-cart-checkout-form fieldset{border:0 none;padding-left:0px;padding-right:8px;margin-bottom:10px;}#sidebar-right .block-uc_cart div.links{clear:both;}#footer #block-menu-devel{display:none;}#footer{_height:0;zoom:1;}#login-box .login-button{clear:both;margin-left:215px;position:relative;top:-42px;}.secure-image-text,.secure-image-text-orange,.secure-text{clear:both;}.page-account form#user-pass,.page-account form#user-profile-form{padding:10px;}.page-account form#user-profile-form fieldset{border:none;}#book_limit select{position:absolute;width:45px;}#block-views-latest_news_homepageblock_1 .views-row,#block-views-latest_articlesblock_1 .views-row{margin-top:15px;margin-bottom:25px;}.sidebar-right #content-inner{padding-right:0px;}div.view-user-group-gmap{width:99.3%;}#page-background div.messages{margin:5px 12px 0;}form#uc-cart-checkout-form fieldset#uc_discounts-pane{float:left;margin:0;padding:0;width:460px;position:absolute;*position:none;margin-top:-222px;*margin-top:-235px; left:13px;}form#uc-cart-checkout-form #packtcountry-vat-number-pane{float:right;margin:0;padding:0;width:auto;white-space:nowrap;margin-right:10px;}.feed-icons{margin-left:581px;margin-top:-20px;position:relative;}.front #content-header{height:0px;margin:0px;padding:0px;margin:0px;display:block;}.front #content-header h1{display:none;}.view-taxonomy-article-list .view-header,.view-article-network-1-month-old .view-header,.view-article-network-2-months-old .view-header,.view-article-network-3-months-old .view-header{margin-bottom:20px;}.page-article-network .views-row,.page-article-network-published-1-month-ago .views-row,.page-article-network-published-2-months-ago .views-row,.page-article-network-published-3-months-ago .views-row{margin-bottom:45px;}.page-article-network .inside .panel-pane h2{margin-top:-3px;}.panel-panel .panel-panel{clear:both;}#sidebar-right .block-uc_cart .block-inner{background-color:#F7CE8A;}#edit-panes-delivery-copy-address-wrapper label{font-size:11px;}.address-pane-table table{margin:0;}.page-cart-checkout .address-pane-table{padding-bottom:10px;}#page #sidebar-left h2.block-title a.block-header-link,#page #sidebar-right h2.block-title a.block-header-link{font-size:15px;color:white;}#book-view .new-version{color:red;margin:15px 0px;font-weight:bold;}td.qty input{width:25px;}.page-account #content-area{padding:0px;}.ebook-options{margin-left:447px;margin-top:-10px;position:relative;}.page-account-files .border_bg .block-inner h2{margin:0px;}div.profile{margin-top:0px;margin:1em 10px;}.page-account fieldset legend{margin-left:-8px;color:#000;}.page-account div.menubar ul li{width:120px;display:block;float:left;}.page-account div.menubar ul li.right{width:auto;position:relative;}.page-account div.full_order_block #recent-orders-form #edit-submit{background:transparent url(/sites/all/themes/packt_new/images/pp/go-button-white.png) repeat scroll 0 0;border:medium none;height:31px;margin-left:228px;margin-top:-24px;position:absolute;width:31px;}div.full_order_block div.menubar ul li.right select{margin-bottom:0;margin-top:0;position:absolute;}td.exx_large div,td.ex_large div{position:relative;padding:20px;}div.block-tagadelic div.block-inner div.block-content-inner{line-height:auto;clear:both;margin-bottom:5px;}div.block-tagadelic div.block-inner div.block-content a{margin:0px 40px 0px 0px;display:inline-block;}div.view-Blogs-Most-Commented div{line-height:1em;}div.view-Blogs-Most-Commented div.views-row{margin-bottom:10px;}div.view-Blogs div.views-row{margin-bottom:20px;}div.view-Blogs div.views-row a:hover{text-decoration:underline;}div.author-name,div.author-name *{font-size:1.2em;}div#book_page_tabs div.tab{float:left;min-width:60px;padding:5px;background-color:#eee;border:1px solid rgb(168,168,168);color:#444;margin-right:5px;font-weight:bold;display:block;text-align:center;position:relative;z-index:5000;cursor:hand;cursor:pointer;top:1px;height:17px;overflow:hidden;}div#book_pricing_block{clear:both;}div#book_page_tabs{width:350px;top:0px;position:relative;margin-left:164px;margin-top:-186px;height:29px;z-index:1000;clear:both;}#book-view .book-cart{width:409px;border:1px solid #A8A8A8;background:#fff;margin-left:164px;position:relative;clear:both;margin-top:0px;height:171px;}div#book_page_tabs div.active{color:black;background-color:white;border-bottom:1px solid white;}div.book-cart div.shipping-block{top:15px;}div.new-version{background-color:white;border:1px solid #a8a8a8;padding:5px 10px;}#book-view .book-cart{-moz-background-clip:border;-moz-background-inline-policy:continuous;-moz-background-origin:padding;background:#FFFFFF none repeat scroll 0 0;border:1px solid #A8A8A8;clear:both;height:171px;margin-left:164px;margin-top:0;width:409px;}#block-packtcountry-2 a{font-weight:bold;font-size:12px;color:rgb(246,140,35);}tr.subtotal{font-weight:bold;}div#primary-links{height:60px;background:transparent url('/sites/all/themes/packt_new/images/pp/top-nav-gradient.png') repeat-x;}.accordion-active{border:0px;}.views-accordion-item .accordion-header-active,h3.accordion-header-active{border:0px;padding:0px;background-color:transparent;}.view-support-page .views-field-field-date-of-publication-value{margin-bottom:-70px;}.page-open-source #block-views-carousel-block_1 .jcarousel-clip-horizontal,.page-enterprise #block-views-carousel-block_1 .jcarousel-clip-horizontal{margin:0px;}#sidebar-left hr{color:#ccc;background-color:#ccc;border-color:#ccc;}#sidebar-left #block-block-34,#sidebar-right #block-block-34{border-top:1px solid rgb(188,188,189);}.page-enterprise .panel-pane,.page-enterprise #block-views-carousel-block_1,.page-open-source .panel-pane,.page-open-source #block-views-carousel-block_1{background-image:none;background-color:transparent;margin-bottom:10px;}.page-enterprise .view-article-network .views-row,.page-open-source .view-article-network .views-row{margin-bottom:20px;}body.section-award div#block-views-Blogs-block_1 div.view-header,body.section-award div#block-views-Blogs-block_1 br,body.section-award div#block-views-latest_articles-block_2 div.view-header{display:none;}body.section-award div#block-views-Blogs-block_1 h1{font-size:11px;margin:0px;padding:0px;font-weight:normal;margin-bottom:5px;}body.section-award div#block-views-Blogs-block_1 div.views-row{margin:0px;}div#block-views-latest_articles-block_2 a{display:block;margin-bottom:5px;}body.page-open-source-awards-home #content #content-area,body.page-award-nominations #content #content-area,body.section-open-source-awards-home #content #content-area,body.page-open-source-awards-home-nominations #content #content-area,body.page-open-source-awards-home-open-source-awards-judges #content #content-area{background-image:none;background:#ffffff;}div.block-views .jcarousel-container-horizontal{height:170px;overflow:hidden;}div.block-views .jcarousel-clip-horizontal{height:180px;margin:0 10px;}div.block-views .jcarousel-clip-horizontal ul{height:172px;}.jcarousel-skin-packt .jcarousel-container{border:none;}.section-open-source-awards-home .jcarousel-skin-packt .jcarousel-container-horizontal{width:506px;}.section-open-source-awards-home div.views-field-field-image-fid{height:120px;}div.child-book{ margin-bottom:5px;float:left;width:114px;}div.child-book-img,div.child-book-title{float:left;margin:0px 10px 0px 10px;}div.child-book-title{display:none;}div.child-book-title{width:450px;}div#primary-links img.right{padding:0px 0px 0px 0px;}div.view-taxonomy-subscription-list .views-field-buyitnowbutton{float:right;margin-top:-50px;}#book_subscription_view ul{margin-left:20px;}.node-type-subscriptions #book-view .book-cart .add-to-cart{margin-top:-22px;}.book-cart .inner{margin-top:-1px;}div.subscription div.book-cart div.shipping-block{height:30px;}.float-right{float:right;}.section-open-source-awards-home .jcarousel-skin-packt .jcarousel-container-horizontal{width:506px;}.section-open-source-awards-home .jcarousel-skin-packt .jcarousel-item{height:175px;}.section-open-source-awards-home div.views-field-field-image-fid{height:120px;}.section-open-source-awards-home div.block-views .jcarousel-clip-horizontal{width:auto;}.section-open-source-awards-home #content-bottom h2.block-title{text-align:left;}.section-open-source-awards-home .jcarousel-item div.view-taxonomy-book-list .views-field-title,.section-open-source-awards-home .jcarousel-skin-packt .jcarousel-item{text-align:left;}.section-open-source-awards-home .jcarousel-item div.view-taxonomy-book-list .views-field-title a,.section-open-source-awards-home .jcarousel-item div.view-taxonomy-book-list .views-field-title a:hover{font-weight:bold;color:#FFFFFF;font-size:11px;}.section-open-source-awards-home .jcarousel-skin-packt .jcarousel-item a,.section-open-source-awards-home .jcarousel-skin-packt .jcarousel-item a:hover{font-weight:bold;font-size:11px;}.section-open-source-awards-home .jcarousel-skin-packt .views-field-title{height:45px;clear:both;}.section-open-source-awards-home div.block-views .jcarousel-clip-horizontal ul{height:180px;}.section-open-source-awards-home .jcarousel-item .views-field-title a{color:white !important;}.section-open-source-awards-home .jcarousel-skin-packt .jcarousel-item{width:90px;}#edit-panes-delivery-copy-address-wrapper label{font-weight:bold;color:red;}img.entrust-seal{}div#entrust_seal-wrapper{width:80px;height:80px;margin:-80px 0px 0px 350px;}div.packtlib-promotion{overflow:hidden;margin-left:auto;margin-right:auto;width:100%;background:#faffbd;border:1px solid #BCBCBD;overflow:auto;margin-bottom:10px;}body.page-index div.packtlib-promotion{width:597px;}div.packtlib-promotion h2{margin:10px 0px 5px 0px;}div.packtlib-promotion div.inner{margin:0px 10px 10px 10px;}div.packtlib-promotion button{width:181px;height:23px;line-height:10px;float:right;}div.packtlib-promotion img{top:7px;margin-top:-7px;position:relative;}form#uc-worldpay-direct-auto-form input#edit-submit{margin-left:130px;}#block-views-taxonomy_book_list-block_2 div.packtlib-promotion{display:none;}.page-books #content-bottom #block-views-taxonomy_book_list-block_2{margin-top:184px;}form#uc-worldpay-direct-auto-form div#secure-blue input#edit-submit,form#uc-worldpay-direct-auto-form div#secure-orange input#edit-submit{color:white;font-weight:bold;width:130px;border:none;height:30px;}form#uc-worldpay-direct-auto-form div#secure-blue input#edit-submit{}form#uc-worldpay-direct-auto-form div#secure-orange input#edit-submit{background:url('/sites/default/files/button-orange-bevel_0.png');}div.packtlib-upsell,div.upsell{width:430px;float:left;padding:9px;background-color:#FAFFBD;border:1px solid gray;}div.upsell{width:auto;float:none;}div.packtlib-upsell.grey,div.packtlib-upsell.float{margin-left:34px;}div.packtlib-upsell.float{margin-top:10px;}div.packtlib-upsell div#edit-qty-wrapper{display:none;}span.red,p.red{color:red;}div.tagline{margin-left:95px;margin-top:80px;position:absolute;font-size:16px;color:white;}div.total-books{margin-left:721px;margin-top:50px;position:absolute;}div.total-books div.total-heading{color:white;font-size:1.6em;margin-bottom:10px;}.orange{color:#F68C23;}.links span.menu-1082{line-height:0;padding-top:9px;}.links span.menu-27973{line-height:0;padding-top:14px;}#uc-cart-checkout-form input{width:auto;}.profile .jcarousel-container-horizontal{width:468px!important;}.profile .jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal{width:480px!important;margin-left:0px!important;} div#count-heading{color:#000;margin-top:5px;font-weight:bold;}div#count-time{color:#000;font-size:35px;margin-top:16px;}table.subs-table tr td,table.subs-table tr th{padding:4px;}table.subs-table tr th{background:#5C5C5C url(/sites/all/themes/packt_new/images/pp/content-header-gradient.jpg) repeat-x scroll 0px 0px;color:white;font-size:13px;height:20px;border:1px solid gray;}table.subs-table tr td{border:1px solid gray;}table.packtlib-table-new td.border{border:1px solid #d0d0d0;}table.packtlib-table-new th.ur{border-left:1px solid #d0d0d0;border-top:1px solid #d0d0d0;border-right:1px solid #d0d0d0;}table.packtlib-table-new th.br{border-left:1px solid #d0d0d0;border-bottom:1px solid #d0d0d0;border-right:1px solid #d0d0d0;}table.packtlib-table-new td.lr{border-left:1px solid #d0d0d0;border-bottom:1px solid #d0d0d0;border-top:1px solid #d0d0d0;}table.packtlib-table-new .yellow{background-color:#ffffcc;width:130px;}table.packtlib-table-new .green{background-color:#ccffcc;width:130px;}table.packtlib-table-new tbody{border:0;}table.packtlib-table-new p{text-align:center;}table.packtlib-table-new div.left p{text-align:left;}p.orange-header{color:#F68C23;font-size:16px;font-weight:bold;}table.packtlib-table-new th,table.packtlib-table-new td{border:0px;padding:5px;} .page-account #details_block,.page-account #subscriptions_block{margin-top:0px;padding-top:5px;margin-left:10px;width:735px;}.page-account h3.noline{margin-top:0px;}.page-account span.larger{font-size:12px;}div.already_logged_in{margin:5px 10px 5px 10px;}div.login_account_links{margin:10px;}div.packtblock_system.border_bg{padding:0 10px 10px;border:none;}.profile h3.noline{color:#F7931E;font-size:16px;}.packtlib-table td{border:1px solid #666;text-align:center;padding:5px 8px;}.packtlib-table-row td{background-color:white;}#packtlib_block{display:none;}.subs select{width:275px;}.grey{color:#aaa;}.packtlib-upsell.grey{color:#888;}table.benefits-table{width:100%;margin-bottom:0px;}table.benefits-table td,table.benefits-table th{width:25%;text-align:left;border:1px solid #CCC;border-top:0px;padding-left:10px;}table.benefits-table td.tick{background-image:url(/sites/default/files/status_tick.png);background-align:center center;background-color:#ccffcc;height:48px;}table.benefits-table td.cross{background-image:url(/sites/default/files/status_cross.png);background-align:center center;background-color:#fee4e4;height:48px;}table.benefits-table.images td{border-bottom:0px solid black;border:0px;padding:0px;text-align:center;}table.benefits-table tr.benefit th{padding-left:10px;}table.benefits-table tr.price th{padding-top:10px;vertical-align:top;}table.benefits-table tr.price td{padding-left:0px;padding-top:10px;text-align:center;font-size:1.4em;height:30px;line-height:30px;vertical-align:top;}table.benefits-table tr.bottom td,table.benefits-table tr.bottom th{border-bottom:0px;}table.benefits-table *.top{border-top:1px solid #CCC;}table.benefits-table *.noleft{border-left:0px;}table.benefits-table *.noright{border-right:0px;}table.benefits-table *.noborder{border:0px;}table.benefits-table div.add-to-cart div.form-item{display:none;}div.benefits-table h3{font-size:1.2em;}#subscribe_packtlib tr,#subscribe_packtlib td,#subscribe_packtlib th,#subscribe_packtlib table,div.view-taxonomy-subscription-list.view-display-id-panel_pane_4 tr,div.view-taxonomy-subscription-list.view-display-id-panel_pane_4 td,div.view-taxonomy-subscription-list.view-display-id-panel_pane_4 th,div.view-taxonomy-subscription-list.view-display-id-panel_pane_4 table{border-collapse:separate!important;}#subscribe_packtlib td.lr,.profile td.lr,div.view-taxonomy-subscription-list.view-display-id-panel_pane_4 td.lr{border-bottom-left-radius:15px;border-top-left-radius:15px;border-collapse:separate!important;}#subscribe_packtlib .br,.profile .br,div.view-taxonomy-subscription-list.view-display-id-panel_pane_4 .br{border-radius:0px!important;border-collapse:collapse!important;border:none!important;}#subscribe_packtlib th.ur,.profile th.ur,div.view-taxonomy-subscription-list.view-display-id-panel_pane_4 th.ur{border-top-left-radius:15px;border-top-right-radius:15px;border-collapse:separate!important;}#subscribe_packtlib .subs-last,.profile .subs-last,div.view-taxonomy-subscription-list.view-display-id-panel_pane_4 .subs-last{border:1px solid #d0d0d0!important;border-top:0px!important;border-bottom-left-radius:15px!important;border-bottom-right-radius:15px!important;border-collapse:separate!important;}#subscribe_packtlib .hpadder,.profile .hpadder,div.view-taxonomy-subscription-list.view-display-id-panel_pane_4 .hpadder{border-top:1px solid #d0d0d0!important;border-bottom:1px solid #d0d0d0!important;border-radius:0px!important;}#subscribe_packtlib .no-border,.profile .no-border,div.view-taxonomy-subscription-list.view-display-id-panel_pane_4 .no-border{border:0px!important;}div.subs-block div.float-left{float:left;}div.subs-block div.float-right{float:right;margin-top:10px;}form#checkout_resolution div.option{width:280px;margin:10px;float:left;}form#checkout_resolution div.option p{margin-top:10px;}form#checkout_resolution div.option2{margin-left:20px;margin-right:0px;}form#checkout_resolution div.option1{margin-left:0px;margin-right:20px;}div.profile.subs h2{font-size:16px;color:#F68C23;margin-top:8px;margin-bottom:6px;}.profile h3{padding-bottom:5px;}.profile .form-submit{width:180px;height:25px;color:#000;}body.gradient div div div#content-area,body.page-lite-editions div div div#content-area{background-color:transparent;background-image:url(/sites/all/themes/packt_new/images/pp/content-gradient-orange.jpg);}div#sidebar-left div#block-block-75{border-top:1px solid #ccc;}div.view-taxonomy-book-list.view-display-id-panel_pane_4 td.col-1,div.view-taxonomy-book-list.view-display-id-panel_pane_4 td.col-2,div.view-taxonomy-book-list.view-display-id-panel_pane_4 td.col-4,div.view-taxonomy-book-list.view-display-id-panel_pane_4 td.col-5,div.view-taxonomy-book-list.view-display-id-panel_pane_4 td.col-3{padding-right:0px;width:auto;}div.underbreadcrumb{margin:2px 15px;}div#tab_packtlib_content img{float:left;}div#tab_packtlib_content div{float:left;width:260px;}div#tab_packtlib_content div p{margin-left:20px;}div#tab_packtlib_content{padding:15px 0px 0px 10px;}div#tab_packtlib_content div.subscription-options{width:100%;text-align:center;}span.red{color:#ff0000;}#checkout-pane-ab-green-box table.cart-review tr.subtotal.top td,#checkout-pane-ab-green-box table.cart-review tr.subtotal td{background-image:none;background:#D3F9BC;color:#000000;}form#packtlite-upgrade-form div#edit-promo-code-wrapper{width:300px;float:left;}form#packtlite-upgrade-form input#edit-submit{margin:1em 0;float:right;}table tr.small td.content-header{font-size:1em;}.float-left{float:left;margin-right:5px;}.clear{float:none;clear:both;}.captcha{display:none;height:0px;}.boxy-inner pre{white-space:pre;}.page-account #content-area{background:none transparent;border:0;padding:0;}div.full_order_block{margin:0;padding:0;}div.full_order_block h2{margin:0;background:none transparent;}div.full_order_block .block-inner{background:gray url(/sites/all/themes/packt_new/images/pp/side-bar-gradient.jpg) repeat-x;height:31px;}div.change-currency-form{ clear:both;}.change-currency-form .form-item{margin:0px;}.change-currency-form form{margin:0px;padding:0px;}div.change-currency-form label{width:56px;margin:0px;vertical-align:top;padding:0px;float:left;}div.shipping-block{padding-top:0px;top:30px;height:60px;vertical-align:middle;position:relative;clear:both;}div.change-currency-form select{margin:0px;padding:0px;float:left;}fieldset#delivery-pane,fieldset#billing-pane{width:320px;display:block;}fieldset#delivery-pane div.description,fieldset#billing-pane div.description,fieldset#customer-pane div.description{font-size:15px;font-weight:normal;margin:0px;padding:5px 0px 9px 5px;background-color:white;background-repeat:repeat-x;color:#F68C23;padding-top:5px;padding-left:10px;border:1px solid #BCBCBD;border-bottom:0px;}fieldset#customer-pane div.description{text-align:left;}.address-pane-table{border:1px solid #BCBCBD;border-top:0;}fieldset#customer-pane .address-pane-table{padding:10px;}#edit-panes-delivery-delivery-address-select-wrapper select,#edit-panes-billing-billing-address-select-wrapper select{width:175px;}.page-account #content-area{border:1px solid #BCBCBD;background-position:0 -1119px;}#contact-mail-user{padding:0px 10px;}.page-account #content-area .form-item{}#edit-currency-wrapper div.description{float:left;width:140px;margin-left:10px;margin-top:-2px;line-height:12px;}div.full_order_block table tr td{padding-right:6px;}#views-exposed-form-taxonomy-article-list-default{border:1px solid #A8A8A8;height:140px;background:white;margin-bottom:15px;}#views-exposed-form-taxonomy-article-list-default input{width:auto;margin-top:0;}#views-exposed-form-taxonomy-article-list-default .form-checkboxes{clear:both;}#views-exposed-form-taxonomy-article-list-default .form-checkboxes .form-item{float:left;}#views-exposed-form-taxonomy-article-list-default .views-exposed-widget div,#views-exposed-form-taxonomy-article-list-default .views-exposed-widget label{float:left;font-weight:normal;margin-left:5px;}#views-exposed-form-taxonomy-article-list-default .views-exposed-widgets{margin-top:10px;}.page-article-network div.books_pager{position:relative;margin-top:-300px;margin-bottom:200px;}.panel-flexible .panel-col-first .inside{background:#a09e9e url(/sites/all/themes/packt_new/images/pp/articles-network-left-block-bg.png) repeat-x;padding:10px;height:180px;margin-right:5px;color:white;border:1px solid #BCBCBD;}.panel-flexible .panel-col-last .inside{background:#e36e11 url(/sites/all/themes/packt_new/images/pp/articles-network-right-block-bg.png) repeat-x;padding:10px;margin-left:5px;height:180px;border:1px solid #BCBCBD;}table.cart-review td,table.cart-review th{padding:0.4em 0.5em;}table.cart-review td.image{padding-left:10px;padding-top:10px;}table.cart-review{border:0;}table.cart-review tr.subtotal{border-left:1px solid #898989;border-right:1px solid #898989;}table.cart-review tr.subtotal td{background-color:#D1A510;color:white;}table.cart-review tr.subtotal td{background-color:#D1A510;color:white;}table.cart-review tr.subtotal.top td{border-top:1px solid #898989;background-color:#F2F2F2;}table.cart-review tr.subtotal.bottom td{border-bottom:1px solid #898989;}table.cart-review tr td.discount{width:150px;text-align:center;}fieldset#payment-pane,fieldset#payment2-pane{margin:0px;float:right;clear:right;margin-top:30px;margin-right:10px;}#header-offer .inner{line-height:1.2em;padding:18px 40px 18px;overflow:none;}#pcontact-mail-page{margin:10px;}div.full_order_block #recent-orders-form #edit-submit{background:url(/sites/all/themes/packt_new/images/pp/go-button-white.png);border:none;height:31px;width:31px;margin-top:-2px;margin-right:5px;position:relative;}div.full_order_block .menubar{height:34px;}div.full_order_block div.menubar ul li{line-height:34px;}div.full_order_block div.menubar ul li.right select{position:relative;margin-top:5px;}div#block-packtcountry-2 h3{padding:0;margin:0;}div.checkout-cards{ width:385px;height:21px;position:relative;margin-top:-60px;margin-left:20px;margin-bottom:40px;clear:both;}table.tracking td p{font-size:12px;margin-left:10px;}td.tick{background:url(/sites/default/files/status_tick.png) center no-repeat;height:55px;width:45px;}td.cross{background:url(/sites/default/files/status_cross.png) center no-repeat;height:55px;width:45px;}td.na{background:url(/sites/default/files/status_na.png) center no-repeat;height:55px;width:60px;}td.t_tick{font-weight:bold;}td.t_na{color:#777;}table.tracking-numbers{margin:5px 10px;width:100%;}div.safari{margin:15px 0px 10px 0px;width:573px;}div.safari-logo{float:left;margin-left:15px;margin-right:60px;}div.safari-link{float:left;width:400px;}div#packtlib_block{margin-top:0px;padding-top:5px;margin-left:50px;}table.benefits-table{width:100%;margin-bottom:0px;}table.benefits-table td,table.benefits-table th{width:25%;text-align:left;border:1px solid #CCC;border-top:0px;padding-left:10px;}table.benefits-table td.tick{background-image:url(/sites/default/files/status_tick.png);background-align:center center;background-color:#ccffcc;height:48px;}table.benefits-table td.cross{background-image:url(/sites/default/files/status_cross.png);background-align:center center;background-color:#fee4e4;height:48px;}table.benefits-table.images td{border-bottom:0px solid black;border:0px;padding:0px;text-align:center;}table.benefits-table tr.benefit th{padding-left:10px;}table.benefits-table tr.price th{padding-top:10px;vertical-align:top;}table.benefits-table tr.price td{padding-left:0px;padding-top:10px;text-align:center;font-size:1.4em;height:30px;line-height:30px;vertical-align:top;}table.benefits-table tr.bottom td,table.benefits-table tr.bottom th{border-bottom:0px;}table.benefits-table *.top{border-top:1px solid #CCC;}table.benefits-table *.noleft{border-left:0px;}table.benefits-table *.noright{border-right:0px;}table.benefits-table *.noborder{border:0px;}div.subscribe-button{margin-left:170px;}div#subscribe-name-wrapper,div#subscribe-email-wrapper{margin-left:15px;}div#subscribe-name-wrapper input,div#subscribe-email-wrapper input{width:150px;}div#block-block-79 div.subscription-centre-text{margin-left:0px;}div#block-block-79 div#subscribe-name-wrapper,div#block-block-79 div#subscribe-name-wrapper input{margin-left:0px;}div#block-block-79 div#subscribe-email-wrapper,div#block-block-79 div#subscribe-email-wrapper input{margin-left:0px;}div#block-block-79 div.subscribe-button{margin-left:92px;}div#block-block-79 div.subscription-centre-link,div#block-block-79 p{display:none;}body{}#pixture-reloaded{font-family:sans-serif;font-size:.75em;color:#444;background:#ffffff none repeat-x left top}#page{line-height:1.5em;text-align:left;width:992px;background:transparent;}body div#page-background{overflow:visible;}#content{ text-align:left;}a,a.active,a:visited{font-weight:400;color:#F68C23;text-decoration:none;}a:hover,a.active:hover{text-decoration:underline}hr{background-color:#b1b1b1;color:#b1b1b1;}ul li{list-style-type:disc;}ol li{list-style-type:decimal;}div#main ul li{list-style-image:url(/sites/default/files/new-design-assets/orange-bullet.png);margin-left:2em;}p,td,ul li,ol li{line-height:1.5em;}h1,h2,h3,h4,h5,h6{margin-bottom:0.3em;}h1{font-size:1.35em;}h2{font-size:1.3em;}h3{font-size:1.2em;}h4,h5,h6{font-size:1.1em;}b,strong{font-weight:bold;}img{vertical-align:bottom;}#top-banner{background-image:url(/sites/all/themes/packt_new/images/header_bonanza.png)!important;background-color:#323335!important;background-repeat:no-repeat;background-position:682px 10px;height:109px;position:relative;top:-7px;width:100%;}#head-elements{clear:both;position:relative;z-index:5;height:60px;width:100%;margin:0;}#page-top{}#page-top li{}#page-top li a{}#page-top div.links span{float:left}#packt-libraries-main-search-form #autocomplete,#views-exposed-form-taxonomy-book-list-default #autocomplete{z-index:10000;}#packt-libraries-main-search-form input.form-submit{}div#search-bar-box{}div#search-bar-box input.form-autocomplete,#views-exposed-form-taxonomy-book-list-default input.form-autocomplete,#packt-categorypages-cat-search-form input.form-autocomplete{height:35px;font-size:20px; background-image:url("/misc/new_loader.gif");background-repeat:no-repeat;background-position:249px 60px;}div#search-bar-box input.form-autocomplete::-ms-clear,#views-exposed-form-taxonomy-book-list-default input.form-autocomplete::-ms-clear,#packt-categorypages-cat-search-form input.form-autocomplete::-ms-clear{display:none;}div#search-bar-box input.form-autocomplete.throbbing,#views-exposed-form-taxonomy-book-list-default input.form-autocomplete.throbbing,#packt-categorypages-cat-search-form input.form-autocomplete.throbbing{background-position:96% 11px;}#views-exposed-form-taxonomy-book-list-default #edit-key-wrapper{position:relative;left:5px;margin:0px;top:-28px;height:39px;}body.main-chromeless.page-books #book_taxonomy_view .views-exposed-widget{width:100%;margin:0px;float:right;height:auto;line-height:30px;}body.main-chromeless.page-books #views-exposed-form-taxonomy-book-list-default #edit-submit-taxonomy-book-list,#packt-categorypages-cat-search-form #edit-submit{margin:0px;position:absolute;top:32px;left:385px;height:39px;width:40px; text-indent:-9999px; background:#F68C23 url(/sites/all/themes/packt_new/images/sprite-assets.png) no-repeat center;background-position:-234px -154px; box-shadow:inset 0px 15px 15px rgba(255,255,255,0.3);border:1px solid orange;}body.main-chromeless.page-books #views-exposed-form-taxonomy-book-list-default #edit-submit-taxonomy-book-list:hover,#packt-categorypages-cat-search-form #edit-submit:hover{border:1px solid #999;box-shadow:inset 0px 15px 15px rgba(255,255,255,0.5);}#packt-categorypages-cat-search-form #edit-submit{position:relative;top:18px;left:-10px;}#packt-categorypages-cat-search-form .solr-book-options{float:left;margin:10px 0px 0px 10px;}#book_taxonomy_view .form-item{margin:0px;float:left;}#views-exposed-form-taxonomy-book-list-default .views-exposed-widget #book_limit,#views-exposed-form-taxonomy-book-list-default .views-exposed-widget #edit-sort-wrapper{float:right;width:280px;clear:both;}#views-exposed-form-taxonomy-book-list-default .form-checkboxes{float:right;width:694px;margin:0px;}div.search-site-box{margin-bottom:10px}#book_taxonomy_view #views-exposed-form-taxonomy-book-list-default .views-exposed-form{position:relative;}#views-exposed-form-taxonomy-book-list-default input.form-autocomplete{width:360px;padding:2px 5px;margin:0px;position:relative;height:33px;font-size:20px;background-position:249px 60px;}#views-exposed-form-taxonomy-book-list-default #edit-key-wrapper #autocomplete,#search-bar-box #edit-keys-wrapper #autocomplete,#search-bar-box #edit-keys-1-wrapper #autocomplete,#packt-categorypages-cat-search-form #autocomplete{background:#fff;margin-left:0px;top:0px;box-shadow:0px 0px 5px #333;padding:5px 1px 1px;border:1px solid #ccc;z-index:3;border-radius:0px 0px 5px 5px;width:auto !important;}#packt-categorypages-cat-search-form #autocomplete{margin-top:107px!important;}div#main #views-exposed-form-taxonomy-book-list-default #edit-key-wrapper #autocomplete ul li,#search-bar-box #edit-keys-wrapper #autocomplete ul li,#search-bar-box #edit-keys-1-wrapper #autocomplete ul li,#packt-categorypages-cat-search-form #autocomplete ul li{list-style-image:none;list-style:none;margin-left:0px;padding:0px 10px;cursor:pointer;}div.search-site-bar{}div.search-site-bar input{}div#search-bar-box label{}div.search-site-switch{text-align:center}div.search-site-button{}div.search-site-button input{}div#search-bar-box{position:relative;top:auto;left:auto;margin:0px 10px;width:390px;float:left;height:100%;}div#search-bar-box .search-bar-inner{margin:15px 0px 0px;height:auto;float:left;}div#search-bar-box #edit-keys-wrapper.form-item,div#search-bar-box #edit-keys-1-wrapper.form-item{float:left;position:relative;}div#search-bar-box input.form-autocomplete{margin:0px;width:330px;height:35px;padding:2px 4px;border-radius:5px 0px 0px 5px;background-position:94% 60px;border-color:#111;position:relative;font-size:20px;}div#search-bar-box input.form-autocomplete:focus{border-color:#333;outline:none;}div#search-bar-box input.form-submit{ margin:12px 0px 20px 0px;height:41px;border-radius:0px 5px 5px 0px;background:#000; border-left:0px;box-shadow:inset 0px 11px 20px rgba(255,255,255,0.3);-webkit-appearance:none;-moz-appearance:none;}#packt-user-login-form h2,#packt-user-register-form h2{font-weight:normal;color:rgb(246,140,35);margin-left:10px;font-size:15px;margin-top:5px;}#packt-user-login-form .forgot-password{float:right;margin-right:47px;}#packt-user-login-form .submit-button,#packt-user-register-form .submit-button{clear:both;float:right;margin:10px 20px 0px;}.login-form #packt-user-login-form label,.login-register-form #packt-user-register-form label{display:inline-block;margin-right:20px;text-align:right;width:121px;}.login-form #packt-user-login-form input.form-text,.login-register-form #packt-user-register-form input.form-text{width:288px;}.login-form #packt-user-login-form .forgot-password{float:none;margin-left:235px;}.login-form #packt-user-login-form .submit-button{position:absolute;margin-left:475px;margin-top:-57px;}.checkout-login{position:absolute;top:115px;}#packt-user-register-form{width:50%;display:block;float:right;font-size:15px;font-weight:normal;margin:0px 20px 0px 0px;padding:5px 0px 9px 5px; background-color:white;background-repeat:repeat-x;color:rgb(246,140,35);border:1px solid #BCBCBD;font-size:1em;}#packt-user-register-form label{width:155px;text-align:left;margin-left:10px;display:inline-block;}.checkout-register-form #packt-user-register-form label{width:115px;}#packt-user-register-form p{color:black;}.login-register-form #packt-user-register-form label{margin-left:0px;}.login-register-form #packt-user-register-form label.option{text-align:left;width:410px;}.login-register-form #packt-user-register-form input.form-checkbox{width:auto;float:left;margin-bottom:70px;margin-right:10px;}.login-register-form #packt-user-register-form,.checkout-register-form #packt-user-register-form,.register-support-form #packt-user-register-form{width:100%;border:none;margin:0px;}.checkout-register-form #packt-user-register-form{border:1px solid #BCBCBD;}.checkout-register-form{position:absolute;top:115px;left:370px;width:320px;height:289px;}.login-register-form #packt-user-register-form .submit-button{position:absolute;margin-left:475px;margin-top:-147px;}.cart-review{}.cart-register-text{padding:10px;}.new-forms .cart-review{margin-top:140px;}.register-support-form input.form-text{width:300px;}div#login-box{border:1px solid #C9C9C9;width:262px;height:87px;position:relative;background-color:#f5f5f5;border-radius:4px;float:left;margin:10px;}div#login-box #packt-login-form-header{position:relative;float:left;}div#login-box .form-item{float:left;margin:0px;}div#login-box input.form-text{float:left;width:240px;margin:5px 5px 0px;padding:5px;}div#login-box #login-password-wrapper input.form-text{width:170px;clear:none;margin:5px 5px 0px;}#login-box .login-button{ float:left;top:auto; margin:0px;clear:none;}#login-box .login-button input.form-submit{background:#0285d5;color:#fff;border:1px solid #0F63B8;box-shadow:inset 0px 7px 10px rgba(255,255,255,0.3);font-size:12px;padding:5px 0;margin:5px 0px 0px;width:64px;-webkit-appearance:none;-moz-appearance:none;}#login-box .register,#login-box .forgot-password{margin:1px 40px 1px 5px;clear:none;float:left;}#login-box .register a,#login-box .forgot-password a{font-size:0.9em;}div#header #header-offer{position:absolute;left:747px;width:230px;top:5px;height:93px;overflow:hidden;}div#header-offer-orange{background:transparent url('/sites/all/themes/packt_new/images/pp/header-offer.jpg') no-repeat 0 0;color:white;height:96px;margin-left:235px;position:relative;width:544px;font-size:12px;float:none;margin-top:-20px;margin-bottom:5px;}div#header-offer-orange div.inner{line-height:1.2em;padding:18px 40px 18px;overflow:hidden;}#breadcrumb{width:60%;float:left;clear:both;}#secondary{width:40%;float:left}div#breadcrumb a,div#breadcrumb a:visited,div#breaadcrumb:active,div#secondary a,div#secondary a:visited,div#secondary:active{}div#breadcrumb div.inner,div#secondary div.inner{margin:5px 15px}div#secondary div.inner{text-align:right}img#free-shipping{margin-right:5px;} div#main{width:602px}div#main.noleft,div#main.noright{width:780px}div#main.noleft.noright{width:930px}div#banners,div#underbanners{margin-bottom:15px;}div#content-inner-inner{background:#fff none repeat scroll 0 0;}div#content-area{color:#000;border-left:1px solid #BCBCBD;border-right:1px solid #BCBCBD;border-bottom:1px solid #BCBCBD;padding:12px;}div#main-container div.block{ border:1px solid #BCBCBD;}#sidebar-left .block,#sidebar-right .block,#panelsidebar-left .block,#panelsidebar-right .block{width:165px;}div.block-heading{background-position:0 -586px;}div.panel-pane{}div#sidebar-left h2.block-title,div#sidebar-right h2.block-title,div.panel-heading h2.title,div.block h2.block-title{ font-weight:400;text-align:left;width:auto;margin:0 10px;padding:0;height:30px;line-height:30px;font-size:1.2em;color:#F68C23;}div#main-container .block .block-content{background-image:none;background-color:#f5f5f5;}div#footer div.block-heading{}div#footer div.block-heading h2.block-title{}div#content-header{ overflow:hidden;width:auto;background-position:0 -586px;}div#content-header h1{color:#f7921e;font-size:1.7em;line-height:38px;margin:0 0 0 .5em;padding:0;}body.page-node div#content-header{border:1px solid #b1b1b1;}div.titlebar{ background-position:0 -586px;border:1px solid #b1b1b1;}div.titlebar h2{color:#f7921e;font-size:1.5em; line-height:32px;margin:0 0 0 .5em;}div#main-container div.block.chromeless{border:0px;background-color:transparent;}div#main-container div.block.chromeless div.block-content{margin:0px;background-color:transparent;}div#main-container div.block.chromeless div.block-content-inner{margin:0px;padding:0px;background-color:transparent;}div#main-container div.block.chromeless p{margin:0em 0em 0.5em 0em;}body.main-chromeless div#content-area{border:0px;padding:0px;}body.main-chromeless div#content-header{display:none;}body.main-chromeless div.panel-pane div#content-header{display:block;}body.main-chromeless div#content-area{background-color:transparent;background-image:none;}input.form-text{border-radius:5px;height:16px;border:1px solid #A8A8A8;color:#838383;} input.button,input.pushbutton,input.form-submit{border-radius:5px;background-position:0 -904px;border-color:#000;font-size:1.2em;color:#F68C23;text-shadow:none;padding-top:3px;padding-bottom:3px;}input.button:hover,input.pushbutton:hover,input.form-submit:hover{color:#F68C23;}div.float-left{float:left;}div.float-right{float:right;}div.clear{float:none;clear:both;}div.center-align{text-align:center;}.tabs ul li a.active,.tabs ul li.active a{color:#F68C23;}p.large{font-size:1.1em;}.smaller{font-size:0.85em;}div.inner{margin:10px;}div.help{font-size:0.9em;}.red{color:red;}.underline{text-decoration:underline;}div#books-menu,div#awards-menu,div#support-menu{font-size:10px;text-align:left;display:none;width:991px;background-color:#fff;position:absolute;top:-1px;left:0px;line-height:12px;margin:49px 0px 0px;z-index:1; border-bottom-left-radius:10px;border-bottom-right-radius:10px;border:1px solid #b1b1b1;border-top:0px;}div#awards-menu{width:550px;left:323px;padding:10px 10px 5px;background-color:white;}div#support-menu{width:200px;left:306px;padding:20px 10px 5px;border-top:solid 1px #B1B1B1;background-color:white;}div#support-menu a{font-size:13px;color:black;}div#books-menu div.lower p,div#awards-menu div.lower p{font-size:1.2em;line-height:1.7em;margin:0em;color:white;}div#books-menu div.lower a,div#awards-menu div.lower a{color:black;font-size:inherit;}div#support-menu div#support-overlay,div#books-menu div#tab-overlay,div#awards-menu div#tab-overlay{width:150px;height:42px;position:relative;top:0px;}div#support-menu div#support-overlay{left:8px; top:-10px;width:85px; border:1px solid #B1B1B1; border-top-left-radius:10px;border-top-right-radius:10px;margin-top:-55px;border-bottom:1px solid black;z-index:10;padding:0px 10px;}div#books-menu div#tab-overlay{width:180px; left:133px;border:1px solid #B1B1B1; border-top-left-radius:10px;border-top-right-radius:10px;margin-top:-50px;border-bottom:1px solid black;z-index:10;}div#awards-menu div#tab-overlay{width:66px; left:467px;}div#books-menu div.tabs{}div#books-menu div.lower{background-color:white;border-bottom-left-radius:10px;border-bottom-right-radius:10px;border:1px solid white;border-top:1px solid #b1b1b1;top:-1px;position:relative;padding-top:0.5em;padding-bottom:0.5em;}div#books-menu div.tabs div.tab{}div#books-menu a.title,div#awards-menu div.title,div#awards-menu a.title{display:block;margin-bottom:10px;margin-top:5px;font-weight:700;font-size:13px;color:#fff;}div#awards-menu div.title{color:black;}div#books-menu div.books-menu-column{float:left;width:242px;height:auto;border-right:1px solid #b1b1b1;}div#books-menu div.books-menu-column.last{border-right:0px solid #b1b1b1;}div#books-menu div.books-menu-column div.inner{margin:0em 1em 0em 1em;}div#books-menu span.category-count{float:none;}div#books-menu .books-menu-column{padding-bottom:5px;padding-top:9px;margin-top:1px;}div.links span.plinks a{position:relative;}div#awards-menu div a{display:block;font-size:1.0em;}div.block-uc_cart div.links span.plinks a{z-index:0;}body.front div.panel-pane{}body.front div.panel-heading{display:block;background-position:0 -586px;height:30px;}body.front div.view-recently-publishd-books-carousel{border:1px solid #BCBCBD;border-top:0px;}div.content.clearfix h1{font-size:1.7em;font-weight:bold;margin-bottom:0.5em;}div.content.clearfix h2{font-size:1.2em;font-weight:bold;margin-bottom:0.5em;}div.box-hidden{display:none;}div.view-forthcoming-books div.views-field-title,div.view-latest-books-view div.views-field-title,div.view-taxonomy-book-list div.views-field-title{}div.bookads{float:left;width:440px;}div.bookads div.bookad{height:222px;}div.libads{float:left;margin-left:18px;width:320px;}div.packtlib-vertical-offer div.sub{width:125px;}div.packtlib-vertical-offer div.buy{margin-top:10px;width:125px;}div.details-tabs.cpage div.tab{padding:0.15em 2px;font-size:1.2em;height:22px;line-height:1.4em;width:148px;border-radius:0px;text-align:center;font-weight:bold;}ol{list-style-type:decimal;}div.news ol li{border-bottom:1px solid #d0d0d0;padding:3px 0px;height:1.8em;line-height:1.9em;overflow:hidden;list-style-position:inside;list-style-type:decimal;margin-left:0px;}div.news ol li.last{border-bottom:0px;}body.books-customised div.books_pager{position:static;top:0px;margin-bottom:10px;text-align:right;margin-right:20px;}body.books-customised .page-books #views-exposed-form-taxonomy-book-list-default{background-color:#E0E0E0;}div#main div.view-taxonomy-book-list div.views-field-title{margin-bottom:0.33em;}div#main div.view-taxonomy-book-list div.views-field-title a{color:#FF7C00;}div.view-forthcoming-books .views-field-buyitnowbutton,div.view-latest-books-view .views-field-buyitnowbutton,div.view-taxonomy-book-list .views-field-buyitnowbutton{float:none;position:relative;top:0px;margin-left:0px;margin-top:5px;padding-left:85px;}div.view-forthcoming-books div.views-field-view-node,div.view-latest-books-view div.views-field-view-node,div.view-taxonomy-book-list div.views-field-view-node{position:relative;top:13px;left:75px;margin-bottom:-11px;}div.buynow-dialogue-outer{width:500px;overflow:hidden;box-shadow:1px 1px 15px 0px #e5e5e5;-webkit-box-shadow:1px 1px 15px 0px #e5e5e5;-moz-box-shadow:1px 1px 15px 0px #e5e5e5;border-radius:10px;border:1px solid #d5d5d5;background-color:#f5f5f5;display:none;position:absolute;left:85px;top:-60px;z-index:2000;}div.buynow-dialogue-outerp{width:500px;overflow:hidden; display:none;position:absolute;left:85px;top:-60px;z-index:2000;}div.buynow-dialogue-inner{margin:10px;}div.price-bp{margin-top:1.5em;}div.buy-right{position:relative;margin-left:230px;}form#views-exposed-form-taxonomy-book-list-default{background-color:#f5f5f5;}form#views-exposed-form-taxonomy-book-list-default div#edit-availability-Forthcoming-wrapper,form#views-exposed-form-taxonomy-book-list-default div#edit-availability-Available-wrapper{float:right;margin:0px;}#views-exposed-form-taxonomy-book-list-default #edit-submit-taxonomy-book-list{background:url('/sites/all/themes/packt_new/images/orange-button.png') no-repeat;position:absolute;height:31px;margin-left:518px;margin-top:-33px;width:118px;font-size:1.3em;border:none;color:black;text-shadow:#ccc 1px 1px 1px;font-weight:bold;}.page-books #views-exposed-form-taxonomy-book-list-default #edit-submit-taxonomy-book-list{}#book_taxonomy_view #edit-keys{width:230px;height:26px;font-size:1em;}#book_taxonomy_view .views-exposed-widget{float:left;width:auto;margin:0px;line-height:28px;font-size:1.2em;padding:0px;margin-top:0.5em;}#book_taxonomy_view .views-exposed-widget select{margin:0.4em 0em;}.page-books #views-exposed-form-taxonomy-book-list-default{height:100px;}div.packtlib-banner-box-large{width:298px;height:106px;background-image:url(/sites/all/themes/packt_new/images/packtlib-banner-box-large.png);background-repeat:no-repeat;overflow:hidden;}div.packtlib-banner-box-large div.inner{width:140px;margin:5px;padding:0px;}div.packtlib-vertical-offer.large{width:auto;margin-left:0px;font-weight:bold;}div.sharing p{float:left;margin-right:30px;}div.sharing div.addthis_toolbox{float:left;margin-top:7px;}div.book-image.tall{height:200px;}div.views-field-view-node span.field-content a{z-index:2;position:relative;}div#main div.view div.views-row{margin-bottom:25px;}div#main div.view div.views-row h1 a{font-weight:bold;}div#main ul.pager li{margin-left:0.25em;}div.block-form-newsletters div.form-item input.form-text{width:130px;}div.block-form-newsletters input.subscribe-form-submit{margin-left:0px;}div#knowbomb h2.title{color:black;margin-left:0px;font-weight:bold;}div.jcarousel-container ul li,div.messages ul li{list-style-image:none !important;}div#footer div.block{height:auto !important;}body div#footer{background-color:#F7921E;}div#footer a.packt-new-footer{font-size:15px;font-weight:lighter;}div#footer p.packt-new-footer2{margin-top:15px;} table td.content-header,#related_items_block h2,#exclusive_offers_block h2,#downloads_block h2,#orders_block h2,div.full_order_block .block-inner{background-position:0 -586px;color:#F68C23;}.page-account fieldset legend{color:#000;font-weight:bold;margin-left:0px;}.page-account fieldset#account_fieldset,.page-account fieldset#ba_fieldset{float:left;width:48%;margin-right:2%;}div#footer div.block-packt_subscribe input.form-image{width:auto;margin-left:5px;}.profile .form-submit{color:#F68C23;height:auto;}.page-account #orders_block input#edit-submit{margin-left:20px;width:auto;} body.page-support .views-field-field-downloads-fid{margin-top:3em;}fieldset#cart-pane{padding-top:10px;}div#main-container div.block-countdown_banner div.block-content{background-color:#FDA;}.captcha{display:none;height:0px;}#page div#book-info .captcha,#form-packt-nid-outer-html .captcha{display:block;height:auto;}.table-hr{position:relative;top:-11px;background-color:#BCBCBD;}.boxy-inner pre{white-space:pre;}#packt-send-to-friend-send-form-popup .captcha{display:block;height:auto;}#code-download-form .captcha{display:block;height:auto;clear:both;}#code-download-form #edit-submit{position:absolute;right:30px;margin-top:80px;}.boxy-shipping-alert-close{text-align:right;height:30%;font-size:1.6em;}.boxy-shipping-alert-text{text-align:center;height:70%;font-size:1.8em;font-weight:bold;}.table-hr{position:relative;top:-11px;background-color:#bcbcbd;}#title-select-form input#edit-submit{float:right;margin-top:-40px;}#code-download-form input#edit-submit-1{float:right;margin-top:-80px;}.page-cart-checkout #payment2-pane{ padding-top:50px;margin-top:-145px;*margin-top:-100px;position:absolute;*position:none;right:0;top:145px;margin-right:-9px;}.cart-card-image-top{width:575px;text-align:center;position:absolute;top:8px;margin-left:-157px;}.cart-entrust-logo-top{float:right;margin-right:130px;margin-top:-25px;}.cart-sameas-billing{width:300px;}form#uc-cart-checkout-form #packtcountry-vat-number-pane .form-item{width:276px;}form#uc-cart-checkout-form #packtcountry-vat-number-pane{float:left;position:absolute;width:460px;left:13px;margin-top:-180px;}.cart-totals-left{background:none;background-color:white;border:none;}.cart-border-left-none{border-left:none;}.price{color:black;}.table.cart-review tr.subtotal td{color:black;}.cart-checkout-buttons-bottom{float:left; display:block;margin-top:120px;}form#uc-cart-checkout-form fieldset#payment-pane{position:relative;margin-top:-130px;}.cart-checkout-sprite{background-image:url('/sites/all/themes/packt_new/images/pp/footer-payment-types.png');}.cart-checkout-worldpay-sprite{height:16px;width:121px;position:absolute;top:0px;margin-top:10px;margin-left:-209px;background-position:-146px -37px;}.cart-checkout-paypal-sprite{height:24px;width:87px;background-position:-170px -121px;position:absolute;top:0px;margin-top:7px;margin-left:-75px;}fieldset#billing-pane,fieldset#delivery-pane{display:block;width:331px;}body fieldset#customer-pane{}div.checkout-order-box{float:left;height:60px;width:100%;}div.checkout-order-box-inner{width:667px;height:33px;margin-left:3px;}.cart-checkout-header-order{font-size:15px;font-weight:normal;color:#F68C23;padding:5px 0px 9px 5px;margin-top:-6px;margin-right:10px;float:left;}#uc_discounts-pane input,#packtcountry-vat-number-pane input{*width:150px;}body.shippable form#uc-cart-checkout-form #packtcountry-vat-number-pane{margin-top:-204px;}body.shippable form#uc-cart-checkout-form #uc_discounts-pane{margin-top:-248px;}body #customer-pane .address-pane-table{border-top:1px solid #BCBCBD;}.logged-out-sprite-worldpay{height:16px;width:121px;background-position:-146px -37px;float:left;margin-right:10px;margin-top:5px;}.logged-out-sprite-paypal{height:24px;width:87px;background-position:-170px -121px;float:left;margin-right:8px;}.logged-out-sprite-wrapper{width:494px;float:right;right:0;position:absolute;height:50px;margin-top:-43px;*margin-top:-103px;}.1000-packt-register-wrapper{display:none;height:400px;width:670px;float:right;margin-top:-10px;}div.1000-packt-login{}body #top-banner.packt1000{ background-color:#353535;background-repeat:no-repeat; background-position:-9px -4px;height:126px;}body #top-banner.packt1000 label{color:#F7921E;}div.viewcar-new-sel{height:auto;width:120px;float:left;}div#new-car-view{overflow:hidden;height:190px;padding-left:45px;background:url(/sites/all/modules/viewcarousel/skins/packt/grad.png);background-color:#9D9D9D;background-position:0 -936px;width:735px;padding-top:20px;}div#new-car-view div.views-field-title{height:40px;width:90px;line-height:13px;font-size:11px;color:white;overflow:hidden;}div#new-car-view .views-label-title,div#new-car-view .views-field-field-author-nid,div#new-car-view .views-label-title-1{display:none;}div#new-car-view span.field-content a{font-weight:bold;}div#new-car-view div.views-field-title span.field-content a{color:white;}div.lightweight-carousel{background-image:url(/sites/all/themes/packt_new/images/sprite-horizontal-gradients.png);background-position:0 -936px;background-repeat:repeat-x;width:743px;height:193px;padding-top:20px;padding-left:37px;}body div#main div.lightweight-carousel ul li{display:block;list-style-image:none;margin:0px 20px;width:75px;float:left;text-align:left;}div.lightweight-carousel div.views-field-field-one-liner-value{display:none;}div.lightweight-carousel div.views-field-title label.views-label-title{display:none;}div.lightweight-carousel div.views-field-title{line-height:13px;}div.lightweight-carousel span.field-content,div.lightweight-carousel span.field-content a{font-size:11px;color:white;font-weight:bold;}div.lightweight-carousel span.field-content a{display:block;height:30px;}div.lightweight-carousel div.field-lightweight-url a{color:#FF7C00;line-height:1em;}div.video-logo{width:207px;height:76px;background-image:url(/sites/all/themes/packt_new/images/packt_video_logo.png);background-repeat:no-repeat;float:right;margin-top:-35px;margin-right:55px;margin-bottom:30px;}div.node-type-videos div.price div.add-to-cart-button{margin-top:20px;}div.video-beta-container{border:1px solid #b1b1b1;padding:10px;}div.video-beta-container strong{color:#F68C23;}.no-overflow{overflow:visible !important;}.price-inner-video{width:130px;margin-top:15px;}.prices-videos{width:50%;float:right;}div.upsell-page h1{font-size:2em;font-weight:bold;color:#F68C23;}div.upsell-page h2{font-size:1.5em;font-weight:bold;color:green;}div.top-pane{border-top:2px solid black;border-bottom:2px solid black;margin-top:1em;}div.sold-nodes{float:left;width:72%;}div.checkout-button{float:left;width:25%;margin-top:1em;}div.sold-node-inner{margin:1em;}div.sold-node-inner div.sold-node-image{float:left;width:150px;padding:0em 1em 1em 1em;}div.sold-node-inner div.sold-node-title{float:left;width:500px;font-size:1.2em;}div.upsell-nodes{width:100%;margin-top:2em;}div.upsell-node{float:left;width:25%;height:310px;}div.upsell-node-inner{margin:1em;}div.upsell-node-inner.first{margin-left:0em;}div.upsell-node-inner.last{margin-right:0em;}div.upsell-node-picture{height:152px;padding-left:1em;}div.upsell-node-title{margin-top:0.25em;font-size:1.2em;color:#F68C23;height:2.4em;overflow:hidden;}div.upsell-node-desc{height:60px;}input.form-submit.xl{font-size:2.0em;background-position:0 0px;background-repeat:repeat-x;background-image:url(/sites/all/themes/packt_new/images/sprite-horizontal-gradients2.png);}div.packt-register-banner{background-image:url("/sites/all/themes/packt_new/images/new-account-banner-register.png");position:relative;width:100%;height:120px;}input.form-submit.orange{border-radius:5px;background-image:url("/sites/all/themes/packt_new/images/button-bg-orange.png");background-position:0px 0px;color:black;font-weight:bold;border-color:transparent;border:0px;font-size:1.3em;background-repeat:repeat-x;height:30px;}div.packt-anytime-journey{position:relative;height:116px;display:block;width:776px;}div.packt-anytime-journey-block1{background-image:url("/sites/all/themes/packt_new/images/packt-anytime-sprite.png");width:200px;height:116px;float:left;display:block;background-position:10px;}div.packt-anytime-journey-arrow{background-image:url("/sites/all/themes/packt_new/images/packt-anytime-sprite.png");width:83px;height:116px;float:left;display:block;background-position:-485px;}div.packt-anytime-journey-block2{background-image:url("/sites/all/themes/packt_new/images/packt-anytime-sprite.png");width:200px;height:116px;display:block;float:left;background-position:-278px;}div.packt-anytime-journey-block3{background-image:url("/sites/all/themes/packt_new/images/packt-anytime-sprite.png");width:200px;height:116px;float:left;display:block;background-position:-566px;}div.packt-anytime-tandc-wrapper{display:block;float:left;position:relative;width:98%;padding:10px;}form#packt-upgrade-register-special-form .captcha{display:none;}body div.upsell-page h2.anytime-subheading{color:#578ED2;font-size:1.2em;}body div.upsell-page h1.anytime-mainheading{color:#F5964F;}form#packt-upgrade-register-login-form input[type=submit],form#packt-upgrade-register-special-form input[type=submit]{float:right;margin-right:10px;}#activate-done-button form#packt-libraries-upgrade-add-to-cart-form-print-only input[type=image]{margin-top:9px;}div.print-upgrade-off{margin:4px 0 0 16px;display:block;}div.add-to-cart-button.bundle{margin-top:9px;}div.clear.upto-fifty-percent{margin-bottom:18px;}div.price-right span{margin-left:-65px;}div.screenshot-list{padding-top:2em;text-align:center;color:#F68C23;}div.screenshot-list p{margin-top:0px;margin-bottom:1.5em;font-size:1.2em;}a#packtlib_subscriptions img{width:140px;}div.books_pager .item-list{}.books_pager .item-list .pager{text-align:right;}#top-banner{background-image:url(/sites/all/themes/packt_new/images/new_anytime_black_top_banner.png)!important;background-color:#323335!important;background-repeat:no-repeat;background-position:678px 5px;}#header-offer{display:none!important;}#top-banner .search-site-switch label{color:#F59131!important;}#block-block-148 p{margin:0!important;}div.article-win-close{position:absolute;z-index:10;height:40px;width:40px;margin-left:600px;top:10px;cursor:pointer;}div.article-win.boxy-content{padding:0px;}.page-account div.full_order_block #recent-orders-form #edit-submit{background:none;background-image:url(/sites/all/themes/packt_new/images/sprite-horizontal-gradients.png);background-repeat:repeat-x;border-radius:5px;background-position:0 -904px;border:1px solid #000;font-size:1.2em;color:#F68C23;text-shadow:none;padding:1px 7px;width:auto;height:26px;margin:0px 7px;position:relative;}#recent-orders-form #edit-order-select{width:auto;}div.inner div.purchase-copy{margin:15px 0px;}div.inner div.purchase-copy h3{font-weight:bold;}#webapp-cookie-outer-outer .cookie-inner p,#webapp-cookie-outer-outer form{text-align:center!important;}body.node-type-article div#content-area{background:none;}.node-type-article .article-summary a{font-weight:bold;color:#00A2E8;}.node-type-article .article-summary.version-b{}.node-type-article .article-book-block{float:right;margin:0 0 15px 20px;width:320px;padding-bottom:10px;}.node-type-article .article-book-block .article-book-header{font-size:16px;overflow:hidden;background-position:0 -586px;height:30px;line-height:32px;padding-left:7px;color:#fff;background:#FF7C00;}.node-type-article .article-book-block .article-book-image{width:50%;height:150px;float:left;text-align:center;}.node-type-article .article-book-block .article-book-image img{margin-top:10px;}.node-type-article .article-book-block .article-book-info{width:48%;margin-left:1%;margin-left:1%;color:#ccc;float:left;}.node-type-article .article-book-block .article-book-info h3{color:#000;font-size:13px;margin-top:10px;}.node-type-article .article-book-block p.article-book-summary{font-size:10px;color:#333;}.node-type-article .article-book-block .article-book-info h3 .discount{color:#F68C23;}.article-book-block{background:#F5F5F5;}.node-type-article .article-book-block .old-book-price{text-decoration:line-through;}.node-type-article .article-book-block .new-book-price{color:#F68C23;font-size:15px;}.node-type-article .article-summary .cta-button{background:#3A7BB6;color:#FFFFFF;display:block;float:none;font-weight:normal;line-height:1em;padding:2px 0;text-align:center;width:170px;margin:0 auto;height:2em;}.node-type-article .article-summary .cta-button:hover{text-decoration:none;}.article-book-block #view-book{float:right;background:#BBB9BA;}.article-book-block .buynow-dialogue{color:#000;}.article-book-block .cta-container{position:relative;margin:10px 7px 0px 7px;}.buynow-dialogue div a img{vertical-align:text-top;}.node-type-article .cta-container input.form-submit.cta-button{background-color:#3A7BB6;}.node-type-article .article_conversion_form{margin-bottom:40px;}.node-type-article .book-ad{margin-top:40px;}div#packt_article_login{background-color:white;border:1px solid #BCBCBD;}div#packt_article_login div.inner{margin:10px;}div#packt_article_login form#packt-login-form label,form#packt-registration-form label{width:121px;}div.packt-hidden-article{overflow:hidden;height:auto;}div.packt-hidden-article div.fadeout{position:absolute;height:750px;width:576px;margin:0px;background-image:url(/sites/all/themes/packt_new/fadeout.png);background-position:bottom center;background-repeat:repeat-x;}div#packt_article_login .register-form .submit-button{margin-left:515px;}form#packt-login-form input.form-image{width:auto;}form#packt-login-form input.form-text,form#packt-registration-form input.form-text{width:288px;}div#packt_article_login form#packt-login-form div.submit-button{position:relative;margin-top:-60px;margin-left:516px;height:60px;}div.rest-article-wrapper .rest-article-header{height:30px;width:100%;text-align:center;line-height:30px;background-color:#00A2E8;color:white;}div.rest-article-wrapper #packt_article_login .inner{margin:0px;}div.rest-article-wrapper .rest-article-header h3{}div.rest-article-inner-wrapper{width:100%;height:30px;}div.inner-article-register{width:314px;height:auto;border-right:1px solid #BCBCBD;float:left;padding:10px;}div.inner-article-login{width:219px;height:auto;float:left;padding:10px;}div.inner-article-register h3,div.inner-article-login h3{ font-weight:bolder;color:#F68C23;padding:5px;padding-left:0px;}#packt-article-register-form label,#packt-article-login-form label,#packt-article-login-form .form-item input{float:left;}#packt-article-register-form .form-item input{float:right;width:190px;}#packt-article-register-form .form-item{height:20px;}#packt-article-login-form .form-item{height:35px;}#packt-article-login-form .form-item input{width:100%;}#packt-article-register-form #edit-submit,#packt-article-login-form #edit-submit-1{float:right;}.inner-article-register ul{margin-left:20px;}.inner-article-register ul li{list-style-image:url(/sites/all/themes/packt_new/images/article_tick.png)!important;}.inner-article-register p{font-weight:bolder;font-size:14px;}.inner-article-register ul li{margin-bottom:1em;}.inner-article-register ul{margin-bottom:2em;}.anytime-login-banner{margin-left:113px;}#edit-submitted-how-did-you-hear-about-packt-other-reason-wrapper label{display:none;}#edit-submitted-how-easy-was-it-for-you-to-navigate-on-packtpubcom-wrapper{clear:both;}.feedback-form .form-item label.option{padding-right:12px;}.article-book-header,.feedback .feedback-inner{ background-color:#F79621;}div.feedback-inner{color:#FFFFFF;font-size:15px;height:20px;padding:5px 0 0 10px;}.partner-offer-price{}.newsletter-terms{padding-top:15px;}div.upsell-page h1{font-size:2em;font-weight:bold;color:#F68C23;}div.top-pane{border-top:2px solid black;border-bottom:2px solid black;margin-top:1em;}div.sold-nodes{float:left;width:72%;}div.checkout-button{float:left;width:25%;margin-top:1em;}div.sold-node-inner{margin:1em;}div.sold-node-inner div.sold-node-image{float:left;width:150px;padding:0em 1em 1em 1em;}div.sold-node-inner div.sold-node-title{float:left;width:340px;font-size:1.2em;}div.upsell-nodes{width:100%;margin-top:2em;}div.upsell-node{float:left;width:25%;height:310px;}div.upsell-node-inner{margin:1em;}div.upsell-node-inner.first{margin-left:0em;}div.upsell-node-inner.last{margin-right:0em;}div.upsell-node-picture{height:152px;padding-left:1em;}div.upsell-node-title{margin-top:0.25em;font-size:1.2em;color:#F68C23;height:2.4em;overflow:hidden;}div.upsell-node-desc{height:60px;}input.form-submit.xl{font-size:2.0em;background-position:0 0px;background-repeat:repeat-x;background-image:url(/sites/all/themes/packt_new/images/sprite-horizontal-gradients2.png);}body div.upsell-node{height:355px;margin-bottom:40px;}div.upsell-node input.form-submit{margin-bottom:0px;width:120px;}a.form-submit,a.form-submit:hover{-moz-appearance:none;background:none repeat scroll 0 0 #0285D5;border:1px solid #0F63B8;box-shadow:0 7px 10px rgba(255,255,255,0.3) inset;color:#FFFFFF;font-size:12px;margin:5px 0 0;padding:5px 0;width:64px;text-decoration:none;border-radius:5px 5px 5px 5px;}.view-product a.form-submit{display:block;margin:3px 0;text-align:center;width:116px;font-size:14px;}div.upsell-node div.add-to-cart-button.bundle,div.upsell-node div.add-to-cart-button.ebook{margin-top:9px;}.newsletter-terms{padding-top:15px;} .node-type-book-offer div.banners{margin:0 auto;cursor:pointer;}.node-type-book-offer div.banners div.top-banner,.node-type-book-offer div.banners div.bottom-banner{width:782px;margin-bottom:0em;}.node-type-book-offer div.banners div.bottom-banner{background-color:#352c2f;height:80px;}.node-type-book-offer div.banners div.bottom-banner div.text{font-size:2em;line-height:1em;margin:0 1em;color:white;width:66%;float:left;margin-top:0.75em;}.node-type-book-offer div.banners div.bottom-banner span.text-subtitle{font-size:0.75em;color:#898788;}.node-type-book-offer div.banners div.bottom-banner div.button{float:left;margin:18px auto;padding:0px 1em;height:44px;font-size:1.4em;line-height:34px;}.node-type-book-offer div.upsell-more{padding:2em 0em;}.node-type-book-offer div.upsell-more.padding{padding:4em 0em 2em 0em;}.node-type-book-offer div.upsell-see-more-line{height:1px;width:100%;border-top:1px solid #dcdcdc;}.node-type-book-offer div.upsell-see-more{margin:0 auto;position:relative;background-color:white;text-align:center;padding:0.5em 0em;margin-top:-1em;text-align:center;width:40%;font-weight:bolder;border-radius:5px;border:1px solid #dcdcdc;cursor:pointer;font-size:2em;}.node-type-book-offer div.extra-books{margin:0 auto;position:relative;background-color:white;text-align:center;padding:0.5em 0em;margin-top:-1.5em;text-align:center;width:75%;}.node-type-book-offer span.additional-books-title{font-weight:bold;font-size:2em;}.node-type-book-offer span.additional-books-subtitle{font-size:1.5em;font-style:italic;color:#898788;}.node-type-book-offer div.extended-nodes{display:none;}.node-type-book-offer div.upsell-node{height:auto !important;width:25%;}.node-type-book-offer div.offer-discount-offer{border:4px dashed #F68C23;padding:2em 0em;margin:2em 0em;}.node-type-book-offer div.offer-discount-offer p{margin:0em auto;font-size:2em;text-align:center;}.node-type-book-offer div.offer-category-links{font-size:1.5em;margin:0.2em 0em;text-align:center;line-height:1.25em;color:#898788;}.node-type-book-offer span.category-link{}.node-type-book-offer h1,.node-type-book-offer div.offer-body{margin-top:1.5em;}.node-type-book-offer input.form-submit{background:#F68C23;background-image:transparent;border-radius:0px;border:0px;color:white;padding:0.5em;}.node-type-book-offer div.upsell-node input.form-submit{width:100%;padding:0.5em 0em;}.node-type-book-offer div.upsell-node-picture{padding-left:0em;height:auto;}.node-type-book-offer div.prices{font-size:1.4em;width:100%;line-height:2em;}.node-type-book-offer div.prices div.discounted-price{font-weight:bold;color:#377bc4;float:left;width:50%;}.node-type-book-offer div.prices div.cover-price{text-decoration:line-through;float:left;width:50%;text-align:right;}div.social-footer{width:100%;line-height:2em;font-size:1.6em;font-weight:bold;background-color:#f0f0f0;overflow:hidden;}div.social-footer div.inner{margin:0em 1em;}div.social-footer table{margin:0em;}div.social-footer table td{padding:1em 0em;}div.social-footer td.br{border-right:1px solid #e3e3e3;}div.social-footer div.facebook,div.social-footer div.googleplus,div.social-footer div.linkedin,div.social-footer div.twitter,div.social-footer div.email{width:34px;height:34px;display:block;overflow:hidden;margin-left:33px;}div.social-footer div.facebook{height:32px;margin-top:1px;margin-bottom:1px;}div.social-footer div.linkedin,div.social-footer div.email{height:30px;margin-top:2px;margin-bottom:2px;}div.social-footer div.facebook img,div.social-footer div.googleplus img,div.social-footer div.linkedin img,div.social-footer div.twitter img,div.social-footer div.email img{position:relative;}div.social-footer div.twitter img{top:-140px;}div.social-footer div.facebook img{top:-174px;}div.social-footer div.linkedin img{top:-240px;}div.social-footer div.googleplus img{top:-206px;}div.social-footer div.email img{top:-269px;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal{padding:0px;width:100%;margin:0em;}div.two-sidebars div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal{margin:0 5.5em;height:auto;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal{padding:20px 47px;}div#cart-box div.cart_button_group div.checkout_button{background-color:#00C508;border:1px solid #00C508;}div.main-chromeless.page-books #views-exposed-form-taxonomy-book-list-default #edit-submit-taxonomy-book-list{margin:0px; position:absolute; left:375px;height:38px;width:40px; text-indent:-9999px; background-position:-8px -8px; border:1px solid orange;background-image:url(/sites/all/themes/packt_v3/design/images/search-button.png);box-shadow:none;}div.section-authors div.views-field-field-image-fid{margin-top:10px;}div#autocomplete{margin-top:30px;margin-left:8px;width:171px;background-color:white;}div#main div#autocomplete li{list-style:none;margin-left:5px;}.affiliate-links{position:relative;margin:0 auto;display:block;text-align:center;font-size:18px;margin-bottom:10px;}body.page-book div#content-area{background-image:none;background-color:#fff;border:0;padding:12px 0}div.bpleft{float:left;width:54%;overflow:hidden; margin-left:0.5em;}div.bpright{float:right;width:35%;}div.author{font-size:1.2em;line-height:36px;color:white;text-align:right;margin-right:15px;}body.page-book div#content-header h1{color:#f7921e;font-size:1.7em;line-height:1.6em;margin:0.15em 0 0.15em 0em;padding:0;width:100%;}div#book-page{margin-bottom:2em;}div.author a,div.author a:hover,div.author a.active{color:inherit;}div.packtlib-toggle{display:inline-block;overflow:hidden;width:56px;*display:inline;}div.anchors{margin:0em 0em;}div.cover-prices{margin:1em 0em;overflow:hidden;width:100%;}div.cover-images{float:left;width:42%;}div.cover-images div.inner{margin:0em 1em;}div.prices{float:left;width:58%;}div.prices div.inner{margin:10px;}div.book-image{float:left;margin:0em 1em 1em 0em;}div.book-image.centred{float:left;margin:0em auto auto 0em;width:100%;text-align:center;}div.price{background-color:#f5f5f5;border:1px solid #b1b1b1;border-bottom:0px;height:70px;}div.price.last{border-bottom:1px solid #b1b1b1;}div.price div.price-inner{margin:10px 0px 10px 10px;font-size:1.1em;float:left;width:190px;}div.price div.add-to-cart-button{float:right;margin:10px 10px 0px 0px;}div.free-shipping{ line-height:19px;width:180px;float:right;margin-top:0.4em;}div.other-locations{margin:1em 0em;line-height:22px;text-align:center;}div.packtlib-subscribe-banner{margin:3em 0em;text-align:center;}div.details-tabs{margin:2em 0em;}div.details-tabs div.tab{border:1px solid #b1b1b1;border-bottom:0px;padding:0.3em 0.9em;margin-right:5px;line-height:30px;font-size:1.5em;float:left;background-position:0 -586px;color:#F68C23;border-bottom:1px solid transparent;cursor:pointer;}div.details-tabs div.tab.active{background-image:none;background-color:white;color:black;border-bottom:1px solid white;cursor:default;}div.tab-content{border:1px solid #b1b1b1;margin-top:-2px;}div.tab-content div.tab-panel{margin:1em;display:none;}div.tab-content div#panel-overview{margin:0em;}div.tab-content div.tab-panel.active{display:block;}div.lightwindow a{cursor:pointer;}div.titlebar{margin-top:2em;}div#special_offers_centre{float:left;width:498px;min-height:100px;margin-top:1em;margin-right:10px;}div#special_offers_centre_small,div#special_offers_right,div#special_offers_left{float:left;width:249px;min-height:100px;margin-top:1em;margin-right:10px;}div.packtlib-vertical-offer{margin-left:5px;background-color:#f5f5f5;border:1px solid #b1b1b1;}div.packtlib-vertical-offer div.inner{margin:10px;}div#special_offers table td{padding:0px 5px;}div.real-price{width:100px;margin:15px 10px 10px 0px;float:left;}span.larger{font-size:1.5em;}span.price{color:#bbb;text-decoration:line-through;}span.saving{color:red;}div.tab-panel ul li{margin-left:1.1em;}div.overview_left{float:left;width:450px;padding:1em;}div.overview_right{border-left:1px solid #b1b1b1;float:left;width:278px;padding:1em;}div.overview_left div.inner{margin:0em 1em 0em 0em;}div.overview_right div.inner{margin:0em 1em;}form#packt-subscribe-body-form-function div.form-item{float:left;margin-right:1em;}form#packt-subscribe-body-form-function div.form-item input.form-text{height:1.3em;font-size:1.3em;}form#packt-subscribe-body-form-function div.form-item label{margin-bottom:0.3em;font-size:1.2em;}form#packt-subscribe-body-form-function input.form-submit{width:110px;height:31px;font-size:1.3em;text-shadow:black 1px 1px;color:#F68C23;margin-top:1.9em;border:0px;} .jcarousel-skin-packt .jcarousel-container{background-position:0 -936px;border:none;-moz-border-radius:0px;}.jcarousel-skin-packt .jcarousel-prev-horizontal:active,.jcarousel-skin-packt .jcarousel-prev-horizontal:hover{}.jcarousel-skin-packt .jcarousel-prev-horizontal{background-position:1px 0px;cursor:pointer;height:59px;left:10px;position:absolute;top:43px;width:24px;}.jcarousel-skin-packt .jcarousel-next-horizontal{background-position:-24px 0px;cursor:pointer;height:59px;position:absolute;right:10px;top:43px;width:24px;}.jcarousel-skin-packt .jcarousel-container-horizontal{height:170px;padding:20px 46px 20px 48px;}.jcarousel-skin-packt .views-label-title{display:none;}.jcarousel-skin-packt .views-field-field-features-value{display:none;}.jcarousel-skin-packt .views-field-title{height:40px;overflow:hidden;}.jcarousel-skin-packt .views-field-title-1 label{display:none;}.jcarousel-skin-packt .views-field-title-1 label{display:none;}.jcarousel-skin-packt a{color:#FF7C00;font-size:11px;}.section-open-source-awards-hometesttest #main .panel-pane .content .jcarousel-skin-packt .views-field-title-1 .field-content,#block-views-carousel-block_1 .jcarousel-skin-packt .views-field-title-1 .field-content{display:block;padding-top:3px;}#block-views-carousel-block_1 .item-list ul li,.section-open-source-awards-hometesttest #main .panel-pane .content .item-list ul li{float:left;margin-bottom:10px;margin-top:10px;font-size:11px;line-height:13px;list-style-type:none;}.section-open-source-awards-hometesttest #main .panel-pane .content .item-list ul li.jcarousel-item,#block-views-carousel-block_1 .item-list ul li.jcarousel-item{margin-top:0px;margin-bottom:0px;}#block-views-carousel-block_1 .item-list ul li a,#block-views-carousel-block_1 .item-list ul li a:hover,#block-views-carousel-block_1 .item-list ul li a:visited,.section-open-source-awards-hometesttest #main .panel-pane .content .item-list ul li a{color:#fff;font-weight:bold;}div#content div.jcarousel-skin-packt .jcarousel-container-horizontal{padding:20px 47px;height:100%;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal{width:506px;border:0px solid black;border:none;margin:0px;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal{width:auto;margin-left:11px;margin-right:11px;height:173px;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal ul{height:100%;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal li{margin:0px 14px;width:95px;text-align:left;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal li div.views-field-field-image-fid,div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal li div.views-field-field-image-fid img{height:115px;width:87px;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal li div.views-field-field-image-fid img{height:auto;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal li div.views-field-title{height:40px;overflow:none;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal li a{font-weight:bold;color:white;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal li div.views-field-title-1{margin-top:3px;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal li div.views-field-title-1 a{color:#FF7C00;line-height:1em;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal li div{height:auto;width:100%;line-height:13px;font-size:11px;}div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal li div.field-content{line-height:85px;}div.carousel h2.block-title{text-align:left;}div#main.noleft div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal{width:684px;}div#main.noleft div#content div.jcarousel-skin-packt div.jcarousel-container.jcarousel-container-horizontal .jcarousel-clip-horizontal li{margin:0px 20px;}div.availability_bookpage{margin-left:327px;}div.availability_bookpage_video{margin-left:0px;}div#special_offers_left table{border-color:#B1B1B1 !important;}div#body_newsletter{margin-top:20px;}div.new-version{margin:1.0em 0em 1.5em;}div.erratum{border:1px solid #b1b1b1;background-color:#f5f5f5;margin-bottom:1.5em;}div.erratum div.inner{margin:1em;}h3.inline{display:inline;}div#panel-downloads h3{font-weight:bold;margin-bottom:0.5em;}div#panel-downloads hr{margin:1em 0em;}form#code-downloads-button{position:relative;margin-top:-39px;margin-left:400px;}div.free-shipping-inner{float:right;width:55px;font-size:0.85em;line-height:12px;margin-top:0px;*margin-top:-18px;margin-right:5px;}.upsell-two{position:absolute;width:280px;height:105px;background-color:white;border:1px solid black;right:0;}.inner-upsell-two{margin-top:20px;}.inner-upsell-two table td{text-align:center;}span.print-cover-price{color:#666;font-size:0.7em;line-height:1em;}div#instant-banner{height:90px;color:#F68C23;line-height:90px;font-size:1.8em;width:100%;}div#instant-banner div.instant-banner-inner{background-color:black;border:1px solid #AAA;text-align:center;}div.other-locations.floater{width:490px;position:relative;margin-top:-69px;margin-left:292px;}div.other-locations.floater td{padding:5px 0px;}div.node-type-subscriptions div.prices{width:100%;}div.node-type-subscriptions div.price-choice{width:49%;display:inline-block;}div.node-type-subscriptions div.price{border-bottom:1px solid #b1b1b1;}div.node-type-subscriptions div.price div.price-inner{margin-top:25px;width:150px;}div.node-type-subscriptions div.real-price{margin-top:25px;margin-left:0px;}div.node-type-subscriptions div.cover-images{margin-top:0em;}div.node-type-subscriptions div.cover-prices{margin:0px;}div.node-type-subscriptions div.cover-description{padding:15px;margin-bottom:10px;}div.node-type-subscriptions div.product-description{ font-size:1em;margin-top:50px;display:inline-block;width:50%;}body div#primary-links,body .block-heading,body div.homepage-block,body #footer,body #sidebar-right .block-uc_cart div.links,body #content-area,body #content-bottom,body div#content-header,body.front div.panel-pane div.panel-heading,body div.titlebar,body .jcarousel-skin-packt .jcarousel-container,body div.details-tabs div.tab,body table td.content-header,body div.staf div.header,body div.staf div.accordion div.content,body div.staf div.accordion div.content,body #related_items_block h2,body #exclusive_offers_block h2,body #downloads_block h2,body #orders_block h2,body div.full_order_block .block-inner,body table.cart-review tr.subtotal.top td,body input.button,body input.pushbutton,body input.form-submit{background-image:url(/sites/all/themes/packt_new/images/sprite-horizontal-gradients.png);background-repeat:repeat-x;}#page #shadow-left,#page #shadow-right,#page #shadow-bottom{background-image:url(/sites/all/themes/packt_new/images/sprite-content-shadow.png);}body div.search-site-button input,body div#header #header-offer div.header-left,body div#header #header-offer div.header-right,body div.search-site-bar,body div#block-packt_subscribe-0.block div.block-content-inner input#edit-subscribe.go-button.form-submit,body div#block-packt_subscribe-0.block div.block-content-inner input#edit-subscribe-1.go-button.form-submit,body #footer div#block-packt_subscribe-1.block div.block-content-inner input#edit-subscribe-1.go-button.form-submit,body div#block-packt_subscribe-1.block div.block-content-inner input#edit-subscribe-2.go-button.form-submit,body #sidebar-right #block-uc_cart-0 .links span a,body .jcarousel-skin-packt .jcarousel-prev-horizontal,body .jcarousel-skin-packt .jcarousel-next-horizontal,.offer-star,.buy-with-confidence,.packtlib-subscribe-logo,#packtpub-logo,#packtlib-logo,.we-accept,#login-box .login-button input#edit-submit,#block-packt_subscribe-0 input#edit-subscribe,#title-select-form input#edit-submit,#code-download-form input#edit-submit-1,.cart-checkout-sprite,.logged-out-sprite{background-image:url(/sites/all/themes/packt_new/images/sprite-assets.png);background-repeat:no-repeat;}#login-box .login-button input#edit-submit,#block-packt_subscribe-0 input#edit-subscribe,#title-select-form input#edit-submit,#code-download-form input#edit-submit-1{background-position:-70px -68px;border:none;width:31px;height:31px;color:transparent;text-transform:capitalize;text-indent:31px;background-color:transparent;}.offer-star{float:left;margin:8px 7px 0 0;width:21px;height:22px;overflow:hidden;background-position:-101px -69px}.we-accept{background-position:-145px -37px;height:108px;margin:10px 0 15px 36px;overflow:hidden;text-indent:-999px;width:130px;}.buy-with-confidence{width:166px;height:52px;display:block;text-decoration:none;overflow:hidden;background-position:0 -176px;}.packtlib-subscribe-logo{background-position:-167px -144px;margin:0 0 0 37px;width:66px;height:81px;overflow:hidden;}#packtpub-logo{overflow:hidden;text-indent:-999px;width:86px;height:36px;background-position:-37px -99px;margin:0 0 0 22px;}#packtlib-logo{overflow:hidden;text-indent:-999px;width:96px;height:29px;background-position:-40px -137px;margin:-1px 0 0 0;}div.content-inner{margin:10px;}table.packt-table tr td,table.packt-table tr th{height:23px;border:1px solid #777;border-right-width:0px;border-bottom-width:0px;padding:4px 10px;}table.packt-table tr.headers th{background:black url('/sites/all/themes/packt_new/images/pp/content-header-gradient.jpg') repeat scroll 0 0;background-color:#5C5C5C;background-repeat:repeat-x;color:white;font-size:15px;font-weight:normal;}table.packt-table tr.last td,table.packt-table tr.last th{border-bottom-width:1px;}table.packt-table td.last,table.packt-table th.last{border-right-width:1px;}a.addthis_button_google_plusone div{width:52px !important;float:left;}th.download-header-title{width:220px;}th.download-header-downloads{width:110px;}th.download-header-share{width:136px;}div.float-left.bimg{width:41px;}div.float-left.btitle{width:165px;}div#send_to_kindle{display:none;width:400px;height:400px;padding:20px;}div.email-suffix div.form-item{float:left;width:200px;}div.email-suffix div.form-item input{width:190px;}div.email-suffix div.suffix{height:58px;line-height:73px;}.packt-table-videos .form-submit{width:129px;background-image:url(/sites/all/themes/packt_new/images/sprite-horizontal-gradients.png);background-repeat:repeat-x;border-radius:5px;background-position:0 -904px;border-color:#000;font-size:1.3em;color:#F68C23;text-shadow:none;padding-bottom:5px;margin-bottom:1px;cursor:pointer;font-weight:bold;border:1px solid #888;padding-left:10px;padding-right:10px;display:block;position:relative;margin-left:38px;height:18px;padding-top:7px;text-decoration:none;text-align:center;margin:0 auto;}.packt-table-videos a{text-decoration:none;}.boxy-download-close{text-align:right;height:30%;font-size:1.6em;}.boxy-download-text{text-align:center;height:70%;}.boxy-download-info{font-weight:bold;font-size:2.8em;}.boxy-download-help{font-size:1.6em;}table.boxy-wrapper{width:auto;height:auto;}.boxy-wrapper{position:absolute;}.boxy-wrapper.fixed{position:fixed;} .boxy-modal-blackout{position:absolute;background-color:black;left:0;top:0;} .boxy-wrapper{empty-cells:show;}.boxy-wrapper .top-left,.boxy-wrapper .top-right,.boxy-wrapper .bottom-right,.boxy-wrapper .bottom-left{width:10px;height:10px;padding:0}.boxy-wrapper .top-left{background:url(/misc/boxy-0.1.4/images/boxy-nw.png);}.boxy-wrapper .top-right{background:url(/misc/boxy-0.1.4/images/boxy-ne.png);}.boxy-wrapper .bottom-right{background:url(/misc/boxy-0.1.4/images/boxy-se.png);}.boxy-wrapper .bottom-left{background:url(/misc/boxy-0.1.4/images/boxy-sw.png);} .boxy-wrapper .top-left{#background:none;#filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/boxy-nw.png');}.boxy-wrapper .top-right{#background:none;#filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/boxy-ne.png');}.boxy-wrapper .bottom-right{#background:none;#filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/boxy-se.png');}.boxy-wrapper .bottom-left{#background:none;#filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/boxy-sw.png');}.boxy-wrapper .top,.boxy-wrapper .bottom{height:10px;background-color:black;opacity:0.6;filter:alpha(opacity=60);padding:0}.boxy-wrapper .left,.boxy-wrapper .right{width:10px;background-color:black;opacity:0.6;filter:alpha(opacity=60);padding:0} .boxy-wrapper .title-bar{background-color:black;padding:6px;position:relative;}.boxy-wrapper .title-bar.dragging{cursor:move;}.boxy-wrapper .title-bar h2{font-size:12px;color:white;line-height:1;margin:0;padding:0;font-weight:normal;}.boxy-wrapper .title-bar .close{color:white;position:absolute;top:6px;right:6px;font-size:90%;line-height:1;} .boxy-inner{background-color:white;padding:0}.boxy-content{padding:15px;} .boxy-wrapper .question{width:350px;min-height:80px;}.boxy-wrapper .answers{text-align:right;} \ No newline at end of file diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/7422OT_Learning Dart(1).jpg b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/7422OT_Learning Dart(1).jpg new file mode 100644 index 0000000..ca5a80c Binary files /dev/null and b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/7422OT_Learning Dart(1).jpg differ diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/7422OT_Learning Dart.jpg b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/7422OT_Learning Dart.jpg new file mode 100644 index 0000000..0afacd1 Binary files /dev/null and b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/7422OT_Learning Dart.jpg differ diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/7ec3b3cf674f4f1d23e9d30c89426cce.js b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/7ec3b3cf674f4f1d23e9d30c89426cce.js new file mode 100644 index 0000000..70bbf28 --- /dev/null +++ b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/7ec3b3cf674f4f1d23e9d30c89426cce.js @@ -0,0 +1,441 @@ + +eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(H(){J w=1b.4M,3m$=1b.$;J D=1b.4M=1b.$=H(a,b){I 2B D.17.5j(a,b)};J u=/^[^<]*(<(.|\\s)+>)[^>]*$|^#(\\w+)$/,62=/^.[^:#\\[\\.]*$/,12;D.17=D.44={5j:H(d,b){d=d||S;G(d.16){7[0]=d;7.K=1;I 7}G(1j d=="23"){J c=u.2D(d);G(c&&(c[1]||!b)){G(c[1])d=D.4h([c[1]],b);N{J a=S.61(c[3]);G(a){G(a.2v!=c[3])I D().2q(d);I D(a)}d=[]}}N I D(b).2q(d)}N G(D.1D(d))I D(S)[D.17.27?"27":"43"](d);I 7.6Y(D.2d(d))},5w:"1.2.6",8G:H(){I 7.K},K:0,3p:H(a){I a==12?D.2d(7):7[a]},2I:H(b){J a=D(b);a.5n=7;I a},6Y:H(a){7.K=0;2p.44.1p.1w(7,a);I 7},P:H(a,b){I D.P(7,a,b)},5i:H(b){J a=-1;I D.2L(b&&b.5w?b[0]:b,7)},1K:H(c,a,b){J d=c;G(c.1q==56)G(a===12)I 7[0]&&D[b||"1K"](7[0],c);N{d={};d[c]=a}I 7.P(H(i){R(c 1n d)D.1K(b?7.V:7,c,D.1i(7,d[c],b,i,c))})},1g:H(b,a){G((b==\'2h\'||b==\'1Z\')&&3d(a)<0)a=12;I 7.1K(b,a,"2a")},1r:H(b){G(1j b!="49"&&b!=U)I 7.4E().3v((7[0]&&7[0].2z||S).5F(b));J a="";D.P(b||7,H(){D.P(7.3t,H(){G(7.16!=8)a+=7.16!=1?7.76:D.17.1r([7])})});I a},5z:H(b){G(7[0])D(b,7[0].2z).5y().39(7[0]).2l(H(){J a=7;1B(a.1x)a=a.1x;I a}).3v(7);I 7},8Y:H(a){I 7.P(H(){D(7).6Q().5z(a)})},8R:H(a){I 7.P(H(){D(7).5z(a)})},3v:H(){I 7.3W(19,M,Q,H(a){G(7.16==1)7.3U(a)})},6F:H(){I 7.3W(19,M,M,H(a){G(7.16==1)7.39(a,7.1x)})},6E:H(){I 7.3W(19,Q,Q,H(a){7.1d.39(a,7)})},5q:H(){I 7.3W(19,Q,M,H(a){7.1d.39(a,7.2H)})},3l:H(){I 7.5n||D([])},2q:H(b){J c=D.2l(7,H(a){I D.2q(b,a)});I 7.2I(/[^+>] [^+>]/.11(b)||b.1h("..")>-1?D.4r(c):c)},5y:H(e){J f=7.2l(H(){G(D.14.1f&&!D.4n(7)){J a=7.6o(M),5h=S.3h("1v");5h.3U(a);I D.4h([5h.4H])[0]}N I 7.6o(M)});J d=f.2q("*").5c().P(H(){G(7[E]!=12)7[E]=U});G(e===M)7.2q("*").5c().P(H(i){G(7.16==3)I;J c=D.L(7,"3w");R(J a 1n c)R(J b 1n c[a])D.W.1e(d[i],a,c[a][b],c[a][b].L)});I f},1E:H(b){I 7.2I(D.1D(b)&&D.3C(7,H(a,i){I b.1k(a,i)})||D.3g(b,7))},4Y:H(b){G(b.1q==56)G(62.11(b))I 7.2I(D.3g(b,7,M));N b=D.3g(b,7);J a=b.K&&b[b.K-1]!==12&&!b.16;I 7.1E(H(){I a?D.2L(7,b)<0:7!=b})},1e:H(a){I 7.2I(D.4r(D.2R(7.3p(),1j a==\'23\'?D(a):D.2d(a))))},3F:H(a){I!!a&&D.3g(a,7).K>0},7T:H(a){I 7.3F("."+a)},6e:H(b){G(b==12){G(7.K){J c=7[0];G(D.Y(c,"2A")){J e=c.64,63=[],15=c.15,2V=c.O=="2A-2V";G(e<0)I U;R(J i=2V?e:0,2f=2V?e+1:15.K;i<2f;i++){J d=15[i];G(d.2W){b=D.14.1f&&!d.at.2x.an?d.1r:d.2x;G(2V)I b;63.1p(b)}}I 63}N I(7[0].2x||"").1o(/\\r/g,"")}I 12}G(b.1q==4L)b+=\'\';I 7.P(H(){G(7.16!=1)I;G(b.1q==2p&&/5O|5L/.11(7.O))7.4J=(D.2L(7.2x,b)>=0||D.2L(7.34,b)>=0);N G(D.Y(7,"2A")){J a=D.2d(b);D("9R",7).P(H(){7.2W=(D.2L(7.2x,a)>=0||D.2L(7.1r,a)>=0)});G(!a.K)7.64=-1}N 7.2x=b})},2K:H(a){I a==12?(7[0]?7[0].4H:U):7.4E().3v(a)},7b:H(a){I 7.5q(a).21()},79:H(i){I 7.3s(i,i+1)},3s:H(){I 7.2I(2p.44.3s.1w(7,19))},2l:H(b){I 7.2I(D.2l(7,H(a,i){I b.1k(a,i,a)}))},5c:H(){I 7.1e(7.5n)},L:H(d,b){J a=d.1R(".");a[1]=a[1]?"."+a[1]:"";G(b===12){J c=7.5C("9z"+a[1]+"!",[a[0]]);G(c===12&&7.K)c=D.L(7[0],d);I c===12&&a[1]?7.L(a[0]):c}N I 7.1P("9u"+a[1]+"!",[a[0],b]).P(H(){D.L(7,d,b)})},3b:H(a){I 7.P(H(){D.3b(7,a)})},3W:H(g,f,h,d){J e=7.K>1,3x;I 7.P(H(){G(!3x){3x=D.4h(g,7.2z);G(h)3x.9o()}J b=7;G(f&&D.Y(7,"1T")&&D.Y(3x[0],"4F"))b=7.3H("22")[0]||7.3U(7.2z.3h("22"));J c=D([]);D.P(3x,H(){J a=e?D(7).5y(M)[0]:7;G(D.Y(a,"1m"))c=c.1e(a);N{G(a.16==1)c=c.1e(D("1m",a).21());d.1k(b,a)}});c.P(6T)})}};D.17.5j.44=D.17;H 6T(i,a){G(a.4d)D.3Y({1a:a.4d,31:Q,1O:"1m"});N D.5u(a.1r||a.6O||a.4H||"");G(a.1d)a.1d.37(a)}H 1z(){I+2B 8J}D.1l=D.17.1l=H(){J b=19[0]||{},i=1,K=19.K,4x=Q,15;G(b.1q==8I){4x=b;b=19[1]||{};i=2}G(1j b!="49"&&1j b!="H")b={};G(K==i){b=7;--i}R(;i-1}},6q:H(b,c,a){J e={};R(J d 1n c){e[d]=b.V[d];b.V[d]=c[d]}a.1k(b);R(J d 1n c)b.V[d]=e[d]},1g:H(d,e,c){G(e=="2h"||e=="1Z"){J b,3X={30:"5x",5g:"1G",18:"3I"},35=e=="2h"?["5e","6k"]:["5G","6i"];H 5b(){b=e=="2h"?d.8f:d.8c;J a=0,2C=0;D.P(35,H(){a+=3d(D.2a(d,"57"+7,M))||0;2C+=3d(D.2a(d,"2C"+7+"4b",M))||0});b-=29.83(a+2C)}G(D(d).3F(":4j"))5b();N D.6q(d,3X,5b);I 29.2f(0,b)}I D.2a(d,e,c)},2a:H(f,l,k){J e,V=f.V;H 3E(b){G(!D.14.2k)I Q;J a=3P.54(b,U);I!a||a.52("3E")==""}G(l=="1y"&&D.14.1f){e=D.1K(V,"1y");I e==""?"1":e}G(D.14.2G&&l=="18"){J d=V.50;V.50="0 7Y 7W";V.50=d}G(l.1I(/4i/i))l=y;G(!k&&V&&V[l])e=V[l];N G(3P.54){G(l.1I(/4i/i))l="4i";l=l.1o(/([A-Z])/g,"-$1").3y();J c=3P.54(f,U);G(c&&!3E(f))e=c.52(l);N{J g=[],2E=[],a=f,i=0;R(;a&&3E(a);a=a.1d)2E.6h(a);R(;i<2E.K;i++)G(3E(2E[i])){g[i]=2E[i].V.18;2E[i].V.18="3I"}e=l=="18"&&g[2E.K-1]!=U?"2F":(c&&c.52(l))||"";R(i=0;i]*?)\\/>/g,H(b,a,c){I c.1I(/^(aK|4f|7E|aG|4T|7A|aB|3n|az|ay|av)$/i)?b:a+">"});J f=D.3k(d).3y(),1v=h.3h("1v");J e=!f.1h("",""]||!f.1h("",""]||f.1I(/^<(aq|22|am|ak|ai)/)&&[1,"<1T>",""]||!f.1h("<4F")&&[2,"<1T><22>",""]||(!f.1h("<22><4F>",""]||!f.1h("<7E")&&[2,"<1T><22><7q>",""]||D.14.1f&&[1,"1v<1v>",""]||[0,"",""];1v.4H=e[1]+d+e[2];1B(e[0]--)1v=1v.5T;G(D.14.1f){J g=!f.1h("<1T")&&f.1h("<22")<0?1v.1x&&1v.1x.3t:e[1]=="<1T>"&&f.1h("<22")<0?1v.3t:[];R(J j=g.K-1;j>=0;--j)G(D.Y(g[j],"22")&&!g[j].3t.K)g[j].1d.37(g[j]);G(/^\\s/.11(d))1v.39(h.5F(d.1I(/^\\s*/)[0]),1v.1x)}d=D.2d(1v.3t)}G(d.K===0&&(!D.Y(d,"3V")&&!D.Y(d,"2A")))I;G(d[0]==12||D.Y(d,"3V")||d.15)k.1p(d);N k=D.2R(k,d)});I k},1K:H(d,f,c){G(!d||d.16==3||d.16==8)I 12;J e=!D.4n(d),40=c!==12,1f=D.14.1f;f=e&&D.3X[f]||f;G(d.2j){J g=/5Q|4d|V/.11(f);G(f=="2W"&&D.14.2k)d.1d.64;G(f 1n d&&e&&!g){G(40){G(f=="O"&&D.Y(d,"4T")&&d.1d)7p"O a3 a1\'t 9V 9U";d[f]=c}G(D.Y(d,"3V")&&d.7i(f))I d.7i(f).76;I d[f]}G(1f&&e&&f=="V")I D.1K(d.V,"9T",c);G(40)d.9Q(f,""+c);J h=1f&&e&&g?d.4G(f,2):d.4G(f);I h===U?12:h}G(1f&&f=="1y"){G(40){d.6B=1;d.1E=(d.1E||"").1o(/7f\\([^)]*\\)/,"")+(3r(c)+\'\'=="9L"?"":"7f(1y="+c*7a+")")}I d.1E&&d.1E.1h("1y=")>=0?(3d(d.1E.1I(/1y=([^)]*)/)[1])/7a)+\'\':""}f=f.1o(/-([a-z])/9H,H(a,b){I b.2r()});G(40)d[f]=c;I d[f]},3k:H(a){I(a||"").1o(/^\\s+|\\s+$/g,"")},2d:H(b){J a=[];G(b!=U){J i=b.K;G(i==U||b.1R||b.4I||b.1k)a[0]=b;N 1B(i)a[--i]=b[i]}I a},2L:H(b,a){R(J i=0,K=a.K;i*",7).21();1B(7.1x)7.37(7.1x)}},H(a,b){D.17[a]=H(){I 7.P(b,19)}});D.P(["6N","4b"],H(i,c){J b=c.3y();D.17[b]=H(a){I 7[0]==1b?D.14.2G&&S.1c["5t"+c]||D.14.2k&&1b["5s"+c]||S.70=="6Z"&&S.1C["5t"+c]||S.1c["5t"+c]:7[0]==S?29.2f(29.2f(S.1c["4y"+c],S.1C["4y"+c]),29.2f(S.1c["2i"+c],S.1C["2i"+c])):a==12?(7.K?D.1g(7[0],b):U):7.1g(b,a.1q==56?a:a+"2X")}});H 25(a,b){I a[0]&&3r(D.2a(a[0],b,M),10)||0}J C=D.14.2k&&3r(D.14.5B)<8H?"(?:[\\\\w*3m-]|\\\\\\\\.)":"(?:[\\\\w\\8F-\\8E*3m-]|\\\\\\\\.)",6L=2B 4v("^>\\\\s*("+C+"+)"),6J=2B 4v("^("+C+"+)(#)("+C+"+)"),6I=2B 4v("^([#.]?)("+C+"*)");D.1l({6H:{"":H(a,i,m){I m[2]=="*"||D.Y(a,m[2])},"#":H(a,i,m){I a.4G("2v")==m[2]},":":{8D:H(a,i,m){I im[3]-0},3a:H(a,i,m){I m[3]-0==i},79:H(a,i,m){I m[3]-0==i},3o:H(a,i){I i==0},3S:H(a,i,m,r){I i==r.K-1},6D:H(a,i){I i%2==0},6C:H(a,i){I i%2},"3o-4u":H(a){I a.1d.3H("*")[0]==a},"3S-4u":H(a){I D.3a(a.1d.5T,1,"4l")==a},"8z-4u":H(a){I!D.3a(a.1d.5T,2,"4l")},6W:H(a){I a.1x},4E:H(a){I!a.1x},8y:H(a,i,m){I(a.6O||a.8x||D(a).1r()||"").1h(m[3])>=0},4j:H(a){I"1G"!=a.O&&D.1g(a,"18")!="2F"&&D.1g(a,"5g")!="1G"},1G:H(a){I"1G"==a.O||D.1g(a,"18")=="2F"||D.1g(a,"5g")=="1G"},8w:H(a){I!a.3R},3R:H(a){I a.3R},4J:H(a){I a.4J},2W:H(a){I a.2W||D.1K(a,"2W")},1r:H(a){I"1r"==a.O},5O:H(a){I"5O"==a.O},5L:H(a){I"5L"==a.O},5p:H(a){I"5p"==a.O},3Q:H(a){I"3Q"==a.O},5o:H(a){I"5o"==a.O},6A:H(a){I"6A"==a.O},6z:H(a){I"6z"==a.O},2s:H(a){I"2s"==a.O||D.Y(a,"2s")},4T:H(a){I/4T|2A|6y|2s/i.11(a.Y)},3T:H(a,i,m){I D.2q(m[3],a).K},8t:H(a){I/h\\d/i.11(a.Y)},8s:H(a){I D.3C(D.3O,H(b){I a==b.T}).K}}},6x:[/^(\\[) *@?([\\w-]+) *([!*$^~=]*) *(\'?"?)(.*?)\\4 *\\]/,/^(:)([\\w-]+)\\("?\'?(.*?(\\(.*?\\))?[^(]*?)"?\'?\\)/,2B 4v("^([:.#]*)("+C+"+)")],3g:H(a,c,b){J d,1t=[];1B(a&&a!=d){d=a;J f=D.1E(a,c,b);a=f.t.1o(/^\\s*,\\s*/,"");1t=b?c=f.r:D.2R(1t,f.r)}I 1t},2q:H(t,o){G(1j t!="23")I[t];G(o&&o.16!=1&&o.16!=9)I[];o=o||S;J d=[o],2o=[],3S,Y;1B(t&&3S!=t){J r=[];3S=t;t=D.3k(t);J l=Q,3j=6L,m=3j.2D(t);G(m){Y=m[1].2r();R(J i=0;d[i];i++)R(J c=d[i].1x;c;c=c.2H)G(c.16==1&&(Y=="*"||c.Y.2r()==Y))r.1p(c);d=r;t=t.1o(3j,"");G(t.1h(" ")==0)6M;l=M}N{3j=/^([>+~])\\s*(\\w*)/i;G((m=3j.2D(t))!=U){r=[];J k={};Y=m[2].2r();m=m[1];R(J j=0,3i=d.K;j<3i;j++){J n=m=="~"||m=="+"?d[j].2H:d[j].1x;R(;n;n=n.2H)G(n.16==1){J g=D.L(n);G(m=="~"&&k[g])1X;G(!Y||n.Y.2r()==Y){G(m=="~")k[g]=M;r.1p(n)}G(m=="+")1X}}d=r;t=D.3k(t.1o(3j,""));l=M}}G(t&&!l){G(!t.1h(",")){G(o==d[0])d.4s();2o=D.2R(2o,d);r=d=[o];t=" "+t.6v(1,t.K)}N{J h=6J;J m=h.2D(t);G(m){m=[0,m[2],m[3],m[1]]}N{h=6I;m=h.2D(t)}m[2]=m[2].1o(/\\\\/g,"");J f=d[d.K-1];G(m[1]=="#"&&f&&f.61&&!D.4n(f)){J p=f.61(m[2]);G((D.14.1f||D.14.2G)&&p&&1j p.2v=="23"&&p.2v!=m[2])p=D(\'[@2v="\'+m[2]+\'"]\',f)[0];d=r=p&&(!m[3]||D.Y(p,m[3]))?[p]:[]}N{R(J i=0;d[i];i++){J a=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];G(a=="*"&&d[i].Y.3y()=="49")a="3n";r=D.2R(r,d[i].3H(a))}G(m[1]==".")r=D.5m(r,m[2]);G(m[1]=="#"){J e=[];R(J i=0;r[i];i++)G(r[i].4G("2v")==m[2]){e=[r[i]];1X}r=e}d=r}t=t.1o(h,"")}}G(t){J b=D.1E(t,r);d=r=b.r;t=D.3k(b.t)}}G(t)d=[];G(d&&o==d[0])d.4s();2o=D.2R(2o,d);I 2o},5m:H(r,m,a){m=" "+m+" ";J c=[];R(J i=0;r[i];i++){J b=(" "+r[i].1F+" ").1h(m)>=0;G(!a&&b||a&&!b)c.1p(r[i])}I c},1E:H(t,r,h){J d;1B(t&&t!=d){d=t;J p=D.6x,m;R(J i=0;p[i];i++){m=p[i].2D(t);G(m){t=t.8r(m[0].K);m[2]=m[2].1o(/\\\\/g,"");1X}}G(!m)1X;G(m[1]==":"&&m[2]=="4Y")r=62.11(m[3])?D.1E(m[3],r,M).r:D(r).4Y(m[3]);N G(m[1]==".")r=D.5m(r,m[2],h);N G(m[1]=="["){J g=[],O=m[3];R(J i=0,3i=r.K;i<3i;i++){J a=r[i],z=a[D.3X[m[2]]||m[2]];G(z==U||/5Q|4d|2W/.11(m[2]))z=D.1K(a,m[2])||\'\';G((O==""&&!!z||O=="="&&z==m[5]||O=="!="&&z!=m[5]||O=="^="&&z&&!z.1h(m[5])||O=="$="&&z.6v(z.K-m[5].K)==m[5]||(O=="*="||O=="~=")&&z.1h(m[5])>=0)^h)g.1p(a)}r=g}N G(m[1]==":"&&m[2]=="3a-4u"){J e={},g=[],11=/(-?)(\\d*)n((?:\\+|-)?\\d*)/.2D(m[3]=="6D"&&"2n"||m[3]=="6C"&&"2n+1"||!/\\D/.11(m[3])&&"8q+"+m[3]||m[3]),3o=(11[1]+(11[2]||1))-0,d=11[3]-0;R(J i=0,3i=r.K;i<3i;i++){J j=r[i],1d=j.1d,2v=D.L(1d);G(!e[2v]){J c=1;R(J n=1d.1x;n;n=n.2H)G(n.16==1)n.4q=c++;e[2v]=M}J b=Q;G(3o==0){G(j.4q==d)b=M}N G((j.4q-d)%3o==0&&(j.4q-d)/3o>=0)b=M;G(b^h)g.1p(j)}r=g}N{J f=D.6H[m[1]];G(1j f=="49")f=f[m[2]];G(1j f=="23")f=6u("Q||H(a,i){I "+f+";}");r=D.3C(r,H(a,i){I f(a,i,m,r)},h)}}I{r:r,t:t}},4S:H(b,c){J a=[],1t=b[c];1B(1t&&1t!=S){G(1t.16==1)a.1p(1t);1t=1t[c]}I a},3a:H(a,e,c,b){e=e||1;J d=0;R(;a;a=a[c])G(a.16==1&&++d==e)1X;I a},5v:H(n,a){J r=[];R(;n;n=n.2H){G(n.16==1&&n!=a)r.1p(n)}I r}});D.W={1e:H(f,i,g,e){G(f.16==3||f.16==8)I;G(D.14.1f&&f.4I)f=1b;G(!g.24)g.24=7.24++;G(e!=12){J h=g;g=7.3M(h,H(){I h.1w(7,19)});g.L=e}J j=D.L(f,"3w")||D.L(f,"3w",{}),1H=D.L(f,"1H")||D.L(f,"1H",H(){G(1j D!="12"&&!D.W.5k)I D.W.1H.1w(19.3L.T,19)});1H.T=f;D.P(i.1R(/\\s+/),H(c,b){J a=b.1R(".");b=a[0];g.O=a[1];J d=j[b];G(!d){d=j[b]={};G(!D.W.2t[b]||D.W.2t[b].4p.1k(f)===Q){G(f.3K)f.3K(b,1H,Q);N G(f.6t)f.6t("4o"+b,1H)}}d[g.24]=g;D.W.26[b]=M});f=U},24:1,26:{},21:H(e,h,f){G(e.16==3||e.16==8)I;J i=D.L(e,"3w"),1L,5i;G(i){G(h==12||(1j h=="23"&&h.8p(0)=="."))R(J g 1n i)7.21(e,g+(h||""));N{G(h.O){f=h.2y;h=h.O}D.P(h.1R(/\\s+/),H(b,a){J c=a.1R(".");a=c[0];G(i[a]){G(f)2U i[a][f.24];N R(f 1n i[a])G(!c[1]||i[a][f].O==c[1])2U i[a][f];R(1L 1n i[a])1X;G(!1L){G(!D.W.2t[a]||D.W.2t[a].4A.1k(e)===Q){G(e.6p)e.6p(a,D.L(e,"1H"),Q);N G(e.6n)e.6n("4o"+a,D.L(e,"1H"))}1L=U;2U i[a]}}})}R(1L 1n i)1X;G(!1L){J d=D.L(e,"1H");G(d)d.T=U;D.3b(e,"3w");D.3b(e,"1H")}}},1P:H(h,c,f,g,i){c=D.2d(c);G(h.1h("!")>=0){h=h.3s(0,-1);J a=M}G(!f){G(7.26[h])D("*").1e([1b,S]).1P(h,c)}N{G(f.16==3||f.16==8)I 12;J b,1L,17=D.1D(f[h]||U),W=!c[0]||!c[0].32;G(W){c.6h({O:h,2J:f,32:H(){},3J:H(){},4C:1z()});c[0][E]=M}c[0].O=h;G(a)c[0].6m=M;J d=D.L(f,"1H");G(d)b=d.1w(f,c);G((!17||(D.Y(f,\'a\')&&h=="4V"))&&f["4o"+h]&&f["4o"+h].1w(f,c)===Q)b=Q;G(W)c.4s();G(i&&D.1D(i)){1L=i.1w(f,b==U?c:c.7d(b));G(1L!==12)b=1L}G(17&&g!==Q&&b!==Q&&!(D.Y(f,\'a\')&&h=="4V")){7.5k=M;1U{f[h]()}1V(e){}}7.5k=Q}I b},1H:H(b){J a,1L,38,5f,4m;b=19[0]=D.W.6l(b||1b.W);38=b.O.1R(".");b.O=38[0];38=38[1];5f=!38&&!b.6m;4m=(D.L(7,"3w")||{})[b.O];R(J j 1n 4m){J c=4m[j];G(5f||c.O==38){b.2y=c;b.L=c.L;1L=c.1w(7,19);G(a!==Q)a=1L;G(1L===Q){b.32();b.3J()}}}I a},6l:H(b){G(b[E]==M)I b;J d=b;b={8o:d};J c="8n 8m 8l 8k 2s 8j 47 5d 6j 5E 8i L 8h 8g 4K 2y 5a 59 8e 8b 58 6f 8a 88 4k 87 86 84 6d 2J 4C 6c O 82 81 35".1R(" ");R(J i=c.K;i;i--)b[c[i]]=d[c[i]];b[E]=M;b.32=H(){G(d.32)d.32();d.80=Q};b.3J=H(){G(d.3J)d.3J();d.7Z=M};b.4C=b.4C||1z();G(!b.2J)b.2J=b.6d||S;G(b.2J.16==3)b.2J=b.2J.1d;G(!b.4k&&b.4K)b.4k=b.4K==b.2J?b.6c:b.4K;G(b.58==U&&b.5d!=U){J a=S.1C,1c=S.1c;b.58=b.5d+(a&&a.2e||1c&&1c.2e||0)-(a.6b||0);b.6f=b.6j+(a&&a.2c||1c&&1c.2c||0)-(a.6a||0)}G(!b.35&&((b.47||b.47===0)?b.47:b.5a))b.35=b.47||b.5a;G(!b.59&&b.5E)b.59=b.5E;G(!b.35&&b.2s)b.35=(b.2s&1?1:(b.2s&2?3:(b.2s&4?2:0)));I b},3M:H(a,b){b.24=a.24=a.24||b.24||7.24++;I b},2t:{27:{4p:H(){55();I},4A:H(){I}},3D:{4p:H(){G(D.14.1f)I Q;D(7).2O("53",D.W.2t.3D.2y);I M},4A:H(){G(D.14.1f)I Q;D(7).4e("53",D.W.2t.3D.2y);I M},2y:H(a){G(F(a,7))I M;a.O="3D";I D.W.1H.1w(7,19)}},3N:{4p:H(){G(D.14.1f)I Q;D(7).2O("51",D.W.2t.3N.2y);I M},4A:H(){G(D.14.1f)I Q;D(7).4e("51",D.W.2t.3N.2y);I M},2y:H(a){G(F(a,7))I M;a.O="3N";I D.W.1H.1w(7,19)}}}};D.17.1l({2O:H(c,a,b){I c=="4X"?7.2V(c,a,b):7.P(H(){D.W.1e(7,c,b||a,b&&a)})},2V:H(d,b,c){J e=D.W.3M(c||b,H(a){D(7).4e(a,e);I(c||b).1w(7,19)});I 7.P(H(){D.W.1e(7,d,e,c&&b)})},4e:H(a,b){I 7.P(H(){D.W.21(7,a,b)})},1P:H(c,a,b){I 7.P(H(){D.W.1P(c,a,7,M,b)})},5C:H(c,a,b){I 7[0]&&D.W.1P(c,a,7[0],Q,b)},2m:H(b){J c=19,i=1;1B(i=0){J i=g.3s(e,g.K);g=g.3s(0,e)}c=c||H(){};J f="2P";G(d)G(D.1D(d)){c=d;d=U}N{d=D.3n(d);f="6g"}J h=7;D.3Y({1a:g,O:f,1O:"2K",L:d,1J:H(a,b){G(b=="1W"||b=="7J")h.2K(i?D("<1v/>").3v(a.4U.1o(/<1m(.|\\s)*?\\/1m>/g,"")).2q(i):a.4U);h.P(c,[a.4U,b,a])}});I 7},aL:H(){I D.3n(7.7I())},7I:H(){I 7.2l(H(){I D.Y(7,"3V")?D.2d(7.aH):7}).1E(H(){I 7.34&&!7.3R&&(7.4J||/2A|6y/i.11(7.Y)||/1r|1G|3Q/i.11(7.O))}).2l(H(i,c){J b=D(7).6e();I b==U?U:b.1q==2p?D.2l(b,H(a,i){I{34:c.34,2x:a}}):{34:c.34,2x:b}}).3p()}});D.P("7H,7G,7F,7D,7C,7B".1R(","),H(i,o){D.17[o]=H(f){I 7.2O(o,f)}});J B=1z();D.1l({3p:H(d,b,a,c){G(D.1D(b)){a=b;b=U}I D.3Y({O:"2P",1a:d,L:b,1W:a,1O:c})},aE:H(b,a){I D.3p(b,U,a,"1m")},aD:H(c,b,a){I D.3p(c,b,a,"3z")},aC:H(d,b,a,c){G(D.1D(b)){a=b;b={}}I D.3Y({O:"6g",1a:d,L:b,1W:a,1O:c})},aA:H(a){D.1l(D.60,a)},60:{1a:5Z.5Q,26:M,O:"2P",2T:0,7z:"4R/x-ax-3V-aw",7x:M,31:M,L:U,5Y:U,3Q:U,4Q:{2N:"4R/2N, 1r/2N",2K:"1r/2K",1m:"1r/4t, 4R/4t",3z:"4R/3z, 1r/4t",1r:"1r/as",4w:"*/*"}},4z:{},3Y:H(s){s=D.1l(M,s,D.1l(M,{},D.60,s));J g,2Z=/=\\?(&|$)/g,1u,L,O=s.O.2r();G(s.L&&s.7x&&1j s.L!="23")s.L=D.3n(s.L);G(s.1O=="4P"){G(O=="2P"){G(!s.1a.1I(2Z))s.1a+=(s.1a.1I(/\\?/)?"&":"?")+(s.4P||"7u")+"=?"}N G(!s.L||!s.L.1I(2Z))s.L=(s.L?s.L+"&":"")+(s.4P||"7u")+"=?";s.1O="3z"}G(s.1O=="3z"&&(s.L&&s.L.1I(2Z)||s.1a.1I(2Z))){g="4P"+B++;G(s.L)s.L=(s.L+"").1o(2Z,"="+g+"$1");s.1a=s.1a.1o(2Z,"="+g+"$1");s.1O="1m";1b[g]=H(a){L=a;1W();1J();1b[g]=12;1U{2U 1b[g]}1V(e){}G(i)i.37(h)}}G(s.1O=="1m"&&s.1Y==U)s.1Y=Q;G(s.1Y===Q&&O=="2P"){J j=1z();J k=s.1a.1o(/(\\?|&)3m=.*?(&|$)/,"$ap="+j+"$2");s.1a=k+((k==s.1a)?(s.1a.1I(/\\?/)?"&":"?")+"3m="+j:"")}G(s.L&&O=="2P"){s.1a+=(s.1a.1I(/\\?/)?"&":"?")+s.L;s.L=U}G(s.26&&!D.4O++)D.W.1P("7H");J n=/^(?:\\w+:)?\\/\\/([^\\/?#]+)/;G(s.1O=="1m"&&O=="2P"&&n.11(s.1a)&&n.2D(s.1a)[1]!=5Z.al){J i=S.3H("6w")[0];J h=S.3h("1m");h.4d=s.1a;G(s.7t)h.aj=s.7t;G(!g){J l=Q;h.ah=h.ag=H(){G(!l&&(!7.3f||7.3f=="68"||7.3f=="1J")){l=M;1W();1J();i.37(h)}}}i.3U(h);I 12}J m=Q;J c=1b.7s?2B 7s("ae.ac"):2B 7r();G(s.5Y)c.6R(O,s.1a,s.31,s.5Y,s.3Q);N c.6R(O,s.1a,s.31);1U{G(s.L)c.4B("ab-aa",s.7z);G(s.5S)c.4B("a9-5R-a8",D.4z[s.1a]||"a7, a6 a5 a4 5N:5N:5N a2");c.4B("X-9Z-9Y","7r");c.4B("9W",s.1O&&s.4Q[s.1O]?s.4Q[s.1O]+", */*":s.4Q.4w)}1V(e){}G(s.7m&&s.7m(c,s)===Q){s.26&&D.4O--;c.7l();I Q}G(s.26)D.W.1P("7B",[c,s]);J d=H(a){G(!m&&c&&(c.3f==4||a=="2T")){m=M;G(f){7k(f);f=U}1u=a=="2T"&&"2T"||!D.7j(c)&&"3e"||s.5S&&D.7h(c,s.1a)&&"7J"||"1W";G(1u=="1W"){1U{L=D.6X(c,s.1O,s.9S)}1V(e){1u="5J"}}G(1u=="1W"){J b;1U{b=c.5I("7g-5R")}1V(e){}G(s.5S&&b)D.4z[s.1a]=b;G(!g)1W()}N D.5H(s,c,1u);1J();G(s.31)c=U}};G(s.31){J f=4I(d,13);G(s.2T>0)3B(H(){G(c){c.7l();G(!m)d("2T")}},s.2T)}1U{c.9P(s.L)}1V(e){D.5H(s,c,U,e)}G(!s.31)d();H 1W(){G(s.1W)s.1W(L,1u);G(s.26)D.W.1P("7C",[c,s])}H 1J(){G(s.1J)s.1J(c,1u);G(s.26)D.W.1P("7F",[c,s]);G(s.26&&!--D.4O)D.W.1P("7G")}I c},5H:H(s,a,b,e){G(s.3e)s.3e(a,b,e);G(s.26)D.W.1P("7D",[a,s,e])},4O:0,7j:H(a){1U{I!a.1u&&5Z.9O=="5p:"||(a.1u>=7e&&a.1u<9N)||a.1u==7c||a.1u==9K||D.14.2k&&a.1u==12}1V(e){}I Q},7h:H(a,c){1U{J b=a.5I("7g-5R");I a.1u==7c||b==D.4z[c]||D.14.2k&&a.1u==12}1V(e){}I Q},6X:H(a,c,b){J d=a.5I("9J-O"),2N=c=="2N"||!c&&d&&d.1h("2N")>=0,L=2N?a.9I:a.4U;G(2N&&L.1C.2j=="5J")7p"5J";G(b)L=b(L,c);G(c=="1m")D.5u(L);G(c=="3z")L=6u("("+L+")");I L},3n:H(a){J s=[];G(a.1q==2p||a.5w)D.P(a,H(){s.1p(3u(7.34)+"="+3u(7.2x))});N R(J j 1n a)G(a[j]&&a[j].1q==2p)D.P(a[j],H(){s.1p(3u(j)+"="+3u(7))});N s.1p(3u(j)+"="+3u(D.1D(a[j])?a[j]():a[j]));I s.6s("&").1o(/%20/g,"+")}});D.17.1l({1N:H(c,b){I c?7.2g({1Z:"1N",2h:"1N",1y:"1N"},c,b):7.1E(":1G").P(H(){7.V.18=7.5D||"";G(D.1g(7,"18")=="2F"){J a=D("<"+7.2j+" />").6P("1c");7.V.18=a.1g("18");G(7.V.18=="2F")7.V.18="3I";a.21()}}).3l()},1M:H(b,a){I b?7.2g({1Z:"1M",2h:"1M",1y:"1M"},b,a):7.1E(":4j").P(H(){7.5D=7.5D||D.1g(7,"18");7.V.18="2F"}).3l()},78:D.17.2m,2m:H(a,b){I D.1D(a)&&D.1D(b)?7.78.1w(7,19):a?7.2g({1Z:"2m",2h:"2m",1y:"2m"},a,b):7.P(H(){D(7)[D(7).3F(":1G")?"1N":"1M"]()})},9G:H(b,a){I 7.2g({1Z:"1N"},b,a)},9F:H(b,a){I 7.2g({1Z:"1M"},b,a)},9E:H(b,a){I 7.2g({1Z:"2m"},b,a)},9D:H(b,a){I 7.2g({1y:"1N"},b,a)},9M:H(b,a){I 7.2g({1y:"1M"},b,a)},9C:H(c,a,b){I 7.2g({1y:a},c,b)},2g:H(k,j,i,g){J h=D.77(j,i,g);I 7[h.36===Q?"P":"36"](H(){G(7.16!=1)I Q;J f=D.1l({},h),p,1G=D(7).3F(":1G"),46=7;R(p 1n k){G(k[p]=="1M"&&1G||k[p]=="1N"&&!1G)I f.1J.1k(7);G(p=="1Z"||p=="2h"){f.18=D.1g(7,"18");f.33=7.V.33}}G(f.33!=U)7.V.33="1G";f.45=D.1l({},k);D.P(k,H(c,a){J e=2B D.28(46,f,c);G(/2m|1N|1M/.11(a))e[a=="2m"?1G?"1N":"1M":a](k);N{J b=a.6r().1I(/^([+-]=)?([\\d+-.]+)(.*)$/),2b=e.1t(M)||0;G(b){J d=3d(b[2]),2M=b[3]||"2X";G(2M!="2X"){46.V[c]=(d||1)+2M;2b=((d||1)/e.1t(M))*2b;46.V[c]=2b+2M}G(b[1])d=((b[1]=="-="?-1:1)*d)+2b;e.3G(2b,d,2M)}N e.3G(2b,a,"")}});I M})},36:H(a,b){G(D.1D(a)||(a&&a.1q==2p)){b=a;a="28"}G(!a||(1j a=="23"&&!b))I A(7[0],a);I 7.P(H(){G(b.1q==2p)A(7,a,b);N{A(7,a).1p(b);G(A(7,a).K==1)b.1k(7)}})},9X:H(b,c){J a=D.3O;G(b)7.36([]);7.P(H(){R(J i=a.K-1;i>=0;i--)G(a[i].T==7){G(c)a[i](M);a.7n(i,1)}});G(!c)7.5A();I 7}});J A=H(b,c,a){G(b){c=c||"28";J q=D.L(b,c+"36");G(!q||a)q=D.L(b,c+"36",D.2d(a))}I q};D.17.5A=H(a){a=a||"28";I 7.P(H(){J q=A(7,a);q.4s();G(q.K)q[0].1k(7)})};D.1l({77:H(b,a,c){J d=b&&b.1q==a0?b:{1J:c||!c&&a||D.1D(b)&&b,2u:b,41:c&&a||a&&a.1q!=9t&&a};d.2u=(d.2u&&d.2u.1q==4L?d.2u:D.28.5K[d.2u])||D.28.5K.74;d.5M=d.1J;d.1J=H(){G(d.36!==Q)D(7).5A();G(D.1D(d.5M))d.5M.1k(7)};I d},41:{73:H(p,n,b,a){I b+a*p},5P:H(p,n,b,a){I((-29.9r(p*29.9q)/2)+0.5)*a+b}},3O:[],48:U,28:H(b,c,a){7.15=c;7.T=b;7.1i=a;G(!c.3Z)c.3Z={}}});D.28.44={4D:H(){G(7.15.2Y)7.15.2Y.1k(7.T,7.1z,7);(D.28.2Y[7.1i]||D.28.2Y.4w)(7);G(7.1i=="1Z"||7.1i=="2h")7.T.V.18="3I"},1t:H(a){G(7.T[7.1i]!=U&&7.T.V[7.1i]==U)I 7.T[7.1i];J r=3d(D.1g(7.T,7.1i,a));I r&&r>-9p?r:3d(D.2a(7.T,7.1i))||0},3G:H(c,b,d){7.5V=1z();7.2b=c;7.3l=b;7.2M=d||7.2M||"2X";7.1z=7.2b;7.2S=7.4N=0;7.4D();J e=7;H t(a){I e.2Y(a)}t.T=7.T;D.3O.1p(t);G(D.48==U){D.48=4I(H(){J a=D.3O;R(J i=0;i7.15.2u+7.5V){7.1z=7.3l;7.2S=7.4N=1;7.4D();7.15.45[7.1i]=M;J b=M;R(J i 1n 7.15.45)G(7.15.45[i]!==M)b=Q;G(b){G(7.15.18!=U){7.T.V.33=7.15.33;7.T.V.18=7.15.18;G(D.1g(7.T,"18")=="2F")7.T.V.18="3I"}G(7.15.1M)7.T.V.18="2F";G(7.15.1M||7.15.1N)R(J p 1n 7.15.45)D.1K(7.T.V,p,7.15.3Z[p])}G(b)7.15.1J.1k(7.T);I Q}N{J n=t-7.5V;7.4N=n/7.15.2u;7.2S=D.41[7.15.41||(D.41.5P?"5P":"73")](7.4N,n,0,1,7.15.2u);7.1z=7.2b+((7.3l-7.2b)*7.2S);7.4D()}I M}};D.1l(D.28,{5K:{9l:9j,9i:7e,74:9g},2Y:{2e:H(a){a.T.2e=a.1z},2c:H(a){a.T.2c=a.1z},1y:H(a){D.1K(a.T.V,"1y",a.1z)},4w:H(a){a.T.V[a.1i]=a.1z+a.2M}}});D.17.2i=H(){J b=0,1S=0,T=7[0],3q;G(T)ao(D.14){J d=T.1d,4a=T,1s=T.1s,1Q=T.2z,5U=2k&&3r(5B)<9c&&!/9a/i.11(v),1g=D.2a,3c=1g(T,"30")=="3c";G(T.7y){J c=T.7y();1e(c.1A+29.2f(1Q.1C.2e,1Q.1c.2e),c.1S+29.2f(1Q.1C.2c,1Q.1c.2c));1e(-1Q.1C.6b,-1Q.1C.6a)}N{1e(T.5X,T.5W);1B(1s){1e(1s.5X,1s.5W);G(42&&!/^t(98|d|h)$/i.11(1s.2j)||2k&&!5U)2C(1s);G(!3c&&1g(1s,"30")=="3c")3c=M;4a=/^1c$/i.11(1s.2j)?4a:1s;1s=1s.1s}1B(d&&d.2j&&!/^1c|2K$/i.11(d.2j)){G(!/^96|1T.*$/i.11(1g(d,"18")))1e(-d.2e,-d.2c);G(42&&1g(d,"33")!="4j")2C(d);d=d.1d}G((5U&&(3c||1g(4a,"30")=="5x"))||(42&&1g(4a,"30")!="5x"))1e(-1Q.1c.5X,-1Q.1c.5W);G(3c)1e(29.2f(1Q.1C.2e,1Q.1c.2e),29.2f(1Q.1C.2c,1Q.1c.2c))}3q={1S:1S,1A:b}}H 2C(a){1e(D.2a(a,"6V",M),D.2a(a,"6U",M))}H 1e(l,t){b+=3r(l,10)||0;1S+=3r(t,10)||0}I 3q};D.17.1l({30:H(){J a=0,1S=0,3q;G(7[0]){J b=7.1s(),2i=7.2i(),4c=/^1c|2K$/i.11(b[0].2j)?{1S:0,1A:0}:b.2i();2i.1S-=25(7,\'94\');2i.1A-=25(7,\'aF\');4c.1S+=25(b,\'6U\');4c.1A+=25(b,\'6V\');3q={1S:2i.1S-4c.1S,1A:2i.1A-4c.1A}}I 3q},1s:H(){J a=7[0].1s;1B(a&&(!/^1c|2K$/i.11(a.2j)&&D.1g(a,\'30\')==\'93\'))a=a.1s;I D(a)}});D.P([\'5e\',\'5G\'],H(i,b){J c=\'4y\'+b;D.17[c]=H(a){G(!7[0])I;I a!=12?7.P(H(){7==1b||7==S?1b.92(!i?a:D(1b).2e(),i?a:D(1b).2c()):7[c]=a}):7[0]==1b||7[0]==S?46[i?\'aI\':\'aJ\']||D.71&&S.1C[c]||S.1c[c]:7[0][c]}});D.P(["6N","4b"],H(i,b){J c=i?"5e":"5G",4f=i?"6k":"6i";D.17["5s"+b]=H(){I 7[b.3y()]()+25(7,"57"+c)+25(7,"57"+4f)};D.17["90"+b]=H(a){I 7["5s"+b]()+25(7,"2C"+c+"4b")+25(7,"2C"+4f+"4b")+(a?25(7,"6S"+c)+25(7,"6S"+4f):0)}})})();',62,669,'|||||||this|||||||||||||||||||||||||||||||||||if|function|return|var|length|data|true|else|type|each|false|for|document|elem|null|style|event||nodeName|||test|undefined||browser|options|nodeType|fn|display|arguments|url|window|body|parentNode|add|msie|css|indexOf|prop|typeof|call|extend|script|in|replace|push|constructor|text|offsetParent|cur|status|div|apply|firstChild|opacity|now|left|while|documentElement|isFunction|filter|className|hidden|handle|match|complete|attr|ret|hide|show|dataType|trigger|doc|split|top|table|try|catch|success|break|cache|height||remove|tbody|string|guid|num|global|ready|fx|Math|curCSS|start|scrollTop|makeArray|scrollLeft|max|animate|width|offset|tagName|safari|map|toggle||done|Array|find|toUpperCase|button|special|duration|id|copy|value|handler|ownerDocument|select|new|border|exec|stack|none|opera|nextSibling|pushStack|target|html|inArray|unit|xml|bind|GET|isReady|merge|pos|timeout|delete|one|selected|px|step|jsre|position|async|preventDefault|overflow|name|which|queue|removeChild|namespace|insertBefore|nth|removeData|fixed|parseFloat|error|readyState|multiFilter|createElement|rl|re|trim|end|_|param|first|get|results|parseInt|slice|childNodes|encodeURIComponent|append|events|elems|toLowerCase|json|readyList|setTimeout|grep|mouseenter|color|is|custom|getElementsByTagName|block|stopPropagation|addEventListener|callee|proxy|mouseleave|timers|defaultView|password|disabled|last|has|appendChild|form|domManip|props|ajax|orig|set|easing|mozilla|load|prototype|curAnim|self|charCode|timerId|object|offsetChild|Width|parentOffset|src|unbind|br|currentStyle|clean|float|visible|relatedTarget|previousSibling|handlers|isXMLDoc|on|setup|nodeIndex|unique|shift|javascript|child|RegExp|_default|deep|scroll|lastModified|teardown|setRequestHeader|timeStamp|update|empty|tr|getAttribute|innerHTML|setInterval|checked|fromElement|Number|jQuery|state|active|jsonp|accepts|application|dir|input|responseText|click|styleSheets|unload|not|lastToggle|outline|mouseout|getPropertyValue|mouseover|getComputedStyle|bindReady|String|padding|pageX|metaKey|keyCode|getWH|andSelf|clientX|Left|all|visibility|container|index|init|triggered|removeAttribute|classFilter|prevObject|submit|file|after|windowData|inner|client|globalEval|sibling|jquery|absolute|clone|wrapAll|dequeue|version|triggerHandler|oldblock|ctrlKey|createTextNode|Top|handleError|getResponseHeader|parsererror|speeds|checkbox|old|00|radio|swing|href|Modified|ifModified|lastChild|safari2|startTime|offsetTop|offsetLeft|username|location|ajaxSettings|getElementById|isSimple|values|selectedIndex|runtimeStyle|rsLeft|_load|loaded|DOMContentLoaded|clientTop|clientLeft|toElement|srcElement|val|pageY|POST|unshift|Bottom|clientY|Right|fix|exclusive|detachEvent|cloneNode|removeEventListener|swap|toString|join|attachEvent|eval|substr|head|parse|textarea|reset|image|zoom|odd|even|before|prepend|exclude|expr|quickClass|quickID|uuid|quickChild|continue|Height|textContent|appendTo|contents|open|margin|evalScript|borderTopWidth|borderLeftWidth|parent|httpData|setArray|CSS1Compat|compatMode|boxModel|cssFloat|linear|def|webkit|nodeValue|speed|_toggle|eq|100|replaceWith|304|concat|200|alpha|Last|httpNotModified|getAttributeNode|httpSuccess|clearInterval|abort|beforeSend|splice|styleFloat|throw|colgroup|XMLHttpRequest|ActiveXObject|scriptCharset|callback|fieldset|multiple|processData|getBoundingClientRect|contentType|link|ajaxSend|ajaxSuccess|ajaxError|col|ajaxComplete|ajaxStop|ajaxStart|serializeArray|notmodified|keypress|keydown|change|mouseup|mousedown|dblclick|focus|blur|stylesheet|hasClass|rel|doScroll|black|hover|solid|cancelBubble|returnValue|wheelDelta|view|round|shiftKey|resize|screenY|screenX|relatedNode|mousemove|prevValue|originalTarget|offsetHeight|keyup|newValue|offsetWidth|eventPhase|detail|currentTarget|cancelable|bubbles|attrName|attrChange|altKey|originalEvent|charAt|0n|substring|animated|header|noConflict|line|enabled|innerText|contains|only|weight|font|gt|lt|uFFFF|u0128|size|417|Boolean|Date|toggleClass|removeClass|addClass|removeAttr|replaceAll|insertAfter|prependTo|wrap|contentWindow|contentDocument|iframe|children|siblings|prevAll|wrapInner|nextAll|outer|prev|scrollTo|static|marginTop|next|inline|parents|able|cellSpacing|adobeair|cellspacing|522|maxLength|maxlength|readOnly|400|readonly|fast|600|class|slow|1px|htmlFor|reverse|10000|PI|cos|compatible|Function|setData|ie|ra|it|rv|getData|userAgent|navigator|fadeTo|fadeIn|slideToggle|slideUp|slideDown|ig|responseXML|content|1223|NaN|fadeOut|300|protocol|send|setAttribute|option|dataFilter|cssText|changed|be|Accept|stop|With|Requested|Object|can|GMT|property|1970|Jan|01|Thu|Since|If|Type|Content|XMLHTTP|th|Microsoft|td|onreadystatechange|onload|cap|charset|colg|host|tfoot|specified|with|1_|thead|leg|plain|attributes|opt|embed|urlencoded|www|area|hr|ajaxSetup|meta|post|getJSON|getScript|marginLeft|img|elements|pageYOffset|pageXOffset|abbr|serialize|pixelLeft'.split('|'),0,{}));var Drupal=Drupal||{'settings':{},'behaviors':{},'themes':{},'locale':{}};Drupal.jsEnabled=document.getElementsByTagName&&document.createElement&&document.createTextNode&&document.documentElement&&document.getElementById;Drupal.attachBehaviors=function(context){context=context||document;if(Drupal.jsEnabled){jQuery.each(Drupal.behaviors,function(){this(context);});}};Drupal.checkPlain=function(str){str=String(str);var replace={'&':'&','"':'"','<':'<','>':'>'};for(var character in replace){var regex=new RegExp(character,'g');str=str.replace(regex,replace[character]);} +return str;};Drupal.t=function(str,args){if(Drupal.locale.strings&&Drupal.locale.strings[str]){str=Drupal.locale.strings[str];} +if(args){for(var key in args){switch(key.charAt(0)){case'@':args[key]=Drupal.checkPlain(args[key]);break;case'!':break;case'%':default:args[key]=Drupal.theme('placeholder',args[key]);break;} +str=str.replace(key,args[key]);}} +return str;};Drupal.formatPlural=function(count,singular,plural,args){var args=args||{};args['@count']=count;var index=Drupal.locale.pluralFormula?Drupal.locale.pluralFormula(args['@count']):((args['@count']==1)?0:1);if(index==0){return Drupal.t(singular,args);} +else if(index==1){return Drupal.t(plural,args);} +else{args['@count['+index+']']=args['@count'];delete args['@count'];return Drupal.t(plural.replace('@count','@count['+index+']'));}};Drupal.theme=function(func){for(var i=1,args=[];i'+Drupal.checkPlain(str)+'';}};;Drupal.behaviors.autocomplete=function(context){var acdb=[];$('input.autocomplete:not(.autocomplete-processed)',context).each(function(){var uri=this.value;if(uri.indexOf("http:")!=-1||uri.indexOf("https:")!=-1){var t=document.location.protocol;if(uri.indexOf(t)==-1){if(t=="http:"){uri=uri.replace("https:",t);}else if(t=="https:"){uri=uri.replace("http:",t);}}} +if(!acdb[uri]){acdb[uri]=new Drupal.ACDB(uri);} +var input=$('#'+this.id.substr(0,this.id.length-13)).attr('autocomplete','OFF')[0];$(input.form).submit(Drupal.autocompleteSubmit);new Drupal.jsAC(input,acdb[uri]);$(this).addClass('autocomplete-processed');});};Drupal.autocompleteSubmit=function(){return true;return $('#autocomplete').each(function(){this.owner.hidePopup();}).size()==0;};Drupal.jsAC=function(input,db){var ac=this;this.input=input;this.db=db;$(this.input).keydown(function(event){return ac.onkeydown(this,event);}).keyup(function(event){ac.onkeyup(this,event);}).blur(function(){ac.hidePopup();ac.db.cancel();});};Drupal.jsAC.prototype.onkeydown=function(input,e){if(!e){e=window.event;} +switch(e.keyCode){case 40:this.selectDown();return false;case 38:this.selectUp();return false;default:return true;}};Drupal.jsAC.prototype.onkeyup=function(input,e){if(!e){e=window.event;} +switch(e.keyCode){case 16:case 17:case 18:case 20:case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:return true;case 9:case 27:this.hidePopup(e.keyCode);return true;case 13:this.hidePopup(e.keyCode);if(this.input.className.indexOf('form-autocomplete')!==-1){$(this.input).parents("form").submit();} +return true;default:if(input.value.length>0) +this.populatePopup();else +this.hidePopup(e.keyCode);return true;}};Drupal.jsAC.prototype.select=function(node){this.input.value=node.autocompleteValue;};Drupal.jsAC.prototype.selectDown=function(){if(this.selected&&this.selected.nextSibling){this.highlight(this.selected.nextSibling);} +else{var lis=$('li',this.popup);if(lis.size()>0){this.highlight(lis.get(0));}}};Drupal.jsAC.prototype.selectUp=function(){if(this.selected&&this.selected.previousSibling){this.highlight(this.selected.previousSibling);}};Drupal.jsAC.prototype.highlight=function(node){if(this.selected){$(this.selected).removeClass('selected');} +$(node).addClass('selected');this.selected=node;};Drupal.jsAC.prototype.unhighlight=function(node){$(node).removeClass('selected');this.selected=false;};Drupal.jsAC.prototype.hidePopup=function(keycode){if(keycode&&keycode===13){if(this.input.id==='edit-product-autocomplete'){$(this.input).parents("#packt-libraries-main-search-form").submit();}} +if(this.selected&&((keycode&&keycode!=46&&keycode!=8&&keycode!=27)||!keycode)){this.input.value=this.selected.autocompleteValue;} +var popup=this.popup;if(popup){this.popup=null;$(popup).fadeOut('fast',function(){$(popup).remove();});} +this.selected=false;};Drupal.jsAC.prototype.populatePopup=function(){if(this.popup){$(this.popup).remove();} +this.selected=false;this.popup=document.createElement('div');this.popup.id='autocomplete';this.popup.owner=this;$(this.popup).css({marginTop:this.input.offsetHeight+'px',width:(this.input.offsetWidth-4)+'px',display:'none'});$(this.input).before(this.popup);this.db.owner=this;this.db.search(this.input.value);};Drupal.jsAC.prototype.found=function(matches){if(!this.input.value.length){return false;} +var ul=document.createElement('ul');var ac=this;for(key in matches){var li=document.createElement('li');$(li).html('
'+matches[key]+'
').mousedown(function(){ac.select(this);}).mouseover(function(){ac.highlight(this);}).mouseout(function(){ac.unhighlight(this);});li.autocompleteValue=key;$(ul).append(li);} +if(this.popup){if(ul.childNodes.length>0){$(this.popup).empty().append(ul).show();} +else{$(this.popup).css({visibility:'hidden'});this.hidePopup();}}};Drupal.jsAC.prototype.setStatus=function(status){switch(status){case'begin':$(this.input).addClass('throbbing');break;case'cancel':case'error':case'found':$(this.input).removeClass('throbbing');break;}};Drupal.ACDB=function(uri){this.uri=uri;this.delay=300;this.cache={};};Drupal.ACDB.prototype.search=function(searchString){var db=this;this.searchString=searchString;var extraString="";if(db.uri.indexOf("searchsolr")!=-1){extraString=$(db.owner.input).parents('form').serialize();} +if(this.cache[searchString]){return this.owner.found(this.cache[searchString]);} +if(this.timer){clearTimeout(this.timer);} +this.timer=setTimeout(function(){db.owner.setStatus('begin');$.ajax({type:"GET",url:db.uri+'/'+Drupal.encodeURIComponent(searchString)+'?'+extraString,dataType:'json',success:function(matches){if(typeof matches['status']=='undefined'||matches['status']!=0){db.cache[searchString]=matches;if(db.searchString==searchString){db.owner.found(matches);} +db.owner.setStatus('found');}},error:function(xmlhttp){if(db.uri.indexOf("searchsolr/autocomplete")===-1){alert(Drupal.ahahError(xmlhttp,db.uri));}}});},this.delay);};Drupal.ACDB.prototype.cancel=function(){if(this.owner)this.owner.setStatus('cancel');if(this.timer)clearTimeout(this.timer);this.searchString='';};$(document).ready(function(){$('#packt-libraries-main-search-form input[type=submit], #book_taxonomy_view #views-exposed-form-taxonomy-book-list-default input[type=submit], #packt-categorypages-cat-search-form input[type=submit]').click(function(){$(this).unbind().click();});});;jQuery.fn.boxy=function(options){options=options||{};return this.each(function(){var node=this.nodeName.toLowerCase(),self=this;if(node=='a'){jQuery(this).click(function(){var active=Boxy.linkedTo(this),href=this.getAttribute('href'),localOptions=jQuery.extend({actuator:this,title:this.title},options);if(active){active.show();}else if(href.indexOf('#')>=0){var content=jQuery(href.substr(href.indexOf('#'))),newContent=content.clone(true);content.remove();localOptions.unloadOnHide=false;new Boxy(newContent,localOptions);}else{if(!localOptions.cache)localOptions.unloadOnHide=true;Boxy.load(this.href,localOptions);} +return false;});}else if(node=='form'){jQuery(this).bind('submit.boxy',function(){Boxy.confirm(options.message||'Please confirm:',function(){jQuery(self).unbind('submit.boxy').submit();});return false;});}});};function Boxy(element,options){this.boxy=jQuery(Boxy.WRAPPER);jQuery.data(this.boxy[0],'boxy',this);this.visible=false;this.options=jQuery.extend({},Boxy.DEFAULTS,options||{});if(this.options.modal){this.options=jQuery.extend(this.options,{center:true,draggable:false});} +if(this.options.actuator){jQuery.data(this.options.actuator,'active.boxy',this);} +this.setContent(element||"
");this._setupTitleBar();this.boxy.css('display','none').appendTo(document.body);this.toTop();if(this.options.fixed){if(jQuery.browser.msie&&jQuery.browser.version<7){this.options.fixed=false;}else{this.boxy.addClass('fixed');}} +if(this.options.center&&Boxy._u(this.options.x,this.options.y)){this.center();}else{this.moveTo(Boxy._u(this.options.x)?this.options.x:Boxy.DEFAULT_X,Boxy._u(this.options.y)?this.options.y:Boxy.DEFAULT_Y);} +if(this.options.show)this.show();};Boxy.EF=function(){};jQuery.extend(Boxy,{WRAPPER:""+""+""+""+"
",DEFAULTS:{title:null,closeable:true,draggable:true,clone:false,actuator:null,center:true,show:true,modal:false,fixed:true,closeText:'[close]',unloadOnHide:false,clickToFront:false,behaviours:Boxy.EF,afterDrop:Boxy.EF,afterShow:Boxy.EF,afterHide:Boxy.EF,beforeUnload:Boxy.EF},DEFAULT_X:50,DEFAULT_Y:50,zIndex:1337,dragConfigured:false,resizeConfigured:false,dragging:null,load:function(url,options){options=options||{};var ajax={url:url,type:'GET',dataType:'html',cache:false,success:function(html){html=jQuery(html);if(options.filter)html=jQuery(options.filter,html);new Boxy(html,options);}};jQuery.each(['type','cache'],function(){if(this in options){ajax[this]=options[this];delete options[this];}});jQuery.ajax(ajax);},get:function(ele){var p=jQuery(ele).parents('.boxy-wrapper');return p.length?jQuery.data(p[0],'boxy'):null;},linkedTo:function(ele){return jQuery.data(ele,'active.boxy');},alert:function(message,callback,options){return Boxy.ask(message,['OK'],callback,options);},confirm:function(message,after,options){return Boxy.ask(message,['OK','Cancel'],function(response){if(response=='OK')after();},options);},ask:function(question,answers,callback,options){options=jQuery.extend({modal:true,closeable:false},options||{},{show:true,unloadOnHide:true});var body=jQuery('
').append(jQuery('
').html(question));var map={},answerStrings=[];if(answers instanceof Array){for(var i=0;i');buttons.html(jQuery.map(answerStrings,function(v){return"";}).join(' '));jQuery('input[type=button]',buttons).click(function(){var clicked=this;Boxy.get(this).hide(function(){if(callback)callback(map[clicked.value]);});});body.append(buttons);new Boxy(body,options);},isModalVisible:function(){return jQuery('.boxy-modal-blackout').length>0;},_u:function(){for(var i=0;i').css({zIndex:Boxy._nextZ(),opacity:0.7,width:jQuery(document).width(),height:jQuery(document).height()}).appendTo(document.body);this.toTop();if(this.options.closeable){jQuery(document.body).bind('keypress.boxy',function(evt){var key=evt.which||evt.keyCode;if(key==27){self.hide();jQuery(document.body).unbind('keypress.boxy');}});}} +this.boxy.stop().css({opacity:1}).show();this.visible=true;this._fire('afterShow');return this;},hide:function(after){if(!this.visible)return;var self=this;if(this.options.modal){jQuery(document.body).unbind('keypress.boxy');this.modalBlackout.animate({opacity:0},function(){jQuery(this).remove();});} +this.boxy.stop().animate({opacity:0},300,function(){self.boxy.css({display:'none'});self.visible=false;self._fire('afterHide');if(after)after(self);if(self.options.unloadOnHide)self.unload();});return this;},toggle:function(){this[this.visible?'hide':'show']();return this;},hideAndUnload:function(after){this.options.unloadOnHide=true;this.hide(after);return this;},unload:function(){this._fire('beforeUnload');this.boxy.remove();if(this.options.actuator){jQuery.data(this.options.actuator,'active.boxy',false);}},toTop:function(){this.boxy.css({zIndex:Boxy._nextZ()});return this;},getTitle:function(){return jQuery('> .title-bar h2',this.getInner()).html();},setTitle:function(t){jQuery('> .title-bar h2',this.getInner()).html(t);return this;},_getBoundsForResize:function(width,height){var csize=this.getContentSize();var delta=[width-csize[0],height-csize[1]];var p=this.getPosition();return[Math.max(p[0]-delta[0]/2,0),Math.max(p[1]-delta[1]/2,0),width,height];},_setupTitleBar:function(){if(this.options.title){var self=this;var tb=jQuery("
").html("

"+this.options.title+"

");if(this.options.closeable){tb.append(jQuery("").html(this.options.closeText));} +if(this.options.draggable){tb[0].onselectstart=function(){return false;} +tb[0].unselectable='on';tb[0].style.MozUserSelect='none';if(!Boxy.dragConfigured){jQuery(document).mousemove(Boxy._handleDrag);Boxy.dragConfigured=true;} +tb.mousedown(function(evt){self.toTop();Boxy.dragging=[self,evt.pageX-self.boxy[0].offsetLeft,evt.pageY-self.boxy[0].offsetTop];jQuery(this).addClass('dragging');}).mouseup(function(){jQuery(this).removeClass('dragging');Boxy.dragging=null;self._fire('afterDrop');});} +this.getInner().prepend(tb);this._setupDefaultBehaviours(tb);}},_setupDefaultBehaviours:function(root){var self=this;if(this.options.clickToFront){root.click(function(){self.toTop();});} +jQuery('.close',root).click(function(){self.hide();return false;}).mousedown(function(evt){evt.stopPropagation();});},_fire:function(event){this.options[event].call(this);}};;Drupal.dhtmlMenu={};Drupal.behaviors.dhtmlMenu=function(){if(Drupal.dhtmlMenu.init){return;} +else{Drupal.dhtmlMenu.init=true;} +var effects=Drupal.settings.dhtmlMenu;$('.collapsed').removeClass('expanded');if(!effects.siblings){var cookie=Drupal.dhtmlMenu.cookieGet();for(var i in cookie){var li=$('#dhtml_menu-'+cookie[i]).parents('li:first');if($(li).hasClass('collapsed')){Drupal.dhtmlMenu.toggleMenu(li);}}} +$('div:not(#block-menu-menu-footer-links) ul.menu li.dhtml-menu:not(.leaf,.no-dhtml)').each(function(){var li=this;if(effects.clone){var ul=$(li).find('ul:first');if(ul.length){$(li).find('a:first').clone().prependTo(ul).wrap('
  • ');}} +if(effects.doubleclick){$(li).find('a:first').dblclick(function(e){window.location=this.href;});} +$(li).find('a:first').click(function(e){Drupal.dhtmlMenu.toggleMenu($(li));return false;});});} +Drupal.dhtmlMenu.toggleMenu=function(li){var par=$(li).parents();if($(par[4]).attr('id')=='block-menu-menu-footer-links'){return false;} +var effects=Drupal.settings.dhtmlMenu;if($(li).hasClass('expanded')){if(effects.slide){$(li).find('ul:first').animate({height:'hide',opacity:'hide'},'1000');} +else $(li).find('ul:first').css('display','none');if(effects.children){if(effects.slide){$(li).find('li.expanded').find('ul:first').animate({height:'hide',opacity:'hide'},'1000');} +else $(li).find('li.expanded').find('ul:first').css('display','none');$(li).find('li.expanded').removeClass('expanded').addClass('collapsed')} +$(li).removeClass('expanded').addClass('collapsed');} +else{if(effects.slide){$(li).find('ul:first').animate({height:'show',opacity:'show'},'1000');} +else $(li).find('ul:first').css('display','block');$(li).removeClass('collapsed').addClass('expanded');if(effects.siblings){var id=$(li).find('a:first').attr('id');$(li).find('li').addClass('own-children-temp');if(effects.relativity){var siblings=$(li).parent().find('li.expanded').not('.own-children-temp').not(':has(#'+id+')');} +else{var siblings=$('ul.menu li.expanded').not('.own-children-temp').not(':has(#'+id+')');} +if(!effects.children){$('li.collapsed li.expanded').addClass('sibling-children-temp');$(siblings).find('li.expanded').addClass('sibling-children-temp');siblings=$(siblings).not('.sibling-children-temp');} +$('.own-children-temp, .sibling-children-temp').removeClass('own-children-temp').removeClass('sibling-children-temp');if(effects.slide){$(siblings).find('ul:first').animate({height:'hide',opacity:'hide'},'1000');} +else $(siblings).find('ul:first').css('display','none');$(siblings).removeClass('expanded').addClass('collapsed');}} +Drupal.dhtmlMenu.cookieSet();} +Drupal.dhtmlMenu.cookieGet=function(){var c=/dhtml_menu=(.*?)(;|$)/.exec(document.cookie);if(c){return c[1];} +else return'';} +Drupal.dhtmlMenu.cookieSet=function(){var expanded=new Array();$('li.expanded').each(function(){expanded.push($(this).find('a:first').attr('id').substr(5));});document.cookie='dhtml_menu='+expanded.join(',')+';path=/';};$(document).ready(function(){if(Drupal.jsEnabled){$("a[@href^=http]").each(function(){if(this.href.indexOf(location.hostname)==-1&&this.href.indexOf('packtpub.com/')<0){$(this).click(function(){window.open(this.href);return false;});}});}});;(function($){$('div#main div#fancy-slide-1').parent().prev().css("display","none");$.fn.fancySlide=function(options){var defaultoptions={animation:'slide',continuous:true,controlsBefore:'',controlsAfter:'',controlsType:'thumbs',showControls:true,slideControls:false,vertical:false,speed:800,rotate:true,pause:4000};var options=$.extend(defaultoptions,options);var obj=$(this);var slidewidth=$(this).outerWidth();var slideheight=$(this).outerHeight();var slides=$('li',obj).length;var currentslide=0;var previousslide=0;var position=0;var timeout;var depth=99;var animationtype=options.animation+'animate';if(options.speed==0){animationtype='dontanimate';} +return this.each(function(){obj.width(slidewidth);obj.height(slideheight);obj.css('overflow','hidden');if(options.showControls){if(options.controlsType=='thumbs'){var html='
    ';html+=''+options.controlsBefore+'';for(i=0;i
    ' +html+='';} +html+='
    ';html+='';html+=options.controlsAfter;$(obj).append(html);controller=$('.controller',obj);$(controller).css('zIndex',depth+1);$(controller).css('width',slidewidth-20);$('img',controller).attr('height','40');showinfo();if(options.slideControls){var controlsheight=$(controller).outerHeight();$(controller).css('bottom','-'+controlsheight+'px');obj.hover(function(){$(controller).stop().animate({bottom:'0px'},300);},function(){$(controller).stop().animate({bottom:'-'+controlsheight+'px'},300);});} +$('a',controller).each(function(i){$(this).bind('click',function(){previousslide=currentslide;currentslide=i;if(animationtype=='dwipe'){$('li #effect .effect1:animated, li #effect .effect2:animated').stop();$('li:not('+currentslide+')',obj).css('display','none');$('li:eq('+currentslide+')',obj).css('display','block');$('li #effect',obj).remove();eval(animationtype+'('+currentslide+','+previousslide+','+true+')');}else{eval(animationtype+'('+currentslide+','+previousslide+','+true+')');} +return false;});});};}else if(options.controlsType=='buttons'){var html='';html+='Back';html+='';html+='';html+='Forward';html+='';$(obj).append(html);};if(animationtype=='slideanimate'){$('ul',obj).css({'margin':0,'padding':0,'width':slides*slidewidth});$('li',obj).css({'float':(options.vertical?'none':'left'),'height':slideheight,'width':slidewidth});}else{$('ul',obj).css({'height':slideheight,'width':slidewidth});$('li',obj).css({'height':slideheight,'left':'0','position':'absolute','top':'0','width':slidewidth});for(i=0;islides-1){currentslide=options.continuous?0:currentslide-1;}}else{currentslide=destination;};return currentslide;};function dontanimate(destination,current,clicked){if(destination==current){if(clicked){clearTimeout(timeout)};return;} +animationmechanic(destination);if(options.showControls){showinfo();};var theslide=currentslide;var diff=(currentslide-theslide+slides)%slides;var speed=diff*options.speed;if(!clicked){if(currentslide==0){previousslide=slides-1;}else{previousslide=currentslide-1;}} +$('li:eq('+previousslide+')',obj).stop(true,true).hide();$('li:eq('+currentslide+')',obj).stop(true,true).show();if(clicked)clearTimeout(timeout);if(options.rotate){timeout=setTimeout(function(){eval(animationtype+'("rotate",'+previousslide+','+false+')');},diff*speed+options.pause);};};function slideanimate(destination,current,clicked){if(destination==current){if(clicked){clearTimeout(timeout)};return;} +animationmechanic(destination);if(options.showControls){showinfo();};var theslide=currentslide;var diff=Math.abs(theslide-currentslide);var speed=diff*options.speed;if(!options.vertical){position=(currentslide*slidewidth*-1);$('ul',obj).stop().animate({marginLeft:position},speed);}else{position=(currentslide*slideheight*-1);$('ul',obj).stop().animate({marginTop:position},speed);};if(clicked)clearTimeout(timeout);if(options.rotate){timeout=setTimeout(function(){eval(animationtype+'("rotate",'+previousslide+','+false+')');},diff*speed+options.pause);};};function fadeanimate(destination,current,clicked){if(destination==current){if(clicked){clearTimeout(timeout)};return;} +animationmechanic(destination);if(options.showControls){showinfo();};if(!clicked){if(currentslide==0){previousslide=slides-1;}else{previousslide=currentslide-1;}} +var theslide=currentslide;var diff=Math.abs(theslide-currentslide);var speed=diff*options.speed;$('li:eq('+previousslide+')',obj).stop(true,true).fadeOut(speed);$('li:eq('+currentslide+')',obj).stop(true,true).fadeIn(speed);if(clicked)clearTimeout(timeout);if(options.rotate){timeout=setTimeout(function(){eval(animationtype+'("rotate",'+previousslide+','+false+')');},diff*speed+options.pause);};};function dwipeanimate(destination,current,clicked){if(destination==current){if(clicked){clearTimeout(timeout)};return;} +animationmechanic(destination);if(options.showControls){showinfo();};if(!clicked){if(currentslide==0){previousslide=slides-1;}else{previousslide=currentslide-1;}} +var effectimg=$('li:eq('+currentslide+') img',obj).attr('src');var effecthtml='
    ';effecthtml+='
    ';effecthtml+='
    ';effecthtml+='
    ';effecthtml+='
    ';effecthtml+='
    ';$('li:eq('+previousslide+')',obj).prepend(effecthtml);$('li:eq('+previousslide+') #effect',obj).css({'height':slideheight,'left':0,'overflow':'hidden','position':'absolute','top':0,'width':slidewidth});$('li:eq('+previousslide+') #effect .effect1',obj).css({'background':'url('+effectimg+') no-repeat '+'0 '+'0','display':'block','height':slideheight/2,'left':'0','position':'relative','top':-slideheight/2,'width':slidewidth});$('li:eq('+previousslide+') #effect .effect2',obj).css({'background':'url('+effectimg+') no-repeat '+'0 '+-slideheight/2+'px','display':'block','height':slideheight/2,'left':'0','position':'relative','top':slideheight,'width':slidewidth});var theslide=currentslide;var diff=Math.abs(theslide-currentslide);var speed=diff*options.speed;$('li:eq('+previousslide+') #effect .effect1',obj).stop(true,true).animate({'top':0,'left':0},speed);$('li:eq('+previousslide+') #effect .effect2',obj).stop(true,true).animate({'top':0,'left':0},speed,function(){$('li:not('+currentslide+')',obj).css('display','none');$('li:eq('+currentslide+')',obj).css('display','block');$('li #effect',obj).remove();});if(clicked)clearTimeout(timeout);if(options.rotate){timeout=setTimeout(function(){eval(animationtype+'("rotate",'+previousslide+','+false+')');},diff*speed+options.pause);};};function showinfo(){controller=$('.controller',obj);$('p',controller).addClass('inactive');$('a',controller).addClass('inactive');$('p.active',controller).removeClass('active');$('a.active',controller).removeClass('active');$('p:eq('+currentslide+')',controller).removeClass('inactive');$('p:eq('+currentslide+')',controller).addClass('active');$('a:eq('+currentslide+')',controller).removeClass('inactive');$('a:eq('+currentslide+')',controller).addClass('active');};};})(jQuery);;$(document).ready(function(){$(document.body).click(function(event){$(event.target).parents("a:first,area:first").andSelf().filter("a,area").each(function(){var ga=Drupal.settings.googleanalytics;var isInternal=new RegExp("^(https?):\/\/"+window.location.host,"i");var isInternalSpecial=new RegExp("(\/go\/.*)$","i");var isDownload=new RegExp("\\.("+ga.trackDownloadExtensions+")$","i");if(isInternal.test(this.href)){if(ga.trackDownload&&isDownload.test(this.href)){var extension=isDownload.exec(this.href);_paq.push(["_trackEvent","Downloads",extension[1].toUpperCase(),this.href.replace(isInternal,'')]);} +else if(isInternalSpecial.test(this.href)){_paq.push(["_trackPageview",this.href.replace(isInternal,'')]);}} +else{if(ga.trackMailto&&$(this).is("a[href^=mailto:],area[href^=mailto:]")){_paq.push(["_trackEvent","Mails","Click",this.href.substring(7)]);} +else if(ga.trackOutgoing&&this.href){if(ga.trackOutboundAsPageview){_paq.push(["_trackPageview",'/outbound/'+this.href.replace(/^(https?|ftp|news|nntp|telnet|irc|ssh|sftp|webcal):\/\//i,'').split('/').join('--')]);} +else{_paq.push(["_trackEvent","Outbound links","Click",this.href]);}}}});});});;function parse_url(url,param){param=param.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");url=url.replace(/&/,"&");var regexS="[\\?&]"+param+"=([^&#]*)";var regex=new RegExp(regexS);var results=regex.exec(url);if(results===null){return"";} +else{return results[1];}} +function lightbox2_init_triggers(classes,rel_type,custom_class){var settings=Drupal.settings.lightbox2;var link_target="";if(settings.node_link_target!==0){link_target='target="'+settings.node_link_target+'"';} +$("a:has("+classes+")").each(function(i){if((!settings.disable_for_gallery_lists&&!settings.disable_for_acidfree_gallery_lists)||(!$(this).parents("td.giAlbumCell").attr("class")&&!$(this).parents(".galleries").length&&!$(this).parents(".acidfree-folder").length&&!$(this).parents(".acidfree-list").length)||($(this).parents(".galleries").length&&!settings.disable_for_gallery_lists)||(($(this).parents(".acidfree-folder").length||$(this).parents(".acidfree-list").length)&&!settings.disable_for_acidfree_gallery_lists)){var child=$(this).find(classes);if($(child).attr("class")&&!$(this).parents("div.acidfree-video").length){var alt=$(child).attr("alt");if(!alt){alt="";} +var link_text=settings.node_link_text;var download_link_text=settings.download_link_text;var rel="lightbox";var lightframe=false;if(rel_type=="lightframe_ungrouped"){rel="lightframe[]";lightframe=true;} +else if(rel_type=="lightframe"){lightframe=true;} +else if(rel_type=="lightbox_ungrouped"){rel="lightbox[]";} +if(rel_type!="lightbox_ungrouped"&&rel_type!="lightframe_ungrouped"){rel=rel_type+"["+$(child).attr("class")+"]";} +var id=null;var href=$(child).attr("src");var download=null;var orig_href=$(this).attr("href");var pattern=new RegExp(settings.file_path);if(orig_href.match(pattern)){var lang_pattern=new RegExp(Drupal.settings.basePath+"\\w\\w\\/");orig_href=orig_href.replace(lang_pattern,Drupal.settings.basePath);} +var frame_href=orig_href;if($(child).attr("class").match("flickr-photo-img")||$(child).attr("class").match("flickr-photoset-img")){href=$(child).attr("src").replace("_s.",".").replace("_t.",".").replace("_m.",".").replace("_b.",".");if(rel_type!="lightbox_ungrouped"&&rel_type!="lightframe_ungrouped"){rel=rel_type+"[flickr]";if($(child).parents("div.block-flickr").attr("class")){id=$(child).parents("div.block-flickr").attr("id");rel=rel_type+"["+id+"]";}}} +else if($(child).attr("class").match("image-img_assist_custom")){orig_href=orig_href.replace(/\+/," ");href=$(child).attr("src").replace(new RegExp("\\.img_assist_custom"),((settings.display_image_size==="")?settings.display_image_size:"."+settings.display_image_size));if(rel_type!="lightbox_ungrouped"&&rel_type!="lightframe_ungrouped"){rel=rel_type+"[node_images]";} +if(lightframe){frame_href=orig_href+"/lightbox2";}} +else if($(child).attr("class").match("inline")){href=orig_href;} +else if($(child).attr("class").match("ImageFrame_image")||$(child).attr("class").match("ImageFrame_none")){var thumb_id=parse_url(href,"g2_itemId");var new_id=parse_url(orig_href,"g2_itemId");if(new_id&&thumb_id){var g2pattern=new RegExp("g2_itemId="+thumb_id);var replacement="g2_itemId="+new_id;href=href.replace(g2pattern,replacement);} +rel=rel_type+"[gallery2]";if($(child).parents("div.block-gallery").attr("class")){id=$(child).parents("div.block-gallery").attr("id");rel=rel_type+"["+id+"]";}} +else if(settings.image_node_sizes!='()'&&!custom_class){href=$(child).attr("src").replace(new RegExp(settings.image_node_sizes),((settings.display_image_size==="")?settings.display_image_size:"."+settings.display_image_size)).replace(/(image\/view\/\d+)(\/[\w\-]*)/,((settings.display_image_size==="")?"$1/_original":"$1/"+settings.display_image_size));if(rel_type!="lightbox_ungrouped"&&rel_type!="lightframe_ungrouped"){rel=rel_type+"[node_images]";if($(child).parents("div.block-multiblock,div.block-image").attr("class")){id=$(child).parents("div.block-multiblock,div.block-image").attr("id");rel=rel_type+"["+id+"]";}} +download=$(child).attr("src").replace(new RegExp(settings.image_node_sizes),"").replace(/(image\/view\/\d+)(\/[\w\-]*)/,"$1/_original");if(lightframe){frame_href=orig_href+"/lightbox2";}} +var img_title=$(child).attr("title");if(!img_title){img_title=$(this).attr("title");if(!img_title){img_title=$(child).attr("alt");} +$(child).attr({title:img_title});} +if(lightframe){href=frame_href;} +if(!custom_class){var title_link="";if(link_text.length){title_link="

    "+link_text+"";} +if(download_link_text.length){title_link=title_link+" - "+download_link_text+"";} +rel=rel+"["+alt+title_link+"]";$(this).attr({rel:rel,href:href});} +else{if(rel_type!="lightbox_ungrouped"&&rel_type!="lightframe_ungrouped"){rel=rel_type+"["+$(child).attr("class")+"]";if($(child).parents("div.block-image").attr("class")){id=$(child).parents("div.block-image").attr("id");rel=rel_type+"["+id+"]";}} +rel=rel+"["+alt+"]";$(this).attr({rel:rel,href:orig_href});}}}});} +function lightbox2_init_acidfree_video(){var settings=Drupal.settings.lightbox2;var link_target="";if(settings.node_link_target!==0){link_target='target="'+settings.node_link_target+'"';} +var link_text=settings.node_link_text;var rel="lightframe";$("div.acidfree-video a").each(function(i){if(!settings.disable_for_acidfree_gallery_lists||(!$(this).parents(".acidfree-folder").length&&!$(this).parents(".acidfree-list").length)||(($(this).parents(".acidfree-folder").length||$(this).parents(".acidfree-list").length)&&!settings.disable_for_acidfree_gallery_lists)){var orig_href=$(this).attr("href");var href=orig_href+"/lightframevideo";var title=$(this).attr("title");var title_link="";if(link_text.length){title_link="
    "+link_text+"";} +$(this).attr({rel:rel,title:title+title_link,href:href});}});} +function lightbox2_image_nodes(){var settings=Drupal.settings.lightbox2;var img_assist=document.getElementById("img_assist_thumbs");if(!img_assist){lightbox2_init_triggers(settings.trigger_lightbox_classes,"lightbox_ungrouped");lightbox2_init_triggers(settings.custom_trigger_classes,settings.custom_class_handler,true);lightbox2_init_triggers(settings.trigger_lightbox_group_classes,"lightbox");lightbox2_init_triggers(settings.trigger_slideshow_classes,"lightshow");lightbox2_init_triggers(settings.trigger_lightframe_classes,"lightframe_ungrouped");lightbox2_init_triggers(settings.trigger_lightframe_group_classes,"lightframe");if(settings.enable_acidfree_videos){lightbox2_init_acidfree_video();}}} +Drupal.behaviors.initAutoLightbox=function(context){lightbox2_image_nodes();};;var Lightbox={overlayOpacity:0.8,overlayColor:'000',disableCloseClick:true,resizeSequence:0,resizeSpeed:'normal',fadeInSpeed:'normal',slideDownSpeed:'slow',minWidth:240,borderSize:10,boxColor:'fff',fontColor:'000',topPosition:'',infoHeight:20,alternative_layout:false,imageArray:[],imageNum:null,total:0,activeImage:null,inprogress:false,disableResize:false,disableZoom:true,isZoomedIn:false,rtl:false,loopItems:false,keysClose:['c','x',27],keysPrevious:['p',37],keysNext:['n',39],keysZoom:['z'],keysPlayPause:[32],slideInterval:5000,showPlayPause:true,autoStart:true,autoExit:true,pauseOnNextClick:false,pauseOnPrevClick:true,slideIdArray:[],slideIdCount:0,isSlideshow:false,isPaused:false,loopSlides:false,isLightframe:false,iframe_width:600,iframe_height:400,iframe_border:1,enableVideo:false,flvPlayer:'/flvplayer.swf',flvFlashvars:'',isModal:false,isVideo:false,videoId:false,modalWidth:400,modalHeight:400,modalHTML:null,initialize:function(){var s=Drupal.settings.lightbox2;Lightbox.overlayOpacity=s.overlay_opacity;Lightbox.overlayColor=s.overlay_color;Lightbox.disableCloseClick=s.disable_close_click;Lightbox.resizeSequence=s.resize_sequence;Lightbox.resizeSpeed=s.resize_speed;Lightbox.fadeInSpeed=s.fade_in_speed;Lightbox.slideDownSpeed=s.slide_down_speed;Lightbox.borderSize=s.border_size;Lightbox.boxColor=s.box_color;Lightbox.fontColor=s.font_color;Lightbox.topPosition=s.top_position;Lightbox.rtl=s.rtl;Lightbox.loopItems=s.loop_items;Lightbox.keysClose=s.keys_close.split(" ");Lightbox.keysPrevious=s.keys_previous.split(" ");Lightbox.keysNext=s.keys_next.split(" ");Lightbox.keysZoom=s.keys_zoom.split(" ");Lightbox.keysPlayPause=s.keys_play_pause.split(" ");Lightbox.disableResize=s.disable_resize;Lightbox.disableZoom=s.disable_zoom;Lightbox.slideInterval=s.slideshow_interval;Lightbox.showPlayPause=s.show_play_pause;Lightbox.autoStart=s.slideshow_automatic_start;Lightbox.autoExit=s.slideshow_automatic_exit;Lightbox.pauseOnNextClick=s.pause_on_next_click;Lightbox.pauseOnPrevClick=s.pause_on_previous_click;Lightbox.loopSlides=s.loop_slides;Lightbox.alternative_layout=s.use_alt_layout;Lightbox.iframe_width=s.iframe_width;Lightbox.iframe_height=s.iframe_height;Lightbox.iframe_border=s.iframe_border;Lightbox.enableVideo=s.enable_video;if(s.enable_video){Lightbox.flvPlayer=s.flvPlayer;Lightbox.flvFlashvars=s.flvFlashvars;} +var output='\ + ';var loading='
    ';var modal='';var frame='';var imageContainer='';var details='
    ';var bottomNav='
    ';var image='';var hoverNav='
    ';var frameNav='
    ';var caption='';var numberDisplay='';var close='';var zoom='';var zoomOut='';var pause='';var play='';$("body").append(output);$('#outerImageContainer').append(modal+frame+imageContainer+loading);if(!s.use_alt_layout){$('#imageContainer').append(image+hoverNav);$('#imageData').append(frameNav+details+bottomNav);$('#imageDetails').append(caption+numberDisplay);$('#bottomNav').append(close+zoom+zoomOut+pause+play);} +else{$('#outerImageContainer').append(bottomNav);$('#imageContainer').append(image);$('#bottomNav').append(close+zoom+zoomOut);$('#imageData').append(hoverNav+details);$('#imageDetails').append(caption+numberDisplay+pause+play);} +if(Lightbox.disableCloseClick){$('#overlay').click(function(){Lightbox.end();return false;}).hide();} +$('#loadingLink, #bottomNavClose').click(function(){Lightbox.end('forceClose');return false;});$('#prevLink, #framePrevLink').click(function(){Lightbox.changeData(Lightbox.activeImage-1);return false;});$('#nextLink, #frameNextLink').click(function(){Lightbox.changeData(Lightbox.activeImage+1);return false;});$('#bottomNavZoom').click(function(){Lightbox.changeData(Lightbox.activeImage,true);return false;});$('#bottomNavZoomOut').click(function(){Lightbox.changeData(Lightbox.activeImage,false);return false;});$('#lightshowPause').click(function(){Lightbox.togglePlayPause("lightshowPause","lightshowPlay");return false;});$('#lightshowPlay').click(function(){Lightbox.togglePlayPause("lightshowPlay","lightshowPause");return false;});$('#prevLink, #nextLink, #framePrevLink, #frameNextLink').css({'paddingTop':Lightbox.borderSize+'px'});$('#imageContainer, #frameContainer, #modalContainer').css({'padding':Lightbox.borderSize+'px'});$('#outerImageContainer, #imageDataContainer, #bottomNavClose').css({'backgroundColor':'#'+Lightbox.boxColor,'color':'#'+Lightbox.fontColor});if(Lightbox.alternative_layout){$('#bottomNavZoom, #bottomNavZoomOut').css({'bottom':Lightbox.borderSize+'px','right':Lightbox.borderSize+'px'});} +else if(Lightbox.rtl==1&&$.browser.msie){$('#bottomNavZoom, #bottomNavZoomOut').css({'left':'0px'});} +if(s.force_show_nav){$('#prevLink, #nextLink').addClass("force_show_nav");}},initList:function(){$("a[@rel^='lightbox']:not(.lightbox-processed), area[@rel^='lightbox']:not(.lightbox-processed)").addClass('lightbox-processed').click(function(e){if(Lightbox.disableCloseClick){$('#lightbox').unbind('click');$('#lightbox').click(function(){Lightbox.end('forceClose');});} +Lightbox.start(this,false,false,false,false);if(e.preventDefault){e.preventDefault();} +return false;});$("a[@rel^='lightshow']:not(.lightbox-processed), area[@rel^='lightshow']:not(.lightbox-processed)").addClass('lightbox-processed').click(function(e){if(Lightbox.disableCloseClick){$('#lightbox').unbind('click');$('#lightbox').click(function(){Lightbox.end('forceClose');});} +Lightbox.start(this,true,false,false,false);if(e.preventDefault){e.preventDefault();} +return false;});$("a[@rel^='lightframe']:not(.lightbox-processed), area[@rel^='lightframe']:not(.lightbox-processed)").addClass('lightbox-processed').click(function(e){if(Lightbox.disableCloseClick){$('#lightbox').unbind('click');$('#lightbox').click(function(){Lightbox.end('forceClose');});} +Lightbox.start(this,false,true,false,false);if(e.preventDefault){e.preventDefault();} +return false;});if(Lightbox.enableVideo){$("a[@rel^='lightvideo']:not(.lightbox-processed), area[@rel^='lightvideo']:not(.lightbox-processed)").addClass('lightbox-processed').click(function(e){if(Lightbox.disableCloseClick){$('#lightbox').unbind('click');$('#lightbox').click(function(){Lightbox.end('forceClose');});} +Lightbox.start(this,false,false,true,false);if(e.preventDefault){e.preventDefault();} +return false;});} +$("a[@rel^='lightmodal']:not(.lightbox-processed), area[@rel^='lightmodal']:not(.lightbox-processed)").addClass('lightbox-processed').click(function(e){$('#lightbox').unbind('click');Lightbox.start(this,false,false,false,true);if(e.preventDefault){e.preventDefault();} +return false;});},start:function(imageLink,slideshow,lightframe,lightvideo,lightmodal){Lightbox.isPaused=!Lightbox.autoStart;Lightbox.toggleSelectsFlash('hide');var arrayPageSize=Lightbox.getPageSize();$("#overlay").hide().css({'width':'100%','zIndex':'10090','height':arrayPageSize[1]+'px','backgroundColor':'#'+Lightbox.overlayColor});if(lightvideo&&this.detectMacFF2()){$("#overlay").removeClass("overlay_default");$("#overlay").addClass("overlay_macff2");$("#overlay").css({'opacity':null});} +else{$("#overlay").removeClass("overlay_macff2");$("#overlay").addClass("overlay_default");$("#overlay").css({'opacity':Lightbox.overlayOpacity});} +$("#overlay").fadeIn(Lightbox.fadeInSpeed);Lightbox.isSlideshow=slideshow;Lightbox.isLightframe=lightframe;Lightbox.isVideo=lightvideo;Lightbox.isModal=lightmodal;Lightbox.imageArray=[];Lightbox.imageNum=0;var anchors=$(imageLink.tagName);var anchor=null;var rel_parts=Lightbox.parseRel(imageLink);var rel=rel_parts["rel"];var rel_group=rel_parts["group"];var title=(rel_parts["title"]?rel_parts["title"]:imageLink.title);var rel_style=null;var i=0;var alt=imageLink.title;if(!alt){var img=$(imageLink).find("img");if(img&&$(img).attr("alt")){alt=$(img).attr("alt");} +else{alt=title;}} +if($(imageLink).attr('id')=='lightboxAutoModal'){rel_style=rel_parts["style"];Lightbox.imageArray.push(['#lightboxAutoModal > *',title,alt,rel_style,1]);} +else{if((rel=='lightbox'||rel=='lightshow')&&!rel_group){Lightbox.imageArray.push([imageLink.href,title,alt]);} +else if(!rel_group){rel_style=rel_parts["style"];Lightbox.imageArray.push([imageLink.href,title,alt,rel_style]);} +else{for(i=0;ii;j--){if(Lightbox.imageArray[i][0]==Lightbox.imageArray[j][0]){Lightbox.imageArray.splice(j,1);}}} +while(Lightbox.imageArray[Lightbox.imageNum][0]!=imageLink.href){Lightbox.imageNum++;}}} +if(Lightbox.isSlideshow&&Lightbox.showPlayPause&&Lightbox.isPaused){$('#lightshowPlay').show();$('#lightshowPause').hide();} +var arrayPageScroll=Lightbox.getPageScroll();var lightboxTop=arrayPageScroll[1]+(Lightbox.topPosition==''?(arrayPageSize[3]/10):Lightbox.topPosition)*1;var lightboxLeft=arrayPageScroll[0];$('#frameContainer, #modalContainer, #lightboxImage').hide();$('#hoverNav, #prevLink, #nextLink, #frameHoverNav, #framePrevLink, #frameNextLink').hide();$('#imageDataContainer, #numberDisplay, #bottomNavZoom, #bottomNavZoomOut').hide();$('#outerImageContainer').css({'width':'250px','height':'250px'});$('#lightbox').css({'zIndex':'10500','top':lightboxTop+'px','left':lightboxLeft+'px'}).show();Lightbox.total=Lightbox.imageArray.length;Lightbox.changeData(Lightbox.imageNum);},changeData:function(imageNum,zoomIn){if(Lightbox.inprogress===false){if(Lightbox.total>1&&((Lightbox.isSlideshow&&Lightbox.loopSlides)||(!Lightbox.isSlideshow&&Lightbox.loopItems))){if(imageNum>=Lightbox.total)imageNum=0;if(imageNum<0)imageNum=Lightbox.total-1;} +if(Lightbox.isSlideshow){for(var i=0;i=targ.w||orig.h>=targ.h)&&orig.h&&orig.w){ratio=((targ.w/orig.w)<(targ.h/orig.h))?targ.w/orig.w:targ.h/orig.h;if(!Lightbox.disableZoom&&!Lightbox.isSlideshow){}} +imageWidth=Math.floor(orig.w*ratio);imageHeight=Math.floor(orig.h*ratio);} +else{$('#bottomNavZoom').hide();if((orig.w>=targ.w||orig.h>=targ.h)&&orig.h&&orig.w){if(!Lightbox.disableResize&&Lightbox.isSlideshow===false&&!Lightbox.disableZoom){}}} +photo.style.width=(imageWidth)+'px';photo.style.height=(imageHeight)+'px';Lightbox.resizeContainer(imageWidth,imageHeight);imgPreloader.onload=function(){};};imgPreloader.src=Lightbox.imageArray[Lightbox.activeImage][0];imgPreloader.alt=Lightbox.imageArray[Lightbox.activeImage][2];} +else if(Lightbox.isLightframe){var src=Lightbox.imageArray[Lightbox.activeImage][0];$('#frameContainer').html('');if($.browser.mozilla&&src.indexOf('.swf')!=-1){setTimeout(function(){document.getElementById("lightboxFrame").src=Lightbox.imageArray[Lightbox.activeImage][0];},1000);} +if(!Lightbox.iframe_border){$('#lightboxFrame').css({'border':'none'});$('#lightboxFrame').attr('frameborder','0');} +var iframe=document.getElementById('lightboxFrame');var iframeStyles=Lightbox.imageArray[Lightbox.activeImage][3];iframe=Lightbox.setStyles(iframe,iframeStyles);Lightbox.resizeContainer(parseInt(iframe.width,10),parseInt(iframe.height,10));} +else if(Lightbox.isVideo||Lightbox.isModal){var container=document.getElementById('modalContainer');var modalStyles=Lightbox.imageArray[Lightbox.activeImage][3];container=Lightbox.setStyles(container,modalStyles);if(Lightbox.isVideo){Lightbox.modalHeight=parseInt(container.height,10);Lightbox.modalWidth=parseInt(container.width,10);Lightvideo.startVideo(Lightbox.imageArray[Lightbox.activeImage][0]);} +Lightbox.resizeContainer(parseInt(container.width,10),parseInt(container.height,10));}}},imgNodeLoadingError:function(image){var s=Drupal.settings.lightbox2;var original_image=Lightbox.imageArray[Lightbox.activeImage][0];if(s.display_image_size!==""){original_image=original_image.replace(new RegExp("."+s.display_image_size),"");} +Lightbox.imageArray[Lightbox.activeImage][0]=original_image;image.onerror=function(){Lightbox.imgLoadingError(image);};image.src=original_image;},imgLoadingError:function(image){var s=Drupal.settings.lightbox2;Lightbox.imageArray[Lightbox.activeImage][0]=s.default_image;image.src=s.default_image;},resizeContainer:function(imgWidth,imgHeight){imgWidth=(imgWidth1){Lightbox.slideIdArray[Lightbox.slideIdCount++]=setTimeout(function(){Lightbox.changeData(Lightbox.activeImage+1);},Lightbox.slideInterval);}} +if(Lightbox.showPlayPause&&Lightbox.total>1&&!Lightbox.isPaused){$('#lightshowPause').show();$('#lightshowPlay').hide();} +else if(Lightbox.showPlayPause&&Lightbox.total>1){$('#lightshowPause').hide();$('#lightshowPlay').show();}} +var arrayPageSize=Lightbox.getPageSize();var arrayPageScroll=Lightbox.getPageScroll();var pageHeight=arrayPageSize[1];if(Lightbox.isZoomedIn&&arrayPageSize[1]>arrayPageSize[3]){var lightboxTop=(Lightbox.topPosition==''?(arrayPageSize[3]/10):Lightbox.topPosition)*1;pageHeight=pageHeight+arrayPageScroll[1]+lightboxTop;} +$('#overlay').css({'height':pageHeight+'px','width':arrayPageSize[0]+'px'});if($.browser.mozilla){if(Lightbox.imageArray[Lightbox.activeImage][0].indexOf(".pdf")!=-1){setTimeout(function(){document.getElementById("lightboxFrame").src=Lightbox.imageArray[Lightbox.activeImage][0];},1000);}}},updateDetails:function(){$("#imageDataContainer").hide();var caption=Lightbox.imageArray[Lightbox.activeImage][1];if(!caption)caption=' ';$('#caption').html(caption).css({'zIndex':'10500'}).show();var s=Drupal.settings.lightbox2;var numberDisplay=null;if(Lightbox.total>1){var currentImage=Lightbox.activeImage+1;if(!Lightbox.isLightframe&&!Lightbox.isModal&&!Lightbox.isVideo){numberDisplay=s.image_count.replace(/\!current/,currentImage).replace(/\!total/,Lightbox.total);} +else if(Lightbox.isVideo){numberDisplay=s.video_count.replace(/\!current/,currentImage).replace(/\!total/,Lightbox.total);} +else{numberDisplay=s.page_count.replace(/\!current/,currentImage).replace(/\!total/,Lightbox.total);} +$('#numberDisplay').html(numberDisplay).css({'zIndex':'10500'}).show();} +$("#imageDataContainer").hide().slideDown(Lightbox.slideDownSpeed,function(){$("#bottomNav").show();});if(Lightbox.rtl==1){$("#bottomNav").css({'float':'left'});} +Lightbox.updateNav();},updateNav:function(){$('#hoverNav').css({'zIndex':'10500'}).show();var prevLink='#prevLink';var nextLink='#nextLink';if(Lightbox.isSlideshow){if((Lightbox.total>1&&Lightbox.loopSlides)||Lightbox.activeImage!==0){$(prevLink).css({'zIndex':'10500'}).show().click(function(){if(Lightbox.pauseOnPrevClick){Lightbox.togglePlayPause("lightshowPause","lightshowPlay");} +Lightbox.changeData(Lightbox.activeImage-1);return false;});} +else{$(prevLink).hide();} +if((Lightbox.total>1&&Lightbox.loopSlides)||Lightbox.activeImage!=(Lightbox.total-1)){$(nextLink).css({'zIndex':'10500'}).show().click(function(){if(Lightbox.pauseOnNextClick){Lightbox.togglePlayPause("lightshowPause","lightshowPlay");} +Lightbox.changeData(Lightbox.activeImage+1);return false;});} +else{$(nextLink).hide();}} +else{if((Lightbox.isLightframe||Lightbox.isModal||Lightbox.isVideo)&&!Lightbox.alternative_layout){$('#frameHoverNav').css({'zIndex':'10500'}).show();$('#hoverNav').css({'zIndex':'10500'}).hide();prevLink='#framePrevLink';nextLink='#frameNextLink';} +if((Lightbox.total>1&&Lightbox.loopItems)||Lightbox.activeImage!==0){$(prevLink).css({'zIndex':'10500'}).show().click(function(){Lightbox.changeData(Lightbox.activeImage-1);return false;});} +else{$(prevLink).hide();} +if((Lightbox.total>1&&Lightbox.loopItems)||Lightbox.activeImage!=(Lightbox.total-1)){$(nextLink).css({'zIndex':'10500'}).show().click(function(){Lightbox.changeData(Lightbox.activeImage+1);return false;});} +else{$(nextLink).hide();}} +if(!Lightbox.isModal){this.enableKeyboardNav();}},enableKeyboardNav:function(){$(document).bind("keydown",this.keyboardAction);},disableKeyboardNav:function(){$(document).unbind("keydown",this.keyboardAction);},keyboardAction:function(e){if(e===null){keycode=event.keyCode;escapeKey=27;} +else{keycode=e.keyCode;escapeKey=e.DOM_VK_ESCAPE;} +key=String.fromCharCode(keycode).toLowerCase();if(Lightbox.checkKey(Lightbox.keysClose,key,keycode)){Lightbox.end('forceClose');} +else if(Lightbox.checkKey(Lightbox.keysPrevious,key,keycode)){if((Lightbox.total>1&&((Lightbox.isSlideshow&&Lightbox.loopSlides)||(!Lightbox.isSlideshow&&Lightbox.loopItems)))||Lightbox.activeImage!==0){Lightbox.changeData(Lightbox.activeImage-1);}} +else if(Lightbox.checkKey(Lightbox.keysNext,key,keycode)){if((Lightbox.total>1&&((Lightbox.isSlideshow&&Lightbox.loopSlides)||(!Lightbox.isSlideshow&&Lightbox.loopItems)))||Lightbox.activeImage!=(Lightbox.total-1)){Lightbox.changeData(Lightbox.activeImage+1);}} +else if(Lightbox.checkKey(Lightbox.keysZoom,key,keycode)&&!Lightbox.disableResize&&!Lightbox.disableZoom&&!Lightbox.isSlideshow&&!Lightbox.isLightframe){if(Lightbox.isZoomedIn){Lightbox.changeData(Lightbox.activeImage,false);} +else if(!Lightbox.isZoomedIn){Lightbox.changeData(Lightbox.activeImage,true);} +return false;} +else if(Lightbox.checkKey(Lightbox.keysPlayPause,key,keycode)&&Lightbox.isSlideshow){if(Lightbox.isPaused){Lightbox.togglePlayPause("lightshowPlay","lightshowPause");} +else{Lightbox.togglePlayPause("lightshowPause","lightshowPlay");} +return false;}},preloadNeighborImages:function(){if((Lightbox.total-1)>Lightbox.activeImage){preloadNextImage=new Image();preloadNextImage.src=Lightbox.imageArray[Lightbox.activeImage+1][0];} +if(Lightbox.activeImage>0){preloadPrevImage=new Image();preloadPrevImage.src=Lightbox.imageArray[Lightbox.activeImage-1][0];}},end:function(caller){var closeClick=(caller=='slideshow'?false:true);if(Lightbox.isSlideshow&&Lightbox.isPaused&&!closeClick){return;} +if(Lightbox.inprogress===true&&caller!='forceClose'){return;} +Lightbox.disableKeyboardNav();$('#lightbox').hide();$("#overlay").fadeOut();Lightbox.isPaused=true;Lightbox.inprogress=false;Lightbox.toggleSelectsFlash('visible');if(Lightbox.isSlideshow){for(var i=0;idocument.body.offsetHeight){xScroll=document.body.scrollWidth;yScroll=document.body.scrollHeight;} +else if(window.innerHeight&&window.scrollMaxY){xScroll=window.innerWidth+window.scrollMaxX;yScroll=window.innerHeight+window.scrollMaxY;} +else{xScroll=document.body.offsetWidth;yScroll=document.body.offsetHeight;} +var windowWidth,windowHeight;if(self.innerHeight){if(document.documentElement.clientWidth){windowWidth=document.documentElement.clientWidth;} +else{windowWidth=self.innerWidth;} +windowHeight=self.innerHeight;} +else if(document.documentElement&&document.documentElement.clientHeight){windowWidth=document.documentElement.clientWidth;windowHeight=document.documentElement.clientHeight;} +else if(document.body){windowWidth=document.body.clientWidth;windowHeight=document.body.clientHeight;} +if(yScroll=0){var w=stylesArray[i].replace('width:','');item.width=jQuery.trim(w);} +else if(stylesArray[i].indexOf('height:')>=0){var h=stylesArray[i].replace('height:','');item.height=jQuery.trim(h);} +else if(stylesArray[i].indexOf('scrolling:')>=0){var scrolling=stylesArray[i].replace('scrolling:','');item.scrolling=jQuery.trim(scrolling);} +else if(stylesArray[i].indexOf('overflow:')>=0){var overflow=stylesArray[i].replace('overflow:','');item.overflow=jQuery.trim(overflow);}} +return item;},togglePlayPause:function(hideId,showId){if(Lightbox.isSlideshow&&hideId=="lightshowPause"){for(var i=0;i1){Lightbox.changeData(Lightbox.activeImage+1);}} +else{Lightbox.isPaused=true;}},triggerLightbox:function(rel_type,rel_group){if(rel_type.length){if(rel_group&&rel_group.length){$("a[@rel^='"+rel_type+"\["+rel_group+"\]'], area[@rel^='"+rel_type+"\["+rel_group+"\]']").eq(0).trigger("click");} +else{$("a[@rel^='"+rel_type+"'], area[@rel^='"+rel_type+"']").eq(0).trigger("click");}}},detectMacFF2:function(){var ua=navigator.userAgent.toLowerCase();if(/firefox[\/\s](\d+\.\d+)/.test(ua)){var ffversion=new Number(RegExp.$1);if(ffversion<3&&ua.indexOf('mac')!=-1){return true;}} +return false;},checkKey:function(keys,key,code){return(jQuery.inArray(key,keys)!=-1||jQuery.inArray(String(code),keys)!=-1);}};Drupal.behaviors.initLightbox=function(context){$('body:not(.lightbox-processed)',context).addClass('lightbox-processed').each(function(){Lightbox.initialize();$('#lightboxAutoModal').triggerHandler('click');return false;});Lightbox.initList();};;if(document.all&&!window.opera&&(navigator.appVersion.search("MSIE 6.0")!=-1)&&$.browser.msie){function IEHoverPseudo(){$("ul.nice-menu li.menuparent").hover(function(){$(this).addClass("over").find("> ul").show().addShim();},function(){$(this).removeClass("over").find("> ul").removeShim().hide();});$("ul.nice-menu li").hover(function(){$(this).addClass("ie-over");},function(){$(this).removeClass("ie-over");});} +$(document).ready(function(){IEHoverPseudo()});} +$.fn.addShim=function(){return this.each(function(){if(document.all&&$("select").size()>0){var ifShim=document.createElement('iframe');ifShim.src="javascript:false";ifShim.style.width=$(this).width()+1+"px";ifShim.style.height=$(this).find("> li").size()*23+20+"px";ifShim.style.filter="progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)";ifShim.style.zIndex="0";$(this).prepend(ifShim);$(this).css("zIndex","99");}});};$.fn.removeShim=function(){return this.each(function(){if(document.all)$("iframe",this).remove();});};;function formFakeSubmit(el){setTimeout("$('"+el+"').unbind('click'); $('"+el+"').click();",1000);};packt_get_external_link=function(){if($('#edit-page').val()==''){$('#edit-page').addClass('error');return false;} +if($('#edit-domain-id').val()==''){$('#edit-domain-id').addClass('error');return false;} +if($('#edit-name').val()==''){$('#edit-name').addClass('error');return false;} +if($('#edit-type').val()==''){$('#edit-type').addClass('error');return false;} +$.ajax({url:'/account/affiliate/generate_link',cache:false,type:'POST',data:{name:$('#edit-name').val(),type:$('#edit-type').val(),domain_id:$('#edit-domain-id').val(),link:$('#edit-page').val()},success:function(data,status,xhr){data=eval("("+data+")");if(status=='success'){if(data.link!==false){$('#affiliate_link_preview .inner')[0].innerHTML=data.link;$('#affiliate_link_text').show();$('#edit-save').show();$('#edit-link').val(data.link);$('#affiliate_link_preview').show();if(typeof(descm)!='undefined'){$('#edit-page-wrapper div.description').html(descm);$('#edit-page').removeClass('error');}}else{$('#edit-page').addClass('error');if(typeof(descm)=='undefined'){descm=$('#edit-page-wrapper div.description').html();} +$('#edit-page-wrapper div.description').html(descm+"

    Please enter a valid link to a currently available page on the site.

    ");$('#affiliate_link_text').hide();$('#affiliate_link_preview').hide();$('#edit-save').hide();$('#edit-link').val("");}}else{console.log("Failed AJAX call");}}});} +packt_aff_switch_form=function(element){if($(element).val()=='paypal'){$('#bank').hide();$('#paypal').show();}else if($(element).val()=='bank'){$('#bank').show();$('#paypal').hide();}else{$('#bank').hide();$('#paypal').hide();}};var packt_cat={init:function(){$(document).ready(function(){$('#packt-categorypages-cat-search-form #edit-types').change(function(){packt_cat.changeCheckbox(this);});});},changeCheckbox:function(t){$('.solr-book-options .form-item').hide();if($(t).val()==1||$(t).val()==2){$('.solr-book-options .form-item').show();}}};packt_cat.init();;packthadoop={init:function(){$(document).ready(function(){$('.hadoop-offer-wrapper input[type=radio]').click(function(){packthadoop.switchForms(this);});});},switchForms:function(ele){$(ele).parent().find('form').parent().removeClass('hidden').addClass('hidden');if($(ele).attr('class')=='bundle_enabler'){$(ele).parent().find(".add-to-cart-button.bundle").removeClass('hidden');}else{$(ele).parent().find(".add-to-cart-button.ebook").removeClass('hidden');}}};packthadoop.init();;jQuery(document).ready(function(){if($('#boxy-content-video').length>0){boxy_video_help=new Boxy($('#boxy-content-video').html(),{show:false,afterShow:function(){$('.boxy-video-close').click(function(){boxy_video_help.hide();});}});$('div.video-download-button').click(function(){boxy_video_help.show();});}});;(function($,window,document,undefined){var $window=$(window);$.fn.lazyload=function(options){var elements=this;var $container;var settings={threshold:0,failure_limit:6,event:"scroll",effect:"show",container:window,data_attribute:"original",skip_invisible:true,appear:null,load:null};function update(){var counter=0;elements.each(function(){var $this=$(this);if(settings.skip_invisible&&!$this.is(":visible")){return;} +if($.abovethetop(this,settings)||$.leftofbegin(this,settings)){}else if(!$.belowthefold(this,settings)&&!$.rightoffold(this,settings)){$this.trigger("appear");counter=0;}else{if(++counter>settings.failure_limit){return false;}}});} +if(options){if(undefined!==options.failurelimit){options.failure_limit=options.failurelimit;delete options.failurelimit;} +if(undefined!==options.effectspeed){options.effect_speed=options.effectspeed;delete options.effectspeed;} +$.extend(settings,options);} +$container=(settings.container===undefined||settings.container===window)?$window:$(settings.container);if(0===settings.event.indexOf("scroll")){$container.bind(settings.event,function(event){return update();});} +this.each(function(){var self=this;var $self=$(self);self.loaded=false;$self.one("appear",function(){if(!this.loaded){if(settings.appear){var elements_left=elements.length;settings.appear.call(self,elements_left,settings);} +$("").bind("load",function(){$self.hide().attr("src",$self.attr('data-'+settings.data_attribute)) +[settings.effect](settings.effect_speed);self.loaded=true;var temp=$.grep(elements,function(element){return!element.loaded;});elements=$(temp);if(settings.load){var elements_left=elements.length;settings.load.call(self,elements_left,settings);}}).attr("src",$self.attr('data-'+settings.data_attribute));}});if(0!==settings.event.indexOf("scroll")){$self.bind(settings.event,function(event){if(!self.loaded){$self.trigger("appear");}});}});jQuery(window).load(function(){update();});$window.bind("resize",function(event){update();});if((/iphone|ipod|ipad.*os 5/gi).test(navigator.appVersion)){$window.bind("pageshow",function(event){if(event.originalEvent.persisted){elements.each(function(){$(this).trigger("appear");});}});} +update();return this;};$.belowthefold=function(element,settings){var fold;if(settings.container===undefined||settings.container===window){fold=$window.height()+$window.scrollTop();}else{fold=$(settings.container).offset().top+$(settings.container).height();} +return fold<=$(element).offset().top-settings.threshold;};$.rightoffold=function(element,settings){var fold;if(settings.container===undefined||settings.container===window){fold=$window.width()+$window.scrollLeft();}else{fold=$(settings.container).offset().left+$(settings.container).width();} +return fold<=$(element).offset().left-settings.threshold;};$.abovethetop=function(element,settings){var fold;if(settings.container===undefined||settings.container===window){fold=$window.scrollTop();}else{fold=$(settings.container).offset().top;} +return fold>=$(element).offset().top+settings.threshold+$(element).height();};$.leftofbegin=function(element,settings){var fold;if(settings.container===undefined||settings.container===window){fold=$window.scrollLeft();}else{fold=$(settings.container).offset().left;} +return fold>=$(element).offset().left+settings.threshold+$(element).width();};$.inviewport=function(element,settings){return!$.rightoffold(element,settings)&&!$.leftofbegin(element,settings)&&!$.belowthefold(element,settings)&&!$.abovethetop(element,settings);};$.extend($.expr[':'],{"below-the-fold":function(a){return $.belowthefold(a,{threshold:0});},"above-the-top":function(a){return!$.belowthefold(a,{threshold:0});},"right-of-screen":function(a){return $.rightoffold(a,{threshold:0});},"left-of-screen":function(a){return!$.rightoffold(a,{threshold:0});},"in-viewport":function(a){return $.inviewport(a,{threshold:0});},"above-the-fold":function(a){return!$.belowthefold(a,{threshold:0});},"right-of-fold":function(a){return $.rightoffold(a,{threshold:0});},"left-of-fold":function(a){return!$.rightoffold(a,{threshold:0});}});})(jQuery,window,document);;eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(5($){$.7.1j=5(o){2(P o==\'5\')o={L:o};o=$.2h({1h:4.X(\'2i\')||1E.2u.3D(),I:4.X(\'2g\')||\'29\'},o||{});3 p={};$.M.N(\'R.2P.2L\',[4,o,p]);2(p.1Q)6 4;3 a=4.1z(o.2r);2(o.V){H(3 n 3u o.V)a.C({z:n,A:o.V[n]})}2(o.28&&o.28(a,4,o)===E)6 4;$.M.N(\'R.K.36\',[a,4,o,p]);2(p.1Q)6 4;3 q=$.1x(a);2(o.I.31()==\'29\'){o.1h+=(o.1h.2Z(\'?\')>=0?\'&\':\'?\')+q;o.V=B}8 o.V=q;3 r=4,U=[];2(o.1r)U.C(5(){r.1r()});2(o.1o)U.C(5(){r.1o()});2(!o.18&&o.14){3 u=o.L||5(){};U.C(5(a){2(4.1N)$(o.14).X("1M",a).1N().D(u,1L);8 $(o.14).2t(a).D(u,1L)})}8 2(o.L)U.C(o.L);o.L=5(a,b){H(3 i=0,F=U.G;i\');3 j=i[0];3 k=$.1i.20&&1E.20.30()<9;2($.1i.1X||k)j.2Y=\'2W:E;1w.2U("");\';i.2S({2R:\'2Q\',23:\'-24\',1R:\'-24\'});3 l={Z:B,1b:B,2K:0,2J:\'n/a\',2H:5(){},2F:5(){},2E:5(){}};3 g=f.2B;2(g&&!$.1O++)$.M.N("2x");2(g)$.M.N("2w",[l,f]);3 m=0;3 n=0;1f(5(){i.2v(\'1n\');j.1K?j.1K(\'1J\',12):j.2s(\'1I\',12,E);3 a=d.1H?\'1H\':\'2q\';3 t=r.X(\'14\');r.X({14:h,2g:\'3C\',2i:f.1h});d[a]=\'3B/R-V\';2(f.1G)1f(5(){n=T;12()},f.1G);d.K();r.X(\'14\',t)},10);5 12(){2(m++)6;j.2o?j.2o(\'1J\',12):j.3A(\'1I\',12,E);3 a=T;3z{2(n)3x\'1G\';3 b,O;O=j.2n?j.2n.1w:j.2l?j.2l:j.1w;l.Z=O.1n?O.1n.1M:B;l.1b=O.2k?O.2k:O;2(f.18==\'2j\'||f.18==\'3s\'){3 c=O.1D(\'1C\')[0];b=c?c.A:l.Z;2(f.18==\'2j\')3r("V = "+b);8 $.3q(b)}8 2(f.18==\'2m\'){b=l.1b;2(!b&&l.Z!=B)b=2d(l.Z)}8{b=l.Z}}3p(e){a=E;$.3n(f,l,\'2b\',e)}2(a){f.L(b,\'L\');2(g)$.M.N("3m",[l,f])}2(g)$.M.N("3k",[l,f]);2(g&&!--$.1O)$.M.N("3j");2(f.27)f.27(l,a?\'L\':\'2b\');1f(5(){i.3i();l.1b=B},3g)};5 2d(s,a){2(1E.26){a=25 26(\'3d.3c\');a.3b=\'E\';a.3a(s)}8 a=(25 38()).37(s,\'1A/2m\');6(a&&a.22&&a.22.1e!=\'34\')?a:B}}};$.7.1j.1a=0;$.7.W=5(a){6 4.21().K(1m).D(5(){4.1u=$.7.W.1a++;$.7.W.1t[4.1u]=a;$(":K,19:Y",4).1Z(1s)})};$.7.W.1a=1;$.7.W.1t={};5 1s(e){3 a=4.R;a.Q=4;2(4.I==\'Y\'){2(e.1Y!=S){a.11=e.1Y;a.16=e.2X}8 2(P $.7.1U==\'5\'){3 b=$(4).1U();a.11=e.1V-b.1R;a.16=e.1W-b.23}8{a.11=e.1V-4.2V;a.16=e.1W-4.32}}1f(5(){a.Q=a.11=a.16=B},10)};5 1m(){3 a=4.1u;3 b=$.7.W.1t[a];$(4).1j(b);6 E};$.7.21=5(){4.1T(\'K\',1m);6 4.D(5(){$(":K,19:Y",4).1T(\'1Z\',1s)})};$.7.1z=5(b){3 a=[];2(4.G==0)6 a;3 c=4[0];3 d=b?c.1D(\'*\'):c.2T;2(!d)6 a;H(3 i=0,F=d.G;i").attr("name",b.submitButton.name).val(b.submitButton.value).appendTo(b.currentForm);b.settings.submitHandler.call(b,b.currentForm);b.submitButton&&f.remove();return false}return true}b.settings.debug&&d.preventDefault();if(b.cancelSubmit){b.cancelSubmit=false;return e()}if(b.form()){if(b.pendingRequest){b.formSubmitted=true;return false}return e()}else{b.focusInvalid();return false}})}return b}else a&&a.debug&&window.console&&console.warn("nothing selected, can't validate, returning nothing")},valid:function(){if(c(this[0]).is("form"))return this.validate().form();else{var a=true,b=c(this[0].form).validate();this.each(function(){a&=b.element(this)});return a}},removeAttrs:function(a){var b={},d=this;c.each(a.split(/\s/),function(e,f){b[f]=d.attr(f);d.removeAttr(f)});return b},rules:function(a,b){var d=this[0];if(a){var e=c.data(d.form,"validator").settings,f=e.rules,g=c.validator.staticRules(d);switch(a){case"add":c.extend(g,c.validator.normalizeRule(b));f[d.name]=g;if(b.messages)e.messages[d.name]=c.extend(e.messages[d.name],b.messages);break;case"remove":if(!b){delete f[d.name];return g}var h={};c.each(b.split(/\s/),function(j,i){h[i]=g[i];delete g[i]});return h}}d=c.validator.normalizeRules(c.extend({},c.validator.metadataRules(d),c.validator.classRules(d),c.validator.attributeRules(d),c.validator.staticRules(d)),d);if(d.required){e=d.required;delete d.required;d=c.extend({required:e},d)}return d}});c.extend(c.expr[":"],{blank:function(a){return!c.trim(""+a.value)},filled:function(a){return!!c.trim(""+a.value)},unchecked:function(a){return!a.checked}});c.validator=function(a,b){this.settings=c.extend(true,{},c.validator.defaults,a);this.currentForm=b;this.init()};c.validator.format=function(a,b){if(arguments.length==1)return function(){var d=c.makeArray(arguments);d.unshift(a);return c.validator.format.apply(this,d)};if(arguments.length>2&&b.constructor!=Array)b=c.makeArray(arguments).slice(1);if(b.constructor!=Array)b=[b];c.each(b,function(d,e){a=a.replace(RegExp("\\{"+d+"\\}","g"),e)});return a};c.extend(c.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",validClass:"valid",errorElement:"label",focusInvalid:true,errorContainer:c([]),errorLabelContainer:c([]),onsubmit:true,ignore:[],ignoreTitle:false,onfocusin:function(a){this.lastActive=a;if(this.settings.focusCleanup&&!this.blockFocusCleanup){this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass);this.addWrapper(this.errorsFor(a)).hide()}},onfocusout:function(a){if(!this.checkable(a)&&(a.name in this.submitted||!this.optional(a)))this.element(a)},onkeyup:function(a){if(a.name in this.submitted||a==this.lastElement)this.element(a)},onclick:function(a){if(a.name in this.submitted)this.element(a);else a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(a,b,d){a.type==="radio"?this.findByName(a.name).addClass(b).removeClass(d):c(a).addClass(b).removeClass(d)},unhighlight:function(a,b,d){a.type==="radio"?this.findByName(a.name).removeClass(b).addClass(d):c(a).removeClass(b).addClass(d)}},setDefaults:function(a){c.extend(c.validator.defaults,a)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",creditcard:"Please enter a valid credit card number.",equalTo:"Please enter the same value again.",accept:"Please enter a value with a valid extension.",maxlength:c.validator.format("Please enter no more than {0} characters."),minlength:c.validator.format("Please enter at least {0} characters."),rangelength:c.validator.format("Please enter a value between {0} and {1} characters long."),range:c.validator.format("Please enter a value between {0} and {1}."),max:c.validator.format("Please enter a value less than or equal to {0}."),min:c.validator.format("Please enter a value greater than or equal to {0}.")},autoCreateRanges:false,prototype:{init:function(){function a(e){var f=c.data(this[0].form,"validator");e="on"+e.type.replace(/^validate/,"");f.settings[e]&&f.settings[e].call(f,this[0])}this.labelContainer=c(this.settings.errorLabelContainer);this.errorContext=this.labelContainer.length&&this.labelContainer||c(this.currentForm);this.containers=c(this.settings.errorContainer).add(this.settings.errorLabelContainer);this.submitted={};this.valueCache={};this.pendingRequest=0;this.pending={};this.invalid={};this.reset();var b=this.groups={};c.each(this.settings.groups,function(e,f){c.each(f.split(/\s/),function(g,h){b[h]=e})});var d=this.settings.rules;c.each(d,function(e,f){d[e]=c.validator.normalizeRule(f)});c(this.currentForm).validateDelegate(":text, :password, :file, select, textarea","focusin focusout keyup",a).validateDelegate(":radio, :checkbox, select, option","click",a);this.settings.invalidHandler&&c(this.currentForm).bind("invalid-form.validate",this.settings.invalidHandler)},form:function(){this.checkForm();c.extend(this.submitted,this.errorMap);this.invalid=c.extend({},this.errorMap);this.valid()||c(this.currentForm).triggerHandler("invalid-form",[this]);this.showErrors();return this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(a){this.lastElement=a=this.clean(a);this.prepareElement(a);this.currentElements=c(a);var b=this.check(a);if(b)delete this.invalid[a.name];else this.invalid[a.name]=true;if(!this.numberOfInvalids())this.toHide=this.toHide.add(this.containers);this.showErrors();return b},showErrors:function(a){if(a){c.extend(this.errorMap,a);this.errorList=[];for(var b in a)this.errorList.push({message:a[b],element:this.findByName(b)[0]});this.successList=c.grep(this.successList,function(d){return!(d.name in a)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){c.fn.resetForm&&c(this.currentForm).resetForm();this.submitted={};this.prepareForm();this.hideErrors();this.elements().removeClass(this.settings.errorClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b=0,d;for(d in a)b++;return b},hideErrors:function(){this.addWrapper(this.toHide).hide()},valid:function(){return this.size()==0},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{c(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(a){}},findLastActive:function(){var a=this.lastActive;return a&&c.grep(this.errorList,function(b){return b.element.name==a.name}).length==1&&a},elements:function(){var a=this,b={};return c(this.currentForm).find("input, select, textarea").not(":submit, :reset, :image, [disabled]").not(this.settings.ignore).filter(function(){!this.name&&a.settings.debug&&window.console&&console.error("%o has no name assigned",this);if(this.name in b||!a.objectLength(c(this).rules()))return false;return b[this.name]=true})},clean:function(a){return c(a)[0]},errors:function(){return c(this.settings.errorElement+"."+this.settings.errorClass,this.errorContext)},reset:function(){this.successList=[];this.errorList=[];this.errorMap={};this.toShow=c([]);this.toHide=c([]);this.currentElements=c([])},prepareForm:function(){this.reset();this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset();this.toHide=this.errorsFor(a)},check:function(a){a=this.clean(a);if(this.checkable(a))a=this.findByName(a.name).not(this.settings.ignore)[0];var b=c(a).rules(),d=false,e;for(e in b){var f={method:e,parameters:b[e]};try{var g=c.validator.methods[e].call(this,a.value.replace(/\r/g,""),a,f.parameters);if(g=="dependency-mismatch")d=true;else{d=false;if(g=="pending"){this.toHide=this.toHide.not(this.errorsFor(a));return}if(!g){this.formatAndAdd(a,f);return false}}}catch(h){this.settings.debug&&window.console&&console.log("exception occured when checking element "+a.id+", check the '"+f.method+"' method",h);throw h;}}if(!d){this.objectLength(b)&&this.successList.push(a);return true}},customMetaMessage:function(a,b){if(c.metadata){var d=this.settings.meta?c(a).metadata()[this.settings.meta]:c(a).metadata();return d&&d.messages&&d.messages[b]}},customMessage:function(a,b){var d=this.settings.messages[a];return d&&(d.constructor==String?d:d[b])},findDefined:function(){for(var a=0;aWarning: No message defined for "+ +a.name+"")},formatAndAdd:function(a,b){var d=this.defaultMessage(a,b.method),e=/\$?\{(\d+)\}/g;if(typeof d=="function")d=d.call(this,b.parameters,a);else if(e.test(d))d=jQuery.format(d.replace(e,"{$1}"),b.parameters);this.errorList.push({message:d,element:a});this.errorMap[a.name]=d;this.submitted[a.name]=d},addWrapper:function(a){if(this.settings.wrapper)a=a.add(a.parent(this.settings.wrapper));return a},defaultShowErrors:function(){for(var a=0;this.errorList[a];a++){var b=this.errorList[a];this.settings.highlight&&this.settings.highlight.call(this,b.element,this.settings.errorClass,this.settings.validClass);this.showLabel(b.element,b.message)}if(this.errorList.length)this.toShow=this.toShow.add(this.containers);if(this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight){a=0;for(b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass)}this.toHide=this.toHide.not(this.toShow);this.hideErrors();this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return c(this.errorList).map(function(){return this.element})},showLabel:function(a,b){var d=this.errorsFor(a);if(d.length){d.removeClass().addClass(this.settings.errorClass);d.attr("generated")&&d.html(b)}else{d=c("<"+this.settings.errorElement+"/>").attr({"for":this.idOrName(a),generated:true}).addClass(this.settings.errorClass).html(b||"");if(this.settings.wrapper)d=d.hide().show().wrap("<"+this.settings.wrapper+"/>").parent();this.labelContainer.append(d).length||(this.settings.errorPlacement?this.settings.errorPlacement(d,c(a)):d.insertAfter(a))}if(!b&&this.settings.success){d.text("");typeof this.settings.success=="string"?d.addClass(this.settings.success):this.settings.success(d)}this.toShow=this.toShow.add(d)},errorsFor:function(a){var b=this.idOrName(a);return this.errors().filter(function(){return c(this).attr("for")==b})},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(a){var b=this.currentForm;return c(document.getElementsByName(a)).map(function(d,e){return e.form==b&&e.name==a&&e||null})},getLength:function(a,b){switch(b.nodeName.toLowerCase()){case"select":return c("option:selected",b).length;case"input":if(this.checkable(b))return this.findByName(b.name).filter(":checked").length}return a.length},depend:function(a,b){return this.dependTypes[typeof a]?this.dependTypes[typeof a](a,b):true},dependTypes:{"boolean":function(a){return a},string:function(a,b){return!!c(a,b.form).length},"function":function(a,b){return a(b)}},optional:function(a){return!c.validator.methods.required.call(this,c.trim(a.value),a)&&"dependency-mismatch"},startRequest:function(a){if(!this.pending[a.name]){this.pendingRequest++;this.pending[a.name]=true}},stopRequest:function(a,b){this.pendingRequest--;if(this.pendingRequest<0)this.pendingRequest=0;delete this.pending[a.name];if(b&&this.pendingRequest==0&&this.formSubmitted&&this.form()){c(this.currentForm).submit();this.formSubmitted=false}else if(!b&&this.pendingRequest==0&&this.formSubmitted){c(this.currentForm).triggerHandler("invalid-form",[this]);this.formSubmitted=false}},previousValue:function(a){return c.data(a,"previousValue")||c.data(a,"previousValue",{old:null,valid:true,message:this.defaultMessage(a,"remote")})}},classRuleSettings:{required:{required:true},email:{email:true},url:{url:true},date:{date:true},dateISO:{dateISO:true},dateDE:{dateDE:true},number:{number:true},numberDE:{numberDE:true},digits:{digits:true},creditcard:{creditcard:true}},addClassRules:function(a,b){a.constructor==String?this.classRuleSettings[a]=b:c.extend(this.classRuleSettings,a)},classRules:function(a){var b={};(a=c(a).attr("class"))&&c.each(a.split(" "),function(){this in c.validator.classRuleSettings&&c.extend(b,c.validator.classRuleSettings[this])});return b},attributeRules:function(a){var b={};a=c(a);for(var d in c.validator.methods){var e=a.attr(d);if(e)b[d]=e}b.maxlength&&/-1|2147483647|524288/.test(b.maxlength)&&delete b.maxlength;return b},metadataRules:function(a){if(!c.metadata)return{};var b=c.data(a.form,"validator").settings.meta;return b?c(a).metadata()[b]:c(a).metadata()},staticRules:function(a){var b={},d=c.data(a.form,"validator");if(d.settings.rules)b=c.validator.normalizeRule(d.settings.rules[a.name])||{};return b},normalizeRules:function(a,b){c.each(a,function(d,e){if(e===false)delete a[d];else if(e.param||e.depends){var f=true;switch(typeof e.depends){case"string":f=!!c(e.depends,b.form).length;break;case"function":f=e.depends.call(b,b)}if(f)a[d]=e.param!==undefined?e.param:true;else delete a[d]}});c.each(a,function(d,e){a[d]=c.isFunction(e)?e(b):e});c.each(["minlength","maxlength","min","max"],function(){if(a[this])a[this]=Number(a[this])});c.each(["rangelength","range"],function(){if(a[this])a[this]=[Number(a[this][0]),Number(a[this][1])]});if(c.validator.autoCreateRanges){if(a.min&&a.max){a.range=[a.min,a.max];delete a.min;delete a.max}if(a.minlength&&a.maxlength){a.rangelength=[a.minlength,a.maxlength];delete a.minlength;delete a.maxlength}}a.messages&&delete a.messages;return a},normalizeRule:function(a){if(typeof a=="string"){var b={};c.each(a.split(/\s/),function(){b[this]=true});a=b}return a},addMethod:function(a,b,d){c.validator.methods[a]=b;c.validator.messages[a]=d!=undefined?d:c.validator.messages[a];b.length<3&&c.validator.addClassRules(a,c.validator.normalizeRule(a))},methods:{required:function(a,b,d){if(!this.depend(d,b))return"dependency-mismatch";switch(b.nodeName.toLowerCase()){case"select":return(a=c(b).val())&&a.length>0;case"input":if(this.checkable(b))return this.getLength(a,b)>0;default:return c.trim(a).length>0}},remote:function(a,b,d){if(this.optional(b))return"dependency-mismatch";var e=this.previousValue(b);this.settings.messages[b.name]||(this.settings.messages[b.name]={});e.originalMessage=this.settings.messages[b.name].remote;this.settings.messages[b.name].remote=e.message;d=typeof d=="string"&&{url:d}||d;if(this.pending[b.name])return"pending";if(e.old===a)return e.valid;e.old=a;var f=this;this.startRequest(b);var g={};g[b.name]=a;c.ajax(c.extend(true,{url:d,mode:"abort",port:"validate"+b.name,dataType:"json",data:g,success:function(h){f.settings.messages[b.name].remote=e.originalMessage;var j=h===true;if(j){var i=f.formSubmitted;f.prepareElement(b);f.formSubmitted=i;f.successList.push(b);f.showErrors()}else{i={};h=h||f.defaultMessage(b,"remote");i[b.name]=e.message=c.isFunction(h)?h(a):h;f.showErrors(i)}e.valid=j;f.stopRequest(b,j)}},d));return"pending"},minlength:function(a,b,d){return this.optional(b)||this.getLength(c.trim(a),b)>=d},maxlength:function(a,b,d){return this.optional(b)||this.getLength(c.trim(a),b)<=d},rangelength:function(a,b,d){a=this.getLength(c.trim(a),b);return this.optional(b)||a>=d[0]&&a<=d[1]},min:function(a,b,d){return this.optional(b)||a>=d},max:function(a,b,d){return this.optional(b)||a<=d},range:function(a,b,d){return this.optional(b)||a>=d[0]&&a<=d[1]},email:function(a,b){return this.optional(b)||/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(a)},url:function(a,b){return this.optional(b)||/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(a)},date:function(a,b){return this.optional(b)||!/Invalid|NaN/.test(new Date(a))},dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(a)},number:function(a,b){return this.optional(b)||/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},creditcard:function(a,b){if(this.optional(b))return"dependency-mismatch";if(/[^0-9-]+/.test(a))return false;var d=0,e=0,f=false;a=a.replace(/\D/g,"");for(var g=a.length-1;g>=0;g--){e=a.charAt(g);e=parseInt(e,10);if(f)if((e*=2)>9)e-=9;d+=e;f=!f}return d%10==0},accept:function(a,b,d){d=typeof d=="string"?d.replace(/,/g,"|"):"png|jpe?g|gif";return this.optional(b)||a.match(RegExp(".("+d+")$","i"))},equalTo:function(a,b,d){d=c(d).unbind(".validate-equalTo").bind("blur.validate-equalTo",function(){c(b).valid()});return a==d.val()}}});c.format=c.validator.format})(jQuery);(function(c){var a={};if(c.ajaxPrefilter)c.ajaxPrefilter(function(d,e,f){e=d.port;if(d.mode=="abort"){a[e]&&a[e].abort();a[e]=f}});else{var b=c.ajax;c.ajax=function(d){var e=("port"in d?d:c.ajaxSettings).port;if(("mode"in d?d:c.ajaxSettings).mode=="abort"){a[e]&&a[e].abort();return a[e]=b.apply(this,arguments)}return b.apply(this,arguments)}}})(jQuery);(function(c){!jQuery.event.special.focusin&&!jQuery.event.special.focusout&&document.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.handle.call(this,e)}c.event.special[b]={setup:function(){this.addEventListener(a,d,true)},teardown:function(){this.removeEventListener(a,d,true)},handler:function(e){arguments[0]=c.event.fix(e);arguments[0].type=b;return c.event.handle.apply(this,arguments)}}});c.extend(c.fn,{validateDelegate:function(a,b,d){return this.bind(b,function(e){var f=c(e.target);if(f.is(a))return d.apply(f,arguments)})}})})(jQuery);;if(typeof Packt=='undefined'){Packt={};} +var live_search={init:function(){$(document).ready(function(){if($('#packt-libraries-main-search-form').length>0){$('#packt-libraries-main-search-form').submit(function(){return true;});} +if(document.cookie.indexOf("__upsell_journey")==-1){var n=Math.floor(Math.random()*2)+1;var c="A";if(n==1){c="A";}else if(n==2){c="B";} +document.cookie="__upsell_journey="+c+"; expires=Tue 19 Jan 2038 00:00:00 UTC; path=/; domain=.packtpub.com";}});},alterUrl:function(){$('#packt-libraries-main-search-form').attr('action',$('#packt-libraries-main-search-form #edit-prepend').val()+encodeURIComponent($('#packt-libraries-main-search-form input#edit-product-autocomplete').val()));}};live_search.init();jQuery(document).ready(function(){if(typeof Drupal.jsAC=='undefined'){return;} +Drupal.jsAC.prototype.hidePopup=function(keycode){var popup=this.popup;if(popup){this.popup=null;$(popup).fadeOut('fast',function(){$(popup).remove();});} +this.selected=false;};Drupal.jsAC.prototype.oldSelect=Drupal.jsAC.prototype.select;Drupal.jsAC.prototype.select=function(node){if(this.input.id!=="undefined"&&(this.input.id==='edit-keys'||this.input.id==='edit-key'||this.input.id==="edit-keys-1")){this.input.value=node.textContent;window.location.href=node.autocompleteValue;}else{this.input.value=node.autocompleteValue;}};if(jQuery('.feedback-form').length>0){jQuery('#edit-submitted-how-did-you-hear-about-packt-other-reason').hide();jQuery("#webform-component-how_did_you_hear_about_packt input[type=radio]").change(function(e){if(jQuery(this).attr('id')=='edit-submitted-how-did-you-hear-about-packt-Other'){jQuery('#edit-submitted-how-did-you-hear-about-packt-other-reason').show();}else{jQuery('#edit-submitted-how-did-you-hear-about-packt-other-reason').hide();}});var other_input=jQuery("#webform-component-how_did_you_hear_about_packt_other_reason").find('input[type=text]');jQuery('#webform-component-how_did_you_hear_about_packt #edit-submitted-how-did-you-hear-about-packt-Other-wrapper').append(other_input);jQuery('#edit-submitted-how-did-you-hear-about-packt-other-reason-wrapper').remove();jQuery('#edit-submitted-how-did-you-hear-about-packt-other-reason').click(function(e){if(jQuery(this).val()=='If other, please specify')jQuery(this).val('');});jQuery('#edit-submitted-how-did-you-hear-about-packt-other-reason').blur(function(e){if(jQuery(this).val()=='')jQuery(this).val('If other, please specify');});}});;jQuery.validator.addMethod("userinfo",function(value,element,params){if(this.optional(element)||$(element).attr('ovalue')!=value){return true;}else{return false;}},"This field requires a valid value.");jQuery.validator.addMethod("kindleemail",function(value,element,params){if(this.optional(element)||value.search(/@(free\.){0,1}kindle.com$/g)!=-1){return true;}else{return false;}},"This address must end in @kindle.com or @free.kindle.com.");jQuery.validator.addClassRules({"vr-userinfo":{userinfo:true},"vr-kindleemail":{kindleemail:true}});jQuery(document).ready(function(){$('form.packt-auto-validation').validate();});;var captcha_popup={init:function(){$(document).ready(function(){$('.captcha').parents('form').not('#packt-send-to-friend-send-form-popup, #code-download-form, .nocaptchapopup, #packt-upgrade-register-do-form').submit(function(){captcha_popup.popup($(this).find('.captcha'),$(this).attr('id'));return false;});});},popup:function(form,id){var h=$(form).find('#captcha-popupz');if(h.length>0){}else{$(form).append('');} +var pop=new Boxy($(form).html(),{modal:true,title:'Captcha',unloadOnHide:true,beforeUnload:function(){$(form).find('#captcha-popupz').remove();}});$('.boxy-inner').find('#captcha-popupz').click(function(){$('#'+id).find('#captcha-popupz').remove();var i=$(this).prev().find('input').val();$('#'+id).find('input[name=captcha_response]').val(i);$('#'+id).unbind('submit');$('#'+id).trigger('submit');});}};captcha_popup.init();;packtoracle={init:function(){$(document).ready(function(){$('.oracle-offer-wrapper input[type=radio]').click(function(){packtoracle.switchForms(this);});});},switchForms:function(ele){$(ele).parent().find('form').parent().removeClass('hidden').addClass('hidden');if($(ele).attr('class')=='bundle_enabler'){$(ele).parent().find(".add-to-cart-button.bundle").removeClass('hidden');}else{$(ele).parent().find(".add-to-cart-button.ebook").removeClass('hidden');}}};packtoracle.init();;if(typeof JSON!=='object'){JSON={};} +(function(){'use strict';function f(n){return n<10?'0'+n:n;} +if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+ +f(this.getUTCMonth()+1)+'-'+ +f(this.getUTCDate())+'T'+ +f(this.getUTCHours())+':'+ +f(this.getUTCMinutes())+':'+ +f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};} +var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';} +function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);} +if(typeof rep==='function'){value=rep.call(holder,key,value);} +switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';} +gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i0){$('#edit-node-format-1').val($('#edit-node-format').val());}} +Packt.save_open=function(nid){Packt.boxes.save=new Boxy('#staf_send',{modal:true});Packt.boxes.save.show();$('#staf_cancel').click(function(){Packt.boxes.save.hide();return false;});$('#packt-send-to-friend-save-form').ajaxForm({beforeSubmit:function(arr,form,options){var x=form.valid();if(!x)return false;},success:function(json){try{var result=eval("("+json+")");}catch(err){alert("Send to a friend failed because the CAPTCHA response was invalid.");window.location.reload();} +if(result.success){Packt.boxes.save.hide();}else{alert("Sorry, we couldn't save this article now. Try again later.");}}});if($('#edit-node-format').length>0){$('#edit-node-format-1').val($('#edit-node-format').val());}} +Packt.getID=function(element){var name=element.name;var ids=name.match(/\[[a-z0-9_-]+\]$/);if(ids.length>0){return ids[0].replace(/[\[\]]/g,'');}} +Packt.removeArticle=function(id){window.location.href="/account/articles/remove/"+id;} +Packt.sendArticle=function(id){var html=$.ajax({url:"/unthemed/staf/send/node/"+id,method:'GET',success:function(data){$('#staf_send').html(data);Packt.staf_open();}});} +Packt.saveArticle=function(id){$.ajax({url:"/unthemed/staf/save/node/"+id,data:{kindle:'1'},method:'GET',success:function(data){data="

    Send to Kindle

    Please confirm your Kindle address.

    \n"+data;$('#staf_send').html(data);$('#edit-kindle-address-wrapper').show();$('#edit-kindle-address').show();Packt.save_open();}});} +Packt.searchRelatedArticles=function(id){$.ajax({url:"/unthemed/staf/get_keywords/node/"+id,success:function(data){try{var result=eval("("+data+")");}catch(err){alert("Send to a friend failed because the CAPTCHA response was invalid.");window.location.reload();} +window.location.href="/search/luceneapi_node/"+escape(result.string);}});} +jQuery(document).ready(function(){if($('select.article-actions').length>0){$('select.article-actions').unbind('change');$('select.article-actions').change(function(){var action=$(this).val();var id=Packt.getID(this);switch(action){case'remove':return Packt.removeArticle(id);case'send':return Packt.sendArticle(id);case'kindle':return Packt.saveArticle(id,'kindle');case'related':return Packt.searchRelatedArticles(id);}});} +if($('div.accordion').length>0){$('select#edit-destination').change(function(){if($(this).val()=='kindle'){$('div#edit-kindle-address-wrapper, div#edit-kindle-address-wrapper *').show();$('div#edit-kindle-address-wrapper input').removeClass('optional');$('div#edit-kindle-address-wrapper input').addClass('vr-kindleemail');}else{$('div#edit-kindle-address-wrapper').hide();$('div#edit-kindle-address-wrapper input').addClass('optional');$('div#edit-kindle-address-wrapper input').removeClass('vr-kindleemail');} +$('#packt-send-to-friend-save-form').validate();});$('div.accordion > div.header').each(function(){this.onclick=function(){$('div.accordion > div.content.active').slideUp(300,function(){$(this).removeClass('active');});$(this).next().slideDown(300,function(){$(this).addClass('active');});}});$('div#staf_send input.form-text').focus(function(){var val=$(this).val();if(val=='First name'||val=='Last name'||val=='Email address') +$(this).val('');});Packt.staf.ajaxConfig={beforeSubmit:function(arr,form,options){var x=form.valid();if(!x)return false;},success:function(json){var result="";try{var result=eval("("+json+")");} +catch(err){alert("Send to a friend failed because the CAPTCHA response was invalid.");window.location.reload();} +if(result.success){$('#save_content div.inner > *').hide();if(result.format&&result.format=='kindle'){$('#saved_kindle').show();}else{$('#saved_account').show();}}else{$('div#staf_send').html('

    An error occurred saving this article.

    ');}}};if($('#packt-send-to-friend-save-form input#edit-login').length==0){$('#packt-send-to-friend-save-form').ajaxForm(Packt.staf.ajaxConfig);}}});;var Packt=Packt||{};var packtupsell={packt_ajax_add:function(nid,qty){if(!qty)qty=1;var r_url="/add_to_cart_upsell";$.ajax({url:r_url,type:"POST",dataType:"html",data:{nids:nid,qty:qty,cookie:packtupsell.readcookie('multiupsell')},success:function(data,textStatus){var cooki=packtupsell.readcookie('multiupsell');if(cooki==2){packtupsell.packt_ajax_callback(data,textStatus);}}});return false;},packt_ajax_callback:function(data,textStatus){$("#block-uc_cart-0").find('div.block-content-inner').html(data);$('#sidebar-right').css('position','relative');$('#block-uc_cart-0').after('
    This item has been added to your basket.
    ');$('div.upsell-two').find('#cont-shop').click(function(){$('div.upsell-two').fadeOut().remove();});},randomnumber:function(){var random=Math.floor(Math.random()*3)+1;return random;},setcookie:function(name,value,days){if(days){var date=new Date();date.setTime(date.getTime()+(days*24*60*60*1000));var expires="; expires="+date.toGMTString();} +else var expires="";document.cookie=name+"="+value+expires+"; path=/";},readcookie:function(name){var nameEQ=name+"=";var ca=document.cookie.split(';');for(var i=0;i0){jwplayer().onPlaylistItem(function(e){Packt.videoStreaming.sectionDetails(this,e);});} +$(window).resize(function(){Packt.videoStreaming.mobileDesktopSwitch();});$('head').append("");},mailToError:false,currentType:'desktop',isMobile:false,initLargeScreen:function(){$('div.collapse-menu').click(function(e){Packt.videoStreaming.collapseMenu(this,e);});$('div.collapse-menu div.chapter-close-button').click(function(e){Packt.videoStreaming.collapseMenu(this,e);});$('div#chapter-scroll-overlay div.chapter-menu-item div.chapter-menu-item-top').click(function(e){Packt.videoStreaming.removeSelected($(this).find('div.expand-chapter-btn'));$(this).parent().parent().addClass('selected');Packt.videoStreaming.sectionDisplay($(this).find('div.expand-chapter-btn'),e);});},initMobileScreen:function(type){Packt.videoStreaming.resizingMobile();if(type=='first'){jwplayer().onReady(function(){Packt.videoStreaming.sectionDisplayIOS($('div.sections-list-container.show').parent(),null);$('div#chapter-scroll-overlay div.chapter-menu-item ').click(function(e){$('div.mobile-section-menu').addClass('selected');Packt.videoStreaming.sectionDisplayIOS($(this).find('div.chapter-menu-item-inner'),e);});$('div.mobile-section-menu-container div.left-placeholder, div.mobile-section-menu-container div.right-placeholder').click(function(e){Packt.videoStreaming.scrollMobileMenu(this,$('div.mobile-section-menu-container'),'div.section-container',e);});$('div#chapter-scroll-overlay div.left-placeholder, div#chapter-scroll-overlay div.right-placeholder').click(function(e){Packt.videoStreaming.scrollMobileMenu(this,$('div#chapter-scroll-overlay'),'div.chapter-menu-item',e);});Packt.videoStreaming.resizingMobile();$(window).resize(function(){Packt.videoStreaming.resizingMobile();});Packt.videoStreaming.phoneChapterResize();});}else if(type=='after'){if($('div#chapter-scroll-overlay div.chapter-menu-item.selected').length<1){var idx=0;$('div#chapter-scroll-overlay div.chapter-menu-item.selected').each(function(){if(idx!=0){$(this).removeClass('selected');}});} +Packt.videoStreaming.sectionDisplayIOS($('div.chapter-menu-item.current div.chapter-menu-item-inner'),null);$('div#chapter-scroll-overlay div.chapter-menu-item').click(function(e){$('div.mobile-section-menu').addClass('selected');Packt.videoStreaming.sectionDisplayIOS($(this).find('div.chapter-menu-item-inner'),e);});$('div.mobile-section-menu-container div.left-placeholder, div.mobile-section-menu-container div.right-placeholder').click(function(e){Packt.videoStreaming.scrollMobileMenu(this,$('div.mobile-section-menu-container'),'div.section-container',e);});$('div#chapter-scroll-overlay div.left-placeholder, div#chapter-scroll-overlay div.right-placeholder').click(function(e){Packt.videoStreaming.scrollMobileMenu(this,$('div#chapter-scroll-overlay'),'div.chapter-menu-item',e);});$(window).resize(function(){Packt.videoStreaming.resizingMobile();});Packt.videoStreaming.phoneChapterResize();}},mobileCheck:function(){var windowWidth=Packt.videoStreaming.getWindowWidth();return windowWidth<981;},scrollMobileMenu:function(btn,container,childSelector,event){event.stopPropagation();var x=0;var scrolled=$(btn).parent().scrollLeft();if($(btn).attr('class').indexOf('left')!==-1){if(scrolled!=0){var items=$(container).find(childSelector);for(var i=0;iscrolled){x-=$(items[i]).outerWidth(true);}else if(x==scrolled){x-=$(items[i]).outerWidth(true);}}}}else{$(container).find(childSelector).each(function(){if(x980){if(Packt.videoStreaming.currentType=='mobile'){Packt.videoStreaming.isMobile=false;Packt.videoStreaming.unbindLargeEvents();Packt.videoStreaming.unbindMobileEvents();Packt.videoStreaming.initLargeScreen();Packt.videoStreaming.currentType='desktop';}}else{if(Packt.videoStreaming.currentType=='desktop'){Packt.videoStreaming.isMobile=true;$('div#chapter-scroll-overlay div.chapter-menu-item').unbind('click');$('div.mobile-section-menu-container div.left-placeholder, div.mobile-section-menu-container div.right-placeholder').unbind('click') +$('div#chapter-scroll-overlay div.left-placeholder, div#chapter-scroll-overlay div.right-placeholder').unbind('click');Packt.videoStreaming.unbindLargeEvents();Packt.videoStreaming.initMobileScreen('after');Packt.videoStreaming.currentType='mobile';}}},unbindLargeEvents:function(){Packt.videoStreaming.hideOpenSections();$('div.collapse-menu').unbind('click');$('div.collapse-menu div.chapter-close-button').unbind('click');$('div.expand-chapter-btn').unbind('click');$('div#chapter-scroll-overlay div.chapter-menu-item').unbind('click');$('div.chapter-menu-item-top').unbind('click');},unbindMobileEvents:function(){Packt.videoStreaming.redisplayChaptersSections();$('div#chapter-scroll-overlay div.chapter-menu-item').unbind('click');$('div.mobile-section-menu-container div.left-placeholder, div.mobile-section-menu-container div.right-placeholder').unbind('click') +$('div#chapter-scroll-overlay div.left-placeholder, div#chapter-scroll-overlay div.right-placeholder').unbind('click');$('div#chapter-scroll-overlay div.section-list-container.show').removeAttr('style');$('div.video-player-container').removeAttr('style');$('div.mobile-section-menu-container').removeAttr('style');$('div#chapter-scroll-overlay').removeAttr('style');$('div.chapter-menu-left').css('height','auto');$('div#page').removeAttr('style');$('div#main').removeAttr('style');},getMobileSectionInfo:function(btn){var width=0;var secId=0;var windowWidth=Packt.videoStreaming.getWindowWidth();var cPlaylistItem=jwplayer().getPlaylistItem();var ctpId=$(btn).parent().attr('id').replace('chapter-','');var maxHeight=0;var x=0;$('div.mobile-section-menu div.section-container').each(function(){if(maxHeight<$(this).outerHeight()&&$(this).outerHeight()<80){maxHeight=$(this).outerHeight();} +if(secId==cPlaylistItem.secId&&cPlaylistItem.ctpId==ctpId){$(this).addClass('current');scroll=x;}else{x+=$(this).outerWidth(true);$(this).removeClass('current');} +width+=$(this).outerWidth(true);secId++;});if(width980){var currentSection=jwplayer().getPlaylistItem();$('div.chapter-menu-item div.sections-list-container.show').each(function(){var secIdx=0;var height=0;$(this).find('div.section-container').each(function(){if(currentSection.secId==secIdx){$(this).addClass('current');}else{$(this).removeClass('current');} +height+=$(this).outerHeight();secIdx++;});$(this).css('height',height);});}},getWindowWidth:function(){var width=0 +if(window.innerWidth!=$('body').outerWidth(true)&&$.browser.safari==false){var scrollBarWidth=window.innerWidth-$('body').outerWidth();width=$(window).width()+scrollBarWidth;}else if(window.innerWidth!=$('body').outerWidth(true)&&$.browser.safari==true){var scrollBarWidth=window.innerWidth-$('body').outerWidth();width=$(window).width()-scrollBarWidth;}else{width=$(window).width();} +return width;},feedbackMailTo:function(link,e){jwplayer('player_2').pause(true);}} +$(document).ready(function(){if($('html').hasClass('streaming-video-page')){Packt.videoStreaming.init();}});;var copy_box_checked=false;var uc_ce_submit_disable=false;Drupal.behaviors.ucShowOnLoad=function(context){$('.show-onload:not(.ucShowOnLoad-processed)',context).addClass('ucShowOnLoad-processed').show();} +Drupal.behaviors.ucSubmitOrderThrobber=function(context){$('form#uc-cart-checkout-review-form input#edit-submit:not(.ucSubmitOrderThrobber-processed)',context).addClass('ucSubmitOrderThrobber-processed').click(function(){$(this).clone().insertAfter(this).attr('disabled',true).after('    ').end().hide();$('#uc-cart-checkout-review-form #edit-back').attr('disabled',true);});} +function uc_cart_next_button_click(button,pane_id,current){if(current!=='false'){$('#'+current+'-pane legend a').click();} +else{button.disabled=true;} +if($('#'+pane_id+'-pane').attr('class').indexOf('collapsed')>-1&&$('#'+pane_id+'-pane').html()!==null){$('#'+pane_id+'-pane legend a').click();} +return false;} +function uc_cart_copy_address(checked,source,target){if(!checked){$('#'+target+'-pane').slideDown();copy_box_checked=false;return false;} +if(target=='billing'){var x=28;} +else{var x=26;} +$('#'+target+'-pane').slideUp();copy_box_checked=true;if($('#edit-panes-'+target+'-'+target+'-zone').html()!=$('#edit-panes-'+source+'-'+source+'-zone').html()){$('#edit-panes-'+target+'-'+target+'-zone').empty().append($('#edit-panes-'+source+'-'+source+'-zone').children().clone());$('#edit-panes-'+target+'-'+target+'-zone').attr('disabled',$('#edit-panes-'+source+'-'+source+'-zone').attr('disabled'));} +$('#'+source+'-pane input, select, textarea').each(function(){if(this.id.substring(0,x)=='edit-panes-'+source+'-'+source){$('#edit-panes-'+target+'-'+target+this.id.substring(x)).val($(this).val());if(target=='billing'){$(this).change(function(){update_billing_field(this);});} +else{$(this).change(function(){update_delivery_field(this);});}}});return false;} +function update_billing_field(field){if(copy_box_checked){if(field.id.substring(29)=='zone'){$('#edit-panes-billing-billing-zone').empty().append($('#edit-panes-delivery-delivery-zone').children().clone());$('#edit-panes-billing-billing-zone').attr('disabled',$('#edit-panes-delivery-delivery-zone').attr('disabled'));} +$('#edit-panes-billing-billing'+field.id.substring(28)).val($(field).val()).change();}} +function update_delivery_field(field){if(copy_box_checked){if(field.id.substring(27)=='zone'){$('#edit-panes-delivery-delivery-zone').empty().append($('#edit-panes-billing-billing-zone').children().clone());$('#edit-panes-delivery-delivery-zone').attr('disabled',$('#edit-panes-billing-billing-zone').attr('disabled'));} +$('#edit-panes-delivery-delivery'+field.id.substring(26)).val($(field).val()).change();}} +function apply_address(type,address_str){var temp=type+'-'+type;if(address_str=='0'){$('#'+type+'-pane input').val('');$('#edit-panes-'+temp+'-zone').val('');return;} +eval('var address = '+address_str+';');$('#edit-panes-'+temp+'-first-name').val(address.first_name).trigger('change');$('#edit-panes-'+temp+'-last-name').val(address.last_name).trigger('change');$('#edit-panes-'+temp+'-phone').val(address.phone).trigger('change');$('#edit-panes-'+temp+'-company').val(address.company).trigger('change');$('#edit-panes-'+temp+'-street1').val(address.street1).trigger('change');$('#edit-panes-'+temp+'-street2').val(address.street2).trigger('change');$('#edit-panes-'+temp+'-city').val(address.city).trigger('change');$('#edit-panes-'+temp+'-postal-code').val(address.postal_code).trigger('change');if($('#edit-panes-'+temp+'-country').val()!=address.country){$('#edit-panes-'+temp+'-country').val(address.country).trigger('change');try{uc_update_zone_select('edit-panes-'+temp+'-country',address.zone);} +catch(err){}} +$('#edit-panes-'+temp+'-zone').val(address.zone).trigger('change');} +Drupal.behaviors.ucDisableNav=function(context){$('form#uc-cart-checkout-review-form input#edit-submit:not(.ucDisableNav-processed)',context).addClass('ucDisableNav-processed').click(function(){if(uc_ce_submit_disable){$(this).clone().insertAfter(this).attr('disabled',true).after('    ').end().hide();$('#uc-cart-checkout-review-form #edit-back').attr('disabled',true);}});} +var editaddress={init:function(){$(document).ready(function(){$('#delivery-button, #billing-button').click(function(){editaddress.slidePane(this);});});},slidePane:function(i){var table=$(i).parents('table:first');var trs=$(table).find('tr.address-tr-hide, tr.address-tr-show');if($(trs).attr('class')=='address-tr-hide'){$(trs).attr('class','address-tr-show');}else{$(trs).attr('class','address-tr-hide');} +$(trs).css('display','');if($(i).attr('value')=='Edit'){$(i).hide();}else{$(i).attr('value','Edit');}}};editaddress.init();;var uc_file_list={};function _uc_file_expiration_disable_check(granularity,quantity){if($(granularity).val()=='never'){$(quantity).attr('disabled','disabled').val('');} +else{$(quantity).removeAttr('disabled');}} +function _uc_file_delete_list_populate(){$('.affected-file-name').empty().append(uc_file_list[$('#edit-recurse-directories').attr('checked')]);} +$(document).ready(function(){_uc_file_expiration_disable_check('#edit-uc-file-download-limit-duration-granularity','#edit-uc-file-download-limit-duration-qty');_uc_file_expiration_disable_check('#edit-download-limit-duration-granularity','#edit-download-limit-duration-qty');_uc_file_expiration_disable_check('#edit-download-limit-duration-granularity','#edit-download-limit-duration-qty');_uc_file_delete_list_populate();toggle_limit_settings('#edit-download-override','#edit-download-limit-number-wrapper');toggle_limit_settings('#edit-location-override','#edit-download-limit-addresses-wrapper');toggle_limit_settings('#edit-time-override','#edit-download-limit-duration-qty-wrapper');toggle_limit_settings('#edit-time-override','#edit-download-limit-duration-granularity-wrapper');});Drupal.behaviors.ucGlobalFileDownloadGranularity=function(context){$('#edit-uc-file-download-limit-duration-granularity:not(.ucGlobalFileDownloadGranularity-processed)',context).addClass('ucGlobalFileDownloadGranularity-processed').change(function(){_uc_file_expiration_disable_check('#edit-uc-file-download-limit-duration-granularity','#edit-uc-file-download-limit-duration-qty');});} +Drupal.behaviors.ucFileDownloadGranularity=function(context){$('#edit-download-limit-duration-granularity:not(.ucFileDownloadGranularity-processed)',context).addClass('ucFileDownloadGranularity-processed').change(function(){_uc_file_expiration_disable_check('#edit-download-limit-duration-granularity','#edit-download-limit-duration-qty');});} +Drupal.behaviors.ucFileSelectAll=function(context){$('#uc_file_select_all:not(.ucFileSelectAll-processed)',context).addClass('ucFileSelectAll-processed').click(function(){$('.form-checkbox').attr('checked',true);});} +Drupal.behaviors.ucFileSelectNone=function(context){$('#uc_file_select_none:not(.ucFileSelectNone-processed)',context).addClass('ucFileSelectNone-processed').click(function(){$('.form-checkbox').removeAttr('checked');});} +Drupal.behaviors.ucFileDeleteList=function(context){$('#edit-recurse-directories:not(.ucFileDeleteList-processed)',context).addClass('ucFileDeleteList-processed').change(function(){_uc_file_delete_list_populate()});} +function uc_file_update_download(id,accessed,limit){if(accessed0){var ebookid=eid;}else{var ebookid=0;} +var senddata=Array();senddata[nid]=bookqty;$.ajax({url:r_url,type:"POST",dataType:"html",data:{nid:nid,qty:bookqty,eid:ebookid,eqty:ebookqty},success:function(){if(stay==false)document.location.href="/cart/checkout";}});return false;} +function packt_ajax_add_bundle(items){var r_url="/cart/ajax_add_bundle";var item=new Object();for(var i=0;i0){$('div.tab').click(function(){$('div.tab').removeClass('active');$('#'+this.id).addClass('active');$('div#tab_ebook_content').hide();$('div#tab_book_content').hide();$('div#tab_book_and_ebook_content').hide();$('div#tab_packtlib_content').hide();$('div#tab_annual_content').hide();$('div#tab_monthly_content').hide();$('div#'+this.id+'_content').show();if($('div#tab_annual_content').length==0){var id=document.getElementById("arg1_hidden").innerHTML;var btn=document.getElementById('edit-submit-'+id);bta=(this.id=='tab_book'||this.id=='tab_book_and_ebook'?1:0);eta=(this.id=='tab_ebook'||this.id=='tab_book_and_ebook'?1:0);$('form[id^=uc-product-add-to-cart] input[id^=edit-submit]').each(function(){this.onclick=function(){var id=document.getElementById("arg1_hidden").innerHTML;packt_ajax_add_multi_generic(id,bta,eta);return false;}});} +$('div#dont_miss_ebook').hide();$('div#dont_miss_multi').hide();$('div#dont_miss_packtlib').hide();if(this.id=='tab_ebook'){$('#dont_miss_multi').show();} +else if(this.id=='tab_book'){$('#dont_miss_ebook').show();} +else if(this.id=='tab_book_and_ebook'){$('#dont_miss_packtlib').show();} +else if(this.id=='tab_packtlib'){$('#dont_miss_multi').show();} +if(this.id=='tab_packtlib'){$('#purchase-options').hide();}else{$('#purchase-options').show();}});$('div.tab.active').click();} +$('[href=$".pdf"]').click(function(){var pageTracker=_gat._getTracker("UA-284627-1");pageTracker._trackEvent('PDF Download',this.href);return true;});if($('.node-type-book-offer').length>0){$('.node-type-book-offer div.upsell-see-more').click(function(){$('div.upsell-more.padding').fadeOut(function(){$('div.extended-nodes').slideDown();});});$('.node-type-book-offer div.banners').click(function(){$('div.banners input[type=submit]').click();});}});function toggleValidFrom(obj){if(jQuery(obj).val()=="SOLO_GB-SSL") +jQuery("#edit-valid-from-wrapper, #edit-valid-from-year-wrapper").show();else +jQuery("#edit-valid-from-wrapper, #edit-valid-from-year-wrapper").hide();} +function do_offer_countdown(){countdown_expires=true_expires-parseInt((+new Date)/1000);if(countdown_expires>=0){var h=Math.floor(countdown_expires/60/60);var m=Math.floor(countdown_expires/60)%60;if(m<10)m='0'+m.toString();var s=countdown_expires%60;if(s<10)s='0'+s.toString();jQuery('#count-time').html(h+":"+m+":"+s);setTimeout(function(){do_offer_countdown();},1000);}} +if(typeof(PacktLib)=='undefined')PacktLib=new Object();PacktLib.otherReasonShowHide=function(select){if($('#'+select.id)[0].value=='other'){$('#other_reason').show();}else{$('#other_reason').hide();}} +function saveForLater(){new Boxy("

    Thank you, your items will be saved in your cart for the next 72 hours.

    ",{title:'Items saved',closeable:true,modal:true});return false;} +function evLog(data,async){var params=[];if(async==null)async=true;if(data==null){params={event_type:'Page view'};}else if(typeof(data)=='object'){params=data;}else{var p=1;for(i in data){params['d'+i]=data[i];p++;}} +params.url=location.pathname;$.ajax({url:'/server_ajax/log.php',type:'POST',async:async,data:{ev:$.toJSON(params)}});} +(function($){$.toJSON=function(o) +{if(typeof(JSON)=='object'&&JSON.stringify) +return JSON.stringify(o);var type=typeof(o);if(o===null) +return"null";if(type=="undefined") +return undefined;if(type=="number"||type=="boolean") +return o+"";if(type=="string") +return $.quoteString(o);if(type=='object') +{if(typeof o.toJSON=="function") +return $.toJSON(o.toJSON());if(o.constructor===Date) +{var month=o.getUTCMonth()+1;if(month<10)month='0'+month;var day=o.getUTCDate();if(day<10)day='0'+day;var year=o.getUTCFullYear();var hours=o.getUTCHours();if(hours<10)hours='0'+hours;var minutes=o.getUTCMinutes();if(minutes<10)minutes='0'+minutes;var seconds=o.getUTCSeconds();if(seconds<10)seconds='0'+seconds;var milli=o.getUTCMilliseconds();if(milli<100)milli='0'+milli;if(milli<10)milli='0'+milli;return'"'+year+'-'+month+'-'+day+'T'+ +hours+':'+minutes+':'+seconds+'.'+milli+'Z"';} +if(o.constructor===Array) +{var ret=[];for(var i=0;i-1){}else{if(window.addthis_cdn!==i){_atc.cdn=window.addthis_cdn;}else{if(E[n]){_atc.cdn=6;}else{if(g[n]){_atc.cdn=(v||p)?0:1;}else{if(o[n]){_atc.cdn=(p)?0:1;}}}}}if(_atc.cdn){for(var z in _atc.rsrcs){if(_atc.rsrcs.hasOwnProperty(z)){_atc.rsrcs[z]=_atc.rsrcs[z].replace(_atr,typeof(window.addthis_cdn)==="string"?window.addthis_cdn:c[_atc.cdn]).replace(/live\/([a-z])07/,"live/$107");}}_atr=c[_atc.cdn];}}catch(B){}function b(k,e,d,a){return function(){if(!this.qs){this.qs=0;}_atc.qs++;if(!((this.qs++>0&&a)||_atc.qs>1000)&&window.addthis){window.addthis.plo.push({call:k,args:arguments,ns:e,ctx:d});}};}function x(e){var d=this,a=this.queue=[];this.name=e;this.call=function(){a.push(arguments);};this.call.queuer=this;this.flush=function(w,r){this.flushed=1;for(var k=0;kda.call(b,e)&&c[m](e)}return c},sa=function(a){"loading"!=D.readyState?Y(a):D.write("<"+W+' src="'+encodeURI(a)+'">")},Y=function(a){var b=D.createElement(W);b.setAttribute("src",a);b.async="true";(a=D.getElementsByTagName(W)[0])?a.parentNode.insertBefore(b,a):(D.head||D.body||D.documentElement).appendChild(b)},ta=function(a,b){var c=b&&b._c;if(c)for(var d=0;d>>0);ia=function(a,c,f){return a.call.apply(a.bind,arguments)}; +la=function(a,c,f){if(!a)throw Error();if(2g)Yb["rate."+f+"-"+g]=!0,Hb=c.timeout||1E3,sb=c.host||"https://plus.google.com",Fb=c.path||"/_/widget/report",Nb=c.apis||[]}if(!qb)return!1;if(Nb)for(c=0;c=c.length?c:c.substr(0,512)+"... ("+c.length+" bytes)"));var f=0!==c.indexOf("!_");f||(c=c.substring(2));var g=kz(c);if(!f&&!g){if(!g&&(f=lz(c))){if(this.ha[f])this.ha[f]();else this.M[f]=1;return}var h=a.origin,l=this.G.zp;this.wc?_.ta.setTimeout(function(){l(c,h)},0):l(c,h)}};jz.prototype.sf=function(a,c){".."===a||this.M[a]?(c(),delete this.M[a]):this.ha[a]=c}; +var CC=function(a,c,f,g){var h=kz(f)?"":"!_";(0,_.ke)("gapi.rpc.send("+Qz+"): "+(!f||512>=f.length?f:f.substr(0,512)+"... ("+f.length+" bytes)"));a.qa(c,h+f,g)};jz.prototype.Ka=function(a,c,f){a.postMessage(c,f)};jz.prototype.send=function(a,c,f){(a=this.G.kq(this.B,a))&&!a.closed&&CC(this,a,c,f)}; +var Lz;var Kz;var Jz;var Iz;var vz;var tz;var Fz;var Dz;var Bz;var zz;var xz;var yz;var rz;var qz;var oz;var nz;var Gz;var mz;var Ez;var Cz;var Qz;var Dl;var Oz;var Pz;var Sz;var Nz;var uz;var pz;var Hz;var Rz;Rz=0;Hz=[];pz={};uz={};Nz=_.R.dc;Sz=Nz();Pz=Sz.rpctoken;Oz=Sz.parent||_.ua.referrer;Dl=Sz.rly;Qz=Dl||(_.ta!==_.ta.top||_.ta.opener)&&_.ta.name||"..";Cz=null;Ez={};mz=function(){};Gz={send:mz,sf:mz}; +nz=function(a,c){"/"==c.charAt(0)&&(c=c.substring(1),a=_.ta.top);for(var f=c.split("/");f.length;){var g;g=f.shift();"{"==g.charAt(0)&&"}"==g.charAt(g.length-1)&&(g=g.substring(1,g.length-1));if(".."===g)a=a==a.parent?a.opener:a.parent;else if(".."!==g&&a.frames[g]){if(a=a.frames[g],!("postMessage"in a))throw"Not a window";}else return null}return a};oz=function(a){return(a=pz[a])&&a.ve}; +qz=function(a){if(a.f in{})return!1;var c=a.t,f=pz[a.r];a=a.origin;return f&&(f.ve===c||!f.ve&&!c)&&(a===f.origin||"*"===f.origin)};rz=function(a){var c=a.id.split("/"),f=c[c.length-1],g=a.origin;return function(a){var c=a.origin;return a.f==f&&(g==c||"*"==g)}};_.sz=function(a,c,f){a=tz(a);uz[a.name]={Ip:c,lf:a.lf,Us:f||qz};vz()};_.wz=function(a){delete uz[tz(a).name]};yz={};xz=function(a,c){var f=yz["_"+a];f&&f[1](this)&&f[0].call(this,c)}; +zz=function(a){var c=a.c;if(!c)return mz;var f=a.r,g=a.g?"legacy__":"";return function(){var a=[].slice.call(arguments,0);a.unshift(f,g+"__cb",null,c);_.Az.apply(null,a)}};Bz=function(a){Cz=a};Dz=function(a){Ez[a]||(Ez[a]=_.ta.setTimeout(function(){Ez[a]=!1;Fz(a)},0))};Fz=function(a){var c=pz[a];if(c&&c.ready){var f=c.kj;for(c.kj=[];f.length;)Gz.send(a,(0,_.mf)(f.shift()),c.origin)}};tz=function(a){return 0===a.indexOf("legacy__")?{name:a.substring(8),lf:!0}:{name:a,lf:!1}}; +vz=function(){for(var a=(0,_.P)("rpc/residenceSec")||60,c=(new Date).getTime()/1E3,f=0,g;g=Hz[f];++f){var h=g.qf;if(!h||0a)Hz.splice(f,1),--f;else{var l=h.s,n=uz[l]||uz["*"];if(n)if(Hz.splice(f,1),--f,h.origin=g.origin,g=zz(h),h.callback=g,n.Us(h)){if("__cb"!==l&&!!n.lf!=!!h.g)break;h=n.Ip.apply(h,h.a);void 0!==h&&g(h)}else(0,_.ke)("gapi.rpc.rejected("+Qz+"): "+l)}}};Iz=function(a,c,f){Hz.push({qf:a,origin:c,timestamp:(new Date).getTime()/1E3});f||vz()}; +Jz=function(a,c){var f=(0,_.of)(a);Iz(f,c,!1)};Kz=function(a){for(;a.length;)Iz(a.shift(),this.origin,!0);vz()};Lz=function(a){var c=!1;a=a.split("|");var f=a[0];0<=f.indexOf("/")&&(c=!0);return{id:f,origin:a[1]||"*",Ki:c}}; +_.Mz=function(a,c,f,g){var h=Lz(a);g&&(_.ta.frames[h.id]=_.ta.frames[h.id]||g);a=h.id;if(!pz.hasOwnProperty(a)){f=f||null;g=h.origin;if(".."===a)g=_.Rb.Pq(Oz),f=f||Pz;else if(!h.Ki){var l=_.ua.getElementById(a);l&&(l=l.src,g=_.Rb.Pq(l),f=f||Nz(l).rpctoken)}"*"===h.origin&&g||(g=h.origin);pz[a]={ve:f,kj:[],origin:g,hs:c,xm:function(){var c=a;pz[c].ready=1;Fz(c)}};Gz.sf(a,pz[a].xm)}return pz[a].xm}; +_.Az=function(a,c,f,g){a=a||"..";(0,_.Mz)(a);a=a.split("|",1)[0];var h=c,l=[].slice.call(arguments,3),n=f,q=Qz,t=Pz,v=pz[a],w=q,A=Lz(a);if(v&&".."!==a){if(A.Ki){if(!(t=pz[a].hs)){t=null;Cz?t=Cz.substring(1).split("/"):t=[Qz];for(var w=t.length-1,F=_.ta.parent;F!==_.ta.top;){var z=F.parent;if(!w--){for(var I=null,E=z.frames.length,K=0;Kc?f[0]=[f[0],"?",t].join(""):(g=[f[0]],ch;h++)g[h]=c.charCodeAt(f)<<24|c.charCodeAt(f+1)<<16|c.charCodeAt(f+2)<<8|c.charCodeAt(f+3),f+=4;else for(h=0;16>h;h++)g[h]=c[f]<<24|c[f+1]<<16|c[f+2]<<8|c[f+3],f+=4;for(h=16;80>h;h++){var l=g[h-3]^g[h-8]^g[h-14]^g[h-16];g[h]=(l<<1|l>>>31)&4294967295}c=a.B[0];f=a.B[1];for(var n=a.B[2],q=a.B[3],t=a.B[4],v,h=0;80>h;h++)40>h?20>h?(l=q^f&(n^q),v=1518500249):(l=f^n^q,v=1859775393):60>h?(l=f&n|q&(f|n),v=2400959708):(l=f^n^q,v=3395469782), +l=(c<<5|c>>>27)+l+t+v+g[h]&4294967295,t=q,q=n,n=(f<<30|f>>>2)&4294967295,f=c,c=l;a.B[0]=a.B[0]+c&4294967295;a.B[1]=a.B[1]+f&4294967295;a.B[2]=a.B[2]+n&4294967295;a.B[3]=a.B[3]+q&4294967295;a.B[4]=a.B[4]+t&4294967295}; +xb.prototype.update=function(a,c){(0,_.kf)(c)||(c=a.length);for(var f=c-this.G,g=0,h=this.Ka,l=this.ha;gthis.ha?this.update(this.M,56-this.ha):this.update(this.M,this.G-(this.ha-56));for(var f=this.G-1;56<=f;f--)this.Ka[f]=c&255,c/=256;yb(this,this.Ka);for(f=c=0;5>f;f++)for(var g=24;0<=g;g-=8)a[c]=this.B[f]>>g&255,++c;return a}; +_.pi=function(){this.B=new xb};_.k=_.pi.prototype;_.k.reset=function(){this.B.reset()};_.k.AF=function(a){this.B.update(a)};_.k.GC=function(){return this.B.wc()};_.k.bB=function(a){a=(0,window.unescape)((0,window.encodeURIComponent)(a));for(var c=[],f=0,g=a.length;f/g;wn=/++g);(0,_.Sa)(5>g,"Error creating iframe id");return f};_.Ln=function(a,c){return a?c+"/"+a:""}; +_.qa=function(a,c,f,g){var h={},l={};a.documentMode&&9>a.documentMode&&(h.hostiemode=a.documentMode);(0,_.Sl)(g.queryParams||{},h);(0,_.Sl)(g.fragmentParams||{},l);var n=g.connectWithQueryParams?h:l,q=g.pfname,t=(0,_.r)();t.id=f;t.parent=a.location.protocol+"//"+a.location.host;f=(0,_.Wa)(a.location.href,"parent");q=q||"";!q&&f&&(q=(0,_.Ln)((0,_.Wa)(a.location.href,"id",""),(0,_.Wa)(a.location.href,"pfname","")));t.pfname=q;(0,_.Sl)(t,n);(t=(0,_.Wa)(c,"rpctoken")||h.rpctoken||l.rpctoken)||(t=n.rpctoken= +g.rpctoken||String(Math.round(1E8*(0,_.Rl)())));g.rpctoken=t;n=a.location.href;a=(0,_.r)();(t=(0,_.Wa)(n,"_bsh",_.bb.bsh))&&(a._bsh=t);(n=_.bb.dpo?_.bb.h:(0,_.Wa)(n,"jsh",_.bb.h))&&(a.jsh=n);g.hintInFragment?(0,_.Sl)(a,l):(0,_.Sl)(a,h);return(0,_.Bn)(c,h,l,g.paramsSerializer)};hc=function(a){(0,_.Sa)(!a||_.Ul.test(a),"Illegal url for new iframe - "+a)}; +_.Pn=function(a,c,f,g,h){hc(f.src);var l,n=nb(g,f),q=n?ob(g):"";try{l=a.createElement('";J=N.firstChild;}J.style.overflow="hidden";J.style.scrolling="no";J.style.scrollbars="no";J.style.border="none";J.style.borderWidth="0px";J.style.width=O+"px";J.style.height=I+"px";J.src="//www.facebook.com/plugins/subscribe.php?href="+_euc(_a.track.mgu(L.share.url,{defrag:1}))+"&layout=button_count&show_faces=false&width=100&action=subscribe&font=arial&"+passthrough;if(!_a.bro.msi){N.appendChild(J);}}N.noh=N.ost=1;}function F(N,L){if(N.ost){return;}var O,I,d,M=_a.api.ptpa(N,"fb:like"),K=M.layout||"button_count",H={standard:[450,M.show_faces?80:35],button_count:[90,25],box_count:[55,65]},P=M.width||(H[K]?H[K][0]:100),J=M.height||(H[K]?H[K][1]:25);passthrough=_a.util.toKV(M);_a.ufbl=1;if(s()){if(M.layout===_1){M.layout="button_count";}if(M.show_faces===_1){M.show_faces="false";}if(M.action===_1){M.action="like";}if(M.width===_1){M.width=P;}if(M.font===_1){M.font="arial";}if(M.href===_1){d=_a.util.clone(L.share.url_transforms||{});d.defrag=1;M.href=_a.track.mgu(L.share.url,d);}M.send=false;if(!L.share.xid){L.share.xid=_a.util.cuid();}h[M.href]={};for(I in L.share){h[M.href][I]=L.share[I];}c("like",N,L,M);}else{if(!_a.bro.msi){O=E.ce("iframe");}else{N.innerHTML="";O=N.firstChild;}O.style.overflow="hidden";O.style.scrolling="no";O.style.scrollbars="no";O.style.border="none";O.style.borderWidth="0px";O.style.width=P+"px";O.style.height=J+"px";O.src="//www.facebook.com/plugins/like.php?href="+_euc(_a.track.mgu(L.share.url,{defrag:1}))+"&layout=button_count&show_faces=false&width=100&action=like&font=arial&"+passthrough;if(!_a.bro.msi){N.appendChild(O);}}N.noh=N.ost=1;}function b(L,J,N,H){var M=(L.passthrough||{}).facebook||{},K={},d,I=G?(o+"?u="+_euc(_a.share.acb("facebook",L,J))+"&p[title]="+_euc(L.title)+"&display=popup"):(z?("http://www.facebook.com/connect/prompt_feed.php?message="+_euc(L.title)+"%0A%0D"+_euc(_a.share.acb("facebook",L,J))):j?"http://www.facebook.com/dialog/feed?redirect_uri="+_euc("http://s7.addthis.com/static/postshare/c00.html")+"&app_id=140586622674265&link="+_euc(_a.share.acb("facebook",L,J))+"&name="+_euc(L.title)+"&description="+_euc(L.description||"")+"&display=popup":_a.share.genurl("facebook",0,L,J));if(G||z||j){for(d in J){K[d]=J[d];}K.hdl=1;_a.share.track("facebook",0,L,K,1);}if(J.ui_use_same_window||H){l.href=I;}else{_a.share.ocw(I,640,375,"facebook");}return false;}f.share=f.share||{};f.share.register({facebook_like:F,facebook_send:C,facebook_share:a,facebook_subscribe:r});f.share.registerSubscriber(g);f.share.registerListeners({facebook:{_after:function(d){d.ins=1;d.noh=1;},onclick:function(H){var I=H.el,d=H.service;if(I.ins!=0&&window.addthis.auth&&window.addthis.auth.fbishare){window.addthis.auth.lockiframe[d]=true;window.addthis.auth.loadIframe(I,d,I.share,I.conf);}else{return _a.share.fb.share(I.share,I.conf);}},onmouseover:function(H){var I=H.el,d=H.service;if(I.ins!=0&&window.addthis.auth&&window.addthis.auth.fbishare){window.addthis.auth.keepiframe[d]++;window.addthis.auth.loadIframe(I,d,I.share,I.conf);}},onmouseout:function(H){var I=H.el,d=H.service;if(I.ins!=0&&window.addthis.auth&&window.addthis.auth.fbishare){window.addthis.auth.keepiframe[d]--;setTimeout(function(){window.addthis.auth.hideIframe(d);},1000);}}}});f.share.fb={like:F,send:C,subs:r,has:t,ns:p,ready:s,compat:n,share:b,sub:g,load:u};})(_a,_a.api,_a);(function(e,f,i){var j=document,c=false,g=0;function b(){return(window.getglue&&window.getglue.on);}function h(m,l){var d=(((m||{}).passthrough||{}).objectId)||"none";_a.share.ocw("http://w.getglue.com/convo/checkins?type=conversation&objectId="+_euc(d)+"&source="+_euc(m.url));setTimeout(function(){(new Image()).src=genurl("getglue",0,m,l);},100);}function k(m,l,o){var d=(((q||{}).passthrough||{}).objectId);if(!d){m.innerHTML="";window.console&&console.log("Skipping Get Glue widget: no objectId defined");return;}if(!b()){var p=document.createElement("script");p.src="//widgets.getglue.com/checkin.js";var t=document.getElementsByTagName("script")[0];}var r=_parseThirdPartyAttributes(m,"getglue"),q=l.share;t.parentNode.insertBefore(p,t);m.innerHTML="Checkin on Get Glue";}function a(d){if(c){return;}var m=d?d.share:addthis_share,l=d?d.conf:addthis_config;if(b()){getglue.on("checkin",function(p){var n={};for(var o in m){n[o]=m[o];}_a.share.track("getglue",0,n,l);});c=true;}else{if(g<5){setTimeout(function(){a(d);},500*(g++));}}}e.share=e.share||{};e.share.registerSubscriber(a);e.share.register({getglue_checkin:k});e.share.getglue={sub:a,ps:h,gg:k};})(_a,_a.api,_a);(function(e,h,m){var p=document,f={},n={},a=0,k=0,g=0,o=true;function i(){return(window.gapi&&window.gapi.plusone);}function b(){if(i()){if(gapi&&gapi.plusone&&Object.prototype.toString.call(gapi.plusone.go)==="[object Function]"){gapi.plusone.go();}return;}else{if(!k){k=1;var d=new _a.resource.Resource("plusoneapi","//apis.google.com/js/plusone.js",i);d.addEventListener("load",function(){b();});d.load();}}}function c(d){var r=d?d.share:addthis_share,q=d?d.conf:addthis_config;window._at_plusonecallback=window._at_plusonecallback||function(u){var s={};for(var t in r){s[t]=r[t];}s.url=u.href;_a.share.track("google_"+(u.state=="off"?"un":"")+"plusone",0,s,q);};window._at_pluscallback=window._at_pluscallback||function(u){var s={};for(var t in r){s[t]=r[t];}s.url=u.href;_a.share.track("googleplus_counter",0,s,q);};}function j(q,d,r){if(q.ost){return;}var v=r==="googleplus_counter"?"plus":"plusone",t=_parseThirdPartyAttributes(q,"g:"+v),s=document.ce("g:"+v),u="";_a.gpl=_a.gpl||{},_a.gpl.lang=_a.gpl.lang||null;t.lang=_a.gpl.lang=_a.gpl.lang||((typeof t.lang=="undefined")?null:t.lang);window.___gcfg=window.___gcfg||{};window.___gcfg.lang=_a.gpl.lang||t.lang||_a.ggl((d.conf||{}).ui_language||window.addthis_language)||"en-US";t.href=d.share.url=t.href||_a.track.mgu(d.share.url,{defrag:1});if(v=="plusone"){t.size=t.size||(check32(q,true)?"standard":"small");t.callback=t.callback||"_at_"+v+"callback";}else{t.href=_a.share.acb("google_plusone_share",d.share,addthis_config);t.action="share";}_a.share.goog.sub(d);_a.util.each(t,function(y,x){s.setAttribute(y,x);});q.appendChild(s);q.noh=q.ost=1;b();}function l(q,d){if(q.ost){return;}q.title="Follow on Google+";var v=_parseThirdPartyAttributes(q,"g:plusone");v.size=(v.size||"").toLowerCase();if(document.head){var x=document.createElement("link");x.setAttribute("href",v.href);x.setAttribute("rel","publisher");document.head.appendChild(x);}v.url=v.href=v.href||"";if(v.size=="badge"||v.size=="smallbadge"){var r=document.ce("g:plus"),u="";_a.gpl=_a.gpl||{},_a.gpl.lang=_a.gpl.lang||null;v.lang=_a.gpl.lang=_a.gpl.lang||((typeof v.lang=="undefined")?null:v.lang);window.___gcfg=window.___gcfg||{};window.___gcfg.lang=_a.gpl.lang||v.lang||_a.ggl((d.conf||{}).ui_language||window.addthis_language)||"en-US";_a.util.each(v,function(z,y){r.setAttribute(z,y);});q.appendChild(r);q.noh=q.ost=1;b();}else{var s="32";if(v.size=="small"){s="16";}else{if(v.size=="large"){s="64";}}var t=txt=txt2=ieQ="";if(v.name){if(document.compatMode=="BackCompat"&&_a.bro.msi){ieQ="onclick=\"window.open("+v.href+"?prsrc=3)\"";}t="cursor:default;display:inline-block;text-decoration:none;color:#333;font:13px/16px arial,sans-serif;"+((v.size=="large")?"text-align:center;white-space:nowrap;":"");if(v.size=="large"){txt2="
    "+v.name+"
    on Google+ ";}else{txt=""+v.name+"on";}}q.setAttribute("target","_blank");q.style.textDecoration="none";q.style.cursor="default";q.innerHTML=""+txt+"\""+q.title+"\""+txt2+"";q.noh=q.ost=1;q.onclick=function(y){if(!y){var y=window.event;}var A=y.originalTarget||y.relatedTarget||y.toElement||y.srcElement,z="";if(!A){return;}while(A.nodeName!="A"){A=A.parentNode;}z=((A.attributes||{})["g:plusone:href"]||{}).value||window.location.href;w.open(z+"?prsrc=3");_a.share.track("google_plusone_badge",1,v,config);return false;};}q.onmouseover=function(){this.className=(this.className.indexOf("at300bo")>-1)?this.className:this.className.replace(/at300b/i,"at300bo");};q.noh=q.ost=1;}e.share=e.share||{};e.share.register({google_plusone:j,googleplus_counter:j,google_plusone_badge:l});e.share.registerSubscriber(c);e.share.registerListeners({google_plusone:{onclick:function(d){return false;}}});e.share.goog={plusone:j,badge:l,has:i,sub:c};})(_a,_a.api,_a);(function(a,e,b){var f=document;function c(g,d){var h=function(j){if((typeof window.Intent==="undefined"&&typeof window.WebKitIntent==="undefined")||(!window.navigator||(typeof window.navigator.startActivity==="undefined"&&typeof window.navigator.webkitStartActivity==="undefined"))){return false;}if(!window.Intent||(typeof window.Intent["native"]!=="undefined"&&!window.Intent["native"])){return true;}if(_a.bro.chr){var l=navigator.userAgent;var k=/Chrome\/(.*)\./.exec(l);if(k.length>=1){var i=parseInt(k[1].substring(0,2));if(i<19){var m=function(){if(typeof addthis_config==="undefined"){return false;}if(typeof addthis_config.webintents==="undefined"){return false;}if(!addthis_config.webintents){return false;}return true;};return(m());}}}return true;};if(!h()){return;}options.noevents=true;g.onclick=function(k){var i=window.Intent||window.WebKitIntent;var j=new i("http://webintents.org/share","text/uri-list",d.share.url);if(typeof navigator.startActivity!=="undefined"){navigator.startActivity(j);}else{if(typeof navigator.webkitStartActivity!=="undefined"){navigator.webkitStartActivity(j);}}_a.share.track("intent_share_url",0,d.share,d.conf);return false;};}a.share=a.share||{};a.share.register({intent_share_url:c});a.share.registerListeners({intent_share_url:{}});})(_a,_a.api,_a);(function(b,e,c){var f=document;function a(g,d,h){if(g.ost){return;}var j=_parseThirdPartyAttributes(g,"pi:pinit"),l=_a.util.clone(d.share),k;if(addthis_share&&addthis_share.passthrough&&addthis_share.passthrough.pinterest_share){k=addthis_share.passthrough.pinterest_share;}else{if(addthis_share&&addthis_share.pinterest_share){k=addthis_share.pinterest_share;}else{if(addthis_share&&addthis_share.passthrough){k=addthis_share.passthrough;}else{if(addthis_share){k=addthis_share;}else{k={};}}}}if(j.media||j.layout){j.url=l.url=j.url||k.url||_a.track.mgu(l.url,{defrag:1});j.url=_euc(_a.track.mgu(l.url));if(j.layout=="horizontal"){j.layout="&layout=horizontal";j.width="100px";j.height="25px";}else{if(j.layout=="vertical"){j.layout="&layout=vertical";j.width="49px";j.height="59px";}else{j.layout="";j.width="40px";j.height="25px";}}g.innerHTML="";pinitButton=g.firstChild;if(!d.conf.pubid){d.conf.pubid=addthis_config.pubid||_a.pub();}j.description=l.description=j.description||k.description||k.title||(addthis_share||{}).title||"";pinitButton.src=_atc.rsrcs.pinit+((_a.bro.ie6||_a.bro.ie7)?"?":"#")+"url="+_euc(j.url)+"&media="+_euc(j.media||k.media||"")+"&description="+_euc(j.description)+j.layout+"&ats="+_euc(_a.util.rtoKV(l))+"&atc="+_euc(_a.util.rtoKV(addthis_config));_a.ed.addEventListener("addthis.pinterest.image",function(n){if(!w.addthis_share){w.addthis_share={};}if(!w.addthis_share.passthrough){w.addthis_share.passthrough={};}if(!w.addthis_share.passthrough.pinterest_share){w.addthis_share.passthrough.pinterest_share={};}var m=w.addthis_share.passthrough.pinterest_share;m.pi_media=j.media;m.pi_media_desc=j.description;_a.share.img();});}else{var i=f.createElement("img");g.innerHTML="";g.onclick=function(){if(!w.addthis_share){w.addthis_share={};}if(!w.addthis_share.passthrough){w.addthis_share.passthrough={};}if(!w.addthis_share.passthrough.pinterest_share){w.addthis_share.passthrough.pinterest_share={};}var m=w.addthis_share.passthrough.pinterest_share;m.pi_media=j.media;m.pi_media_desc=j.description;_a.share.img();return false;};}g.noh=g.ost=1;}b.share=b.share||{};b.share.register({pinterest:a,pinterest_count:a,pinterest_pinit:a});b.share.registerListeners({pinterest_share:{onclick:function(g){var h=g.el;if(_atc.ver>=300){var d=_a.util.clone(h.config||addthis_config);d.ui_pane="image";d.image_service="pinterest_share";d.image_header="Pin It on Pinterest";window.addthis.menu(h,d,h.share||addthis_share);}else{_a.share.imgVer("pinterest_share");}return false;}}});b.share.pinterest={pinit:a};})(_a,_a.api,_a);(function(f,g,h,e){var i=document;function a(n,l,q){if(n.ost){return;}var m=_a.util.clone(l.share),k={type:"webpage",url:l.share.url,title:l.share.title,style:"number"},r=_parseThirdPartyAttributes(n,"wb:like"),p=j(),o=c(r,p),d=c(k,p);meta_tags=_a.util.extend(d,o),wb_elem=i.createElement("wb:like");if(_a.bro.ie6||_a.bro.ie7||_a.bro.ie8||(_a.bro.msi&&document.compatMode=="BackCompat")){n.parentNode.insertBefore(wb_elem,n.nextSibling);}else{n.appendChild(wb_elem);}b(wb_elem,meta_tags);_a.ajs("//tjs.sjs.sinajs.cn/open/api/js/wb.js",1);if(!l.conf.pubid){l.conf.pubid=addthis_config.pubid||_a.pub();}n.onclick=function(){_a.share.track("sinaweibo_like",0,l.share,l.conf);};n.noh=n.ost=1;}function j(){var o=i.getElementsByTagName("meta"),d={},p,k,n,m;for(var l=0;l=300){var d=_a.util.clone(g.config||addthis_config);d.ui_pane="image";d.image_service="thefancy";d.image_header="Fancy It";window.addthis.menu(g,d,g.share||addthis_share);}else{_a.share.imgVer("thefancy");}return false;}}});})(_a,_a.api,_a);(function(b,c,h){var i=document,f=0,m=0,a=0;function k(){return(window.twttr&&window.twttr.events);}function g(d){if(k()&&f==1){e();f=a=0;return;}else{if(!f){_a.ajs("//platform.twitter.com/widgets.js",1,null,null,null,true);f=1;}}if(a<3){setTimeout(g,3000+1000*2*(a++));}}function e(d){if(window.twttr&&!m&&twttr.events){m=1;twttr.events.bind("click",function(s){if(s.region=="tweetcount"){return;}if(((s.target||{}).conf||{}).follow){return false;}var r=(s.target.parentNode&&s.target.parentNode.share)?s.target.parentNode.share:{},p=r.url||s.target.baseURI,t=r.title||addthis_share.title,n={};for(var o in addthis_share){n[o]=addthis_share[o];}for(var o in r){n[o]=r[o];}n.url=p;if(t){n.title=t;}var q=(s.region=="follow"||s.region=="following")?false:true;_a.share.track(((q)?"tweet":"twitter_follow_native"),((q)?0:1),n,addthis_config);});}}function j(u,r,y){if(u.ost){return;}var v=_parseThirdPartyAttributes(u,"tw"),z=r.share,s=v.width||56,o=v.height||20,t,B="",q;r.share.url_transforms=r.share.url_transforms||{};r.share.url_transforms.defrag=1;var n=_a.util.clone(r.share),p=((_a.bro.msi&&i.compatMode=="BackCompat")||r.conf.ui_use_tweet_iframe||(r.share.url_transforms.shorten||{}).twitter=="bitly")?true:false;if(typeof v.url!="undefined"){n.url=v.url;}else{n.url=v.url=_a.track.mgu((n.url||(addthis_share||{}).url),n.url_transforms,n,"twitter");}if(!v.counturl){v.counturl=(p)?v.url.replace(/=/g,"%253D"):v.url;}if(n.url.search(/\.+.*(\/|\?)/)==-1){n.url+="/";}v.url=_a.share.acb("twitter",n,addthis_config);v.count=v.count||"horizontal";z.passthrough=z.passthrough||{};var A=z.passthrough.twitter||{};r.text=v.text=v.text||((r.share.title==i.title)?A.text:r.share.title)||"";r.related=v.related=v.related||A.related||"";r.hashtags=v.hashtags=v.hashtags||A.hashtags||"";if(v.via||A.via||(r.text.match(/via\s+@[a-zA-Z0-9_\.]+/i))){r.via=v.via=v.via||A.via||(r.text.match(/via\s+@[a-zA-Z0-9_\.]+/i)?r.text.match(/via\s+@[a-zA-Z0-9_\.]+/i).split("@")[1]:"");}B=_a.util.rtoKV(z,"#@!");if(v.count==="vertical"){o=62;v.height=v.height||o;}else{if(v.count==="horizontal"){s=110;v.width=v.width||s;}}if(v.width){s=v.width;}if(v.height){o=v.height;}t=_a.util.toKV(v,"#@!");if(p){u.innerHTML="";q=u.firstChild;if(!r.conf.pubid){r.conf.pubid=addthis_config.pubid||_a.pub();}q.src=_atc.rsrcs.tweet+((_a.bro.ie6||_a.bro.ie7)?"?":"#")+"href="+_euc(v.url)+"&dr="+_euc(_a.dr)+"&conf="+_euc(_a.util.toKV(r.conf))+"&share="+_euc(B)+"&tw="+_euc(t);}else{var x=(z.templates||{}).twitter||"";if(!v.text){v.text=z.title==""?"":z.title+":";}var w=i.ce("a");w.href="http://twitter.com/share";w.className="twitter-share-button";w.innerHTML="Tweet";for(var d in v){if(v.hasOwnProperty(d)){w.setAttribute("data-"+d,v[d]);}}u.appendChild(w);if(!r.conf.pubid){r.conf.pubid=addthis_config.pubid||_a.pub();}g(u);}u.noh=u.ost=1;}function l(o,n){var q=_parseThirdPartyAttributes(o,"tf"),d=_parseThirdPartyAttributes(o,"tw"),p=document.ce("a");q.screen_name=d.screen_name||q.screen_name||"addthis";p.href="http://twitter.com/"+q.screen_name;p.className="twitter-follow-button";p.innerHTML="Follow @"+q.screen_name;_a.util.each(q,function(s,r){p.setAttribute("data-"+s,r);});_a.util.each(d,function(s,r){p.setAttribute("data-"+s,r);});o.ost=1;o.appendChild(p);if(!n.conf.pubid){n.conf.pubid=addthis_config.pubid||_a.pub();}g(o);}b.share=b.share||{};b.share.register({tweet:j,twitter_follow_native:l});b.share.registerSubscriber(e);b.share.registerListeners({twitter:{_after:function(d){d.ins=1;d.noh=1;},onclick:function(n){var p=n.el,d=n.service;if(p.ins!=0&&window.addthis.auth&&window.addthis.auth.twishare){window.addthis.auth.lockiframe[d]=true;window.addthis.auth.loadIframe(p,d,p.share,p.conf);}else{return _a.share.pts(p.share,p.conf);}},onmouseover:function(n){var p=n.el,d=n.service;if(p.ins!=0&&window.addthis.auth&&window.addthis.auth.twishare){window.addthis.auth.keepiframe[d]++;window.addthis.auth.loadIframe(p,d,p.share,p.conf);}},onmouseout:function(n){var p=n.el,d=n.service;if(p.ins!=0&&window.addthis.auth&&window.addthis.auth.twishare){window.addthis.auth.keepiframe[d]--;setTimeout(function(){window.addthis.auth.hideIframe(d);},1000);}}}});b.share.twitter={tweet:j,follow:l,sub:e};})(_a,_a.api,_a);(function(h,i,k){var l=document;function g(p,o,q){if(p.ost||_a.bro.ie6){return;}var d=_parseThirdPartyAttributes(p,"su:badge"),t=d.style||"1",u=o.share.url=d.href||_a.track.mgu(o.share.url,{defrag:1}),s=d.height||"20px",r=d.width||"75px";if(t=="5"){s=d.height||"60px";}else{if(t=="6"){s=d.height||"31px";}}p.innerHTML="".replace("{{STYLE}}",t).replace("{{URL}}",_euc(u)).replace("{{HEIGHT}}",s).replace("{{WIDTH}}",r);p.noh=p.ost=1;}function c(A,y){if(A.ost){return;}var o=l.ce("div"),p="http://userapi.com/js/api/openapi.js?52",s=y.share.url.replace(/#.*$/,""),v=y.share.title,d=y.share.description,r=_parseThirdPartyAttributes(A,"vk"),t=r&&(r.apiId||r.apiid),q={type:"full",pageDescription:d,pageTitle:v,pageUrl:s},z=function(){return w.VK&&w.VK.init&&w.VK.Widgets&&w.VK.Widgets.Like;},u=function(B){VK.init({apiId:t,onlyWidgets:true});VK.Widgets.Like(B.id,B.configuration);},x=_a.util.bind(function(){u(this);},o);if(!t){return;}o.id="addthis_vk_like"+_a.util.cuid();o.configuration=q;A.appendChild(o);if(z()){u(o,q);}else{if(!i._vkr){i._vkr=new _a.resource.Resource("vklike",p,z);i._vkr.load();}i._vkr.addEventListener("load",x);}A.noh=A.ost=1;}function j(o,d){if(o.ost){return;}var p=_parseThirdPartyAttributes(o,"4sq"),q=document.createElement("a");q.href="https://foursquare.com/intent/venue.html";q.className="fourSq-widget";if(p["data-variant"]){q.setAttribute("data-variant",p["data-variant"]);}o.appendChild(q);_a.ajs("//platform.foursquare.com/js/widgets.js",1);o.noh=o.ost=1;}function f(o,d){if(o.ost){return;}var r=_parseThirdPartyAttributes(o,"rk:healthy"),q=l.createElement("div"),p=new _a.resource.Resource("runkeeperjs","//runkeeper.com/static/js/healthy/rkHealthyButton.js");q.className="rk-healthy";q.setAttribute("data-healthyUrl",(r.url||d.share.url||window.location.href));q.setAttribute("data-buttonType",(r.type||"normal"));o.appendChild(q);o.noh=o.ost=1;p.load();}function e(o,d){if(_a.bro.ie9&&_a.ver()<300){return;}o.title="Permalink";}function m(o,d){if(o.ost){return;}var r=_parseThirdPartyAttributes(o,"svejo:"),q=document.ce("div"),p=new _a.resource.Resource("svejojs","//svejo.net/button.js",function(){return!!window.load_svejo_buttons;});p.addEventListener("load",function(){window.load_svejo_buttons();});q.className="svejo-button";r.href=d.share.url=r.href||_a.track.mgu(d.share.url,{defrag:1});r.size=r.size||(check32(o,true)?"standard":"compact");_a.util.each(r,function(t,s){q.setAttribute("data-"+t,s);});o.appendChild(q);o.noh=o.ost=1;p.load();}function b(r,p){if(r.ost){return;}var d=_parseThirdPartyAttributes(r,"li"),u=p.share,s=d.width||100,o=d.height||18,q,v="",t;if(!d.counter){d.counter="horizontal";}if(!u.passthrough){u.passthrough={};}u.passthrough.linkedin=_a.util.toKV(d);v=_a.util.rtoKV(u);if(d.counter==="top"){o=55;s=57;if(!d.height){d.height=o;}if(!d.width){d.width=s;}}else{if(d.counter==="right"){s=100;if(!d.width){d.width=s;}}else{if(d.counter==="none"){s=57;if(!d.width){d.width=s;}}}}if(d.width){s=d.width;}if(d.height){o=d.height;}q=_a.util.toKV(d),r.innerHTML="";t=r.firstChild;if(!p.conf.pubid){p.conf.pubid=addthis_config.pubid||_a.pub();}t.src=_atc.rsrcs.linkedin+((_a.bro.ie6||_a.bro.ie7)?"?":"#")+"href="+_euc(p.share.url)+"&dr="+_euc(_a.dr)+"&conf="+_euc(_a.util.toKV(p.conf))+"&share="+_euc(v)+"&li="+_euc(q);r.noh=r.ost=1;}function n(s,q){var r=_parseThirdPartyAttributes(s,"am:wishlist"),v=q.share.url=r.url=r.url||_a.track.mgu(q.share.url,{defrag:1}),p=l.ce("div"),t=l.ce("div"),d=l.ce("div"),u=l.ce("div"),o=addthis_config||{};o.hdl=1;if(!r.id){r.id=Math.floor(Math.random()*10000);}p.style.display=t.style.display=d.style.display=u.style.display="none";p.id="AUWLBkURL."+r.id;p.innerHTML=r.url=_a.share.acb("amazonwishlist_native",q.share,o);t.id="AUWLBkPrice."+r.id;t.innerHTML=r.price;d.id="AUWLBkTitle."+r.id;d.innerHTML=r.title;u.id="AUWLBkImage."+r.id;u.innerHTML=r.img||"";s.appendChild(p);if(r.price){s.appendChild(t);}if(r.title){s.appendChild(d);}s.appendChild(u);_a.ajs("http://www.amazon.com/wishlist/bookmarklet/getbutton.js?name="+r.id+"&image="+(r.style||"2"),1,"1","AddToAUWLButton."+r.id,s);s.onclick=function(){_a.share.track("amazonwishlist_native",0,r,o);};s.noh=s.ost=1;}function a(o,d){if(o.className.indexOf("chiclet_style")!=-1){throw new Error("just do a chiclet");}if(o.ost){return;}var s=_parseThirdPartyAttributes(o,"tm"),r=50,q=61;passthrough=_a.util.toKV(s);if(s.style==="compact"){r=95;q=25;}o.innerHTML="";var p=o.firstChild;p.src="//api.tweetmeme.com/button.js?url="+_euc(d.share.url)+"&"+passthrough;o.noh=o.ost=1;}h.share=h.share||{};h.share.register({foursquare:j,svejo_counter:m,linkedin_counter:b,runkeeper_healthy:f,stumbleupon_badge:g,tweetmeme:a,vk_like:c});h.share.registerListeners({more:{require:function(o,p,d){return!p.noh&&_a.ver()>=300&&!_a.bro.iph&&!_a.bro.wph&&!_a.bro.dro;},onclick:function(d){var p=d.el||{};window.addthis.menu(p,p.conf,p.share);return false;}},email:{require:function(o,p,d){return!p.noh&&_a.ver()>=300&&!_a.bro.iph&&!_a.bro.wph&&!_a.bro.dro;},onclick:function(q){var r=q.el||{},p=q.service,d=_a.util.clone(r.conf);d.ui_pane=p;if(document.location.href.search(/bookmark\.php/)==-1){window.addthis.menu(r,d,r.share);}else{window.location=_a.share.genurl(p,0,r.share,r.conf);}return false;}},foursquare:{onclick:function(p){var q=p.el||{},d=p.service;_a.share.track(d,1,q.share,q.conf);return false;}},link:{onclick:function(r){var s=r.el||{},p=r.service,d=_euc((s.share||{}).url||addthis_share.url);if(_a.ver()>=300){var q=_a.util.clone(s.config||addthis_config);q.ui_pane="link";window.addthis.menu(s,q,s.share||addthis_share);}else{addthis_open(document.body,"link",d);if(document.getElementById("at16p")){document.getElementById("at16p").style.display="block";}if(document.getElementById("at15s")){document.getElementById("at15s").style.display="none";}}return false;}}});})(_a,_a.api,_a);(function(f,g,k){function a(){try{if(_a.ver()>=300){return(l.href.search(/bookmark\d+\.html/i)!=-1);}return l.href.search(/addthis\.com\/static\/r07\/bookmark\d+\.html/i)!=-1;}catch(o){return 0;}}var m={pinterest_share:{img_service:"pinterest_share",img_header:"Pin It to Pinterest",img_base_url:"//pinterest.com/pin/create/button/",img_param:"media=",ctype:"",windowProps:{height:"335",width:"750"}},pinterest:{img_service:"pinterest",img_header:"Pin It to Pinterest",img_base_url:"//pinterest.com/pin/create/button/",img_param:"media=",ctype:"",windowProps:{height:"335",width:"750"}},thefancy:{img_service:"thefancy",img_header:"Add to Fancy",img_base_url:"//thefancy.com/offer.html",img_param:"imageurl=",ctype:"&ctype=image",windowProps:{height:"500",width:"700"}}},j=document.body;function b(t,Y,Z){var t=typeof t=="undefined"?"pinterest_share":t,V=d.getElementById("atImgBox_"+t),o=d.getElementById("at16pccImg"),H=m[t]||m.pinterest_share,U=(t.indexOf("pinterest")!==-1)?true:false;Y=a()?(Y||w.addthis_media_msg):Y;if(V&&!Y){if(a()){e("main");e("filter");e("details");V.style.display="block";}else{V.style.display="block";if(o){o.style.height="100%";}}}else{var z=_a.util.gebcn(j,"DIV","atPinWin",true,true);for(var P in z){if(z[P].style){z[P].style.display="none";}}var s=d.getElementById("atImgBox_"+t)||d.createElement("div"),Q=w.addthis_media_msg||null,u=Q||Y||(n()||"").split(";"),p=new Array(),E,r=new Array(),R=d.createElement("div"),C=d.createElement("div"),B=d.createElement("span"),F=d.createElement("span"),O=(((w.addthis_share_msg||w.addthis_share||{}).passthrough||{}).pinterest_share||{}).media,K=false,G=a()?"3":"",y=a()?"15":0;if((s.innerHTML||"").search(/at3/)!=-1){s.innerHTML="";}if(_a.ver()>=300&&a()&&Q){Y=Q;}if(Z){u.push(Z);}for(var T in u){if(typeof u[T].split!=="function"){continue;}var W=(u[T]||"").split("!|");if(!W[0]||typeof W[0]=="undefined"){continue;}if(U&&W.length>2){if(W[1]!=="?"&&W[2]!=="?"){if(parseInt(W[1],10)*parseInt(W[2],10)<20000){continue;}}}p.push({src:unescape(W[0]),offsetHeight:W[1],offsetWidth:W[2],alt:W[3],og:W[4]});}if(!o&&!Y&&_a.ver()<300){o=d.createElement("div");o.id="at16pccImg";d.body.appendChild(o);if(_a.bro.msi&&d.compatMode.toLowerCase()=="backcompat"){o.style.position="absolute";}}else{if(o&&o.style){o.style.height="100%";}}R.className="atPinHdr";B.innerHTML=""+H.img_header+"";B.className="atPinHdrMsg";R.appendChild(B);F.innerHTML="X";F.className="atPinClose";F.onclick=function(){d.getElementById("atImgBox_"+t).style.display="none";o.style.height="0";};R.appendChild(F);s.appendChild(R);var J=0,v={};for(var T in p){E=p[T];if(!E||typeof E.src==="undefined"){continue;}var I=d.createElement("img"),L=d.createElement("span"),q=d.createElement("span"),A=d.createElement("span"),X=d.createElement("div"),N=4;I.alt=I.title=E.alt;if(_a.ver()>=300){X.className="atImgActBtn at300bs at15nc at15t_"+t;}X.style.display="none";X.onmouseover=function(){(this.style||{}).opacity="1";};I.src=E.src;J++;v=E;if(isNaN(E.offsetHeight)||isNaN(E.offsetWidth)){I.height=200-y;}else{N=Math.min((E.offsetHeight/(205-y)),(E.offsetWidth/(205-y)));I.height=E.offsetHeight/N;I.width=E.offsetWidth/N;I.style.marginTop=I.height>(202-y)?(-(I.height-(200-y))/2)+"px":0+"px";I.style.marginLeft=I.width>(202-y)?(-(I.width-(200-y))/2)+"px":0+"px";}A.innerHTML=(E.og)?"Preferred Image":E.offsetHeight+" x "+E.offsetWidth;q.className="atImgSpanInner";L.className="at"+G+"ImgSpanOuter addthis_32x32_style";A.className="atImgSpanSize";q.appendChild(I);q.appendChild(X);L.appendChild(q);L.appendChild(A);L.onmouseover=function(x){this.getElementsByTagName("div")[0].style.display="block";this.getElementsByTagName("img")[0].style.opacity=("0.4");this.getElementsByTagName("img")[0].style.filter="alpha(opacity=40)";};L.onmouseout=function(x){this.getElementsByTagName("div")[0].style.display="none";this.getElementsByTagName("img")[0].style.opacity=("1");this.getElementsByTagName("img")[0].style.filter="alpha(opacity=100)";};I.onclick=X.onclick=function(){var x=_a.util.clone(Y?addthis_share_msg:(g.share||w.addthis_share||{}));g.config=g.config||w.addthis_config||{};x.passthrough=x.passthrough||{};x.passthrough.pinterest_share={media:this.parentNode.getElementsByTagName("img")[0].src,description:(this.title||this.alt||x.description||x.title||this.parentNode.getElementsByTagName("img")[0].src.split("/").pop()||"")};if(Y){x.url=_euc(x.url);if(_a.ver()>=300){_a.share.track((U?"pinterest_share":t),0,x,addthis_config,this);h({windowUrl:H.img_base_url+"?"+H.img_param+_euc(x.passthrough.pinterest_share.media)+"&url="+x.url+"&description="+_euc(x.passthrough.pinterest_share.description)+H.ctype,width:H.windowProps.width,height:H.windowProps.height},H.img_service);}else{w.location=f.share.genurl(t,0,x,g.config);}}else{x.url=_euc(x.url);_a.share.track((U?"pinterest_share":t),0,w.addthis_share,w.addthis_config,this);h({windowUrl:H.img_base_url+"?"+H.img_param+_euc(x.passthrough.pinterest_share.media)+"&url="+x.url+"&description="+_euc(x.passthrough.pinterest_share.description)+H.ctype,width:H.windowProps.width,height:H.windowProps.height},H.img_service);d.getElementById("atImgBox_"+t).style.display="none";o.style.height="0";return false;}};if(_a.bro.msi&&document.compatMode.toLowerCase()=="backcompat"){L.style.margin="5px";}C.appendChild(L);}if(J==0){var D=d.createElement("span");D.className="atNoImg";D.innerHTML="There are no valid images to share.";C.appendChild(D);}if(J==1&&!a()){var S=_a.util.clone(a()?addthis_share_msg:(g.share||w.addthis_share||{}));S.url=_euc(S.url);g.config=g.config||addthis_config||{};S.passthrough=S.passthrough||{};S.passthrough.pinterest_share={media:v.src,description:(S.title||v.src.split("/").pop()||"")};if(_a.ver()>=300&&_a.share.inBm()){_a.share.notify((U?"pinterest_share":t),S,addthis_config_msg,this);h({windowUrl:H.img_base_url+"?"+H.img_param+_euc(S.passthrough.pinterest_share.media)+"&url="+S.url+"&description="+_euc(S.passthrough.pinterest_share.description)+H.ctype,width:H.windowProps.width,height:H.windowProps.height},H.img_service);return false;}else{h({windowUrl:H.img_base_url+"?"+H.img_param+_euc(S.passthrough.pinterest_share.media)+"&url="+S.url+"&description="+_euc(S.passthrough.pinterest_share.description)+H.ctype,width:H.windowProps.width,height:H.windowProps.height},H.img_service);((d.getElementById("atImgBox_"+t)||{}).style||{}).display="none";if(_a.ver()<300){o.style.height="0";}return false;}}if(_a.ver()>=300&&!a()){var M=_a.util.clone(w.addthis_config);M.ui_pane="image";M.image_service=(U?"pinterest":t);M.image_header=H.img_header;if(!(f.menu||{}).open){w.addthis.menu((_a.maf&&_a.maf.sib),M,w.addthis_share);}else{f.menu.open((_a.maf&&_a.maf.sib),M,w.addthis_share);}return;}if(Y){C.lastChild.style.marginBottom="40px";}s.appendChild(C);if(!a()){if(_a.bro.msi&&document.compatMode.toLowerCase()=="backcompat"||_a.bro.ie6){s.style.position="absolute";}s.className="atPinBox";C.className="atPinMn";R.className="atPinHdr";}else{s.className=s.id="atPinWin";s.style.display="block";C.className="at"+G+"PinWinMn";R.style.display="none";j.style.margin="0px";F.style.display="none";e("filter");e("main");e("details");}s.id="atImgBox_"+t;if(Y){if(typeof jQuery!="undefined"){s.style.display="none";d.body.appendChild(s);$(s).fadeIn();}else{d.body.appendChild(s);}}else{if(!a()){o.appendChild(s);o.onclick=function(ac){if(!ac){var ac=w.event||{};}if((ac.target||{}).id!="at16pccImg"&&(ac.srcElement||{}).id!="at16pccImg"){return;}var ab=_a.util.gebcn(j,"DIV","atPinBox",true,true);for(var aa in ab){if(ab[aa].style){ab[aa].style.display="none";}}o.style.height="0";};}}}}function e(o){if(typeof jQuery=="undefined"){((d.getElementById(o)||{}).style||{}).display="none";}else{$("#"+o).fadeOut();}}function n(y,J,x,B){var C="",r=new Array(),G=new Array(),H,u=new Array(),s=(((w.addthis_share_msg||w.addthis_share||{}).passthrough||{}).pinterest_share||{}),A=s.media,F=s.description,t=s.pi_media,I=s.pi_media_desc,D=false,z=null,p=typeof x=="string"?x:(typeof(w.addthis_config||{}).image_include=="string"?addthis_config.image_include:null),o=typeof B=="string"?B:(typeof(w.addthis_config||{}).image_exclude=="string"?addthis_config.image_exclude:null);if(typeof y!="undefined"&&y!=null){if(y.search(/^\#/)>-1){z=(document.getElementById(y.replace(/\#/,""))||document).getElementsByTagName("img");}else{if(y.search(/^\./)>-1&&typeof J!="undefined"){var q=J,v=(y||"").replace(".","");while(q.className!=v&&q.nodeName.toLowerCase()!="body"&&q.parentNode){q=q.parentNode;}z=(q||document).getElementsByTagName("img");}else{z=document.getElementsByTagName("img");}}}else{z=document.getElementsByTagName("img");}if(A){r[A]=true;G.push({url:A,title:F});}if(t){r[t]=true;G.push({url:t,title:I});}for(var E in z){if(!z[E]||typeof z[E].src==="undefined"){continue;}if(r[z[E].src]){if(z[E].src==A||z[E].src==t){D=true;if(D&&(z[E].src==A||z[E].src==t)&&((o&&(z[E].className||"").search(o)>-1)||(p&&(z[E].className||"").search(p)==-1))){continue;}G[0]=z[E];}continue;}else{if(typeof z[E].nodeName=="undefined"||(p&&(z[E].className||"").search(p)==-1)||(o&&(z[E].className||"").search(o)>-1)){continue;}G.push(z[E]);r[z[E].src]=true;}}for(var E in G){if(typeof G.hasOwnProperty!=="undefined"&&!G.hasOwnProperty(E)){continue;}H=G[E];if(typeof H=="object"&&H.url){C+=H.url+"!|undefined!|undefined!|"+H.title+"!|true;";continue;}if(!H.src||H.src=="undefined"||!H.offsetHeight||typeof H.offsetHeight=="undefined"||H.offsetHeight=="undefined"||!H.offsetWidth||H.offsetWidth=="undefined"||(parseInt(H.offsetWidth)==16&&parseInt(H.offsetWidth==16))||(parseInt(H.offsetWidth)==32&&parseInt(H.offsetWidth==32))||H.src.search("btn/v2/lg-share-")>-1||(H.offsetWidth!="?"&&H.offsetHeight!="?"&&parseInt(H.offsetWidth)<50&&parseInt(H.offsetWidth)<50)||!!H.getAttribute("nopin")){continue;}C+=escape(_a.util.rel2abs(H.src))+"!|"+H.offsetHeight+"!|"+H.offsetWidth+"!|"+(H.alt||H.title)+"!|;";}return(C.replace(/;$/,""));}function c(p){if(_a.ver()>=300){var o=_a.util.clone(w.addthis_config);o.ui_pane="image";o.image_service=p;g.menu(_a.maf.pre,o,w.addthis_share);}else{((document.getElementById("at16p")||{}).style||{}).display="none";_a.share.img(p);}return false;}function i(p){var r=r||_a.share.media();if(_a.bro.msi){_a.track.msg("atimg_ie"+r);}else{var q=setInterval(function(){p.postMessage("atimg_more"+r,"*");},500),o=setTimeout(function(){clearInterval(q);},10000);}return false;}function h(q,s){var u={height:350,left:0,location:0,menubar:0,resizable:0,scrollbars:0,status:0,width:700,windowName:null,windowURL:null,top:0,toolbar:0};_a.util.mrg(q,u);var p="height="+q.height+",width="+q.width+",toolbar="+q.toolbar+",scrollbars="+q.scrollbars+",status="+q.status+",resizable="+q.resizable+",location="+q.location+",menuBar="+q.menubar,r=(screen.height-q.height)/3,t=(screen.width-q.width)/2,o=window.open(q.windowUrl,q.windowName,p+",left="+t+",top="+r);if(o){o.focus();}_a.xf.send(window.parent,"addthis.menu.shareimg",{service:s,type:"share"});}f.share=f.share||{};f.util.extend(f.share,{img:b,media:n,imgVer:c,imgOcw:i,inBm:a});})(_a,_a.api,_a);(function(){var a=function(){if(typeof addthis_config==="undefined"){return false;}if(typeof addthis_config.webintents==="undefined"){return false;}if(!addthis_config.webintents){return false;}return true;};if(!a()){return;}var b=function(d){if(typeof w.WebKitIntent!=="undefined"){return true;}if((typeof w.Intent==="undefined"&&typeof w.WebKitIntent==="undefined")||(typeof w.navigator.startActivity==="undefined"&&typeof w.navigator.webkitStartActivity==="undefined")){return false;}var f=navigator.userAgent;if(/Chrome\/(.*)\./.test(f)){var e=/Chrome\/(.*)\./.exec(f);if(e.length>=1){var c=parseInt(e[1].substring(0,2));if(c<19){return false;}}}return true;};catchIntents=function(){if(b()){return;}w.Intent=function(f,e,d,c){this.verb=f;this.noun=e;this.data=d;};w.navigator.startActivity=function(d){if(d.verb==="http://webintents.org/share"&&d.noun==="text/uri-list"){_6.update("share","url",d.data);for(var c in d.extras){_6.update("share",c,d.extras);}var e="http://addthis.com/bookmark.php";e+="?v=300&url="+encodeURIComponent(d.data);w.open(e,"","width=700,height=500");}};};catchIntents();})();(function(b,c,e){if(!b.services){b.services={};}b.services.refget=function(f){f=f.split(".").slice(-3).join(".");var g={"mail.google.com":"gmail","mail.yahoo.com":"yahoomail","mail.aol.com":"aolmail","mail.live.com":"hotmail"};if(g[f]){return g[f];}f=f.split(".").slice(-2).shift();if(b.services.map[f]){return f;}return"";};b.services.map={facebook:"",twitter:"",reddit:"",stumbleupon:"",gmail:"mail.google.com",blogger:"",linkedin:"",tumblr:"",delicious:"",yahoomail:"compose.mail.yahoo.com",hotmail:"hotmail.msn.com","100zakladok":"100zakladok.ru","2tag":"2tag.nl","2linkme":"","7live7":"",a1webmarks:"a1-webmarks.com",a97abi:"",addio:"add.io",menu:"api.addthis.com",adfty:"",adifni:"",aerosocial:"",allmyfaves:"",amazonwishlist:"amazon.com",amenme:"",aim:"lifestream.aol.com",aolmail:"webmail.aol.com",armenix:"",arto:"",baang:"baang.ir",baidu:"cang.baidu.com",biggerpockets:"",bitly:"bit.ly",bizsugar:"",bleetbox:"",blinklist:"",bloggy:"bloggy.se",blogmarks:"blogmarks.net",blogtrottr:"",blurpalicious:"",bobrdobr:"bobrdobr.ru",bonzobox:"",socialbookmarkingnet:"social-bookmarking.net",bookmarkycz:"bookmarky.cz",bookmerkende:"bookmerken.de",bordom:"bordom.net",box:"box.net",brainify:"",bryderi:"bryderi.se",buddymarks:"",buzzzy:"",camyoo:"",care2:"",chiq:"",cirip:"cirip.ro",citeulike:"citeulike.org",classicalplace:"",cndig:"cndig.org",colivia:"colivia.de",technerd:"",cosmiq:"cosmiq.de",curateus:"curate.us",designmoo:"",digaculturanet:"digacultura.net",digg:"",diggita:"diggita.it",diglog:"",digo:"digo.it",diigo:"",domelhor:"domelhor.net",dotnetshoutout:"",woscc:"wos.cc",douban:"",draugiem:"draugiem.lv",dropjack:"",dwellicious:"",dzone:"",efactor:"",ekudos:"ekudos.nl",elefantapl:"elefanta.pl",embarkons:"",evernote:"",extraplay:"",ezyspot:"",stylishhome:"",fabulously40:"",informazione:"fai.informazione.it",fark:"",farkinda:"",fashiolista:"",fashionburner:"",favable:"",faves:"",favlogde:"favlog.de",favoritende:"favoriten.de",favoritus:"",financialjuice:"",flaker:"flaker.pl",folkd:"",formspring:"formspring.me",thefreedictionary:"",fresqui:"",friendfeed:"",friendster:"",funp:"",fwisp:"",gabbr:"",gamekicker:"",givealink:"givealink.org",govn:"my.go.vn",goodnoows:"",googletranslate:"translate.google.com",gravee:"",greaterdebater:"",hackernews:"news.ycombinator.com",hatena:"b.hatena.ne.jp",gluvsnap:"healthimize.com",hedgehogs:"hedgehogs.net",historious:"historio.us",hitmarks:"",hotklix:"",hootsuite:"",w3validator:"validator.w3.org",idearef:"",identica:"identi.ca",ihavegot:"",indexor:"indexor.co.uk",instapaper:"",iorbix:"",isociety:"isociety.be",iwiw:"iwiw.hu",jamespot:"",jappy:"jappy.de",jumptags:"",zooloo:"kablog.com",kaboodle:"",kaevur:"",kaixin:"kaixin001.com",kindleit:"fivefilters.org",kirtsy:"",kledy:"kledy.de",kommenting:"",latafaneracat:"latafanera.cat",laaikit:"laaik.it",ladenzeile:"ladenzeile.de",librerio:"",linkagogo:"",linksgutter:"",linkshares:"linkshares.net",linkuj:"linkuj.cz",livejournal:"",lockerblogger:"",logger24:"",mymailru:"connect.mail.ru",markme:"markme.me",margarin:"mar.gar.in",mashbord:"",mawindo:"",meinvz:"meinvz.net",mekusharim:"mekusharim.walla.co.il",memonic:"",memori:"memori.ru",meneame:"meneame.net",myvidster:"",live:"profile.live.com",misterwong:"mister-wong.com",misterwong_de:"mister-wong.de",moemesto:"moemesto.ru",moikrug:"moikrug.ru",mrcnetworkit:"mrcnetwork.it",myspace:"",n4g:"",naszaklasa:"nk.pl",netlog:"",netvibes:"",netvouz:"",newsmeback:"",newstrust:"newstrust.net",newsvine:"",nujij:"nujij.nl",odnoklassniki_ru:"odnoklassniki.ru",oknotizie:"oknotizie.virgilio.it",ongobee:"",openthedoor:"otd.to",orkut:"promote.orkut.com",dashboard:"api.addthis.com",oyyla:"",packg:"",pafnetde:"pafnet.de",pdfonline:"savepageaspdf.pdfonline.com",pdfmyurl:"",phonefavs:"",planypus:"planyp.us",plaxo:"",plurk:"",popedition:"",posteezy:"",printfriendly:"",pusha:"pusha.se",qrfin:"qrf.in",quantcast:"",qzone:"sns.qzone.qq.com",pocket:"getpocket.com",rediff:"share.rediff.com",redkum:"",scoopat:"scoop.at",scoopit:"scoop.it",sekoman:"sekoman.lv",select2gether:"www2.select2gether.com",shaveh:"shaveh.co.il",shetoldme:"",shirintar:"shir.intar.in",simpy:"",sinaweibo:"v.t.sina.com.cn",slashdot:"slashdot.org",smiru:"smi2.ru",sodahead:"",sonico:"",sphinn:"",spinsnap:"",sportpost:"",sulia:"",yiid:"spread.ly",springpad:"springpadit.com",startaid:"",startlap:"startlap.hu",storyfollower:"",studivz:"studivz.net",stuffpit:"",stumpedia:"",sunlize:"",stylehive:"",svejo:"svejo.net",symbaloo:"",taaza:"",tagmarksde:"tagmarks.de",tagvn:"",tagza:"",tellmypolitician:"",thewebblend:"",thinkfinity:"community.thinkfinity.org",thisnext:"",thrillon:"",throwpile:"",tipd:"",topsitelernet:"ekle.topsiteler.net",transferr:"",tuenti:"",tulinq:"",tusul:"",tvinx:"",tweetmeme:"api.tweetmeme.com",twitthis:"",typepad:"",upnews:"upnews.it",urlaubswerkde:"urlaubswerk.de",viadeo:"",virb:"",visitezmonsite:"",vk:"vkontakte.ru",vkrugudruzei:"vkrugudruzei.ru",voxopolis:"",vybralisme:"vybrali.sme.sk",webnews:"webnews.de",domaintoolswhois:"domaintools.com",wanelo:"",windows:"api.addthis.com",wirefan:"",wishmindr:"",wordpress:"",wykop:"wykop.pl",xanga:"",xing:"",yahoobkm:"bookmarks.yahoo.com",yammer:"",yardbarker:"",yigg:"yigg.de",yoolink:"go.yoolink.to",yorumcuyum:"",youmob:"",yuuby:"",zakladoknet:"zakladok.net",zanatic:"",ziczac:"ziczac.it",zingme:"link.apps.zing.vn",zootool:""};var d={more:1,compact:1,expanded:1,facebook:1,email:1,twitter:1,print:1,google:1,google_plusone_share:1,live:1,stumbleupon:1,vk:1,pinterest_share:1,myspace:1,favorites:1,digg:1,delicious:1,orkut:1,blogger:1,mailto:1,linkedin:1,mymailru:1,gmail:1,yahoomail:1,reddit:1,tumblr:1,live:1},a={more:1,compact:1,expanded:1,"100zakladok":1,adifni:1,aim:1,amazonwishlist:1,arto:1,baidu:1,bitly:1,blogger:1,bloggy:1,bobrdobr:1,delicious:1,digg:1,diggita:1,draugiem:1,ekudos:1,email:1,facebook:1,favorites:1,friendfeed:1,gmail:1,google:1,google_plusone_share:1,hatena:1,hotmail:1,jappy:1,linkedin:1,live:1,livejournal:1,mailto:1,meinvz:1,meneame:1,misterwong:1,mymailru:1,myspace:1,netlog:1,nujij:1,oknotizie:1,orkut:1,oyyla:1,pinterest_share:1,plurk:1,print:1,pusha:1,reddit:1,settings:1,sonico:1,studivz:1,stumbleupon:1,tuenti:1,tumblr:1,twitter:1,viadeo:1,vk:1,wordpress:1,wykop:1,xing:1,yahoobkm:1,yahoomail:1,yorumcuyum:1};_a._top_services=d;_a._top_services16=a;b.services.isTop=function(f,g){if(g==16){return a[f];}else{return d[f];}};})(_a,_a.api,_a);(function(b,d,e){var a={googlebuzz:"Google Buzz",googlereader:"Google Reader",googletranslate:"Google Translate",google_follow:"Google",rss:"RSS"};var f={"100zakladok":"100zakladok","2linkme":"2linkme","2tag":"2 Tag",a97abi:"A97abi",adfty:"Adfty",adifni:"Adifni",advqr:"ADV QR",aim:"Lifestream",amazonwishlist:"Amazon",amenme:"Amen Me!",aolmail:"AOL Mail",apsense:"APSense",arto:"Arto",azadegi:"Azadegi",baang:"Baang",baidu:"Baidu",balltribe:"BallTribe",beat100:"Beat100",biggerpockets:"BiggerPockets",bitly:"Bit.ly",bizsugar:"BizSugar",bland:"Bland takkinn",blinklist:"Blinklist",blogger:"Blogger",bloggy:"Bloggy",blogkeen:"Blogkeen",blogmarks:"Blogmarks",blurpalicious:"Blurpalicious",bobrdobr:"Bobrdobr",bonzobox:"BonzoBox",bookmarkycz:"Bookmarky.cz",bookmerkende:"Bookmerken",box:"Box",brainify:"Brainify",bryderi:"Bryderi.se",buddymarks:"BuddyMarks",buffer:"Buffer",buzzzy:"Buzzzy",camyoo:"Camyoo",care2:"Care2",chimein:"Chime",chiq:"Chiq",cirip:"Cirip",citeulike:"CiteULike",classicalplace:"ClassicalPlace",cleanprint:"CleanPrint",cleansave:"CleanSave",cndig:"Cndig",colivia:"Colivia.de",cosmiq:"COSMiQ",cssbased:"CSS Based",curateus:"Curate.us",delicious:"Delicious",digaculturanet:"DigaCultura",digg:"Digg",diggita:"Diggita",digo:"Digo",diigo:"Diigo",domaintoolswhois:"Whois Lookup",domelhor:"DoMelhor",dotnetshoutout:".netShoutout",douban:"Douban",draugiem:"Draugiem.lv",dropjack:"Dropjack",dudu:"Dudu",dzone:"Dzone",efactor:"EFactor",ekudos:"eKudos",elefantapl:"elefanta.pl",email:"Email",embarkons:"Embarkons",evernote:"Evernote",extraplay:"extraplay",ezyspot:"EzySpot",fabulously40:"Fabulously40",facebook:"Facebook",fark:"Fark",farkinda:"Farkinda",fashiolista:"Fashiolista",favable:"FAVable",faves:"Faves",favlogde:"favlog",favoritende:"Favoriten",favorites:"Favorites",favoritus:"Favoritus",financialjuice:"Financial Juice",flaker:"Flaker",folkd:"Folkd",foodlve:"Cherry Share",formspring:"Formspring",fresqui:"Fresqui",friendfeed:"FriendFeed",funp:"funP",fwisp:"fwisp",gabbr:"Gabbr",gamekicker:"Gamekicker",gg:"GG",giftery:"Giftery.me",gigbasket:"GigBasket",givealink:"GiveALink",gluvsnap:"Healthimize",gmail:"Gmail",goodnoows:"Good Noows",google:"Google",google_plusone_share:"Google+",googletranslate:"Translate",govn:"Go.vn",greaterdebater:"GreaterDebater",hackernews:"Hacker News",hatena:"Hatena",hedgehogs:"Hedgehogs",historious:"historious",hotklix:"Hotklix",hotmail:"Outlook",hootsuite:"Hootsuite",identica:"Identi.ca",ihavegot:"ihavegot",indexor:"Indexor",informazione:"Informazione",instapaper:"Instapaper",iorbix:"iOrbix",irepeater:"IRepeater",isociety:"iSociety",iwiw:"iWiW",jamespot:"Jamespot",jappy:"Jappy Ticker",jolly:"Jolly",jumptags:"Jumptags",kaboodle:"Kaboodle",kaevur:"Kaevur",kaixin:"Kaixin Repaste",ketnooi:"Ketnooi",kindleit:"Kindle It",kledy:"Kledy",kommenting:"Kommenting",latafaneracat:"La tafanera",librerio:"Librerio",lidar:"LiDAR Online",link:"Copy Link",linkedin:"LinkedIn",linksgutter:"Links Gutter",linkshares:"LinkShares",linkuj:"Linkuj.cz",live:"Messenger",livejournal:"LiveJournal",lockerblogger:"LockerBlogger",logger24:"Logger24",mailto:"Email App",margarin:"mar.gar.in",markme:"Markme",mashant:"Mashant",mashbord:"Mashbord",me2day:"me2day",meinvz:"meinVZ",mekusharim:"Mekusharim",memonic:"Memonic",memori:"Memori.ru",mendeley:"Mendeley",meneame:"Men\u221a\xa9ame",misterwong:"Mister Wong",mixi:"Mixi",myvidster:"myVidster",moemesto:"Moemesto.ru",moikrug:"Moikrug",mrcnetworkit:"mRcNEtwORK",mymailru:"Mail.ru",myspace:"Myspace",n4g:"N4G",naszaklasa:"Nasza-klasa",netlog:"NetLog",netvibes:"Netvibes",netvouz:"Netvouz",newsmeback:"NewsMeBack",newstrust:"NewsTrust",newsvine:"Newsvine",nujij:"Nujij",odnoklassniki_ru:"Odnoklassniki",oknotizie:"OKNOtizie",openthedoor:"OpenTheDoor",orkut:"Orkut",oyyla:"Oyyla",packg:"Packg",pafnetde:"Pafnet",pdfmyurl:"PDFmyURL",pdfonline:"PDF Online",phonefavs:"PhoneFavs",pinterest_share:"Pinterest",planypus:"Planypus",plaxo:"Plaxo",plurk:"Plurk",pocket:"Pocket",posteezy:"Posteezy",print:"Print",printfriendly:"PrintFriendly",pusha:"Pusha",qrfin:"QRF.in",qrsrc:"QRSrc.com",quantcast:"Quantcast",qzone:"Qzone",raiseyourvoice:"Write Your Rep",reddit:"Reddit",rediff:"Rediff MyPage",redkum:"RedKum",researchgate:"ResearchGate",safelinking:"Safelinking",scoopat:"Scoop.at",scoopit:"Scoop.it",sekoman:"Sekoman",select2gether:"Select2Gether",sharer:"WebMoney",shaveh:"Shaveh",shetoldme:"She Told Me",sinaweibo:"Sina Weibo",skyrock:"Skyrock Blog",smiru:"SMI",socialbookmarkingnet:"BookmarkingNet",sodahead:"SodaHead",sonico:"Sonico",spinsnap:"SpinSnap",springpad:"springpad",startaid:"Startaid",startlap:"Startlap",storyfollower:"StoryFollower",studivz:"studiVZ",stuffpit:"Stuffpit",stumbleupon:"StumbleUpon",stumpedia:"Stumpedia",stylishhome:"FabDesign",sulia:"Sulia",sunlize:"Sunlize",supbro:"SUP BRO",surfingbird:"Surfingbird",svejo:"Svejo",symbaloo:"Symbaloo",taaza:"TaazaShare",tagza:"Tagza",taringa:"Taringa!",technerd:"Communicate",textme:"Textme",thefancy:"The Fancy",thefreedictionary:"FreeDictionary",thewebblend:"The Web Blend",thinkfinity:"Thinkfinity",thisnext:"ThisNext",thrillon:"Thrill On",throwpile:"Throwpile",toly:"to.ly",topsitelernet:"TopSiteler",transferr:"Transferr",tuenti:"Tuenti",tulinq:"Tulinq",tumblr:"Tumblr",tvinx:"Tvinx",twitter:"Twitter",twitthis:"TwitThis",typepad:"Typepad",upnews:"Upnews.it",urlaubswerkde:"Urlaubswerk",wanelo:"Wanelo",wishmindr:"WishMindr",viadeo:"Viadeo",virb:"Virb",visitezmonsite:"VisitezMonSite",vk:"VKontakte",vkrugudruzei:"vKruguDruzei",voxopolis:"VOX Social",vybralisme:"VybraliSME",w3validator:"HTML Validator",webnews:"Webnews",webshare:"WebShare",werkenntwen:"WerKenntWen",wirefan:"WireFan",windows:"Windows Gadget",wordpress:"WordPress",wowbored:"WowBored",wykop:"Wykop",xanga:"Xanga",xing:"XING",yahoobkm:"Y! Bookmarks",yahoomail:"Y! Mail",yammer:"Yammer",yardbarker:"Yardbarker",yigg:"Yigg",yiid:"Spreadly",yookos:"Yookos",yoolink:"Yoolink",yorumcuyum:"Yorumcuyum",youmob:"YouMob",yuuby:"Yuuby",zakladoknet:"Zakladok.net",ziczac:"ZicZac",zingme:"ZingMe"};function c(g,h){var i;if(f[g]){i=f[g];}else{if(a[g]){i=a[g];}else{i=(h?g:(g.substr(0,1).toUpperCase()+g.substr(1)));}}return(i||"").replace(/ /g," ");}if(!b.services){b.services={};}b.services.list=f;b.services.getName=c;b.services.exists=function(g){return!!f[g];};})(_a,_a.api,_a);(function(g,s,u){var v,d,c,k={},h,n,r,b,e=_a.util.each;function f(B){var D=new Array();o:for(var C=0;Cx.timestamp){return-1;}return 1;}function m(x,B,A){if(!A){A=window;}if(A[x]===_1||A[x]===""){A[x]=B;}return A[x];}function l(F){a();var B=j(),E=function(){var K=g.cookie.ssc.getServices(),I=g.ups||{},J;for(var H=0;H-1&&g.services.map.facebook!==_1)){G++;}if(B==x){C=1;}if(E[x]){delete E[x];}}e(E,function(H,I){A.push(I);});A.sort(p);for(D=0;D=300&&(aa||{}).parentServices){_a.util.each(aa.parentServices,function(ae,ad){aa.services_exclude+=((aa.services_exclude.length>1)?",":"")+ae;});}if(!X){X=[];}m("addthis_options_default",P.split(",").slice(0,11).join(",")+",more");m("addthis_options_rank",P.split(",").join(","));m("addthis_options",window.addthis_options_default);a();R=l(Q);addthis_options=(Q!=""?Q+",":"")+addthis_options;if(Q&&((addthis_options&&addthis_options.indexOf(Q)==-1)||(aa.services_compact&&aa.services_compact.indexOf(Q)==-1))){aa.services_compact=aa.services_compact?(Q+","+aa.services_compact):addthis_options;}addthis_options=f(addthis_options.split(",")).join(",");if(aa.services_compact){aa.services_compact=f(aa.services_compact.split(",")).join(",");}if(((window.addthis_ssh&&window.addthis_use_personalization&&R)||X.length||aa.services_exclude||addthis_exclude)){var D=addthis_options_rank.split(","),L=[],U,B=(aa.services_exclude||addthis_exclude||"").split(","),H={},T,Z=Q.join(","),ac=[],A={},x=0,K=11,J=0,F=aa.product||"",G=F.indexOf("ffext")>-1||F.indexOf("fxe")>-1;if(X.length&&addthis_options.indexOf(X[0].code)==-1){addthis_options+=","+X[0].code;}if(X.length&&X[0]){L.push(X[0].code);}for(W=0;W-1){x++;}}for(W=0;W=K){break;}U=Q[W];if(!A[U]&&!H[U]&&(g.services.map[U]!==_1||U.indexOf("facebook_")>-1)){A[U]=1;T=k[U]||new RegExp("(?:^|,)("+U+")(?:$|,)");k[U]=T;if(addthis_options.search(T)>-1){ac.push(U);addthis_options=addthis_options.replace(T,",").replace(",,",",");J++;}else{ac.push(U);}}}addthis_ssh=ac.join(",");addthis_options=(window.addthis_ssh?addthis_ssh+",":"")+addthis_options.replace(/[,]+/g,",").replace(/,$/,"").replace(/^,/,"").replace(/^more,|,more|^more$/,"");if(addthis_options.indexOf("email")>-1&&g.pub()===""&&!G){addthis_options=addthis_options.replace(/^email,|,email|^email$/,"");}while(addthis_options.split(",").length>11){addthis_options=addthis_options.split(",").slice(0,-1).join(",");}var S=g.util.fromKV(addthis_options.replace(/,|$/g,"=1&"));var Y=addthis_options.split(",").length;if(Y%2===0||Y<11){var W=Math.min(Y,11),M=P.split(","),I=Y;while((I<11||I%2===0)&&W15){F=F.substr(0,15)+"...";}B.innerHTML="Hi, "+F;B.style.padding="5px 10px";B.style.color="#87AC10";y.appendChild(B);E.href="#";E.innerHTML=k;E.onclick=function(){return v();};z.appendChild(E);y.appendChild(z);b.href="#";b.innerHTML=m;b.onclick=function(){return i(C);};G.appendChild(b);y.appendChild(G);}};j.signinAuth=function(){g();};function i(){var b=d.ce("IFRAME");b.src="//"+_atd+"user/logout?hidden=1";b.style.display="none";u.appendChild(b);a();j.authupdated=false;return false;}function g(){j.authupdated=false;_a.share.ocw("//"+_atd+"user/auth",710,620);setTimeout(function(){e();},1000);return false;}function v(){_a.share.ocw("//"+_atd+"user/settings",710,620);}function a(){n("","");}function n(x){t();j.user=x.replace("+"," ");var D=get("atic_auth");if(!D){D=d.ce("DIV");D.id="atic_auth";G=get("at15pf");if(G&&G.parentNode){G.parentNode.insertBefore(D,G);}}D.innerHTML="";var b;if(!x){var G=get("at15pf"),E=d.ce("DIV");if(G){G.style.top="0px";}b=d.ce("A");b.id="atic_signin";b.onclick=function(){return g();};b.onmouseover=function(){};b.onmouseout=function(){};E.id="at_auth";E.innerHTML=s;b.appendChild(E);}else{b=d.ce("DIV");b.id="at_auth";var B=d.ce("A"),y=d.ce("IMG"),C=d.ce("A"),z=d.ce("SPAN"),A=d.ce("A");x=x.replace("+"," ");if(x.length>15){x=x.substr(0,15)+"...";}B.id="atic_usersettings";B.onclick=function(){return v();};B.title="Signed in as "+x;b.appendChild(B);y.src=h;B.appendChild(y);C.id="atic_usersettings";C.onclick=function(){return v();};C.title="AddThis "+k;b.appendChild(C);z.innerHTML=k;C.appendChild(z);A.id="atic_usersignout";A.onclick=function(){return i("menu");};A.style.display="none";A.innerHTML=m;b.appendChild(A);D.onmouseover=function(){show("atic_usersignout");};D.onmouseout=function(){hide("atic_usersignout");};}D.appendChild(b);hide("atic_settings");var F=get("at3winssi");if(F){j.generateProfile(F,"at");}}function e(){if(j.authupdated){return;}_a.track.msg("cmd=auth");}function l(z){var x=get("atic_auth"),y=get("atic_signin"),b=get("at3winssi");if(!x||(z!==""&&y)||(z!==""&&b)){j.authupdated=true;n(z);}else{setTimeout(function(){e();},1000);}}c.util.extend(q.auth,{init:p});})(_a,_a.api,_a);(function(a,d,b){function c(e){var f=this,h=e||{};if(e instanceof Array){h={};for(var g=0;g=0,q=[1,2,3,4,11,12,13,14,15],h=B?q[Math.round((Math.random()*(q.length)-1))]:1,l,i=G.addthis_feed_url||"//q.addthis.com/feeds/1.0/",p="trending.json",c="viewsrnd.json",O="controlfeed.json",n="viewscf.json",P="views.json",J="views2.json",F="url.json",t="clusters.json",X="clusters2.json",L=e.util.each,z={feed:J,vectors:null},W={},N,K=e.data.Set;function a(w,d){if(w.score>d.score){return-1;}else{if(w.score==d.score){return 0;}else{return 1;}}}function Q(d){return(Math.max(0,(d||"").length-8)/9);}function f(w,d){if(!d&&z.ab){e.ab="per-"+z.ab;l=true;return z.ab;}if(d){h+=100;}if(e.ab=="-"&&!w){e.ab="per-"+h;l=true;}else{if(!l){h=parseInt(e.ab.split("-").pop(),10);}}return h;}function M(d){e.log.debug("u="+(z._used?1:0),"c=",d);if(!d){return;}z._set=1;z.ab=d["per-cell"];z.feed=d["per-feed"];if(d["per-vectors"]=="NONE"){z.vectors=[];}else{z.vectors=typeof(d["per-vectors"])=="string"?d["per-vectors"].split(","):d["per-vectors"];}}function C(Y,ac,w){var Z=Y.pubid||e.pub(),d=window.addthis_domain?_a.util.gUD(window.addthis_domain):"",ab=d||Y.domain,aa;_a.ajs([i,w||P,"?pubid=",Z,((Y.period)?"&period="+Y.period:""),((Y.service)?"&service="+Y.service:""),(ab?"&domain="+ab:""),(window.addthis_bt2?"&bt2="+e.bt2:""),((w.indexOf(F)>-1||w.indexOf(n)>-1)&&!!_a.dr?"&referer="+_a.dr:""),(w.indexOf("views")>-1?"&limit=25":""),"&callback=",e.util.scb("fds",Z,function(){clearTimeout(aa);ac.apply(this,arguments);})].join(""),1,true,true,null,true);aa=setTimeout(function(){ac([]);},4500);}function I(w){var Y=w.pubid||e.pub(),d=p;w._callback=w.callback;w.callback=function(Z){w._callback(u(Z,w));};if(w.type){d=d.replace("trending",w.type);}C(w,w.callback,d);}function S(d){d=d||[];if(d.length<2){return d;}var w=parseInt(d.length/2);var Z=d.slice(0,w);var Y=d.slice(w,d.length);return H(S(Z),S(Y));}function H(aa,Z){var w=[],Y,d;while(aa.length&&Z.length){Y=aa[0];d=Z[0];if(((!!Y.image&&!!Y.title)||(!d.image||!d.title))&&(!m(Y)||m(d))){w.push(aa.shift());}else{w.push(Z.shift());}}while(aa.length){w.push(aa.shift());}while(Z.length){w.push(Z.shift());}return w;}function v(d,Y,w){if(e.uid=="4e13435baa56415b"){e.log.debug.call(this,d,Y,w);}}function V(){return true;}function m(d){try{if(!d||!d.url){return false;}if(d.promoted==1){return false;}if(W[d.url]!==_1){return W[d.url];}W[d.url]=_a.track.hist.seenBefore(d.url);return W[d.url];}catch(w){}return false;}function j(d){e.log.debug("sfc",z);d.ab=z.ab;var w=(z.vectors||[]).length||0;if(!z.vectors||w===0){if(z.feed==n){x(d,n,J);}else{C(d,d.callback,z.feed);}}else{var Z=z.vectors[0],Y=z.vectors[1];if(w==1){if(e.bt2||(Z!=t&&Z!=X)){E(d,z.feed,Z);}else{C(d,d.callback,z.feed);}}else{if(e.bt2||(Z!=t&&Z!=X&&Y!=t&&Y!=X)){D(d,z.feed,z.vectors[0],z.vectors[1]);}else{if(Z==t||Z==X){E(d,z.feed,Y);}else{if(Y==t||Y==X){E(d,z.feed,Z);}else{E(d,z.feed,Z);C(d,d.callback,z.feed);}}}}}}function r(Z){if(_a.sfmp>0&&Z&&!Z._wait&&!z._set){Z._wait=1;setTimeout(function(){r(Z);},2500);return;}f();var Y=Z.pubid||e.pub(),w=h,d,ae=Math.floor(Z.total/2),aa="__feed_"+Y+"_"+h,af=false,ab=0;if(!N&&(V()||w==14)){N={features:e.ad.gcv()};}Z._callback=Z.callback;Z.callback=function(ag){ag=ag||[];if(e.bro.chr){ag=S(ag);}else{ag.sort(function(aj,ai){return!!aj.image?(!!ai.image?(!!aj.title?(!!ai.title?(m(aj)?(m(ai)?0:-1):(m(ai)?1:0)):-1):(!!ai.title?0:1)):-1):1;});}if(ag.length&&_a.uls&&window.JSON){af=localStorage.getItem(aa);if(af){try{af=JSON.parse(af);}catch(ah){}if(af.o){ab=af.o%10;af.o=ab+2;}else{af={o:2};}}else{af={o:2};}if(ab>0){while(ab-->0){arguments[0].push(arguments[0].shift());}}localStorage.setItem(aa,JSON.stringify(af));}Z._callback(u(ag,Z));};if(_a.sfmp>0&&z._set&&h<100){z._used=1;j(Z);return;}else{Z.ab=e.ab;}if(h>=100){d=p;w=h-100;}e.log.debug("rec; c="+h,"m="+w,"hbt="+(!!e.bt2),Z);switch(w){case 1:C(Z,Z.callback,c);break;case 2:C(Z,Z.callback,J);break;case 3:E(Z,d||J,F);break;case 4:if(e.bt2){E(Z,d||J,t);}else{E(Z,d||J,F);}break;case 5:Z.split=ae;R(Z,d||P,t,"merged");break;case 6:Z.split=ae;R(Z,P,t);break;case 7:Z.split=ae;R(Z,J,t,"merged");break;case 8:Z.split=ae;R(Z,J,t);break;case 9:var ac=0;try{ac=Q(e.bt2)||0;}catch(ad){}if(ac>=3){E(Z,J,t);}else{C(Z,Z.callback,J);}break;case 10:var ac=0;try{ac=Q(e.bt2)||0;}catch(ad){}if(ac>=3){E(Z,J,X);}else{C(Z,Z.callback,J);}break;case 11:x(Z,n,d||J);break;case 12:if(e.bt2){E(Z,J,X);}else{E(Z,d||J,F);}break;case 13:if(e.bt2){D(Z,d||J,X,F);}else{E(Z,d||J,F);}break;case 14:s(Z,d||J,N);break;case 15:C(Z,Z.callback,O);break;default:C(Z,Z.callback,d||J);break;}}function y(d){return((d||{}).pvector||{}).features||{};}function b(af,ac,w){var Y,aa=new K(),Z=0,ab=[],d,ad,w=typeof w=="function"?w:y,ae;L(ac.features||[],function(ah,ag){aa.add(ag.name,ag.weight);});L(af,function(ah,ag){Z=0;d=ag.url;ad=d.split("#").shift();if((e.share.links.canonical||"").indexOf(ad)>-1){return;}ae=w(ag);L(ae,function(aj,ai){if(typeof(Y=aa.get(ai.name))!="undefined"){Z+=(Y+ai.weight);}});af[ah].score=Z;d.score=Z;ab.push(ag);});ab.sort(a);return ab;}function E(Y,d,Z,w){D(Y,d,Z,null,w);}function s(Y,d,aa,w){var Z,ab;C(Y,function(ac){Z=ac;ab=T(Y,Z,aa,w);Y.callback(ab);},d);}function D(Y,ac,ab,aa,Z){var d,ae,ad,w;C(Y,function(af){d=af;if(ae!==_1&&(!aa||ad!==_1)){w=T(Y,d,ae,Z);if(aa){w=T(Y,w,ad,Z);}Y.callback(w);}},ac);C(Y,function(af){ae=af;if(d!==_1&&(!aa||ad!==_1)){w=T(Y,d,ae,Z);if(aa){w=T(Y,w,ad,Z);}Y.callback(w);}},ab);if(aa){C(Y,function(af){ad=af;if(d!==_1&&ae!==_1){w=T(Y,d,ae,Z);w=T(Y,w,ad,Z);Y.callback(w);}},aa);}}function R(w,Z,aa,d){var Y=5;if(w.split!==_1){w.split=Y;}E(w,Z,aa,d);}function x(aa,Z,w){var Y,d;C(aa,function(ab){Y=ab;if(ab&&ab.length>1){aa.callback(ab);}else{if(d&&d.length>1){aa.callback(d);}}},Z);C(aa,function(ab){d=ab;if(Y&&Y.length<=1){aa.callback(ab);}},w);}function T(Y,d,aa,w){if(!d||!d.length){e.log.debug("no url data; returning []");return[];}if(!aa||!aa.features||!aa.features.length){e.log.debug("no vector data; returning urls");return d;}var ab=b(d,aa,y),ad=ab,Z=Y.split;if(Z){ad=[];var ac=w?ab:d;second=w?d:ab;ad=ad.concat(ac.slice(0,Z));ad=ad.concat(second.slice(0,Z));}return ad;Y.callback(ad);}function g(d){if(!d.ab){d.ab=e.ab;}if(!d.bt){d.bt=e.bt2;}return function(w){L(w,function(Z,Y){d[Z]=Y;});return o(d);};}function u(d,w,Z){if(!Z||typeof(Z)!=="function"){Z=o;}if(!w.total){w.total=d.length;}var Y=0;L(d,function(aa,ab){w.pos=Y++;w.url=ab.url;ab.url=Z(w);ab.title=ab.title||"";});return d;}function o(Z){var Y=Z.url,d=Z.pco,ac=Z.total,ae=Z.pos,w=Z.bt,ad=Z.ab||"-",aa="cfd-1.0";if(Y&&Y.indexOf("at_pco")>-1){Y=(d?Y.replace(/at_pco=(.*)&/,"at_pco="+d+"&"):Y);if(Y.indexOf("at_ab")>-1){if(ad!="-"){Y=Y.replace(/at_ab=(.*)&/,"at_ab="+ad+"&");}}else{Y+="&at_ab="+(Z.ab||e.ab);}if(Y.indexOf("at_pos")>-1){if(ae!==_1){Y=Y.replace(/at_pos=([0-9]+)/,"at_pos="+ae);}}else{Y+="&at_pos="+(ae||0);}if(Y.indexOf("at_tot")>-1){if(ac!==_1){Y=Y.replace(/at_tot=([0-9]+)/,"at_tot="+ac);}}else{Y+="&at_tot="+(ac||0);}if(Y.indexOf("si=")===-1){Y+="&at_si="+_a.sid;}}return Y;}e.feeds={ab:f,_ad:V,configure:M,get:C,recommend:r,trend:I,decorator:g};e.dctu=o;})(_a,_a.api,_a);var w=window,ac=w.addthis_config||{},css=new _a.resource.Resource("widgetcss",_atc.rsrcs.widgetcss,function(){return true;}),_8c3=new _a.resource.Resource("widgetIE67css",_atc.rsrcs.widgetIE67css,function(){return true;}),_8c4=new _a.resource.Resource("widget32css",_atc.rsrcs.widget32css,function(){return true;});if(w.addthis&&w.addthis.timer){w.addthis.timer.core=(new Date()).getTime();}function main(){if(w.addthis&&w.addthis.timer){w.addthis.timer.main=(new Date()).getTime();}try{if(_atc.xol&&!_atc.xcs&&ac.ui_use_css!==false){css.load();if(_a.bro.ie6||_a.bro.ie7){_8c3.load();}if(_a.bro.ipa){_8c4.load();}}var a=_a,msi=a.bro.msi,hp=0,_8c8=w.addthis_config||{},dt=d.title,dr=(typeof(a.rdr)!=="undefined")?a.rdr:(d.referer||d.referrer||""),du=dl?dl.href:null,dh=dl.hostname,_8cd=du,_8ce=0,al=(_a.lng().split("-")).shift(),_8d0=_a.track.eop(dl,dr),cvt=[],nabc=!!a.cookie.rck("nabc"),cfc=_8d0.cfc,ab=_8d0.ab,pos=_8d0.pos?parseInt(_8d0.pos,10):null,tot=_8d0.tot?parseInt(_8d0.tot,10):null,rsiq=_8d0.rsiq,rsi=_8d0.rsi,rxi=_8d0.rxi,rsc=_8d0.rsc.split("&").shift().split("%").shift().replace(/[^a-z0-9_]/g,""),gen=_8d0.gen,fuid=_8d0.fuid,csi=_8d0.csi,_8de,ifr,_8e0=_atc.rsrcs.sh+"#",data,_8e2=function(){if(!_a.track.pcs.length){_a.track.apc(w.addthis_product||("men-"+_a.ver()));}data.pc=_a.track.pcs.join(",");},ljep=w.ljep||false,_8e4=a.pub(),_8e5=[2,3,4,12];if(dl&&dl.hash&&dl.hash.indexOf("sky_ab=1")>-1){a.sfmp=1;}if((du||"").indexOf(_atr)===-1){a.cookie.view.update(true);}if(rsc==="tweet"){rsc="twitter";}_8d0.rsc=rsc;if(w.addthis_product){_a.track.apc(addthis_product);if(addthis_product.indexOf("fxe")===-1&&addthis_product.indexOf("bkm")===-1){_a.track.spc=addthis_product;}}var l=_a.share.links.canonical;if(l){if(l.indexOf("http")!==0){_8cd=(du||"").split("//").pop().split("/");if(l.indexOf("/")===0){_8cd=_8cd.shift()+l;}else{_8cd.pop();_8cd=_8cd.join("/")+"/"+l;}_8cd=dl.protocol+"//"+_8cd;}else{_8cd=l;}_a.usu(0,1);}_8cd=_8cd.split("#{").shift();a.igv(_8cd,d.title||"");if(_8cd){_a.share.links.canonical=_8cd;}var _8e7=addthis_share.view_url_transforms||addthis_share.track_url_transforms||addthis_share.url_transforms||{};_8e7.defrag=1;if(_8e7){_8cd=_a.track.mgu(_8cd,_8e7);}try{var atsp=(addthis_share||{}).passthrough||{};if(!(atsp.pinterest_share||{}).media){var tags=_a.ad.og(),_8ea={},og=typeof(tags)==="string"?_a.util.fromKV(tags):tags;atsp={};if(og.image||_a.share.links.image_src){if(!w.addthis_share){w.addthis_share={};}addthis_share=w.addthis_share;addthis_share.passthrough=atsp=addthis_share.passthrough||{};atsp.pinterest_share=_8ea=atsp.pinterest_share||{};_8ea.media=og.image||_a.share.links.image_src;_8ea.url=_8ea.url||og.url||w.location.href;_8ea.description=_8ea.description||og.title||addthis_share.description||addthis_share.title||"";}}}catch(e){}if(rsi){rsi=rsi.substr(0,8)+fuid;}if(a.bro.mod===-1){var m=document.compatMode;if(m){var md=1;if(m==="BackCompat"){md=2;}else{if(m==="CSS1Compat"){md=0;}}a.bro.mode=md;if(a.bro.msi){a.bro.mod=md;}}}a.dr=a.tru(dr,"fr");a.du=a.tru(_8cd,"fp");a.dt=dt=w.addthis_share.title;a.smd={rsi:rsi,rxi:rxi,gen:gen,rsc:rsc};w.addthis_share.smd=a.smd;if(a.upm){w.addthis_share.smd.dr=a.dr;}if(a.upm){w.addthis_share.smd.sta=a.track.sta();}a.cb=a.ad.cla();a.kw=(a.cb!==1?a.ad.kw():"");a.dh=dl.hostname;a.ssl=du&&du.indexOf("https")===0?1:0;a.ab=ab||w.addthis_ab||"-";w.addthis_config=w.addthis_config||{};if(!w.addthis_config.ignore_server_config&&_8e4){if(_a.upm&&!a.bro.ie6&&!a.bro.ie7){_a.ipc=true;var _8ee="__atpro_"+_8e4,_8ef="",_8f0=false,_8f1=false,_8f2=_a.uls,_8f3={cfs:true},_8f4=function _8f4(_8f5){_a.isProUser=true;if(!_8f5.sponsored&&(_8f4.called||_8f1)){return;}_a.ab="per-"+_8e5[Math.round((Math.random()*(_8e5.length-1)))];_8f5.cfs=true;_6.layers(_8f5,_8f3);_8f4.called=true;},_8f6=function(){if(_6.layers.length){_6.layers({cfs:true});}else{_a.ipc=false;}},_8f7=function(){_8f1=true;if(!_8f0){_8f6();}};if(_8f2){_8ef=localStorage.getItem(_8ee);}if(_8ef&&_8ef!=="false"){try{_8ef=JSON.parse(_8ef);}catch(e){}_8f4(_8ef);_8ef="false";}else{if(_8ef==="false"){_8f6();}else{setTimeout(_8f7,5000);}}_a.ed.addEventListener("addthis.pro.init",function(e){_8f0=true;if(e.data&&e.data._default){if(!_8ef||_8ef==="false"||e.data.sponsored===1){_8f4(e.data);}if(_8f2&&!e.data.sponsored){localStorage.setItem(_8ee,JSON.stringify(e.data));}}else{if(_8ef!=="false"){_8f6();}if(_8f2){localStorage.setItem(_8ee,"false");}}});}else{var _8f9="__atpro_"+_8e4,_8fa=_a.cookie.rck(_8f9),_8fb=new Date(),_8fc={pubid:_8e4},cb=function(_8fe){_8fb.setDate(_8fb.getDate()+7);if(_8fe&&_8fe._default){_a.cookie.sck(_8f9,"true",0,1,_8fb);}else{_a.cookie.sck(_8f9,"false",0,1,_8fb);}_8fe.cfs=true;_6.layers(_8fe,"cfs");};if(_8fa!=="false"){_a.ipc=true;a.feeds.get(_8fc,cb,"config.json");}}}data={iit:(new Date()).getTime(),tmr:_50((w.addthis||{}).timer||{}),cb:a.cb,cdn:_atc.cdn,chr:_a.ad.gch(),kw:a.kw,ab:a.ab,dh:a.dh,dr:a.dr,du:a.du,dt:dt,dbg:_a.log.level,md:a.bro.mode,cap:_50({tc:_8c8.data_track_textcopy?1:0,ab:_8c8.data_track_addressbar?1:0}),inst:a.inst,vcl:a.cookie.view.cla(),jsl:a.track.jsl(),prod:a.track.prod(),lng:a.lng(),ogt:_a.ad.gog().join(","),pc:w.addthis_product||"men",pub:a.pub(),ssl:a.ssl,sid:_a.track.ssid(),srpl:_atc.plmp,srcs:_atc.cscs,srd:_atc.damp,srf:_atc.famp,srx:_atc.xamp,ver:_a.ver(),xck:_atc.xck||0,xtr:_atc.xtr||0,og:_a.ad.og(),aa:0,csi:csi};if(_atc.noup){data.noup=1;}if(a.dcp==Number.MAX_VALUE){data.dnp=1;}if(a.pixu){data.pixu=a.pixu;}if(a.trl.length){data.trl=a.trl.join(",");}if(a.rev){data.rev=a.rev;}data.ct=a.ct=(_8c8.data_track_clickback||_8c8.data_track_linkback||_a.track.ctp(data.pc,_8c8))?1:0;if(a.prv){data.prv=_50(a.prv);}if(rsc){data.sr=rsc;}_a.track.ssc(rsc);if(ljep){data.ljep=ljep;}if(a.vamp>=0&&!a.sub){if(cfc){cvt.push(a.track.fcv("plv",Math.round(1/_atc.vamp)));cvt.push(a.track.fcv("typ","lnk"));if(!isNaN(pos)){cvt.push(a.track.fcv("ttpos",pos));}if(!isNaN(tot)){cvt.push(a.track.fcv("ttnl",tot));}if(csi){cvt.push(a.track.fcv("csi",csi));}cvt.push(a.track.fcv("pco",("string"===typeof cfc)?cfc:"cfd-1.0"));cvt.push(a.track.fcv("pur",a.track.mgu(_8cd,{defrag:1})));if(a.dr){data.pre=a.track.mgu(a.dr,{defrag:1});}data.ce=cvt.join(",");}else{if(rsi&&(fuid!=a.ad.gub())){cvt.push(a.track.fcv("plv",Math.round(1/_atc.vamp)));cvt.push(a.track.fcv("rsi",rsi));cvt.push(a.track.fcv("gen",gen));cvt.push(a.track.fcv("abc",1));cvt.push(a.track.fcv("fcu",a.ad.gub()));cvt.push(a.track.fcv("rcf",dl.hash));data.ce=cvt.join(",");_8ce="addressbar";_8d0.rsc=rsc="addressbar";}else{if(rxi||rsiq||rsc){cvt.push(a.track.fcv("plv",Math.round(1/_atc.vamp)));if(rsc){cvt.push(a.track.fcv("rsc",rsc));}if(rxi){cvt.push(a.track.fcv("rxi",rxi));}else{if(rsiq){cvt.push(a.track.fcv("rsi",rsiq));}}if(rsiq||rxi){cvt.push(a.track.fcv("gen",gen));}data.ce=cvt.join(",");_8ce=rsc||"unknown";}}}}a.track.ts.reset(_8d0);if(a.feeds._ad()){a.track.hist.log();}if(_8ce){if(a.bamp>=0){data.clk=1;if(a.dcp!=Number.MAX_VALUE){a.dcp=data.gen=a.ad.type.CLICK;}}_a.ed.fire("addthis.user.clickback",w.addthis||{},{service:_8ce,hash:_a.hash});}if(ab=="per-5"||ab=="per-6"||ab=="per-0"||ab=="per-7"||ab=="per-8"||ab=="per-9"||ab=="per-10"){ab=_a.ab="per-"+a.feeds.ab(true);}if(!w.at_noxld){data.xld=1;}if(a.upm){data.xd=1;}if(!nabc&&w.history&&typeof(history.replaceState)=="function"&&(!_a.bro.chr||_a.bro.chb)&&(_8c8.data_track_addressbar||_8c8.data_track_addressbar_paths)&&((du||"").split("#").shift()!=dr)&&(du.indexOf("#")==-1||rsi||(_8d0.hash&&rxi)||cfc)){var path=dl.pathname||"",_900,_901=path!="/";if(_8c8.data_track_addressbar_paths){_901=0;for(var i=0;i<_8c8.data_track_addressbar_paths.length;i++){_900=new RegExp(_8c8.data_track_addressbar_paths[i].replace(/\*/g,".*")+"$");if(_900.test(path)){_901=1;break;}}}if(_901&&(!rsi||a.util.ioc(rsi,5))){_8de=_a.track.cur(dl.href.split("#").shift(),null,_a.track.ssid());history.replaceState({d:(new Date()),g:gen},d.title,_8de);data.fcu=_8de.split("#.").pop();}}if(w.addthis&&w.addthis.timer){w.addthis.timer.ifr=(new Date()).getTime();if(data.tmr){data.tmr+="&ifr="+w.addthis.timer.ifr;}}if(a.aa===1&&w.postMessage){data.srd=1;data.aa=1;_a.ed.addEventListener("addthis.layers.warning.show",function(e){if(e.data&&e.data.alertId){_a.swl=e.data.alertId;}});}_8e2();if(dl.href.indexOf(_atr)==-1&&!a.sub){if(a.upm){if(_a.bro.ffx){ifr=a.track.ctf();ifr.src=_8e0;_a.track.qtp(data);}else{if(_a.bro.ie9){setTimeout(function(){ifr=a.track.ctf(_8e0+_50(data),true);a.track.stf(ifr);},0);}else{ifr=a.track.ctf();ifr.src=_8e0+_50(data);a.track.stf(ifr);}}}else{ifr=a.track.ctf();ifr.src=_8e0+_50(data);a.track.gtf().appendChild(ifr);a.track.stf(ifr);}}_6._pmh.flushed=1;_6._pmh.flush(_a.pmh,_a);if(w.addthis_language||ac.ui_language){a.alg();}if(a.plo.length>0){a.jlo();}}catch(e){_a.log.debug("lod",e);}}w._ate=a;w._adr=r;a._ssc=a._ssh=[];a.dat={};a._rec.push(function(data){var rdy=a.dat.rdy,s,i;_1c(data,function(k,v){a.dat[k]=v;});if(data.rdy&&!rdy){a.xfr=1;a.track.xtp();}if(data.ssc){a._ssc=data.ssc;}if(data.sshs){data.sshs=data.sshs.replace(/\bpinterest\b/,"pinterest_share");s=w.addthis_ssh=_duc(data.sshs);a.gssh=1;a._ssh=s.split(",");_a.ed.fire("addthis-internal.data.ssh",{},{ssh:s});}if(data.uss){data.uss=data.uss.replace(/\bpinterest\b/,"pinterest_share");var u=a._uss=_duc(data.uss).split(",");if(w.addthis_ssh){var seen={},_90c=[];u=u.concat(a._ssh);for(i=0;i-1){var ckv=_5f(d.cookie,";");a._rec[a._rec.length-1](ckv);}var _911={},_293=_a.util.gsp("addthis_widget.js");if(typeof(_293)=="object"){if(_293.provider){_911={provider:_a.mun(_293.provider_code||_293.provider),auth:_293.auth||_293.provider_auth||""};if(_293.uid||_293.provider_uid){_911.uid=_a.mun(_293.uid||_293.provider_uid);}if(_293.logout){_911.logout=1;}_a.prv=_911;}if(_293.headless){_atc.xcs=1;}if(_293.dnp){_a.dcp=Number.MAX_VALUE;}if(_293.dnt){_atc.xtr=1;}_a.util.pae(_293);_a.util.pas(_a.util.pae);if(_293.pubid||_293.pub||_293.username){w.addthis_pub=_duc(_293.pubid||_293.pub||_293.username);}if(w.addthis_pub&&w.addthis_config){w.addthis_config.username=w.addthis_pub;}if(_293.domready){_atc.dr=1;}if(_293.onready&&_293.onready.match(/[a-zA-Z0-9_\.\$]+/)){try{_a.onr=_a.evl(_293.onready);}catch(e){w.console&&console.log("addthis: onready function ("+_293.onready+") not defined",e);}}if(_293.async){_atc.xol=1;}}if(_293.delayupgrade){_atc.noup=1;}else{if(_atc.ver>=152||(w.addthis_conf||{}).ver>=152){_atc.ver=300;}}_a.ed.fire("addthis-internal.params.loaded",{},{geo:a.geo});if((w.addthis_conf||{}).xol){_atc.xol=1;}if(_atc.ver===120){var rc="atb"+_a.util.cuid(),_913=_a.util.gst("addthis_widget"),span=d.ce("span");span.id=rc;_913.parentNode.appendChild(span);_a.igv();_a.lad(["span",rc,addthis_share.url||"[url]",addthis_share.title||"[title]"]);}if(w.addthis_clickout){_a.lad(["cout"]);}if(!_atc.xol&&!_atc.xcs&&ac.ui_use_css!==false){css.load();if(_a.bro.ie6||_a.bro.ie7){_8c3.load();}if(_a.bro.ipa){_8c4.load();}}}catch(e){_a.log.error("main",e);}_d1.bindReady();_d1.append(main);(function(e,g,i){var b;var a=false,m=_a.upm&&(w.postMessage&&(typeof w.postMessage=="function"||(typeof(w.postMessage||{}).call=="function"&&typeof(w.postMessage||{}).apply=="function"))&&!_a.bro.ie6&&!_a.bro.ie7),h=false;function f(n){if(_a.unj&&!_a.bro.msi){return JSON.stringify(n);}else{return _a.util.rtoKV(n,"&&","==");}}function j(p){if(p&&typeof(p)=="string"){if(_a.unj&&p.indexOf("{")===0){try{return JSON.parse(p);}catch(n){return _a.util.rfromKV(p);}}else{return _a.util.rfromKV(p,"&&","==");}}else{return p;}}function c(o){var n;if(!a||o.origin.slice(-12)==".addthis.com"){if(!o.data){return;}n=j(o.data);n.origin=o.origin;k(n);}}function k(n){if(n.addthisxf){_a.ed.fire(n.addthisxf,n.target||n.payload,n.payload);}}_41(_a,{xf:{upm:m,listen:function(){if(h){return;}if(m){if(l.href.indexOf(".addthis.com")==-1){a=true;}if(w.attachEvent){w.attachEvent("onmessage",c,false);d.attachEvent("onmessage",c,false);}else{w.addEventListener("message",c,false);}window.addthis._pml.push(c);}h=true;},send:function(o,n,p){if(m){setTimeout(function(){o.postMessage(f({addthisxf:n,payload:p}),"*");},0);}}}});})(_a,_a.api,_a);(function(a,d,b){d.HIGH=3;d.MED=2;d.LOW=1;d.ASC=1;d.DSC=d.DESC=0;function c(j){var e=j||[],f=e.length===0?{}:h(e),m=e;e._map=f;function g(n){e.sort(function(p,o){return i(p,o,d.ASC,n);});}function k(n){e.sort(function(p,o){return i(p,o,d.DSC,n);});}function i(p,n,r,s){var q=p[s],o=n[s];if(typeof(q)=="string"&&!isNaN(parseInt(q,10))){q=parseInt(q,10);o=parseInt(o,10);if(r){return q-q;}return q-o;}if(q>o){return r?1:-1;}else{if(q==o){return 0;}}return r?-1:1;}function h(){var o={};for(var n=0;n30){throw new Error("Service code must be between 5 and 30 characters.");}else{if(_96c.search(/^[a-zA-Z0-9_]+$/)==-1){throw new Error("Service code must consist entirely of letters, numbers and underscores.");}}return true;}_6.logShare=function(url,_96e,_96f,_970){var c=_970||addthis_config,s=_96f||addthis_share;c.product="hdl-"+_a.ver();s.imp_url=0;var url=url||(_96f&&_96f.url)||addthis_share.url,ct=_a.track.dcu(url);if(ct.rsc&&!_96e){_96e=ct.rsc;}if(validateServiceCode(_96e)){s.url=url;_a.share.track(_96e,0,s,c);}};_6.addClickTag=function(url,_975,_976,_977){var url=url||_976&&_976.url||addthis_share.url;if(validateServiceCode(_975)){url=_a.track.cur(_a.track.cof(url),_975);}return url;};})();if(!window.addthis){window.addthis={};}_6.user=(function(){var a=_a,at=_6,_97a=1000,u={},_97b=0,_97c=0,_97d={tags:a.cookie.tag.get()},_97e=false,_97f=a.data.OrderedSet,_980=_a.data.Set,_981;function apiReduce(fn,acc){return a.reduce(["getID","getGeolocation","getServiceShareHistory"],fn,acc);}function reply(key,def){return function(fn){setTimeout(function(){fn(a[key]||def);},0);};}function setup(data){if(_97b){return;}if(!data||!data.uid){return;}if(_981!==null){clearTimeout(_981);}_981=null;_97b=1;apiReduce(function(_988,name,i){u[name]=u[name].queuer.flush(reply.apply(at,_988[i]),at);return _988;},[["uid",""],["geo",""],["_ssh",[]]]);}function fakeData(){var data={uid:"x",geo:{},ssh:"",ups:""};_97c=1;setup(data);}_981=setTimeout(fakeData,_97a);a._rec.push(setup);function arrmap(_98c){var map={};for(var i=0;i<_98c.length;i++){map[_98c[i]]=_98c[i];}return map;}function isLocatedIn(desc){return a.util.geo.isin(desc,a.geo);}function hasInterest(desc){return _97d.interests.iskey(desc);}function hasTag(tag){return _97d.tags.iskey(tag);}function hasTags(tags){return _97d.tags.hasKeys(tags);}function ready(fn){if(!_a.uud){_a.ed.fire("addthis-internal.api",window.addthis||{},{call:"rdy"});}_a.uud=1;if(_97b&&(_a.jlng()=="en"||window.addthis_translations)){var _994=_a.share.services.init(window.addthis_config),opts=(window.addthis_options||"").replace(",more","").split(",");if(isOptedOut()){fn(_97d);return;}var _996=[];var tags=a.cookie.tag.get();for(var k in _a.bti){_996.push(_a.bti[k]);}_97d.interests=new _97f(_996);_97d.tags=new _97f(tags);var _999=new _97f();_a.util.each(a._uss,function(k,name){_999.addOne({name:name,score:_6.HIGH});});_a.util.each(a._ssc,function(name,val){_999.addOne({name:name,score:val});});_97d.services=_999;_97d.activity={};_97d.activity.social=_a.bts;_97d.activity.view=_a.vts;_97d.source=getSource();api.location=_97d.location=_a.geo||{};_97d.location.contains=isLocatedIn;if(fn){fn(_97d);}_a.ed.fire("addthis.user.data",window.addthis||{},{});}else{if(_a.jlng()!=="en"&&!window.addthis_translations){_a.ed.addEventListener("addthis.i18n.ready",function(){ready(fn);});_a.alg();}else{setTimeout(function(){ready(fn);},250);}}}function getUserData(fn){ready(fn);}u.getData=getUserData;u.getPreferredServices=function(fn){if(_a.jlng()=="en"||window.addthis_translations){var _9a0=_a.share.services.init(window.addthis_config),opts=(window.addthis_options||"").replace(",more","").split(",");fn(opts);}else{_a.ed.addEventListener("addthis.i18n.ready",function(){var _9a2=_a.share.services.init(window.addthis_config),opts=(window.addthis_options||"").replace(",more","").split(",");fn(opts);});_a.alg();}};function isReturning(){return(_a.cookie.view.cla())>0;}function tag(tag){var tags=tag;if(typeof(tags)=="string"){tags=tags.split(",");}_a.cookie.tag.add(tags);}function measuredCallFactory(code,_9a6){var rv=function(a,b,c){var args=Array.prototype.slice.call(arguments);_a.ed.fire("addthis-internal.api",window.addthis||{},{call:code});return _9a6.apply(this,args);};return rv;}function measureCall(code){_a.ed.fire("addthis-internal.api",window.addthis||{},{call:code});}function getInterests(){measureCall("gti");return _97d.interests;}function getServices(){measureCall("gts");return _97d.services;}function getSource(){measureCall("gtt");return a.track.ts.get();}function getLocation(){measureCall("gtl");return _97d.location;}function isOptedOut(){return a.uid=="0000000000000000";}function isUserOf(_9ad){return((a._ssh&&a._ssh.indexOf(_9ad)>-1)||(a._ssc&&a._ssc[_9ad]));}function isSocial(_9ae){var _9af=getSource();if(_9af.type=="social"){if(typeof(_9ae)=="string"){_9ae=_9ae.split(",");}if(_9ae instanceof Array){var _9b0={};for(var i=0;i<_9ae.length;i++){_9b0[_9ae[i]]=1;}if(!_9b0[_9af.service]){return false;}}return true;}return false;}function isSearch(_9b2){var _9b3=getSource(),i;if(_9b3.type=="search"){if(typeof(_9b2)=="string"){_9b2=_9b2.split(",");}if(_9b2 instanceof Array){var _9b5={};for(i=0;i<_9b2.length;i++){_9b5[_9b2[i]]=1;}if(_9b3.terms&&_9b3.terms.length){for(i=0;i<_9b3.terms.length;i++){if(!_9b5[_9b3.terms[i]]){return false;}}}}return true;}return false;}var api={ready:ready,isReturning:isReturning,isOptedOut:measuredCallFactory("ioo",isOptedOut),isUserOf:measuredCallFactory("iuf",isUserOf),hasInterest:hasInterest,hasTag:hasTag,hasTags:hasTags,isLocatedIn:isLocatedIn,tag:tag,interests:getInterests,services:getServices,location:getLocation};_6.session={source:getSource,isSocial:measuredCallFactory("isl",isSocial),isSearch:measuredCallFactory("ish",isSearch)};_a.extend(u,api);return apiReduce(function(o,name){o[name]=(new at._Queuer(name)).call;return o;},u);})();if(!window.addthis.osta){_6.osta=1;window.addthis.cache={};window.addthis.ed=_a.ed;window.addthis.init=function(){_d1.onReady();_6.ready&&_6.ready();};window.addthis.cleanup=function(){_a.util.each(((window.addthis||{})._pml||[]),function(i,_9ba){_a.util.unlisten(window,"message",_9ba);});};_a.extend(window.addthis.util,{getServiceName:_a.services.getName});window.addthis.addEventListener=_a.util.bind(_a.ed.addEventListener,_a.ed);window.addthis.removeEventListener=_a.util.bind(_a.ed.removeEventListener,_a.ed);_a.extend(_6,_a.api);var d=document,_9bb=0,u=_1,w=window,_9bc={},_8c4=new _a.resource.Resource("widget32css",_atc.rsrcs.widget32css),_9bd=new _a.resource.Resource("widget20css",_atc.rsrcs.widget20css),_9be=false,_9bf=false,_9c0,_9c1,_9c2,_9c3,_9c4={},_9c5={},body=null,_9c7=_a.util.select,_9c8=[],_9c9=[],_9ca=[],v,_9cb={rss:"Subscribe"},_9cc={tweet:"Tweet",pinterest_share:"Pinterest",email:"Email",mailto:"Email",print:"Print",favorites:"Favorites",twitter:"Tweet",digg:"Digg",more:"View more services"},json={email_vars:1,passthrough:1,modules:1,templates:1,services_custom:1},_9ce={feed:1,more:_a.ver()<300,email:_a.ver()<300,mailto:1},_9cf={feed:1,email:_a.ver()<300,mailto:1,print:1,more:!_a.bro.ipa&&_a.ver()<300,favorites:1},_9d0={print:1,favorites:1,mailto:1},_9d1={pinterest_pinit:(_a.ver()<300),pinterest_share:(_a.ver()<300)},_9d2={email:_a.ver()>=300,more:_a.ver()>=300};_a.ed.addEventListener("addthis-internal.data.ssh",function(){_9c3=1;});_a.ulg(function(_9d3){_9cc.email=_9cc.mailto=_9d3[0][4];_9cc.print=_9d3[0][22];_9cc.favorites=_9d3[0][5];_9cc.more=_9d3[0][2];while(_9ca.length>0){v=_9ca.pop();if(v&&v.link&&v.title){v.link.title=_9cc[v.title]||v.link.title;}}});function unaccent(s){if(s.indexOf("&")>-1){s=s.replace(/&([aeiou]).+;/g,"$1");}return s;}function mrg(o,n){if(n&&o!==n){for(var k in n){if(o[k]===u){o[k]=n[k];}}}}function addIEHoverFix(){if(_a.bro.msi&&!d.getElementById("at300bhoveriefilter")){var head=d.getElementsByTagName("head")[0],_9d9=d.ce("style"),_9da=d.createTextNode(".at300b:hover,.at300bs:hover {filter:alpha(opacity=80);}");_9d9.id="at300bhoveriefilter";_9d9.type="text/css";if(_9d9.styleSheet){_9d9.styleSheet.cssText=_9da.nodeValue;}else{_9d9.appendChild(_9da);}head.appendChild(_9d9);}}_6.addEvents=function(o,ss,au){if(!o){return;}var _9de=o.onclick||function(){},_9df=_9d0[ss]?function(){_a.share.track(ss,0,o.share,o.conf);}:function(){_a.share.notify(ss,o.share,o.conf,o);};if(o.conf.data_ga_tracker||addthis_config.data_ga_tracker||o.conf.data_ga_property||addthis_config.data_ga_property){o.onclick=function(){_a.gat(ss,au,o.conf,o.share);_9df();return _9de();};}else{o.onclick=function(e){if(!_9d1[ss]){_9df();}return _9de(e);};}};function check32(o,_9e2){if(_9be&&!_9e2){return true;}var opc=_a.util.parent(o,".addthis_toolbox");_9be=((opc.className||"").search(/32x32/i)>-1||(o.className||"").search(/32x32/i)>-1);return _9be;}function check20(o,_9e5){if(_9bf&&!_9e5){return true;}var opc=_a.util.parent(o,".addthis_toolbox");_9bf=((opc.className||"").search(/20x20/i)>-1||(o.className||"").search(/20x20/i)>-1);return _9bf;}function registerProductCode(o){var opc=(o.parentNode||{}).className||"",pc=o.conf&&o.conf.product&&opc.indexOf("toolbox")==-1?o.conf.product:"tbx"+(o.className.indexOf("32x32")>-1||opc.indexOf("32x32")>-1?"32":"")+"-"+_a.ver();if(pc.indexOf(32)>-1){_9be=true;}_a.track.apc(pc);return pc;}function rpl(o,n){var r={};for(var k in o){if(n[k]){r[k]=n[k];}else{r[k]=o[k];}}return r;}function _makeButton(w,h,alt,url){var img=document.ce("img");img.width=w;img.height=h;img.border=0;img.alt=alt;img.src=url;return img;}function _parseThirdPartyAttributes(el,_9f4){var key,attr=[],rv={},len=Math.min((el.attributes||[]).length||0,160),_9f9=_9f4.replace(/:/g,"-");if(isNaN(len)){return rv;}for(var i=0;i-1&&(o.conf.product||"").indexOf("men")===0){o.conf.product="tbx"+(_a31.className.indexOf("32x32")>-1?"32":(_a31.className.indexOf("20x20")>-1?"20":""))+"-"+_a.ver();_a.track.apc(o.conf.product);}if(ss&&ss!=="more"){o.conf.product=registerProductCode(o);}if((!o.conf||(!o.conf.ui_click&&!o.conf.ui_window_panes))&&!_a.bro.ipa){_a.maf=_a.maf||{};_a.maf.home=this;_a.maf.key=null;_a.maf.shift=null;if(_a14){o.onfocus=o.onmouseover=function(){_a.maf.sib=this.nextSibling;while(_a.maf.sib&&_a.maf.sib.nodeType==3&&_a.maf.sib.nextSibling){_a.maf.sib=_a.maf.sib.nextSibling;}if(!_a.maf.sib||_a.maf.sib.nodeType==3){var el=this.parentNode;if(!el){el=document.body.firstChild||document.body;while(el.nodeType==3&&el.nextSibling){el=el.nextSibling;}}else{while(el.nextSibling&&el.nodeType==3){el=el.nextSibling;}}_a.maf.sib=el;}_a.maf.sib.onfocus=function(){_a.maf.sib.tabIndex="";};return _a14(this,this.conf,this.share);};}if(_a15){o.onmouseout=function(){return _a15(this);};}if(_a16){o.onclick=function(){return _a16(o,(this.conf||o.conf),(this.share||o.share));};}if(_a15||_a16){o.onkeypress=o.onkeydown=function(e){if(!e){var e=window.event;}if(e.shiftKey){_a.maf.shift=true;}if(e.keyCode){_a.maf.key=e.keyCode;}else{if(e.which){_a.maf.key=e.which;}}if(_a.maf.key==13){_a.maf.pre=this;}else{_a.maf.pre=null;}};o.onblur=function(e){if(_a.maf.key==9&&_a.maf.firstCompact&&!_a.maf.shift&&this.className.indexOf("compact")>-1){_a.maf.key=null;_a.maf.acm=true;document.getElementById(_a.maf.firstCompact).focus();}else{_a.maf.key=null;_a.maf.shift=null;if(_a15){return _a15(this);}}};}}else{if(_a16){if(ss){o.onclick=function(){return _a16(this,this.conf,this.share);};}else{if(!o.conf.ui_window_panes){o.onclick=function(){if(_a.bro.iph||_a.bro.wph||_a.bro.dro){return addthis_sendto("more",this.conf,this.share);}else{return addthis_open(this,"",null,null,this.conf,this.share);}};}else{o.onclick=function(){return addthis_sendto("more",this.conf,this.share);};}}}}if(o.tagName.toLowerCase()=="a"){var url=o.share.url||addthis_share.url;_a.usu(url);if(ss){var _a37=_getCustomService(ss,o.conf),cbtn=o.firstChild;if(_a37&&_a37.code&&_a37.icon){if(cbtn&&cbtn.className.indexOf("at300bs")>-1){var size="16";if(check32(o,1)){cbtn.className=cbtn.className.split("at15nc").join("");size="32";}else{if(check20(o,1)){cbtn.className=cbtn.className.split("at15nc").join("");size="20";}}cbtn.style.background="url("+_a37.icon+") no-repeat top left transparent";if(!cbtn.style.cssText){cbtn.style.cssText="";}cbtn.style.cssText="line-height:"+size+"px!important;width:"+size+"px!important;height:"+size+"px!important;background:"+cbtn.style.background+"!important";}}if(!_9cf[ss]){if(_a10.follow){if(ss!="twitter"){o.href=o.share.followUrl;}else{o.href="http://twitter.com/"+o.share.userid;}o.conf=(o.conf||{});o.conf.follow=true;o.onclick=function(ev){_a.share.track(ss,1,o.share,o.conf);if(ss=="twitter"){ev.preventDefault();return _a.share.ocw(o.share.followUrl,520,520);}};if(o.children&&o.children.length==1&&o.parentNode&&o.parentNode.className.indexOf("toolbox")>-1){var sp=document.ce("span");sp.className="addthis_follow_label";sp.innerHTML=_a.services.getName(ss).replace("_follow","");o.appendChild(sp);}}else{if(_a.share.externEvents(ss,o,_a10)){}else{if(!o.noh){if(o.conf.ui_open_windows||_a.share.auw(ss)){o.onclick=function(e){return _a.share.stw(ss,o.share,o.conf);};}else{o.onclick=function(e){return _a.share.siw(ss,o.share,o.conf);};o.href=_a.share.genurl(ss,0,o.share,o.conf);}}}}if(!o.conf.follow){_6.addEvents(o,ss,url);}if(!o.noh&&!o.target){o.target="_blank";}_6.links.push(o);}else{if(ss=="mailto"||(ss=="email"&&(o.conf.ui_use_mailto||_a.bro.iph||_a.bro.wph||_a.bro.ipa||_a.bro.dro))){o.onclick=function(){o.share.xid=_a.util.cuid();(new Image()).src=_a.share.genurl("mailto",0,o.share,o.config);_a.gat(ss,url,o.conf,o.share);};o.href=_a.share.genieu(o.share,o.config||o.conf);_6.ems.push(o);}}if(!o.title||o.at_titled){var _a3e=_a.services.getName(ss,!_a37);if(_9cc[ss]){_9ca.push({link:o,title:ss});}o.title=unaccent(_a10.follow?(_9cb[ss]?_9cb[ss]:"Follow on "+_a3e):(_9cc[ss]?_9cc[ss]:_a3e));o.at_titled=1;}if(!o.href){o.href="#";}}else{if(o.conf.product&&o.parentNode.className.indexOf("toolbox")==-1){registerProductCode(o);}}}var app;switch(_a17){case"img":if(!o.hasChildNodes()){var lang=(o.conf.ui_language||_a.lng()).split("-").shift(),_a41=_a.ivl(lang);if(!_a41){lang="en";}else{if(_a41!==1){lang=_a41;}}app=_makeButton(_a.iwb(lang)?150:125,16,"Share",_atr+"static/btn/v2/lg-share-"+lang.substr(0,2)+".gif");}break;}if(app){o.appendChild(app);}}}}function _renderPreferredItem(b,_a43,_a44,_a45,_a46,_a47,_a48){if(b._iss){return;}var _a49,excl,sv,_a4c,c=b.className||"",_a4e,opts,_a50={pinterest:"pinterest_share"};if(!_9c2){_9c2=1;b.parentNode._atsharedconf=_a49=_a.share.services.init(b.conf);}else{_a49=b.parentNode._atsharedconf||{};}if(!b.parentNode.services){b.parentNode.services={};}excl=_a49.services_exclude||"";if(_a.bro.ie9){excl+=(excl.length?",":"")+"link";}_a4c=_a.share.services.loc;_a4e=b.parentNode.services;opts=_a.util.unqconcat((window.addthis_options||"").replace(",more","").split(","),_a4c.split(","));do{sv=opts[_a43++];if(_a50[sv]){sv=_a50[sv];}}while(_a43-1||_a4e[sv]));if(_a4e[sv]){_a.util.each(_a.services.list,function(k,v){if(!_a4e[k]&&excl.indexOf(k)==-1){sv=k;return false;}});}b._ips=1;if(b.className.indexOf(sv)==-1){b.className="addthis_button_"+sv+" "+b.className;b._iss=1;}b.parentNode.services[sv]=1;if(_a44){_renderToolbox([b],_a45,_a46,true,_a48);}}function _renderToolbox(_a53,_a54,_a55,_a56,_a57){for(var i=0;i<_a53.length;i++){var b=_a53[i],d=document;if(b==null){continue;}if(_a56!==false||!b.ost){var attr=_getATtributes(b,_a54,_a55,!_a57),hc=0,c=b.className||"",_a5d="",s=c.match(/addthis_button_([\w\-\.]+)(?:\s|$)/),cArr=c.match(/addthis_counter_([\w\.]+)(?:\s|$)/),_a60={},sv=s&&s.length?s[1]:0,csv=cArr&&cArr.length?cArr[1]:0,_a63=_getCustomService(sv);mrg(attr.conf,_9c0);mrg(attr.share,_9c1);if(sv&&!_a.share.extern(sv,b,attr)){if(sv.indexOf("preferred")>-1){if(b._iss||b._iwps){continue;}s=c.match(/addthis_button_preferred_([0-9]+)(?:\s|$)/);var _a64=((s&&s.length)?Math.min(16,Math.max(1,parseInt(s[1]))):1)-1;if(!b.conf||_a57){b.conf=attr.conf||b.conf||{};}if(!b.share||_a57){b.share=attr.share||b.share||{};}b.conf.product="tbx-"+_a.ver();registerProductCode(b);if(!_9c3){var _a65=_a.util.bind(_renderPreferredItem,b,b,_a64,true,_a54,_a55,_a56,_a57);_a.ed.addEventListener("addthis-internal.data.ssh",_a65);setTimeout(_a65,2000);b._iwps=1;continue;}else{_renderPreferredItem(b,_a64,true);}}else{if(sv.indexOf("follow")>-1){if(sv=="google_follow"){b.title="Follow on Google";}else{sv=sv.split("_follow").shift();}_a60.follow=true;_a.track.apc("flw-"+_a.ver());attr.share.followUrl=_a.share.gfu(sv,attr.share.userid,attr.share.usertype,attr.share)||attr.share.url;}else{if(!_a.services.exists(sv)&&!_a.services.isTop(sv)&&(!_a63||!_a63.code)){continue;}}}if(!_a.services.isTop(sv,32)&&(_9be||check32(b))){_8c4.load();}if(!_a.services.isTop(sv,16)&&(_9bf||check20(b))){_9bd.load();}var _a66=(_a.services.isTop(sv,16)&&!check32(b,true)&&!check20(b,true));if(!b.childNodes.length){var sp=d.ce("span");b.appendChild(sp);sp.className=(_a66?"at16nc ":" ")+"at300bs at15nc at15t_"+sv+(_a66?" at16t_"+sv:"");if(((((b.parentNode||{}).parentNode||{}).parentNode||{}).className||"").indexOf("label_style")>-1){var sp2=d.createTextNode(_a.services.getName(sv));b.appendChild(sp2);}else{var _a69="";if(sp!=_1&&(sv==="compact"||sv==="expanded")){_a69="More Sharing Services";}else{if(sp!=_1){_a69="Share on "+sv;}}try{sp.innerHTML=""+_a69+"";}catch(e){var _a6a=d.ce("span");_a6a.className="at_a11y";_a6a.appendChild(document.createTextNode(_a69));sp.appendChild(_a6a);}}}else{if(b.childNodes.length==1){var cn=b.childNodes[0];if(cn.nodeType==3){var sp=d.ce("span");b.insertBefore(sp,cn);sp.className=(_a66?"at16nc ":" ")+"at300bs at15nc at15t_"+sv+(_a66?" at16t_"+sv:"");}if(sp!=_1&&(sv==="compact"||sv==="expanded")){sp.innerHTML="More Sharing Services";}else{if(sp!=_1){sp.innerHTML="Share on "+sv+"";}}}else{if(b.firstChild&&b.firstChild.nodeType==3&&b.firstChild.textContent=="\n"){}else{hc=1;}}}if(sv==="compact"||sv==="expanded"){if(!hc&&c.indexOf("at300")==-1){b.className+=" at300m";}if(attr.conf.product&&attr.conf.product.indexOf("men-")==-1){attr.conf.product+=",men-"+_a.ver();}if(!b.href){b.href="#";}if(b.parentNode&&b.parentNode.services){attr.conf.parentServices=b.parentNode.services;}if(sv==="expanded"){_a60.nohover=true;_a60.singleservice="more";}}else{if((b.parentNode.className||"").indexOf("toolbox")>-1){if(!b.parentNode.services){b.parentNode.services={};}b.parentNode.services[sv]=1;}if(!hc&&c.indexOf("at300")==-1){b.className+=" at300b";}_a60.singleservice=sv;}if(b._ips){_a60.issh=true;}_render([b],attr,_a60,_a57);b.ost=1;registerProductCode(b);}else{if(csv){if(b.ost){continue;}b.ost=1;var _a6c=d.ce("a"),_a6d=d.ce("a");_a6c.className="addthis_native_counter_sibling addthis_button_"+csv;_a6d.className="addthis_native_counter addthis_counter addthis_bubble_style";b.className+=" addthis_native_counter_parent";b.appendChild(_a6c);b.appendChild(_a6d);attr.conf.service=csv.indexOf("pinterest")>-1?"pinterest_share":csv;_render(_a6c,attr,{singleservice:csv});_6.counter(_a6d,attr.conf,attr.share);}}}}}function gat(s,au,conf,_a71){if(s=="facebook_unlike"||s=="google_unplusone"){return;}conf=conf||{};var _a72=conf.data_ga_tracker,_a73=conf.data_ga_property;if(_a73){if(typeof(window._gat)=="object"&&_gat._getTracker){_a72=_gat._getTracker(_a73);}else{if(typeof(window._gaq)=="object"&&_gaq._getAsyncTracker){_a72=_gaq._getAsyncTracker(_a73);}else{if(window._gaq instanceof Array){_gaq.push([function(){_a.gat(s,au,conf,_a71);}]);}}}}if(_a72&&typeof(_a72)=="string"){_a72=window[_a72];}if(!_a72&&window.GoogleAnalyticsObject){var ga=window[window.GoogleAnalyticsObject];if(ga.getAll){_a72=ga.getAll();}}if(_a72&&typeof(_a72)=="object"){if(s=="more"||s=="settings"){return;}var _a75=au||(_a71||{}).url||location.href,_a76=s,_a77="share";if(_a76.indexOf("_")>-1){_a76=_a76.split("_");_a77=_a76.pop();if(_a77.length<=2){_a77="share";}_a76=_a76.shift();}if(_a75.toLowerCase().replace("https","http").indexOf("http%3a%2f%2f")==0){_a75=_duc(_a75);}try{if(conf.data_ga_social&&_a72._trackSocial&&s!="google_plusone"){_a72._trackSocial(_a76,_a77,_a71.url);}else{if(_a72._trackEvent){_a72._trackEvent("addthis",s,_a75);}else{if(conf.data_ga_social&&s!="google_plusone"){ga("send","social",_a76,_a77,_a75);}else{ga("send","event","addthis",s,_a75);}}}}catch(e){try{if(_a72._initData){_a72._initData();}if(conf.data_ga_social&&_a72._trackSocial&&s!="google_plusone"){_a72._trackSocial(_a76,_a77,_a71.url);}else{if(_a72._trackEvent){_a72._trackEvent("addthis",s,_a75);}else{if(conf.data_ga_social&&s!="google_plusone"){ga("send","social",_a76,_a77,_a75);}else{ga("send","event","addthis",s,_a75);}}}}catch(e){}}}}_a.gat=gat;_6.update=function(_a78,what,_a7a){if(_a78=="share"){if(what=="url"){_a.usu(0,1);}if(!window.addthis_share){window.addthis_share={};}window.addthis_share[what]=_a7a;_9c5[what]=_a7a;for(var i in _6.links){var o=_6.links[i],rx=new RegExp("&"+what+"=(.*)&"),ns="&"+what+"="+_euc(_a7a)+"&";if((o.conf||{}).follow||!o.nodeType){continue;}if(o.share){o.share[what]=_a7a;}if(!o.noh){o.href=o.href.replace(rx,ns);if(o.href.indexOf(what)==-1){o.href+=ns;}}}for(var i in _6.ems){var o=_6.ems[i];o.href=_a.share.genieu(addthis_share);}}else{if(_a78=="config"){if(!window.addthis_config){window.addthis_config={};}window.addthis_config[what]=_a7a;_9c4[what]=_a7a;}}};_6._render=_render;_6.button=function(what,_a80,_a81){_a80=_a80||{};if(!_a80.product){_a80.product="men-"+_a.ver();}_render(what,{conf:_a80,share:_a81},{internal:"img"});};_6.toolbox=function(what,_a83,_a84,_a85,_a86){var _a87=_9c7(what);for(var i=0;i<_a87.length;i++){var tb=_a87[i],_a8a=window.jQuery,attr=_getATtributes(tb,_a83,_a84,_a85),sp=document.ce("div"),c;tb.services={};if(tb&&tb.className){if(!attr.conf.product){attr.conf.product="tbx"+(tb.className.indexOf("32x32")>-1?"32":(tb.className.indexOf("20x20")>-1?"20":""))+"-"+_a.ver();}if(tb.className.indexOf("peekaboo_style")>-1){if(!_atc._ld_pkcss){(new _a.resource.Resource("peekaboo",_atc.rsrcs.peekaboocss,function(){return true;})).load();_atc._ld_pkrcss=1;}if(!tb.peekaboo){tb.peekaboo=true;tb.onmouseover=function(){tb.is_hovered=1;tb.timeout=setTimeout(function(){if(tb.is_hovered){if(_a8a){_a8a(".addthis_peekaboo_style ul").slideDown("fast");}else{tb.getElementsByTagName("ul")[0].style.display="block";}}},500);};tb.onmouseout=function(){tb.is_hovered=0;if(tb.timeout){clearTimeout(tb.timeout);}tb.timeout=setTimeout(function(){if(!tb.is_hovered){if(_a8a){_a8a(".addthis_peekaboo_style ul").slideUp("fast");}else{tb.getElementsByTagName("ul")[0].style.display="none";}}},500);};}}if(tb.className.indexOf("floating_style")>-1){if(!_atc._ld_barcss){(new _a.resource.Resource("fixedcss",_atc.rsrcs.fltcss,function(){return true;})).load();_atc._ld_barcss=1;}if(!tb.fixed){tb.fixed=true;function dce(t,c,i){var el=d.ce(t);el.className=c;if(i){el.id=i;}return el;}var _a92=dce("DIV","at-floatingbar-inner"),_a84=dce("DIV","at-floatingbar-share"),_a93=dce("DIV","addthis_internal_container");while(tb.childNodes.length>0){_a93.appendChild(tb.firstChild);}_a84.appendChild(_a93);_a92.appendChild(_a84);tb.appendChild(_a92);if(document.compatMode=="BackCompat"&&_a.bro.msi&&!_a86){tb.setAttribute("className",tb.className.replace("addthis_bar","").replace("addthis_bar_vertical","").replace("addthis_floating_style","addthis_quirks_mode"));if(tb.className.indexOf("addthis_32x32_style")>-1){tb.setAttribute("className",tb.className+" addthis_bar_vertical_medium");}else{if(tb.className.indexOf("addthis_16x16_style")>-1){tb.setAttribute("className",tb.className+" addthis_bar_vertical_small");}else{if(tb.className.indexOf("addthis_counter_style")>-1){tb.setAttribute("className",tb.className+" addthis_bar_vertical_large");}}}}}}}if(tb&&tb.getElementsByTagName){c=tb.getElementsByTagName("a");if(c){_renderToolbox(c,attr.conf,attr.share,!_a85,!_a85);}tb.appendChild(sp);}sp.className="atclear";}};_6.ready=function(_a94){if(_6.ost){return;}_6.ost=1;doRenderPass();_a.ed.fire("addthis.ready",_6);if(_a.onr){_a.onr(_6);}callPostLoads();_a.share.sub();w.addthis_config.eua=w.addthis_config.eua||true;if(w.addthis_config.eua&&_a.ver()>=300&&!_atc.xck&&!_a.bro.ie6&&!_a.bro.ie7){_6.auth.init();}if(_a94&&typeof _a94=="function"){_a94();}};_6.util.getAttributes=_getATtributes;_6.ad=_a.extend(_6.ad,_a.ad);function doRenderPass(){var at=_6,a=".addthis_";if(at.osrp){return;}at.osrp=1;_9c1=w.addthis_share;_9c0=w.addthis_config;body=d.body;_9c8=_a.util.gebcn(body,"A","addthis_button_",true,true);_9c9=_a.util.gebcn(body,"A","addthis_counter_",true,true);addIEHoverFix();_6.toolbox(a+"toolbox",null,null,true,_9c9.length);_6.button(a+"button");_6.counter(a+"counter");_6.count(a+"count");if(typeof _6.overlay==="function"){_6.overlay(a+"shareable");}if(typeof _6.dock==="function"){_6.dock(a+"bar");}_renderToolbox(_9c8,null,null,false);_renderToolbox(_9c9,null,null,false);if(((_9c0||{}).login||{}).services){_6.login.initialize();}}addEventListeners();if(_atc.xol){callPostLoads();if(_d1.isReady){doRenderPass();}}else{_d1.append((function(){window.addthis.ready();}));}_a.ed.fire("addthis-internal.ready",_6);}function callPostLoads(){if(_9bb){return;}var at=window.addthis,func;for(var i=0,plo=at.plo,q;i + +
    \ No newline at end of file diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/formats.png b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/formats.png new file mode 100644 index 0000000..b24643e Binary files /dev/null and b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/formats.png differ diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/free-shipping.png b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/free-shipping.png new file mode 100644 index 0000000..85417f3 Binary files /dev/null and b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/free-shipping.png differ diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/ga.js b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/ga.js new file mode 100644 index 0000000..e7c16d5 --- /dev/null +++ b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/ga.js @@ -0,0 +1,67 @@ +(function(){var aa=encodeURIComponent,ba=Infinity,ca=setTimeout,da=isNaN,m=Math,ea=decodeURIComponent;function Ie(a,b){return a.onload=b}function Je(a,b){return a.onerror=b}function ha(a,b){return a.name=b} +var n="push",ia="test",ja="slice",p="replace",ka="load",la="floor",ma="charAt",na="value",q="indexOf",oa="match",pa="port",qa="createElement",ra="path",r="name",g="getTime",u="host",v="toString",w="length",x="prototype",sa="clientWidth",y="split",ta="stopPropagation",ua="scope",z="location",va="search",A="protocol",wa="clientHeight",xa="href",B="substring",ya="apply",za="navigator",C="join",D="toLowerCase",E;function Aa(a,b){switch(b){case 0:return""+a;case 1:return 1*a;case 2:return!!a;case 3:return 1E3*a}return a}function Ba(a){return"function"==typeof a}function Ca(a){return void 0!=a&&-1<(a.constructor+"")[q]("String")}function F(a,b){return void 0==a||"-"==a&&!b||""==a}function Da(a){if(!a||""==a)return"";for(;a&&-1<" \n\r\t"[q](a[ma](0));)a=a[B](1);for(;a&&-1<" \n\r\t"[q](a[ma](a[w]-1));)a=a[B](0,a[w]-1);return a}function Ea(){return m.round(2147483647*m.random())}function Fa(){} +function G(a,b){if(aa instanceof Function)return b?encodeURI(a):aa(a);H(68);return escape(a)}function I(a){a=a[y]("+")[C](" ");if(ea instanceof Function)try{return ea(a)}catch(b){H(17)}else H(68);return unescape(a)}var Ga=function(a,b,c,d){a.addEventListener?a.addEventListener(b,c,!!d):a.attachEvent&&a.attachEvent("on"+b,c)},Ha=function(a,b,c,d){a.removeEventListener?a.removeEventListener(b,c,!!d):a.detachEvent&&a.detachEvent("on"+b,c)}; +function Ia(a,b){if(a){var c=J[qa]("script");c.type="text/javascript";c.async=!0;c.src=a;c.id=b;var d=J.getElementsByTagName("script")[0];d.parentNode.insertBefore(c,d);return c}}function K(a){return a&&0a[y]("/")[0][q](":")&&(a=k+f[2][B](0,f[2].lastIndexOf("/"))+"/"+a):a=k+f[2]+(a||Be);d.href=a;e=c(d);return{protocol:(d[A]||"")[D](),host:e[0], +port:e[1],path:e[2],Oa:d[va]||"",url:a||""}}function Na(a,b){function c(b,c){a.contains(b)||a.set(b,[]);a.get(b)[n](c)}for(var d=Da(b)[y]("&"),e=0;ef?c(d[e],"1"):c(d[e][B](0,f),d[e][B](f+1))}}function Pa(a,b){if(F(a)||"["==a[ma](0)&&"]"==a[ma](a[w]-1))return"-";var c=J.domain;return a[q](c+(b&&"/"!=b?b:""))==(0==a[q]("http://")?7:0==a[q]("https://")?8:0)?"0":a};var Qa=0;function Ra(a,b,c){1<=Qa||1<=100*m.random()||ld()||(a=["utmt=error","utmerr="+a,"utmwv=5.5.2","utmn="+Ea(),"utmsp=1"],b&&a[n]("api="+b),c&&a[n]("msg="+G(c[B](0,100))),M.w&&a[n]("aip=1"),Sa(a[C]("&")),Qa++)};var Ta=0,Ua={};function N(a){return Va("x"+Ta++,a)}function Va(a,b){Ua[a]=!!b;return a} +var Wa=N(),Xa=Va("anonymizeIp"),Ya=N(),$a=N(),ab=N(),bb=N(),O=N(),P=N(),cb=N(),db=N(),eb=N(),fb=N(),gb=N(),hb=N(),ib=N(),jb=N(),kb=N(),lb=N(),nb=N(),ob=N(),pb=N(),qb=N(),rb=N(),sb=N(),tb=N(),ub=N(),vb=N(),wb=N(),xb=N(),yb=N(),zb=N(),Ab=N(),Bb=N(),Cb=N(),Db=N(),Eb=N(),Fb=N(!0),Gb=Va("currencyCode"),Hb=Va("page"),Ib=Va("title"),Jb=N(),Kb=N(),Lb=N(),Mb=N(),Nb=N(),Ob=N(),Pb=N(),Qb=N(),Rb=N(),Q=N(!0),Sb=N(!0),Tb=N(!0),Ub=N(!0),Vb=N(!0),Wb=N(!0),Zb=N(!0),$b=N(!0),ac=N(!0),bc=N(!0),cc=N(!0),R=N(!0),dc=N(!0), +ec=N(!0),fc=N(!0),gc=N(!0),hc=N(!0),ic=N(!0),jc=N(!0),S=N(!0),kc=N(!0),lc=N(!0),mc=N(!0),nc=N(!0),oc=N(!0),pc=N(!0),qc=N(!0),rc=Va("campaignParams"),sc=N(),tc=Va("hitCallback"),uc=N();N();var vc=N(),wc=N(),xc=N(),yc=N(),zc=N(),Ac=N(),Bc=N(),Cc=N(),Dc=N(),Ec=N(),Fc=N(),Gc=N(),Hc=N(),Ic=N();N();var Mc=N(),Nc=N(),Oc=N(),Oe=Va("uaName"),Pe=Va("uaDomain"),Qe=Va("uaPath");var Re=function(){function a(a,c,d){T($[x],a,c,d)}a("_createTracker",$[x].r,55);a("_getTracker",$[x].oa,0);a("_getTrackerByName",$[x].u,51);a("_getTrackers",$[x].pa,130);a("_anonymizeIp",$[x].aa,16);a("_forceSSL",$[x].la,125);a("_getPlugin",Pc,120)},Se=function(){function a(a,c,d){T(U[x],a,c,d)}Qc("_getName",$a,58);Qc("_getAccount",Wa,64);Qc("_visitCode",Q,54);Qc("_getClientInfo",ib,53,1);Qc("_getDetectTitle",lb,56,1);Qc("_getDetectFlash",jb,65,1);Qc("_getLocalGifPath",wb,57);Qc("_getServiceMode", +xb,59);V("_setClientInfo",ib,66,2);V("_setAccount",Wa,3);V("_setNamespace",Ya,48);V("_setAllowLinker",fb,11,2);V("_setDetectFlash",jb,61,2);V("_setDetectTitle",lb,62,2);V("_setLocalGifPath",wb,46,0);V("_setLocalServerMode",xb,92,void 0,0);V("_setRemoteServerMode",xb,63,void 0,1);V("_setLocalRemoteServerMode",xb,47,void 0,2);V("_setSampleRate",vb,45,1);V("_setCampaignTrack",kb,36,2);V("_setAllowAnchor",gb,7,2);V("_setCampNameKey",ob,41);V("_setCampContentKey",tb,38);V("_setCampIdKey",nb,39);V("_setCampMediumKey", +rb,40);V("_setCampNOKey",ub,42);V("_setCampSourceKey",qb,43);V("_setCampTermKey",sb,44);V("_setCampCIdKey",pb,37);V("_setCookiePath",P,9,0);V("_setMaxCustomVariables",yb,0,1);V("_setVisitorCookieTimeout",cb,28,1);V("_setSessionCookieTimeout",db,26,1);V("_setCampaignCookieTimeout",eb,29,1);V("_setReferrerOverride",Jb,49);V("_setSiteSpeedSampleRate",Dc,132);a("_trackPageview",U[x].Fa,1);a("_trackEvent",U[x].F,4);a("_trackPageLoadTime",U[x].Ea,100);a("_trackSocial",U[x].Ga,104);a("_trackTrans",U[x].Ia, +18);a("_sendXEvent",U[x].t,78);a("_createEventTracker",U[x].ia,74);a("_getVersion",U[x].qa,60);a("_setDomainName",U[x].B,6);a("_setAllowHash",U[x].va,8);a("_getLinkerUrl",U[x].na,52);a("_link",U[x].link,101);a("_linkByPost",U[x].ua,102);a("_setTrans",U[x].za,20);a("_addTrans",U[x].$,21);a("_addItem",U[x].Y,19);a("_clearTrans",U[x].ea,105);a("_setTransactionDelim",U[x].Aa,82);a("_setCustomVar",U[x].wa,10);a("_deleteCustomVar",U[x].ka,35);a("_getVisitorCustomVar",U[x].ra,50);a("_setXKey",U[x].Ca,83); +a("_setXValue",U[x].Da,84);a("_getXKey",U[x].sa,76);a("_getXValue",U[x].ta,77);a("_clearXKey",U[x].fa,72);a("_clearXValue",U[x].ga,73);a("_createXObj",U[x].ja,75);a("_addIgnoredOrganic",U[x].W,15);a("_clearIgnoredOrganic",U[x].ba,97);a("_addIgnoredRef",U[x].X,31);a("_clearIgnoredRef",U[x].ca,32);a("_addOrganic",U[x].Z,14);a("_clearOrganic",U[x].da,70);a("_cookiePathCopy",U[x].ha,30);a("_get",U[x].ma,106);a("_set",U[x].xa,107);a("_addEventListener",U[x].addEventListener,108);a("_removeEventListener", +U[x].removeEventListener,109);a("_addDevId",U[x].V);a("_getPlugin",Pc,122);a("_setPageGroup",U[x].ya,126);a("_trackTiming",U[x].Ha,124);a("_initData",U[x].v,2);a("_setVar",U[x].Ba,22);V("_setSessionTimeout",db,27,3);V("_setCookieTimeout",eb,25,3);V("_setCookiePersistence",cb,24,1);a("_setAutoTrackOutbound",Fa,79);a("_setTrackOutboundSubdomains",Fa,81);a("_setHrefExamineLimit",Fa,80)};function Pc(a){var b=this.plugins_;if(b)return b.get(a)} +var T=function(a,b,c,d){a[b]=function(){try{return void 0!=d&&H(d),c[ya](this,arguments)}catch(a){throw Ra("exc",b,a&&a[r]),a;}}},Qc=function(a,b,c,d){U[x][a]=function(){try{return H(c),Aa(this.a.get(b),d)}catch(e){throw Ra("exc",a,e&&e[r]),e;}}},V=function(a,b,c,d,e){U[x][a]=function(f){try{H(c),void 0==e?this.a.set(b,Aa(f,d)):this.a.set(b,e)}catch(Be){throw Ra("exc",a,Be&&Be[r]),Be;}}},Te=function(a,b){return{type:b,target:a,stopPropagation:function(){throw"aborted";}}};var Rc=new RegExp(/(^|\.)doubleclick\.net$/i),Sc=function(a,b){return Rc[ia](J[z].hostname)?!0:"/"!==b?!1:0!=a[q]("www.google.")&&0!=a[q](".google.")&&0!=a[q]("google.")||-1b[w]||ad(b[0],c))return!1;b=b[ja](1)[C](".")[y]("|");0=b[w])return!0; +b=b[1][y](-1==b[1][q](",")?"^":",");for(c=0;cb[w]||ad(b[0],c))return a.set(ec,void 0),a.set(fc,void 0),a.set(gc,void 0),a.set(ic,void 0),a.set(jc,void 0),a.set(nc,void 0),a.set(oc,void 0),a.set(pc,void 0),a.set(qc,void 0),a.set(S,void 0),a.set(kc,void 0),a.set(lc,void 0),a.set(mc,void 0),!1;a.set(ec,1*b[1]);a.set(fc,1*b[2]);a.set(gc,1*b[3]);Ve(a,b[ja](4)[C]("."));return!0},Ve=function(a,b){function c(a){return(a=b[oa](a+"=(.*?)(?:\\|utm|$)"))&& +2==a[w]?a[1]:void 0}function d(b,c){c?(c=e?I(c):c[y]("%20")[C](" "),a.set(b,c)):a.set(b,void 0)}-1==b[q]("=")&&(b=I(b));var e="2"==c("utmcvr");d(ic,c("utmcid"));d(jc,c("utmccn"));d(nc,c("utmcsr"));d(oc,c("utmcmd"));d(pc,c("utmctr"));d(qc,c("utmcct"));d(S,c("utmgclid"));d(kc,c("utmgclsrc"));d(lc,c("utmdclid"));d(mc,c("utmdsid"))},ad=function(a,b){return b?a!=b:!/^\d+$/[ia](a)};var Uc=function(){this.filters=[]};Uc[x].add=function(a,b){this.filters[n]({name:a,s:b})};Uc[x].cb=function(a){try{for(var b=0;b=100*a.get(vb)&&a[ta]()}function kd(a){ld(a.get(Wa))&&a[ta]()}function md(a){"file:"==J[z][A]&&a[ta]()}function Ge(a){He()&&a[ta]()}function nd(a){a.get(Ib)||a.set(Ib,J.title,!0);a.get(Hb)||a.set(Hb,J[z].pathname+J[z][va],!0)}var kf=/^(UA|YT|MO|GP)-(\d+)-(\d+)$/; +function lf(a){kf[ia](a.get(Wa))||H(152)};var od=new function(){var a=[];this.set=function(b){a[b]=!0};this.Xa=function(){for(var b=[],c=0;c=b[0]||0>=b[1]?"":b[C]("x");a.Wa=d}catch(k){H(135)}qd=a}},td=function(){sd();for(var a= +qd,b=W[za],a=b.appName+b.version+a.language+b.platform+b.userAgent+a.javaEnabled+a.Q+a.P+(J.cookie?J.cookie:"")+(J.referrer?J.referrer:""),b=a[w],c=W.history[w];0d?(this.i=b[B](0,d),this.l=b[B](d+1,c),this.h=b[B](c+1)):(this.i=b[B](0,d),this.h=b[B](d+1));this.k=a[ja](1);this.Ma=!this.l&&"_require"==this.h;this.J=!this.i&&!this.l&&"_provide"==this.h}},Y=function(){T(Y[x],"push",Y[x][n],5);T(Y[x],"_getPlugin",Pc,121);T(Y[x], +"_createAsyncTracker",Y[x].Sa,33);T(Y[x],"_getAsyncTracker",Y[x].Ta,34);this.I=new Ja;this.p=[]};E=Y[x];E.Na=function(a,b,c){var d=this.I.get(a);if(!Ba(d))return!1;b.plugins_=b.plugins_||new Ja;b.plugins_.set(a,new d(b,c||{}));return!0};E.push=function(a){var b=Z.Va[ya](this,arguments),b=Z.p.concat(b);for(Z.p=[];0e?b+"#"+d:b+"&"+d;c="";f=b[q]("?");0f?b+"?"+d+c:b+"&"+d+c},$d=function(a,b,c,d){for(var e=0;3>e;e++){for(var f=0;3>f;f++){if(d==Yc(a+b+c))return H(127),[b,c]; +var Be=b[p](/ /g,"%20"),k=c[p](/ /g,"%20");if(d==Yc(a+Be+k))return H(128),[Be,k];Be=Be[p](/\+/g,"%20");k=k[p](/\+/g,"%20");if(d==Yc(a+Be+k))return H(129),[Be,k];try{var s=b[oa]("utmctr=(.*?)(?:\\|utm|$)");if(s&&2==s[w]&&(Be=b[p](s[1],G(I(s[1]))),d==Yc(a+Be+c)))return H(139),[Be,c]}catch(t){}b=I(b)}c=I(c)}};var de="|",fe=function(a,b,c,d,e,f,Be,k,s){var t=ee(a,b);t||(t={},a.get(Cb)[n](t));t.id_=b;t.affiliation_=c;t.total_=d;t.tax_=e;t.shipping_=f;t.city_=Be;t.state_=k;t.country_=s;t.items_=t.items_||[];return t},ge=function(a,b,c,d,e,f,Be){a=ee(a,b)||fe(a,b,"",0,0,0,"","","");var k;t:{if(a&&a.items_){k=a.items_;for(var s=0;sb[w]||!/^\d+$/[ia](b[0])||(b[0]=""+c,Fd(a,"__utmx",b[C]("."),void 0))},be=function(a,b){var c=$c(a.get(O),pd("__utmx"));"-"==c&&(c="");return b?G(c):c},Ye=function(a){try{var b=La(J[z][xa],!1),c=ea(L(b.d.get("utm_referrer")))||"";c&&a.set(Jb,c);var d=ea(K(b.d.get("utm_expid")))||"";d&&(d=d[y](".")[0],a.set(Oc,""+d))}catch(e){H(146)}},l=function(a){var b=W.gaData&&W.gaData.expId;b&&a.set(Oc,""+b)};var ke=function(a,b){var c=m.min(a.b(Dc,0),100);if(a.b(Q,0)%100>=c)return!1;c=Ze()||$e();if(void 0==c)return!1;var d=c[0];if(void 0==d||d==ba||da(d))return!1;0a[b])return!1;return!0},le=function(a){return da(a)|| +0>a?0:5E3>a?10*m[la](a/10):5E4>a?100*m[la](a/100):41E5>a?1E3*m[la](a/1E3):41E5},je=function(a){for(var b=new yd,c=0;cc[w])){for(var d=[],e=0;e=f)return!1;c=1*(""+c);if(""==a||!wd(a)||""==b||!wd(b)||!xd(c)||da(c)||0>c||0>f||100=a||a>e.get(yb))a=!1;else if(!b||!c||128=a&&Ca(b)&&""!=b){var c=this.get(Fc)||[];c[a]=b;this.set(Fc,c)}};E.V=function(a){a=""+a;if(a[oa](/^[A-Za-z0-9]{1,5}$/)){var b=this.get(Ic)||[];b[n](a);this.set(Ic,b)}};E.v=function(){this.a[ka]()};E.Ba=function(a){a&&""!=a&&(this.set(Tb,a),this.a.j("var"))};var ne=function(a){"trans"!==a.get(sc)&&500<=a.b(cc,0)&&a[ta]();if("event"===a.get(sc)){var b=(new Date)[g](),c=a.b(dc,0),d=a.b(Zb,0),c=m[la]((b-(c!=d?c:1E3*c))/1E3*1);0=a.b(R,0)&&a[ta]()}},pe=function(a){"event"===a.get(sc)&&a.set(R,m.max(0,a.b(R,10)-1))};var qe=function(){var a=[];this.add=function(b,c,d){d&&(c=G(""+c));a[n](b+"="+c)};this.toString=function(){return a[C]("&")}},re=function(a,b){(b||2!=a.get(xb))&&a.Za(cc)},se=function(a,b){b.add("utmwv","5.5.2");b.add("utms",a.get(cc));b.add("utmn",Ea());var c=J[z].hostname;F(c)||b.add("utmhn",c,!0);c=a.get(vb);100!=c&&b.add("utmsp",c,!0)},te=function(a,b){b.add("utmht",(new Date)[g]());b.add("utmac",Da(a.get(Wa)));a.get(Oc)&&b.add("utmxkey",a.get(Oc),!0);a.get(vc)&&b.add("utmni",1);var c=a.get(Ic); +c&&0=a[w])gf(a,b,c);else if(8192>=a[w]){if(0<=W[za].userAgent[q]("Firefox")&&![].reduce)throw new De(a[w]);df(a,b)||ef(a,b)||Ee(a,b)}else throw new Ce(a[w]);},gf=function(a,b,c){c=c||Ne()+"/__utm.gif?";var d=new Image(1,1);d.src=c+a;Ie(d, +function(){Ie(d,null);Je(d,null);b()});Je(d,function(){Ie(d,null);Je(d,null);b()})},ef=function(a,b){var c;c=W.XDomainRequest;if(!c)return!1;c=new c;c.open("POST",Ne()+"/p/__utm.gif");Je(c,function(){b()});Ie(c,b);c.send(a);return!0},df=function(a,b){var c=W.XMLHttpRequest;if(!c)return!1;var d=new c;if(!("withCredentials"in d))return!1;d.open("POST",Ne()+"/p/__utm.gif",!0);d.withCredentials=!0;d.setRequestHeader("Content-Type","text/plain");d.onreadystatechange=function(){4==d.readyState&&(b(),d= +null)};d.send(a);return!0},Ee=function(a,b){if(J.body){a=aa(a);try{var c=J[qa]('')}catch(d){c=J[qa]("iframe"),ha(c,a)}c.height="0";c.width="0";c.style.display="none";c.style.visibility="hidden";var e=J[z],e=Ne()+"/u/post_iframe.html#"+aa(e[A]+"//"+e[u]+"/favicon.ico"),f=function(){c.src="";c.parentNode&&c.parentNode.removeChild(c)};Ga(W,"beforeunload",f);var Be=!1,k=0,s=function(){if(!Be){try{if(9>21:b;return b};})(); diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/oreilly_logo2.jpg b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/oreilly_logo2.jpg new file mode 100644 index 0000000..beff128 Binary files /dev/null and b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/oreilly_logo2.jpg differ diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/plusone.js b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/plusone.js new file mode 100644 index 0000000..fe53052 --- /dev/null +++ b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/plusone.js @@ -0,0 +1,47 @@ +var gapi=window.gapi=window.gapi||{};gapi._bs=new Date().getTime();(function(){var aa=encodeURIComponent,k=window,ba=Object,q=document,ca=Array,da=parseInt,r=String,ea=decodeURIComponent;function fa(a,b){return a.type=b} +var ga="appendChild",s="push",t="test",ha="shift",ia="exec",ja="width",ka="slice",v="replace",la="getElementById",ma="concat",na="charAt",oa="JSON",x="indexOf",pa="nodeName",qa="match",y="createElement",z="setAttribute",ra="type",sa="bind",ta="getTime",ua="getElementsByTagName",A="substr",B="toString",D="length",E="prototype",F="split",G="location",H="style",va="removeChild",I="call",K="getAttribute",wa="protocol",L="charCodeAt",M="href",xa="substring",ya="documentMode",za="action",N="apply",Aa="attributes", +O="parentNode",Ba="update",Ca="height",P="join",Da="toLowerCase",Ea=function(a,b,c){return a[I][N](a[sa],arguments)},Fa=function(a,b,c){if(!a)throw Error();if(2/g,Pa=/"/g,Qa=/'/g,Ra=function(a){return r(a)[v](Ma,"&")[v](Na,"<")[v](Oa,">")[v](Pa,""")[v](Qa,"'")},T=function(){var a;if((a=ba.create)&&Ja[t](a))a=a(null);else{a={};for(var b in a)a[b]= +void 0}return a},U=function(a,b){return ba[E].hasOwnProperty[I](a,b)},Sa=function(a){if(Ja[t](ba.keys))return ba.keys(a);var b=[],c;for(c in a)U(a,c)&&b[s](c);return b},V=function(a,b){a=a||{};for(var c in a)U(a,c)&&(b[c]=a[c])},Ta=function(a){return function(){Q.setTimeout(a,0)}},Ua=function(a,b){if(!a)throw Error(b||"");},W=S(Q,"gapi",{});var X=function(a,b,c){var d=new RegExp("([#].*&|[#])"+b+"=([^&#]*)","g");b=new RegExp("([?#].*&|[?#])"+b+"=([^&#]*)","g");if(a=a&&(d[ia](a)||b[ia](a)))try{c=ea(a[2])}catch(e){}return c},Va=/^([^?#]*)(\?([^#]*))?(\#(.*))?$/,Wa=function(a){a=a[qa](Va);var b=T();b.H=a[1];b.j=a[3]?[a[3]]:[];b.o=a[5]?[a[5]]:[];return b},Xa=function(a){return a.H+(0Ka[I](b,e)&&c[s](e)}return c},Ob=function(a){"loading"!=R.readyState?Nb(a):R.write("<"+Lb+' src="'+encodeURI(a)+'">")},Nb=function(a){var b=R[y](Lb);b[z]("src",a);b.async="true";(a=R[ua](Lb)[0])?a[O].insertBefore(b,a):(R.head||R.body||R.documentElement)[ga](b)},Pb=function(a,b){var c=b&&b._c;if(c)for(var d=0;de;e++)d[e]=b[L](c)<<24|b[L](c+1)<<16|b[L](c+2)<<8|b[L](c+3),c+=4;else for(e=0;16>e;e++)d[e]=b[c]<<24|b[c+1]<<16|b[c+2]<<8|b[c+3],c+=4;for(e=16;80>e;e++){var f=d[e-3]^d[e-8]^d[e-14]^d[e-16];d[e]=(f<<1|f>>>31)&4294967295}b=a.b[0];c=a.b[1];for(var g=a.b[2],h=a.b[3],l=a.b[4],n,e=0;80>e;e++)40>e?20>e?(f=h^c&(g^h),n=1518500249):(f=c^g^h,n=1859775393):60>e?(f=c&g|h&(c|g),n=2400959708):(f=c^g^h,n=3395469782),f=(b<<5|b>>>27)+f+ +l+n+d[e]&4294967295,l=h,h=g,g=(c<<30|c>>>2)&4294967295,c=b,b=f;a.b[0]=a.b[0]+b&4294967295;a.b[1]=a.b[1]+c&4294967295;a.b[2]=a.b[2]+g&4294967295;a.b[3]=a.b[3]+h&4294967295;a.b[4]=a.b[4]+l&4294967295}; +sc[E].update=function(a,b){void 0===b&&(b=a[D]);for(var c=b-this.c,d=0,e=this.p,f=this.g;da.g?a[Ba](a.n,56-a.g):a[Ba](a.n,a.c-(a.g-56));for(c=a.c-1;56<=c;c--)a.p[c]=d&255,d/=256;tc(a,a.p);for(c=d=0;5>c;c++)for(e=24;0<=e;e-=8)b[d]=a.b[c]>>e&255,++d;a="";for(c=0;c')}catch(l){f=a[y]("iframe"),g&&(f.onload=function(){f.onload= +null;g[I](this)},Ic(d))}for(var n in c)a=c[n],"style"===n&&"object"===typeof a?V(a,f[H]):Kc[n]||f[z](n,r(a));(n=e&&e.beforeNode||null)||e&&e.dontclear||gb(b);b.insertBefore(f,n);f=n?n.previousSibling:b.lastChild;c.allowtransparency&&(f.allowTransparency=!0);return f};var Oc=/^:[\w]+$/,Pc=/:([a-zA-Z_]+):/g,Qc=function(){var a=pc()||"0",b=qc(),c;c=pc(void 0)||a;var d=qc(void 0),e="";c&&(e+="u/"+c+"/");d&&(e+="b/"+d+"/");c=e||null;(e=(d=!1===Z("isLoggedIn"))?"_/im/":"")&&(c="");var f=Z("iframes/:socialhost:"),g=Z("iframes/:im_socialhost:");return mc={socialhost:f,ctx_socialhost:d?g:f,session_index:a,session_delegate:b,session_prefix:c,im_prefix:e}},Rc=function(a,b){return Qc()[b]||""},Sc=function(a){return function(b,c){return a?Qc()[c]||a[c]||"":Qc()[c]||""}};var Tc={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},Uc=function(a){var b,c,d;b=/[\"\\\x00-\x1f\x7f-\x9f]/g;if(void 0!==a){switch(typeof a){case "string":return b[t](a)?'"'+a[v](b,function(a){var b=Tc[a];if(b)return b;b=a[L]();return"\\u00"+Math.floor(b/16)[B](16)+(b%16)[B](16)})+'"':'"'+a+'"';case "number":return isFinite(a)?r(a):"null";case "boolean":case "null":return r(a);case "object":if(!a)return"null";b=[];if("number"===typeof a[D]&&!a.propertyIsEnumerable("length")){d= +a[D];for(c=0;c=c&&(g.ic="1");l=/^#|^fr-/;c={};for(var n in g)U(g,n)&&l[t](n)&&(c[n[v](l,"")]=g[n],delete g[n]);n="q"==Z("iframes/"+ +a+"/params/si")?g:c;l=ec();for(var m in l)!U(l,m)||U(g,m)||U(c,m)||(n[m]=l[m]);m=[][ma](hd);(n=Z("iframes/"+a+"/methods"))&&"object"===typeof n&&Ja[t](n[s])&&(m=m[ma](n));for(var p in b)U(b,p)&&/^on/[t](p)&&("plus"!=a||"onconnect"!=p)&&(m[s](p),delete g[p]);delete g.callback;c._methods=m[P](",");return Za(d,g,c)},kd=["style","data-gapiscan"],md=function(a){for(var b=T(),c=0!=a[pa][Da]()[x]("g:"),d=0,e=a[Aa][D];dtype"]=a;V(c,b);n=f;c=m;f=h||{};b=f[Aa]||{};Ua(!f.allowPost||!b.onload,"onload is not supported by post iframe"); +h=b=n;Oc[t](b)&&(h=Z("iframes/"+h[xa](1)+"/url"),Ua(!!h,"Unknown iframe url config for - "+b));n=$a(R,h[v](Pc,Rc));b=c.ownerDocument||R;m=0;do h=f.id||["I",Lc++,"_",(new Date)[ta]()][P]("");while(b[la](h)&&5>++m);Ua(5>m,"Error creating iframe id");m={};var p={};b[ya]&&9>b[ya]&&(m.hostiemode=b[ya]);V(f.queryParams||{},m);V(f.fragmentParams||{},p);var w=f.connectWithQueryParams?m:p,C=f.pfname,u=T();u.id=h;u.parent=b[G][wa]+"//"+b[G].host;var J=X(b[G][M],"parent"),C=C||"";!C&&J&&(J=X(b[G][M],"id",""), +C=X(b[G][M],"pfname",""),C=J?C+"/"+J:"");u.pfname=C;V(u,w);(u=X(n,"rpctoken")||m.rpctoken||p.rpctoken)||(u=w.rpctoken=f.rpctoken||r(Math.round(1E8*Bc())));f.rpctoken=u;u=b[G][M];w=T();(J=X(u,"_bsh",Y.bsh))&&(w._bsh=J);(u=ib(u))&&(w.jsh=u);f.hintInFragment?V(w,p):V(w,m);n=Za(n,m,p,f.paramsSerializer);p=T();V(Jc,p);V(f[Aa],p);p.name=p.id=h;p.src=n;f.eurl=n;if((f||{}).allowPost&&2E3a.i)a=e,b=d}});if(null!==b){var c;Vd.iterate(function(b,d){var e=Xd(b);e&&e.w&&e.d==a.d&&e.i==a.i&&(c=d)});if(c){var d=Zd(c), +e=d&&d.O[Number(b)],d=d&&d.t;if(e)return{P:b,Q:e,t:d}}}return null};var ae=function(a){this.G=a};ae[E].k=0;ae[E].F=2;ae[E].G=null;ae[E].v=!1;ae[E].L=function(){this.v||(this.k=0,this.v=!0,this.D())};ae[E].D=function(){this.v&&(this.G()?this.k=this.F:this.k=Math.min(2*(this.k||this.F),120),k.setTimeout(Ga(this.D,this),1E3*this.k))};for(var be=0;64>be;++be);var ce=null,ic=function(){return Y.oa=!0},jc=function(){Y.oa=!0;var a=$d();(a=a&&a.P)&&$b("googleapis.config/sessionIndex",a);ce||(ce=S(Y,"ss",new ae(de)));a=ce;a.L&&a.L()},de=function(){var a=$d(),b=a&&a.Q||null,c=a&&a.t;Tb("auth",{callback:function(){var a=Q.gapi.auth,e={client_id:c,session_state:b};a.checkSessionState(e,function(b){var c=e.session_state,h=Z("isLoggedIn");b=c&&b||!c&&!b;if(h=h!=b)$b("isLoggedIn",b),jc(),od(),b||((b=a.signOut)?b():(b=a.setToken)&&b(null));b=ec();var l=Z("savedUserState"), +c=a._guss(b.cookiepolicy),l=l!=c&&"undefined"!=typeof l;$b("savedUserState",c);(h||l)&&fc(b)&&!Z("disableRealtimeCallback")&&a._pimf(b,!0)})}});return!0};sb("bs0",!0,k.gapi._bs);sb("bs1",!0);delete k.gapi._bs;})(); +gapi.load("plusone",{callback:window["gapi_onload"],_c:{"jsl":{"ci":{"llang":"nl","client":{"headers":{"response":["Cache-Control","Content-Disposition","Content-Encoding","Content-Language","Content-Length","Content-MD5","Content-Range","Content-Type","Date","ETag","Expires","Last-Modified","Location","Pragma","Range","Server","Transfer-Encoding","WWW-Authenticate","X-Goog-Safety-Content-Type","X-Goog-Safety-Encoding","X-Goog-Upload-Chunk-Granularity","X-Goog-Upload-Control-URL","X-Goog-Upload-Size-Received","X-Goog-Upload-Status","X-Goog-Upload-URL","X-Goog-Diff-Download-Range","X-Goog-Hash","X-Guploader-Customer","X-Guploader-Upload-Result","X-Guploader-Uploadid"],"request":["Accept","Accept-Language","Authorization","Cache-Control","Content-Disposition","Content-Encoding","Content-Language","Content-Length","Content-MD5","Content-Range","Content-Type","Date","GData-Version","Host","If-Match","If-Modified-Since","If-None-Match","If-Unmodified-Since","Origin","OriginToken","Pragma","Range","Slug","Transfer-Encoding","X-ClientDetails","X-GData-Client","X-GData-Key","X-Goog-AuthUser","X-Goog-PageId","X-Goog-Encode-Response-If-Executable","X-Goog-Correlation-Id","X-Goog-Request-Info","X-Goog-Upload-Command","X-Goog-Upload-Content-Disposition","X-Goog-Upload-Content-Length","X-Goog-Upload-Content-Type","X-Goog-Upload-File-Name","X-Goog-Upload-Offset","X-Goog-Upload-Protocol","X-Goog-Visitor-Id","X-HTTP-Method-Override","X-JavaScript-User-Agent","X-Pan-Versionid","X-Origin","X-Referer","X-Upload-Content-Length","X-Upload-Content-Type","X-Use-HTTP-Status-Code-Override","X-YouTube-VVT","X-YouTube-Page-CL","X-YouTube-Page-Timestamp"]},"cors":false},"plus_layer":{"isEnabled":false},"enableMultilogin":true,"disableRealtimeCallback":false,"isLoggedIn":true,"iframes":{"additnow":{"methods":["launchurl"],"url":"https://apis.google.com/additnow/additnow.html?usegapi\u003d1"},"person":{"url":":socialhost:/:session_prefix:_/widget/render/person?usegapi\u003d1"},"visibility":{"params":{"url":""},"url":":socialhost:/:session_prefix:_/widget/render/visibility?usegapi\u003d1"},"plus_followers":{"params":{"url":""},"url":":socialhost:/_/im/_/widget/render/plus/followers?usegapi\u003d1"},"signin":{"methods":["onauth"],"params":{"url":""},"url":":socialhost:/:session_prefix:_/widget/render/signin?usegapi\u003d1"},"commentcount":{"url":":socialhost:/:session_prefix:_/widget/render/commentcount?usegapi\u003d1"},"page":{"url":":socialhost:/:session_prefix:_/widget/render/page?usegapi\u003d1"},"hangout":{"url":"https://talkgadget.google.com/:session_prefix:talkgadget/_/widget"},"plus_circle":{"params":{"url":""},"url":":socialhost:/:session_prefix::se:_/widget/plus/circle?usegapi\u003d1"},"youtube":{"methods":["scroll","openwindow"],"params":{"location":["search","hash"]},"url":":socialhost:/:session_prefix:_/widget/render/youtube?usegapi\u003d1"},"zoomableimage":{"url":"https://ssl.gstatic.com/microscope/embed/"},"card":{"url":":socialhost:/:session_prefix:_/hovercard/card"},"evwidget":{"params":{"url":""},"url":":socialhost:/:session_prefix:_/events/widget?usegapi\u003d1"},"follow":{"url":":socialhost:/:session_prefix:_/widget/render/follow?usegapi\u003d1"},"shortlists":{"url":""},"plus":{"url":":socialhost:/:session_prefix:_/widget/render/badge?usegapi\u003d1"},"configurator":{"url":":socialhost:/:session_prefix:_/plusbuttonconfigurator?usegapi\u003d1"},":socialhost:":"https://apis.google.com","post":{"params":{"url":""},"url":":socialhost:/:session_prefix::im_prefix:_/widget/render/post?usegapi\u003d1"},"community":{"url":":socialhost:/:session_prefix:_/widget/render/community?usegapi\u003d1"},":gplus_url:":"https://plus.google.com","rbr_s":{"params":{"url":""},"url":":socialhost:/:session_prefix::se:_/widget/render/recobarsimplescroller"},"autocomplete":{"params":{"url":""},"url":":socialhost:/:session_prefix:_/widget/render/autocomplete"},"plus_share":{"params":{"url":""},"url":":socialhost:/:session_prefix::se:_/+1/sharebutton?plusShare\u003dtrue\u0026usegapi\u003d1"},":source:":"3p","blogger":{"methods":["scroll","openwindow"],"params":{"location":["search","hash"]},"url":":socialhost:/:session_prefix:_/widget/render/blogger?usegapi\u003d1"},"savetowallet":{"url":"https://clients5.google.com/s2w/o/savetowallet"},"rbr_i":{"params":{"url":""},"url":":socialhost:/:session_prefix::se:_/widget/render/recobarinvitation"},"appcirclepicker":{"url":":socialhost:/:session_prefix:_/widget/render/appcirclepicker"},":im_socialhost:":"https://plus.googleapis.com","savetodrive":{"methods":["save"],"url":"https://drive.google.com/savetodrivebutton?usegapi\u003d1"},":signuphost:":"https://plus.google.com","plusone":{"params":{"count":"","size":"","url":""},"url":":socialhost:/:session_prefix::se:_/+1/fastbutton?usegapi\u003d1"},"comments":{"methods":["scroll","openwindow"],"params":{"location":["search","hash"]},"url":":socialhost:/:session_prefix:_/widget/render/comments?usegapi\u003d1"},"ytsubscribe":{"url":"https://www.youtube.com/subscribe_embed?usegapi\u003d1"}},"isPlusUser":true,"debug":{"host":"https://apis.google.com","reportExceptionRate":0.05,"rethrowException":false},"enableContextualSignin":false,"enableSigninTooltip":false,"deviceType":"desktop","inline":{"css":1},"lexps":[102,99,97,79,109,45,17,117,115,81,127,123,122,61,30],"include_granted_scopes":true,"oauth-flow":{"usegapi":false,"disableOpt":true,"authUrl":"https://accounts.google.com/o/oauth2/auth","proxyUrl":"https://accounts.google.com/o/oauth2/postmessageRelay"},"report":{"host":"https://apis.google.com","rate":0.001,"apis":["iframes\\..*","gadgets\\..*","gapi\\.appcirclepicker\\..*","gapi\\.auth\\..*","gapi\\.client\\..*","gapi\\.signin\\..*"]},"csi":{"rate":0.01},"googleapis.config":{"auth":{"useFirstPartyAuthV2":true}}},"h":"m;/_/scs/apps-static/_/js/k\u003doz.gapi.nl.uNnqE9DTnL8.O/m\u003d__features__/am\u003dAQ/rt\u003dj/d\u003d1/z\u003dzcms/rs\u003dAItRSTOsjMXyWed0VvrJZ-uyOpdDIPkKZA","u":"https://apis.google.com/js/plusone.js","hee":true,"fp":"57875a01341336a511be5fd92ecbdd705df4c30d","dpo":false},"platform":["additnow","blogger","comments","commentcount","community","follow","page","person","plus","plusone","post","savetodrive","savetowallet","shortlists","visibility","youtube","ytsubscribe","zoomableimage","hangout"],"fp":"57875a01341336a511be5fd92ecbdd705df4c30d","annotation":["interactivepost","recobar","autocomplete","profile"],"bimodal":["signin"]}}); \ No newline at end of file diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/postmessageRelay.htm b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/postmessageRelay.htm new file mode 100644 index 0000000..fa66bdc --- /dev/null +++ b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/postmessageRelay.htm @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/prum.min.js b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/prum.min.js new file mode 100644 index 0000000..2c24c59 --- /dev/null +++ b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/prum.min.js @@ -0,0 +1 @@ +var PRUM_EPISODES=PRUM_EPISODES||{};PRUM_EPISODES.q=PRUM_EPISODES.q||[];PRUM_EPISODES.version="0.2";PRUM_EPISODES.targetOrigin=document.location.protocol+"//"+document.location.hostname;PRUM_EPISODES.bPostMessage=false;PRUM_EPISODES.beaconUrl=PRUM_EPISODES.beaconUrl||"/images/beacon.gif";PRUM_EPISODES.autorun=false;PRUM_EPISODES.init=function(){PRUM_EPISODES.bDone=false;PRUM_EPISODES.bUnloaded=false;PRUM_EPISODES.marks={};PRUM_EPISODES.measures={};PRUM_EPISODES.starts={};PRUM_EPISODES.findStartTime();PRUM_EPISODES.addEventListener("beforeunload",PRUM_EPISODES.beforeUnload,false);PRUM_EPISODES.addEventListener("pagehide",PRUM_EPISODES.beforeUnload,false);PRUM_EPISODES.addEventListener("unload",PRUM_EPISODES.beforeUnload,false);PRUM_EPISODES.addOnLoadListener(PRUM_EPISODES.onload);PRUM_EPISODES.processQ()};PRUM_EPISODES.processQ=function(){var a=PRUM_EPISODES.q.length;for(var b=0;b + +AddThis utility frame + \ No newline at end of file diff --git a/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/widget120.css b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/widget120.css new file mode 100644 index 0000000..c6d6209 --- /dev/null +++ b/Chapter 7/serving_files/bin/Learning Dart Packt Publishing_files/widget120.css @@ -0,0 +1 @@ +#at16lb{display:none;position:absolute;top:0;left:0;width:100%;height:100%;z-index:1001;background-color:black;opacity:.001;}#at20mc,#at_email,#at16pib,#at16pc,#at16pi,#at_share,#at_complete,#at_success,#at_error{position:static!important;}#at20mc{position:absolute;left:0;top:0;float:none;}#at20mc a{color:#36B;}#at20mc div{float:none;}.at15dn{display:none;}.at15a{border:0;height:0;margin:0;padding:0;width:100%;width:230px;}.atnt{text-align:center!important;padding:6px 0 0 0!important;height:24px!important;}.atnt a{text-decoration:none;color:#36b;}.atnt a:hover{text-decoration:underline;}#at16recap,#at_msg,#at16p label,#at16nms,#at16sas,#at_share .at_item,#at16p,#at15s,#at16p form input,#at16p textarea{font-family:arial,helvetica,tahoma,verdana,sans-serif!important;font-size:12px!important;outline-style:none;outline-width:0;line-height:1em;}* html #at15s.mmborder{position:absolute!important;}#at15s.mmborder{position:fixed!important;}/*\*/ #at15s.mmborder{width:250px!important;}/**/ #at20mc div.at15sie6{color:#4c4c4c!important;width:256px!important;}#at15s{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABtJREFUeNpiZGBgaGAgAjAxEAlGFVJHIUCAAQDcngCUgqGMqwAAAABJRU5ErkJggg==);float:none;line-height:1em;margin:0;overflow:visible;padding:5px;text-align:left;position:absolute;}#at15s a,#at15s span{outline:0;direction:ltr;}html>body #at15s{width:250px!important;}#at20mc .atm.at15satmie6{background:none!important;padding:0!important;width:150px!important;}#at15s.atm{background:none!important;padding:0!important;width:160px!important;}#at15s.atiemode2{width:252px!important;}#at15s_inner{background:#fff;border:1px solid #fff;margin:0;}#at15s_head{position:relative;background:#f2f2f2;padding:4px;cursor:default;border-bottom:1px solid #e5e5e5;}.at15s_head_success{background:#cafd99!important;border-bottom:1px solid #a9d582!important;}.at15s_head_success span,.at15s_head_success a{color:#000!important;text-decoration:none;}#at15s_brand,#at16_brand,#at15sptx{position:absolute;}#at15s_brand{top:4px;right:4px;}.at15s_brandx{right:20px!important;}a#at15sptx{top:4px;right:4px;text-decoration:none;color:#4c4c4c;font-weight:bold;}.at15sie6 a#at15sptx,#at15s.atiemode2 a#at15sptx{right:8px;}#at15sptx:hover{text-decoration:underline;}#at16_brand{top:5px;right:30px;cursor:default;}#at_hover{padding:4px;}#at_hover .at_item,#at_share .at_item{background:#fff!important;float:left!important;color:#4c4c4c!important;}#at_hover .at_bold{font-weight:bold;color:#000!important;}#at16nms,#at16sas{padding:4px 5px;}#at16nms{display:none;}#at16sas{clear:left;padding-top:16px;padding-bottom:16px;}#at_hover .at_item{width:112px!important;padding:2px 3px!important;margin:1px;text-decoration:none!important;}#at_hover .at_item.atiemode2{width:114px!important;}#at_hover .at_item:hover,#at_hover .at_item:focus,#at_hover .at_item.athov{margin:0!important;}#at_hover .at_item:hover,#at_hover .at_item:focus,#at_hover .at_item.athov,#at16ps .at_item:focus,#at_share .at_item:hover,#at_share .at_item.athov{background:#f2f2f2!important;border:1px solid #e5e5e5;color:#000!important;text-decoration:none;}.ipad #at_hover .at_item:focus{background:#fff!important;border:1px solid #fff;}* html #at_hover .at_item{border:1px solid #fff;}* html #at_hover .at_item.athov{border:1px solid #e5e5e5!important;margin:1px!important;}#at_email15{padding-top:5px;}.at15e_row{height:28px;}.at15e_row label,.at15e_row span{padding-left:10px!important;display:block!important;width:60px!important;float:left!important;}.at15e_row input,.at15e_row textarea{display:block!important;width:150px!important;float:left!important;background:#fff!important;border:1px solid #ccc!important;color:#333!important;font-size:11px!important;font-weight:normal!important;padding:0!important;}#at_email label,#at_email input,#at_email textarea{font-size:11px!important;}#at_email #at16meo{margin:15px 0 0 2px;}#at16meo span{float:left;margin-right:5px;padding-top:4px;}#at16meo a{float:left;margin:0;}#at_sending{top:130px;left:110px;position:absolute;text-align:center;}#at_sending img{padding:10px;}.at15t{display:block!important;height:16px!important;line-height:16px!important;padding-left:20px!important;background:url(widget060.gif) no-repeat left;background-position:0 0;cursor:pointer;text-align:left;}.addthis_gray_style .at15t{background:url(widgetgray060.gif) no-repeat left;}.addthis_button{cursor:pointer;}.addthis_toolbox.addthis_vertical_style{width:140px;}.addthis_toolbox.addthis_close_style .addthis_button_google_plusone{width:65px;overflow:hidden;}.addthis_toolbox.addthis_close_style .addthis_button_facebook_like{width:85px;overflow:hidden;}.addthis_toolbox.addthis_close_style .addthis_button_tweet{width:90px;overflow:hidden;}.addthis_button_facebook_like .fb_iframe_widget{line-height:100%;}.addthis_button_facebook_like iframe.fb_iframe_widget_lift{max-width:none;}.addthis_toolbox span.addthis_follow_label{display:none;}.addthis_toolbox.addthis_vertical_style span.addthis_follow_label{display:block;}.addthis_toolbox.addthis_vertical_style a{display:block;padding-bottom:5px;}.addthis_toolbox.addthis_vertical_style.addthis_32x32_style a{line-height:32px;}.addthis_toolbox.addthis_vertical_style .at300bs{margin-right:4px;float:left;}.addthis_toolbox.addthis_20x20_style span{line-height:20px;*height:20px;}.addthis_toolbox.addthis_32x32_style span{line-height:32px;*height:32px;}.addthis_toolbox.addthis_pill_combo_style a,.addthis_toolbox.addthis_pill_combo_style .addthis_button_compact .at15t_compact{float:left;}.addthis_toolbox.addthis_pill_combo_style a.addthis_button_tweet{margin-top:-2px;}.addthis_toolbox.addthis_pill_combo_style .addthis_button_compact .at15t_compact{margin-right:4px;}.addthis_default_style .addthis_separator{margin:0 5px;display:inline;}div.atclear{clear:both;}.addthis_default_style .addthis_separator,.addthis_default_style .at300b,.addthis_default_style .at300bo,.addthis_default_style .at300m,.addthis_default_style .at300bs{float:left;}.at300b img,.at300bo img{border:0;}.addthis_default_style .at300b,.addthis_default_style .at300bo,.addthis_default_style .at300m{padding:0 2px;}.at300b,.at300bo,.at300m,.at300bs{cursor:pointer;}.addthis_button_facebook_like.at300b:hover,.addthis_button_facebook_send.at300b:hover,.addthis_button_facebook_like.at300bs:hover,.addthis_button_facebook_send.at300bs:hover{opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);}.addthis_20x20_style .dummy .at300bs,.addthis_20x20_style .at300bs,.addthis_20x20_style .at15t{background:url(widget007_20x20_top.gif) no-repeat left;overflow:hidden;display:block;height:20px!important;width:20px!important;line-height:20px!important;}.addthis_32x32_style .dummy .at300bs,.addthis_32x32_style .at300bs,.addthis_32x32_style .at15t{background:url(widget016_32x32_top.gif) no-repeat left;overflow:hidden;display:block;height:32px!important;width:32px!important;line-height:32px!important;}.addthis_32x32_style .at15t_010-blank{background-position:0 -0px!important;}.hi32 .addthis_32x32_style .dummy .at15t_010-blank{background-position:0 -0px!important;}.addthis_32x32_style .at15t_blogger{background-position:0 -32px!important;}.hi32 .addthis_32x32_style .dummy .at15t_blogger{background-position:0 -32px!important;}.addthis_32x32_style .at15t_delicious{background-position:0 -64px!important;}.hi32 .addthis_32x32_style .dummy .at15t_delicious{background-position:0 -64px!important;}.addthis_32x32_style .at15t_digg{background-position:0 -96px!important;}.hi32 .addthis_32x32_style .dummy .at15t_digg{background-position:0 -96px!important;}.addthis_32x32_style .at15t_email{background-position:0 -128px!important;}.hi32 .addthis_32x32_style .dummy .at15t_email{background-position:0 -128px!important;}.addthis_32x32_style .at15t_facebook{background-position:0 -160px!important;}.hi32 .addthis_32x32_style .dummy .at15t_facebook{background-position:0 -160px!important;}.addthis_32x32_style .at15t_favorites{background-position:0 -192px!important;}.hi32 .addthis_32x32_style .dummy .at15t_favorites{background-position:0 -192px!important;}.addthis_32x32_style .at15t_gmail{background-position:0 -224px!important;}.hi32 .addthis_32x32_style .dummy .at15t_gmail{background-position:0 -224px!important;}.addthis_32x32_style .at15t_google{background-position:0 -256px!important;}.hi32 .addthis_32x32_style .dummy .at15t_google{background-position:0 -256px!important;}.addthis_32x32_style .at15t_google_plusone_share{background-position:0 -288px!important;}.hi32 .addthis_32x32_style .dummy .at15t_google_plusone_share{background-position:0 -288px!important;}.addthis_32x32_style .at15t_linkedin{background-position:0 -320px!important;}.hi32 .addthis_32x32_style .dummy .at15t_linkedin{background-position:0 -320px!important;}.addthis_32x32_style .at15t_live{background-position:0 -352px!important;}.hi32 .addthis_32x32_style .dummy .at15t_live{background-position:0 -352px!important;}.addthis_32x32_style .at15t_mailto{background-position:0 -384px!important;}.hi32 .addthis_32x32_style .dummy .at15t_mailto{background-position:0 -384px!important;}.addthis_32x32_style .at15t_more{background-position:0 -416px!important;}.hi32 .addthis_32x32_style .dummy .at15t_more{background-position:0 -416px!important;}.addthis_32x32_style .at15t_mymailru{background-position:0 -448px!important;}.hi32 .addthis_32x32_style .dummy .at15t_mymailru{background-position:0 -448px!important;}.addthis_32x32_style .at15t_myspace{background-position:0 -480px!important;}.hi32 .addthis_32x32_style .dummy .at15t_myspace{background-position:0 -480px!important;}.addthis_32x32_style .at15t_orkut{background-position:0 -512px!important;}.hi32 .addthis_32x32_style .dummy .at15t_orkut{background-position:0 -512px!important;}.addthis_32x32_style .at15t_pinterest_share{background-position:0 -544px!important;}.hi32 .addthis_32x32_style .dummy .at15t_pinterest_share{background-position:0 -544px!important;}.addthis_32x32_style .at15t_print{background-position:0 -576px!important;}.hi32 .addthis_32x32_style .dummy .at15t_print{background-position:0 -576px!important;}.addthis_32x32_style .at15t_reddit{background-position:0 -608px!important;}.hi32 .addthis_32x32_style .dummy .at15t_reddit{background-position:0 -608px!important;}.addthis_32x32_style .at15t_stumbleupon{background-position:0 -640px!important;}.hi32 .addthis_32x32_style .dummy .at15t_stumbleupon{background-position:0 -640px!important;}.addthis_32x32_style .at15t_tumblr{background-position:0 -672px!important;}.hi32 .addthis_32x32_style .dummy .at15t_tumblr{background-position:0 -672px!important;}.addthis_32x32_style .at15t_twitter{background-position:0 -704px!important;}.hi32 .addthis_32x32_style .dummy .at15t_twitter{background-position:0 -704px!important;}.addthis_32x32_style .at15t_vk{background-position:0 -736px!important;}.hi32 .addthis_32x32_style .dummy .at15t_vk{background-position:0 -736px!important;}.addthis_32x32_style .at15t_yahoomail{background-position:0 -768px!important;}.hi32 .addthis_32x32_style .dummy .at15t_yahoomail{background-position:0 -768px!important;}.addthis_32x32_style .at15t_compact{background-position:0 -416px!important;}.hi32 .addthis_32x32_style .dummy .at15t_compact{background-position:0 -416px!important;}.addthis_32x32_style .at15t_expanded{background-position:0 -416px!important;}.hi32 .addthis_32x32_style .dummy .at15t_expanded{background-position:0 -416px!important;}.at300bs{background:url(widget060.gif) no-repeat left;overflow:hidden;display:block;background-position:0 0;height:16px;width:16px;line-height:16px!important;}.addthis_gray_style .at300bs{background:url(widgetgray060.gif) no-repeat left;}.at16nc{background:url(widget016_top.gif) no-repeat;overflow:hidden;display:block;height:16px;width:16px;line-height:16px!important;}.addthis_gray_style .at16nc{background:url(widgetgray016_top.gif) no-repeat left;}.at16t{padding-left:20px!important;width:auto;cursor:pointer;text-align:left;overflow:visible!important;}#at_feed{display:none;padding:10px;height:300px;}#at_feed span{margin-bottom:10px;font-size:12px;}#at_feed div{width:102px!important;height:26px!important;line-height:26px!important;float:left!important;margin-right:68px;}#at_feed div.at_litem{margin-right:0;}#at_feed a{margin:10px 0;height:17px;line-height:17px;}#at_feed.atused .fbtn{background:url(//s7.addthis.com/static/r05/feed00.gif) no-repeat;float:left;width:102px;cursor:pointer;text-indent:-9000px;}#at_feed .fbtn.bloglines{background-position:0 0!important;width:94px;height:20px!important;line-height:20px!important;margin-top:8px!important;}#at_feed .fbtn.yahoo{background-position:0 -20px!important;}#at_feed .fbtn.newsgator,.fbtn.newsgator-on{background-position:0 -37px!important;}#at_feed .fbtn.technorati{background-position:0 -71px!important;}#at_feed .fbtn.netvibes{background-position:0 -88px!important;}#at_feed .fbtn.pageflakes{background-position:0 -141px!important;}#at_feed .fbtn.feedreader{background-position:0 -172px!important;}#at_feed .fbtn.newsisfree{background-position:0 -207px!important;}#at_feed .fbtn.google{background-position:0 -54px!important;width:104px;}#at_feed .fbtn.winlive{background-position:0 -105px!important;width:100px;height:19px!important;line-height:19px;margin-top:9px!important;}#at_feed .fbtn.mymsn{background-position:0 -158px;width:71px;height:14px!important;line-height:14px!important;margin-top:12px!important;}#at_feed .fbtn.aol{background-position:0 -189px;width:92px;height:18px!important;line-height:18px!important;}.at15t_000{background-position:0 -0px;}.at15t_100zakladok{background-position:0 -16px;}.at15t_2linkme{background-position:0 -32px;}.at15t_2tag{background-position:0 -48px;}.at15t_a97abi{background-position:0 -64px;}.at15t_addressbar{background-position:0 -80px;}.at15t_addthis{background-position:0 -96px;}.at15t_addthis_error{background-position:0 -112px;}.at15t_adfty{background-position:0 -128px;}.at15t_adifni{background-position:0 -144px;}.at15t_advqr{background-position:0 -160px;}.at15t_aim{background-position:0 -176px;}.at15t_amazonwishlist{background-position:0 -192px;}.at15t_amenme{background-position:0 -208px;}.at15t_aolmail{background-position:0 -224px;}.at15t_apsense{background-position:0 -240px;}.at15t_arto{background-position:0 -256px;}.at15t_azadegi{background-position:0 -272px;}.at15t_baang{background-position:0 -288px;}.at15t_baidu{background-position:0 -304px;}.at15t_balltribe{background-position:0 -320px;}.at15t_beat100{background-position:0 -336px;}.at15t_biggerpockets{background-position:0 -352px;}.at15t_bitly{background-position:0 -368px;}.at15t_bizsugar{background-position:0 -384px;}.at15t_bland{background-position:0 -400px;}.at15t_blinklist{background-position:0 -416px;}.at15t_blogger{background-position:0 -432px;}.at15t_bloggy{background-position:0 -448px;}.at15t_blogkeen{background-position:0 -464px;}.at15t_blogmarks{background-position:0 -480px;}.at15t_blurpalicious{background-position:0 -496px;}.at15t_bobrdobr{background-position:0 -512px;}.at15t_bonzobox{background-position:0 -528px;}.at15t_bookmarkycz{background-position:0 -544px;}.at15t_bookmerkende{background-position:0 -560px;}.at15t_box{background-position:0 -576px;}.at15t_brainify{background-position:0 -592px;}.at15t_bryderi{background-position:0 -608px;}.at15t_buddymarks{background-position:0 -624px;}.at15t_buffer{background-position:0 -640px;}.at15t_buzzzy{background-position:0 -656px;}.at15t_camyoo{background-position:0 -672px;}.at15t_care2{background-position:0 -688px;}.at15t_chimein{background-position:0 -704px;}.at15t_chiq{background-position:0 -720px;}.at15t_cirip{background-position:0 -736px;}.at15t_citeulike{background-position:0 -752px;}.at15t_classicalplace{background-position:0 -768px;}.at15t_cleanprint{background-position:0 -784px;}.at15t_cleansave{background-position:0 -800px;}.at15t_cndig{background-position:0 -816px;}.at15t_colivia{background-position:0 -832px;}.at15t_compact{background-position:0 -848px;}.at15t_cosmiq{background-position:0 -864px;}.at15t_cssbased{background-position:0 -880px;}.at15t_curateus{background-position:0 -896px;}.at15t_delicious{background-position:0 -912px;}.at15t_digaculturanet{background-position:0 -928px;}.at15t_digg{background-position:0 -944px;}.at15t_diggita{background-position:0 -960px;}.at15t_digo{background-position:0 -976px;}.at15t_diigo{background-position:0 -992px;}.at15t_domaintoolswhois{background-position:0 -1008px;}.at15t_domelhor{background-position:0 -1024px;}.at15t_dotnetshoutout{background-position:0 -1040px;}.at15t_douban{background-position:0 -1056px;}.at15t_draugiem{background-position:0 -1072px;}.at15t_dropjack{background-position:0 -1088px;}.at15t_dudu{background-position:0 -1104px;}.at15t_dzone{background-position:0 -1120px;}.at15t_efactor{background-position:0 -1136px;}.at15t_ekudos{background-position:0 -1152px;}.at15t_elefantapl{background-position:0 -1168px;}.at15t_email{background-position:0 -1184px;}.at15t_embarkons{background-position:0 -1200px;}.at15t_evernote{background-position:0 -1216px;}.at15t_expanded{background-position:0 -1232px;}.at15t_extraplay{background-position:0 -1248px;}.at15t_ezyspot{background-position:0 -1264px;}.at15t_fabulously40{background-position:0 -1280px;}.at15t_facebook{background-position:0 -1296px;}.at15t_facebook_like{background-position:0 -1312px;}.at15t_fark{background-position:0 -1328px;}.at15t_farkinda{background-position:0 -1344px;}.at15t_fashiolista{background-position:0 -1360px;}.at15t_favable{background-position:0 -1376px;}.at15t_faves{background-position:0 -1392px;}.at15t_favlogde{background-position:0 -1408px;}.at15t_favoritende{background-position:0 -1424px;}.at15t_favorites{background-position:0 -1440px;}.at15t_favoritus{background-position:0 -1456px;}.at15t_financialjuice{background-position:0 -1472px;}.at15t_flaker{background-position:0 -1488px;}.at15t_flickr{background-position:0 -1504px;}.at15t_folkd{background-position:0 -1520px;}.at15t_foodlve{background-position:0 -1536px;}.at15t_formspring{background-position:0 -1552px;}.at15t_foursquare{background-position:0 -1568px;}.at15t_fresqui{background-position:0 -1584px;}.at15t_friendfeed{background-position:0 -1600px;}.at15t_funp{background-position:0 -1616px;}.at15t_fwisp{background-position:0 -1632px;}.at15t_gabbr{background-position:0 -1648px;}.at15t_gamekicker{background-position:0 -1664px;}.at15t_gg{background-position:0 -1680px;}.at15t_giftery{background-position:0 -1696px;}.at15t_gigbasket{background-position:0 -1712px;}.at15t_givealink{background-position:0 -1728px;}.at15t_gluvsnap{background-position:0 -1744px;}.at15t_gmail{background-position:0 -1760px;}.at15t_goodnoows{background-position:0 -1776px;}.at15t_google{background-position:0 -1792px;}.at15t_google_follow{background-position:0 -1808px;}.at15t_google_plusone{background-position:0 -1824px;}.at15t_google_plusone_share{background-position:0 -1840px;}.at15t_googletranslate{background-position:0 -1856px;}.at15t_govn{background-position:0 -1872px;}.at15t_greaterdebater{background-position:0 -1888px;}.at15t_hackernews{background-position:0 -1904px;}.at15t_hatena{background-position:0 -1920px;}.at15t_hedgehogs{background-position:0 -1936px;}.at15t_historious{background-position:0 -1952px;}.at15t_hootsuite{background-position:0 -1968px;}.at15t_hotklix{background-position:0 -1984px;}.at15t_hotmail{background-position:0 -2000px;}.at15t_identica{background-position:0 -2016px;}.at15t_ihavegot{background-position:0 -2032px;}.at15t_indexor{background-position:0 -2048px;}.at15t_informazione{background-position:0 -2064px;}.at15t_instagram{background-position:0 -2080px;}.at15t_instapaper{background-position:0 -2096px;}.at15t_iorbix{background-position:0 -2112px;}.at15t_irepeater{background-position:0 -2128px;}.at15t_isociety{background-position:0 -2144px;}.at15t_iwiw{background-position:0 -2160px;}.at15t_jamespot{background-position:0 -2176px;}.at15t_jappy{background-position:0 -2192px;}.at15t_jolly{background-position:0 -2208px;}.at15t_jumptags{background-position:0 -2224px;}.at15t_kaboodle{background-position:0 -2240px;}.at15t_kaevur{background-position:0 -2256px;}.at15t_kaixin{background-position:0 -2272px;}.at15t_ketnooi{background-position:0 -2288px;}.at15t_kindleit{background-position:0 -2304px;}.at15t_kledy{background-position:0 -2320px;}.at15t_kommenting{background-position:0 -2336px;}.at15t_latafaneracat{background-position:0 -2352px;}.at15t_librerio{background-position:0 -2368px;}.at15t_lidar{background-position:0 -2384px;}.at15t_link{background-position:0 -2400px;}.at15t_linkedin{background-position:0 -2416px;}.at15t_linksgutter{background-position:0 -2432px;}.at15t_linkshares{background-position:0 -2448px;}.at15t_linkuj{background-position:0 -2464px;}.at15t_live{background-position:0 -2480px;}.at15t_livejournal{background-position:0 -2496px;}.at15t_lockerblogger{background-position:0 -2512px;}.at15t_logger24{background-position:0 -2528px;}.at15t_mailto{background-position:0 -2544px;}.at15t_margarin{background-position:0 -2560px;}.at15t_markme{background-position:0 -2576px;}.at15t_mashant{background-position:0 -2592px;}.at15t_mashbord{background-position:0 -2608px;}.at15t_me2day{background-position:0 -2624px;}.at15t_meinvz{background-position:0 -2640px;}.at15t_mekusharim{background-position:0 -2656px;}.at15t_memonic{background-position:0 -2672px;}.at15t_memori{background-position:0 -2688px;}.at15t_mendeley{background-position:0 -2704px;}.at15t_meneame{background-position:0 -2720px;}.at15t_menu{background-position:0 -2736px;}.at15t_misterwong{background-position:0 -2752px;}.at15t_misterwong_de{background-position:0 -2768px;}.at15t_misterwong_ru{background-position:0 -2784px;}.at15t_mixi{background-position:0 -2800px;}.at15t_moemesto{background-position:0 -2816px;}.at15t_moikrug{background-position:0 -2832px;}.at15t_more{background-position:0 -2848px;}.at15t_mrcnetworkit{background-position:0 -2864px;}.at15t_mymailru{background-position:0 -2880px;}.at15t_myspace{background-position:0 -2896px;}.at15t_myvidster{background-position:0 -2912px;}.at15t_n4g{background-position:0 -2928px;}.at15t_naszaklasa{background-position:0 -2944px;}.at15t_netlog{background-position:0 -2960px;}.at15t_netvibes{background-position:0 -2976px;}.at15t_netvouz{background-position:0 -2992px;}.at15t_newsmeback{background-position:0 -3008px;}.at15t_newstrust{background-position:0 -3024px;}.at15t_newsvine{background-position:0 -3040px;}.at15t_nujij{background-position:0 -3056px;}.at15t_odnoklassniki_ru{background-position:0 -3072px;}.at15t_oknotizie{background-position:0 -3088px;}.at15t_openthedoor{background-position:0 -3104px;}.at15t_orkut{background-position:0 -3120px;}.at15t_oyyla{background-position:0 -3136px;}.at15t_packg{background-position:0 -3152px;}.at15t_pafnetde{background-position:0 -3168px;}.at15t_pdfmyurl{background-position:0 -3184px;}.at15t_pdfonline{background-position:0 -3200px;}.at15t_phonefavs{background-position:0 -3216px;}.at15t_pinterest{background-position:0 -3232px;}.at15t_pinterest_share{background-position:0 -3248px;}.at15t_planypus{background-position:0 -3264px;}.at15t_plaxo{background-position:0 -3280px;}.at15t_plurk{background-position:0 -3296px;}.at15t_pocket{background-position:0 -3312px;}.at15t_posteezy{background-position:0 -3328px;}.at15t_print{background-position:0 -3344px;}.at15t_printfriendly{background-position:0 -3360px;}.at15t_pusha{background-position:0 -3376px;}.at15t_qrfin{background-position:0 -3392px;}.at15t_qrsrc{background-position:0 -3408px;}.at15t_quantcast{background-position:0 -3424px;}.at15t_qzone{background-position:0 -3440px;}.at15t_raiseyourvoice{background-position:0 -3456px;}.at15t_reddit{background-position:0 -3472px;}.at15t_rediff{background-position:0 -3488px;}.at15t_redkum{background-position:0 -3504px;}.at15t_researchgate{background-position:0 -3520px;}.at15t_rss{background-position:0 -3536px;}.at15t_safelinking{background-position:0 -3552px;}.at15t_scoopat{background-position:0 -3568px;}.at15t_scoopit{background-position:0 -3584px;}.at15t_sekoman{background-position:0 -3600px;}.at15t_select2gether{background-position:0 -3616px;}.at15t_settings{background-position:0 -3632px;}.at15t_sharer{background-position:0 -3648px;}.at15t_shaveh{background-position:0 -3664px;}.at15t_shetoldme{background-position:0 -3680px;}.at15t_sinaweibo{background-position:0 -3696px;}.at15t_skyrock{background-position:0 -3712px;}.at15t_smiru{background-position:0 -3728px;}.at15t_socialbookmarkingnet{background-position:0 -3744px;}.at15t_sodahead{background-position:0 -3760px;}.at15t_sonico{background-position:0 -3776px;}.at15t_spinsnap{background-position:0 -3792px;}.at15t_springpad{background-position:0 -3808px;}.at15t_startaid{background-position:0 -3824px;}.at15t_startlap{background-position:0 -3840px;}.at15t_storyfollower{background-position:0 -3856px;}.at15t_studivz{background-position:0 -3872px;}.at15t_stuffpit{background-position:0 -3888px;}.at15t_stumbleupon{background-position:0 -3904px;}.at15t_stumpedia{background-position:0 -3920px;}.at15t_stylishhome{background-position:0 -3936px;}.at15t_sulia{background-position:0 -3952px;}.at15t_sunlize{background-position:0 -3968px;}.at15t_supbro{background-position:0 -3984px;}.at15t_surfingbird{background-position:0 -4000px;}.at15t_svejo{background-position:0 -4016px;}.at15t_symbaloo{background-position:0 -4032px;}.at15t_taaza{background-position:0 -4048px;}.at15t_tagza{background-position:0 -4064px;}.at15t_taringa{background-position:0 -4080px;}.at15t_technerd{background-position:0 -4096px;}.at15t_textme{background-position:0 -4112px;}.at15t_thefancy{background-position:0 -4128px;}.at15t_thefreedictionary{background-position:0 -4144px;}.at15t_thewebblend{background-position:0 -4160px;}.at15t_thinkfinity{background-position:0 -4176px;}.at15t_thisnext{background-position:0 -4192px;}.at15t_thrillon{background-position:0 -4208px;}.at15t_throwpile{background-position:0 -4224px;}.at15t_toly{background-position:0 -4240px;}.at15t_topsitelernet{background-position:0 -4256px;}.at15t_transferr{background-position:0 -4272px;}.at15t_tuenti{background-position:0 -4288px;}.at15t_tulinq{background-position:0 -4304px;}.at15t_tumblr{background-position:0 -4320px;}.at15t_tvinx{background-position:0 -4336px;}.at15t_twitter{background-position:0 -4352px;}.at15t_twitter_follow_native{background-position:0 -4368px;}.at15t_twitthis{background-position:0 -4384px;}.at15t_typepad{background-position:0 -4400px;}.at15t_upnews{background-position:0 -4416px;}.at15t_urlaubswerkde{background-position:0 -4432px;}.at15t_viadeo{background-position:0 -4448px;}.at15t_vimeo{background-position:0 -4464px;}.at15t_virb{background-position:0 -4480px;}.at15t_visitezmonsite{background-position:0 -4496px;}.at15t_vk{background-position:0 -4512px;}.at15t_vkrugudruzei{background-position:0 -4528px;}.at15t_voxopolis{background-position:0 -4544px;}.at15t_vybralisme{background-position:0 -4560px;}.at15t_w3validator{background-position:0 -4576px;}.at15t_wanelo{background-position:0 -4592px;}.at15t_webnews{background-position:0 -4608px;}.at15t_webshare{background-position:0 -4624px;}.at15t_werkenntwen{background-position:0 -4640px;}.at15t_windows{background-position:0 -4656px;}.at15t_wirefan{background-position:0 -4672px;}.at15t_wishmindr{background-position:0 -4688px;}.at15t_wordpress{background-position:0 -4704px;}.at15t_wowbored{background-position:0 -4720px;}.at15t_wykop{background-position:0 -4736px;}.at15t_xanga{background-position:0 -4752px;}.at15t_xing{background-position:0 -4768px;}.at15t_yahoobkm{background-position:0 -4784px;}.at15t_yahoomail{background-position:0 -4800px;}.at15t_yammer{background-position:0 -4816px;}.at15t_yardbarker{background-position:0 -4832px;}.at15t_yigg{background-position:0 -4848px;}.at15t_yiid{background-position:0 -4864px;}.at15t_yookos{background-position:0 -4880px;}.at15t_yoolink{background-position:0 -4896px;}.at15t_yorumcuyum{background-position:0 -4912px;}.at15t_youmob{background-position:0 -4928px;}.at15t_youtube{background-position:0 -4944px;}.at15t_yuuby{background-position:0 -4960px;}.at15t_zakladoknet{background-position:0 -4976px;}.at15t_ziczac{background-position:0 -4992px;}.at15t_zingme{background-position:0 -5008px;}.at15t_compact{background-position:0 -2848px;}.at15t_expanded{background-position:0 -2848px;}.at16nc.at16t_100zakladok{background-position:0 -0px;}.at16nc.at16t_addthis{background-position:0 -16px;}.at16nc.at16t_adifni{background-position:0 -32px;}.at16nc.at16t_aim{background-position:0 -48px;}.at16nc.at16t_amazonwishlist{background-position:0 -64px;}.at16nc.at16t_arto{background-position:0 -80px;}.at16nc.at16t_baidu{background-position:0 -96px;}.at16nc.at16t_bitly{background-position:0 -112px;}.at16nc.at16t_blogger{background-position:0 -128px;}.at16nc.at16t_bloggy{background-position:0 -144px;}.at16nc.at16t_bobrdobr{background-position:0 -160px;}.at16nc.at16t_delicious{background-position:0 -176px;}.at16nc.at16t_digg{background-position:0 -192px;}.at16nc.at16t_diggita{background-position:0 -208px;}.at16nc.at16t_draugiem{background-position:0 -224px;}.at16nc.at16t_ekudos{background-position:0 -240px;}.at16nc.at16t_email{background-position:0 -256px;}.at16nc.at16t_facebook{background-position:0 -272px;}.at16nc.at16t_favorites{background-position:0 -288px;}.at16nc.at16t_friendfeed{background-position:0 -304px;}.at16nc.at16t_gmail{background-position:0 -320px;}.at16nc.at16t_google{background-position:0 -336px;}.at16nc.at16t_google_plusone_share{background-position:0 -352px;}.at16nc.at16t_hatena{background-position:0 -368px;}.at16nc.at16t_hotmail{background-position:0 -384px;}.at16nc.at16t_jappy{background-position:0 -400px;}.at16nc.at16t_linkedin{background-position:0 -416px;}.at16nc.at16t_live{background-position:0 -432px;}.at16nc.at16t_livejournal{background-position:0 -448px;}.at16nc.at16t_mailto{background-position:0 -464px;}.at16nc.at16t_meinvz{background-position:0 -480px;}.at16nc.at16t_meneame{background-position:0 -496px;}.at16nc.at16t_misterwong{background-position:0 -512px;}.at16nc.at16t_more{background-position:0 -528px;}.at16nc.at16t_mymailru{background-position:0 -544px;}.at16nc.at16t_myspace{background-position:0 -560px;}.at16nc.at16t_netlog{background-position:0 -576px;}.at16nc.at16t_nujij{background-position:0 -592px;}.at16nc.at16t_oknotizie{background-position:0 -608px;}.at16nc.at16t_orkut{background-position:0 -624px;}.at16nc.at16t_oyyla{background-position:0 -640px;}.at16nc.at16t_pinterest_share{background-position:0 -656px;}.at16nc.at16t_plurk{background-position:0 -672px;}.at16nc.at16t_print{background-position:0 -688px;}.at16nc.at16t_pusha{background-position:0 -704px;}.at16nc.at16t_reddit{background-position:0 -720px;}.at16nc.at16t_settings{background-position:0 -736px;}.at16nc.at16t_sonico{background-position:0 -752px;}.at16nc.at16t_studivz{background-position:0 -768px;}.at16nc.at16t_stumbleupon{background-position:0 -784px;}.at16nc.at16t_tuenti{background-position:0 -800px;}.at16nc.at16t_tumblr{background-position:0 -816px;}.at16nc.at16t_twitter{background-position:0 -832px;}.at16nc.at16t_viadeo{background-position:0 -848px;}.at16nc.at16t_vk{background-position:0 -864px;}.at16nc.at16t_wordpress{background-position:0 -880px;}.at16nc.at16t_wykop{background-position:0 -896px;}.at16nc.at16t_xing{background-position:0 -912px;}.at16nc.at16t_yahoobkm{background-position:0 -928px;}.at16nc.at16t_yahoomail{background-position:0 -944px;}.at16nc.at16t_yorumcuyum{background-position:0 -960px;}.at16nc.at16t_compact{background-position:0 -528px;}.at16nc.at16t_expanded{background-position:0 -528px;}.addthis_default_style .at15t_expanded,.addthis_default_style .at15t_compact{margin-right:4px;}#at16clb{font-size:16pt;font-family:"verdana bold",verdana,arial,sans-serif;}#at_share .at_item{width:123px!important;padding:4px;margin-right:2px;border:1px solid #fff;}#at16pm{background:#fff;width:298px;height:380px;text-align:left;border-right:1px solid #ccc;position:static;}#at16pcc,#at16pccImg{position:fixed;top:0;left:0;width:100%;margin:0 auto;font-size:10px!important;color:#4c4c4c;padding:0;z-index:10000001;overflow:visible;}#at16pccImg{height:100%;}* html #at16pcc{position:absolute;}#at16abifc{overflow:hidden;margin:0;top:10px;left:10px;height:355px;width:492px;position:absolute;border:0;}#at16abifc iframe{border:0;position:absolute;height:380px;width:516px;top:-10px;left:-10px;}* html div#at16abifc.atiemode2{height:374px;width:482px;}* html #at16abifc iframe{height:368px;left:-10px;top:-10px;overflow:hidden;}#at16p{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABtJREFUeNpiZGBgaGAgAjAxEAlGFVJHIUCAAQDcngCUgqGMqwAAAABJRU5ErkJggg==);z-index:10000001;}#at16p,#atie6ifh,#atie6cmifh{position:absolute;top:50%;left:50%;width:300px;padding:10px;margin:0 auto;margin-top:-185px;margin-left:-155px;font-family:arial,helvetica,tahoma,verdana,sans-serif;font-size:12px;color:#5e5e5e;}#atie6ifh{width:322px;padding:0;height:381px;margin-left:-165px;z-index:100001;}#atie6cmifh{width:240px;padding:0;height:225px;margin:0;z-index:100001;}#at_share{margin:0;padding:0;}#at16ps{overflow-y:scroll;height:304px;padding:5px;}a#at16pit{position:absolute;top:37px;right:10px;display:block;background:url(data:image/gif;base64,R0lGODlhEAAUAKIFAKqqquHh4cLCwszMzP///////wAAAAAAACH5BAEAAAUALAAAAAAQABQAAAMtOLqsAqWQSSsN0OoLthfeNoTaSFbmOaUqe7okHMoeLaqUXeITiGM/SGM4eEQSADs=) no-repeat;width:16px;height:20px;line-height:19px;margin-right:-17px;text-align:center;overflow:hidden;color:#36b;}#at16pi{background:#e5e5e5;text-align:left;border:1px solid #ccc;border-bottom:0;}#at16pi a{text-decoration:none;color:#36b;}#at16p #at16abc{margin-left:2px!important;}#at16pi a:hover{text-decoration:underline;}#at16pt{position:relative;background:#f2f2f2;height:13px;padding:5px 10px;}#at16pt h4,#at16pt a{font-weight:bold;}#at16pt h4{display:inline;margin:0;padding:0;font-size:12px;color:#4c4c4c;cursor:default;}#at16pt a{position:absolute;top:5px;right:10px;color:#4c4c4c;text-decoration:none;padding:2px;}#at15sptx:focus,#at16pt a:focus{outline:dotted thin;}#at16pc form{margin:0;}#at16pc form label{display:block;font-size:11px;font-weight:bold;padding-bottom:4px;float:none;text-align:left;}#at16pc form label span{font-weight:normal;color:#4c4c4c;display:inline;}#at_email form .abif{width:272px!important;}#at_email textarea{height:55px!important;word-wrap:break-word;}* html #at_email textarea{height:42px!important;}*:first-child+html #at_email textarea{height:42px!important;}#at_email label{width:220px;}#at_email input,#at_email textarea{background:#fff;border:1px solid #bbb;width:272px!important;margin:0;margin-bottom:8px;font-weight:normal;padding:3px!important;font-family:arial,helvetica,tahoma,verdana,sans-serif;font-size:11px;line-height:1.4em;color:#333;}#at_email form .atfxmode2{width:279px!important;}#at16pc form .at_ent{color:#333!important;}#at16pc textarea{height:48px;}#at16pc form input:focus,#at16pc textarea:focus{background:#fffff0;color:#333;}#at16p .atbtn,#at16recap .atbtn{background:#fff;border:1px solid #b5b5b5;width:60px!important;padding:2px 4px;margin:0;margin-right:2px!important;font-size:11px!important;font-weight:bold;color:#333;cursor:pointer;}#at16p .atbtn:hover,#at16p .atbtn:focus,#at16recap .atbtn:hover,#at16recap .atbtn:focus{border-color:#444;color:#06c;}#at16p .atrse,#at16recap .atrse{font-weight:normal!important;color:#666;margin-left:2px!important;}#atsb .atbtn{width:78px!important;margin:0!important;}#at_email #ateml{text-align:right;font-size:10px;color:#999;}#at16pc{height:343px!important;font-size:11px;text-align:left;color:#4c4c4c;}#at_email{padding:5px 10px;}#at16pc .tmsg{padding:4px 2px;text-align:right;}#at16psf{position:relative;background:#f2f2f2 url(data:image/gif;base64,R0lGODlhGQEVAMQYAGZmZuDg4Ozs7MjIyMzMzPj4+LOzs3BwcMbGxsvLy5+fn/X19djY2IODg+bm5paWlnl5eeLi4oyMjKmpqdXV1dvb28/Pz////////wAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABgALAAAAAAZARUAAAX/ICaOGJFYaKqubOu+cCzPdG3feK7vPJwQpOBoEChcjsikcslsOp/QqHRKrVqv2Kx2Gy0EBkKRgMEtm8/otHrNTjMEQYGjTa/b7/h82gEfVfSAgYKDhGcVQ0sLBhAAEAYLhZGSk5RqYBgBSgsNAA0GnA2QlaOkpaZHASVGSQYACEgIABOntLW2eAUmSxASShIHt8HCw1snSwAGSq3EzM3OSyhLBw9KD8DP2Nm30UoKrrAACtrj5KMWCYmcCgbeAAcR5fHygT+rSQvtAA8A7vDz/wDV5MIUJVa/gAgTZkmFYYAUg70USpz45BKGPwUPiKPIseOhEXI6ihzphE8cMiRTMI58E6ZhEZUwEXqx2LIEAwsUKujcybOnz59AgwodSrSo0aNIkypdyrSpU58ofoQJAQA7) no-repeat center center;border-bottom:1px solid #ccc;height:20px;padding:4px 10px;text-align:center;}* html #at16psf input,*:first-child+html #at16psf input{padding:0;}#at16psf input,#at16psf input:focus{background:#fff;border:none;width:220px;margin:2px 0 0;color:#666;outline-style:none;outline-width:0;padding:2px 0 0;line-height:12px;font-family:arial,helvetica,tahoma,verdana,sans-serif;font-size:12px;}#at16pcc .at_error,#at16recap .at_error{background:#f26d7d;border-bottom:1px solid #df5666;padding:5px 10px;color:#fff;}#at16pcc #at_success{background:#d0fbda;border-bottom:1px solid #a8e7b7;padding:5px 10px;color:#4c4c4c;}#at15pf,#at16pf{position:relative;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;-o-box-sizing:content-box;background:#f2f2f2;height:12px;border-top:1px solid #e5e5e5;}.ipad #at15pf{padding-top:4px;background:#fff;}#at15pf a,#at16pf a,#at15pf span,#at16pf span{position:absolute;outline:none;padding:0;margin:0;overflow:hidden;font-size:10px;color:#4c4c4c;font-family:Arial,Helvetica,Sans-Serif;text-decoration:none;}#at15pf a:hover,#at15pf a:focus,#at16pf a:hover,#at16pf a:focus{text-decoration:underline;}#at15pf a.at-settings,#at16pf a.at-settings{left:75px;width:65px;}#at15pf a.at-settingsclose,#at16pf a.at-settingsclose{left:8px;}#at15pf a.at-whatsthis{left:8px;}#at16pf a.at-whatsthis{left:10px;}#at16pf a#at-privacy,#at16pf a.at-privacy,#at16pf a.at-privacy-close{width:39px;left:140px;}#at_complete{font-size:13pt;color:#47731d;text-align:center;padding-top:130px;height:208px!important;width:472px;}#at_s_msg{margin-bottom:10px;}.atabout{left:55px;}.ac-about{right:20px;}#at20mc a.ac-logo:hover{text-decoration:none!important;}#at15pf .ac-logo,#at16pf .ac-logo{background:url(data:image/gif;base64,R0lGODlhDAAMAMQAAAOqygqszBGvzROwzRqyzx2z0Cm30S240zK71EG/10bB2E7E21XF217J3mfM3mjN33PQ4nrT5YzZ5pvd6aLf66Pg7Lno8MHr8c7v9NXx9uL1+ez4+/T7/f7+/wAAAAAAACH5BAkAAB4ALAAAAAAMAAwAAAVCoCeO5GgUU0kCxnBYqhdc2zMkWTl7koUEjs1o1oFwPJQCYXjhHISeBYC5GWgkAtQQ4wEQBEZSoFFRMDSqASASa5dCADs=) no-repeat left;padding-left:10px;top:0;right:2px;}.ipad #at15pf .ac-logo{top:3px;}#at15pf a.at-logo,#at16pf a.at-logo{background:url(data:image/gif;base64,R0lGODlhBwAHAJEAAP9uQf///wAAAAAAACH5BAkKAAIALAAAAAAHAAcAAAILFH6Ge8EBH2MKiQIAOw==) no-repeat left 2px;padding-left:10px;right:10px;float:left;}.at_baa{display:block;overflow:hidden;outline:none;}#at15s #at16pf a{top:1px;}#at16pc form #at_send{width:80px!important;}#at16pp{color:#4c4c4c;position:absolute;top:12px;right:12px;font-size:11px;}#at16pp label{font-size:11px!important;}#at16ppc{padding:10px;width:179px;}#at16pph{padding:5px 0 10px 0;}#at16pph select{margin:5px 0 8px 0;}#at16pp .atinp{width:156px;}html>/**/body #at16pp .atinp{width:176px;}#at16ppb{background:#fff;border:1px solid #ccc;height:274px;}#at16ep{height:16px;padding:8px;}#at16ep a{display:block;height:16px;line-height:16px;padding-left:22px;margin-bottom:8px;font-size:12px;}#at16ep a.at_gmail{background:url(data:image/gif;base64,R0lGODlhEAAQALMPAPKqo95TU+NkY/TCwP74+PbX1/zo59wtJ/nx7uZ7fvnRzfCTgvq2td9DQf///////yH5BAEAAA8ALAAAAAAQABAAAARi8MlJq700hMS6/4vWNIdQOERKOMgyvqSgOLRjJAe8CUcw0ApeYyF4DQpCwCDQGyCKo59BGDtNjbRBIvazQRtSxgCwGDAMrO/AcK7ZztcRoO1+B43oOs0Qb8w/gAxFGISFFREAOw==) no-repeat left;}#at16ep a.at_hotmail{background:url(data:image/gif;base64,R0lGODlhEAAQAMQfAP7XFG7B4/zjl/JZIAm7TK7V7v3FY/aLRGDNhOqmkA2ql/2YJvfr2Pn7++9vWtXe6/jQvOfw9funZg2EzEWv3zil0heg0zDCbESHx9PpxY6TvJ3QpPJtQf7+/v///////yH5BAEAAB8ALAAAAAAQABAAAAWO4CeOpNhAUFeuzDEMiRepK/S+XDBVjzd6kAWHc3tMjpVZhyE8cByvDsViOQYehsPCSeR8IpQpFZMwGCQHl/dToAQoionGLEHDRJ5CoHJRkM92ED8FCgQEGHNoDgsCJB4XhgpzZwsAjSQZFxcIGgCengwlHRsIpQKfAg0rHQiGEacGqisfDZsdtzSzHz4rIQA7) no-repeat left;}#at16ep a.at_yahoo{background:url(data:image/gif;base64,R0lGODlhEAAQAKIHAPylpevx8bsICNJfX/jQ0Kahof8AAP///yH5BAEAAAcALAAAAAAQABAAAANJeLrc/jAuAmolcQhjhBiBBRDDAChAVxzE5g3csKRGQQpFqDL0fsCCQCOFUwR8vI7wECgtjQDg6CfA8DxYmWbVCHi/TK9kTC4zEgA7) no-repeat left;}#at16ppf p#atsb{padding-top:20px;font-size:10px;}#at16abr{margin-top:10px;}#at16abr input{padding:0;margin:0;margin-right:5px;}#at16ppso{display:none;text-align:right;margin-top:2px;}#at16ppa{background:#fff;border:1px solid #ccc;height:228px;width:178px;overflow:auto;}#at16ppa a{display:block;white-space:nowrap;padding:4px 8px;font-size:12px!important;}#at16eatdr{position:absolute;background:#fff;border-top:0;max-height:110px;overflow:auto;z-index:500;top:129px;left:21px;width:277px;}* html #at_email #at16eatdr{top:115px!important;width:272px!important;}*:first-child+html #at_email #at16eatdr{top:115px!important;width:272px!important;}html>/**/body #at_email form #at16eatdr.abif{top:137px;width:278px!important;}#at16eatdr a{display:block;overflow:hidden;border-bottom:1px dotted #eee;padding:4px 8px;}#at16eatdr a:hover,#at16eatdr a.hover{background:#e0eefa;text-decoration:none;color:#333;}#at_pspromo{height:130px;padding-top:10px;}#at_pspromo,#at15psp{width:205px;padding-left:5px;}#at_testpromo{font-size:12px;width:220px;display:none;}.atm-i #at_pspromo{height:150px;}.atm-i #at_testpromo,.atm-i #at_pspromo{width:140px;}#at_testpromo input{width:200px;}#at_promo .at-promo-content,#at_testpromo .at-promo-content{margin-top:12px;}#at_promo .at-promo-btn,#at_testpromo .at-promo-btn{padding-top:10px;}#at_promo h4,#at_testpromo h4{font-family:arial,helvetica,tahoma,verdana,sans-serif;background:0;font-size:14px;font-weight:bold;margin:0 0 4px;padding:0;line-height:18px;height:36px;}.atm-i #at_promo h4,.atm-i #at_testpromo h4{height:66px;}#at_testpromo h4{font-size:13.5px;}#at_promo h4 sup{font-size:11px;color:#ee6a44;}#at_promo span{display:block;}#_atssh{width:1px!important;height:1px!important;border:0!important;}.at-promo-single{padding:10px;padding-top:2px;line-height:1.5em;}.at-promo-single img{padding:3px;}.at-promo-content img{margin-right:5px;margin-bottom:20px;float:left;}@media print{#at20mc{display:none;}}#at20mc.ipad #at15s{background-color:#fff!important;background-image:none!important;border:1px solid #b7b7b7;width:244px!important;padding:6px!important;-moz-border-radius:12px;-webkit-border-radius:12px;-moz-box-shadow:0 0 10px #000;-webkit-box-shadow:0 0 10px #000;}html>body #at20mc.ipad #at15s{width:244px!important;}#at20mc.ipad #at15s .at_item{padding:10px 15px!important;line-height:32px!important;}#at20mc.ipad #at15pfpro,#at20mc.ipad #at15s_head{display:none!important;}#at20mc.ipad #at_hover{padding:0 0 4px 0;}#at20mc.ipad #at_hover .at_item{width:210px!important;font-size:18px;border-bottom:0;margin-bottom:0;}#at20mc.ipad #at_hover .at_item:hover{background:#2095f0!important;text-decoration:none;color:#fff!important;}#at20mc.ipad #at_hover .at15t{background-color:transparent!important;height:32px!important;width:200px!important;line-height:32px!important;padding-left:42px!important;padding-top:0!important;}#at20mc.ipad #at16pf{background:#fff!important;border-top:none;line-height:12px;}.addthis_textshare{display:block;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABKCAYAAAAYJRJMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABmNJREFUeNrsmk9oI3UUx99vZjL5n7TbukLbxYve1INa8KAi6F48ubAHV2+KWCoqyF4WpZZ6cuthq1gKe1oU9+CCoLjg+uciCEWQPSjuReyy7Vq7W7LNJM0kM5nxvV9+U5JNk/xCm8wmMw8ek06TXyafee/93u83XwY1Y+jKwsLCiUwm87Gqqg8oigJBMsdxoFqtXs/n86fn5ua+plPoLhNw1NnZ2ZPZbPai67pgWRbQMUjGGINIJMKPOzs7p5aXly/h6apGkYMexZOLSA/K5TKYpgm2bQcKkKZpEIvFIBqNUnAs4qlv0U0CpKInt7a2pjC1+JuDBoeMsobcMAxKtSliQqcJEHmSIofCK8hGgMhEeSFABS+CopVKJfCAPBOAosRG82awEFATIKrNTPOKuAyg54/p8O6Lz0AylgKnVAEo4WxXruKIdW9SGBSNPCz9+jv8uFUdZEAchgcIZAC98+wjMK644Nz8B9x8GcCogFvarQHC6g9ModEhHk/CWw/eD5fX1wYZEDQAkinSCasCzvYdcAwsZgWLR6H78ttgRxDOd1+AbuTAxZnQrZiQsC0+5tAAkomgqmGCi/0C5EuYk5heWMKSj06DFU9B8fKXwBAgRHRe2qr4JTTm0ADyprj2gMocgJPIQnUkhhmlAMPBGEaLmRkDB18r2LJHSwb/kkEF1NBAdgPIMTFqbBOiL7wCypPP1apYMg0RPIzNvs+hFK6uAlz8hC9kZMYcKkBAk1KEpjwTtGKeF3oWT2ApUkEvl8Ct2qBZou5gLQoeIPzRDAty5colqHz/FQeTPHMObKxBhU8/AH17k69pIJ3ka+GhAoTrj84ppqje3gC6eD8WZBfP8887uIajFXEGZzXLkRpzYADRfkjHANIwv1yH9gZ49FDfY63+DI4eraVWIopwYgAp/NtmUmMODCAZy+EqfyyVxS66KKhWofLN55yXnsK0ymI9SscgMn4Ecuvrw7EN0s2b3/tpFT48/hSMJBKw1ypo0VrhjmtYn3SEpUJ+YxPOXPhhODbS0DPox9D/CJepDfYw+g0l5NDeQkAhoMMBRCV3LcSxZ2uCCQfkikXEEnopZMMZLAkmLrXGfE8anR5lUPNyH/rRgML5E/08+m/oW+iGJsBQ53dL/OM6ekr0SL3YpGYicl9Cf6zLz/6LfgH9NjRu9B54C0hwKAgwtwQT27tYiqARET1j6Gl0vUeAFDE2PVZ5Df0Jyc/dRD8nLr7spcAhAqLNKwN9W3zHHfoeVndH6aIT4sJjPYwgSum4uBGUyrPo0xJwzorovi3utNWDCDJF5OwKYA6jTS5W22tl4uK9Z2W9agEUcQOOoE+gT6K/2QYSwfkI/W/x+pb4AdVDTjNHjGl7Y5OxfosUxM2IiDpHETTVBlI9nBvo/6Hv0N3F6+7LVkHfG0W3dkesuoJIM+cG+mdikmgFZ7PfcPgN9Uvm0iKSJkQkTYqaUw8n3284vgJqAYkAjYsamBNR5BucrveDepFuyMhLNxDF0RCpnxdTrm9wfI+gfSIpJloAJnqdkp9w7hlAdZAUkV7etOv4CQfqGkF+cSsrKyfS6TQXcQZNCkOBQiJOwzBOz8zMNIs45+fnT46OjnIRJ0nwgijipGd6dMzlcqeQR6OIs1gsLpZKJf48nVQZw/BMq6v1j6pyAaeu6/S4qlnEub29PeVpo4MGxzNPbIGAmkWcGEEQNPF4y0VZ7YFno4iTtNEhoAZAjSLOEFAToEYRJxXoToCePurCG8cfh2QsCc4uNsDm/iLO3YIB569eg192tEEG1CjipAjq1Pu8Pv0QjDEHnI32Is5YPAmvTqTgymZuYHuiprUYRVAnQHHLlBdx4oxAYwYKkE0iTlVOxGm7MFyAZFLMIRGnJifipFSjMYcGkIym2SFlvSYr4hxcQPvuB8kA4utqJifipFo0qELyfQFJaZoVrSsR51DppGV+jOP1SZIizqECFIo4OwCSsVDE2cFCEWdo9RaKOGUsBBQCOhxAoYiz0dYgFHG2tFDE2cZCEWerdTiEIs6OgEIRp0QENYs468I+FHHeJeIEIV7wY3lzYBGn+EE9N9Wn2cK7W5Y4ErRrAtRkCzh7Urx+wfETUCtIf9UBOus3HOhRET5IurUVcfYbTtf7QT2wes00QBsRpx9w7oUIujuS9hVx+gWHX1jQpHbd2v8CDAAwldUwLVojIgAAAABJRU5ErkJggg==) no-repeat 0 0;width:44px;height:37px;line-height:28px;padding:0 0 0 28px;margin:0;text-decoration:none;font-family:helvetica,arial,sans-serif;font-size:12px;color:#fff;cursor:pointer;}.addthis_textshare:hover{background-position:0 -37px;text-decoration:none;}.at_img_share{position:absolute;opacity:0;background:url(data:image/gif;base64,R0lGODlhFwAVAMQAAP7+/vLy8vv7+/X19fj4+Pz8/PHx8f39/fDw8O/v7/T09Pn5+fPz8/r6+vb29vf394CAgHZ2dm5ubklJSWRkZFtbW39/f4KCglJSUnt7e3h4eAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAAXABUAAAWLICCOZGmeaAocbOu+MFvMdG3fs6DvfO//PY0QqGsYj8iMEslsLJ7QqGUarS4I2Kz2wtV6vwSIeEyGfB/odGTNbkfSaYd8Lqnb75L5fMDv+ymAfoKDghWGhH0KiouMGI6MkAoMk5SVE5eVmQwBnJ2en6ChoqMBBqanqKmqpgitrq+wsa0JtLW2t7i0IQA7) repeat-x bottom;border:1px solid #ccc;width:23px;height:21px;line-height:21px;text-indent:-9999px;padding:0;margin:0;cursor:pointer;z-index:1000;}.at_img_share:hover{border-color:#8b8b8b;}.at_img_share .addthis_toolbox{width:180px;margin:0 auto;}.atm{width:160px!important;padding:0;margin:0;line-height:12px;letter-spacing:normal;font-family:arial,helvetica,tahoma,verdana,sans-serif;font-size:12px;color:#444;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABtJREFUeNpiZGBgaGAgAjAxEAlGFVJHIUCAAQDcngCUgqGMqwAAAABJRU5ErkJggg==);padding:4px;}.atm-i{background:#fff;border:1px solid #d5d6d6;padding:0;margin:0;-webkit-box-shadow:1px 1px 5px rgba(0,0,0,.15);-moz-box-shadow:1px 1px 5px rgba(0,0,0,.15);box-shadow:1px 1px 5px rgba(0,0,0,.15);}.atm-s{margin:0!important;padding:0!important;}.atm-s a:focus{border:transparent;outline:0;-webkit-transition:none;-moz-transition:none;transition:none;}.atm-s a,#at_hover.atm-s a{display:block;text-decoration:none;padding:4px 10px;color:#235dab!important;font-weight:normal;font-style:normal;-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}#at_hover.atm-s .at_bold{color:#235dab!important;}.atm-s a:hover,#at_hover.atm-s a:hover{background:#2095f0;text-decoration:none;color:#fff!important;}#at_hover.atm-s .at_bold{font-weight:bold;}#at_hover.atm-s a:hover .at_bold{color:#fff!important;}.atm-s a span{padding-left:20px;direction:ltr;}.atm-i #at15pf.atm-f-iemode2,.atm-i #at16pf.atm-f-iemode2{height:24px!important;}.atm-i #atic_settings{border:none!important;border-top:1px solid #d5d6d6!important;padding-top:6px!important;top:4px;}.atm-f,#at15pf .atm-f{position:relative;border:none!important;border-top:1px solid #d5d6d6!important;background:none!important;padding:5px 10px;font-size:9px;top:4px;}.atm-f a{margin-right:4px;text-decoration:none!important;color:#939292!important;top:4px!important;font-weight:normal;font-style:normal;}.atm-f a:hover{color:#4f4f4f!important;}.atm-f .atm-f-logo{position:absolute;top:5px;right:6px;background:url(data:image/gif;base64,R0lGODlhBwAHAJEAAP9uQf///wAAAAAAACH5BAkKAAIALAAAAAAHAAcAAAILFH6Ge8EBH2MKiQIAOw==) no-repeat left;padding-left:10px;}.at_a11y{position:absolute!important;top:auto!important;width:1px!important;height:1px!important;overflow:hidden!important;}.at_a11y_container{margin:0;padding:0;}.addthis_overlay_container{position:absolute;}.addthis_overlay_toolbox{-webkit-border-top-left-radius:10px;-webkit-border-top-right-radius:10px;-moz-border-radius-topleft:10px;-moz-border-radius-topright:10px;border-top-left-radius:10px;border-top-right-radius:10px;padding:5px;background-color:#000;background-color:rgba(0,0,0,0.6);}.linkServiceDiv{height:200px;width:400px;border:1px solid black;background-color:#AAA;}.at_redloading{background:url(data:image/gif;base64,R0lGODlhCgAKAJEDAMzMzP9mZv8AAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQFAAADACwAAAAACgAKAAACF5wncgaAGgJzJ647cWua4sOBFEd62VEAACH5BAUAAAMALAEAAAAIAAMAAAIKnBM2IoMDAFMQFAAh+QQFAAADACwAAAAABgAGAAACDJwHMBGofKIRItJYAAAh+QQFAAADACwAAAEAAwAIAAACChxgOBPBvpYQYxYAIfkEBQAAAwAsAAAEAAYABgAAAgoEhmPJHOGgEGwWACH5BAUAAAMALAEABwAIAAMAAAIKBIYjYhOhRHqpAAAh+QQFAAADACwEAAQABgAGAAACDJwncqi7EQYAA0p6CgAh+QQJAAADACwHAAEAAwAIAAACCpRmoxoxvQAYchQAOw==);height:16px;width:16px;background-repeat:no-repeat;background-size:16px;margin:0 auto;}.at-promo-single-dl-ch{width:120px;height:37px;}.at-promo-single-dl-ff{width:120px;height:44px;}.at-promo-single-dl-saf{width:120px;height:48px;}.at-promo-single-dl-ie{width:129px;height:51px;}.atPinBox{position:fixed;top:25%;left:35%;background:#fff;width:482px;margin:0 auto;overflow:auto;overflow-x:hidden;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABtJREFUeNpiZGBgaGAgAjAxEAlGFVJHIUCAAQDcngCUgqGMqwAAAABJRU5ErkJggg==);border-radius:8px;-webkit-border-radius:8px;-moz-border-radius:8px;padding:8px;font-family:arial,helvetica,tahoma,verdana,sans-serif;font-size:12px;color:#CFCACA;z-index:10000001;}.atPinHdr,.atPinWinHdr{display:block;background:#f1f1f1;border-bottom:1px solid #ccc;box-shadow:0 0 3px rgba(0,0,0,.1);-webkit-box-shadow:0 0 3px rgba(0,0,0,.1);-moz-box-shadow:0 0 3px rgba(0,0,0,.1);padding:8px 10px;font-size:16px;line-height:16px;color:#8C7E7E;}.atPinHdr img,.atPinWinHdr img{vertical-align:bottom;margin-left:5px;cursor:pointer;}.atPinHdr span{vertical-align:top;}.atPinHdr{height:16px;}.atPinMn{background:#fff;padding:10px;height:296px;overflow:auto;overflow-x:hidden;text-align:center;position:relative;}.atPinHdrMsg{left:20px;}.atPinClose{width:12px;text-align:right;font-weight:bold;position:absolute;right:15px;cursor:pointer;}.atImgSpanOuter{position:relative;overflow:hidden;height:200px;width:200px;border:1px solid #a0a0a0;float:left;display:block;margin:10px;background-color:#FFF;}.atImgSpanInner img{cursor:pointer;}.atImgSpanSize{position:absolute;bottom:0;left:0;right:0;display:block;background:#fff;height:22px;line-height:24px;color:#000;overflow:hidden;font-size:10px;zoom:1;filter:alpha(opacity=70);opacity:.7;}.atImgActBtn{display:none;width:32px;height:32px;position:absolute;top:75px;left:80px;background-color:#FFF;}.atPinWin{font-family:arial,helvetica,tahoma,verdana,sans-serif;text-align:center;}.atPinWinHdr{display:block;font-size:20px;height:20px;width:100%;position:fixed;z-index:1;}.atPinWinMn{text-align:center;padding:40px 0 0 0;display:inline-block;}.atImgMsg,.atImgIco{float:left;}.atImgIco{margin-right:5px;}.atNoImg{display:block;margin-top:40px;font-size:16px;line-height:16px;color:#8C7E7E;}.at_PinItButton{display:block;width:40px;height:20px;padding:0;margin:0;background-image:url(//s7.addthis.com/static/t00/pinit00.png);background-repeat:no-repeat;}.at_PinItButton:hover{background-position:0 -20px;}.addthis_toolbox .addthis_button_pinterest_pinit{position:relative;}.at3PinWinMn{text-align:center;padding:20px 0 0 20px;overflow:auto;height:437px;}.at3ImgSpanOuter{position:relative;width:185px;height:185px;border:1px solid #dedede;margin:0 10px 10px 0;overflow:hidden;float:left;}.at3ImgSpanOuter:hover{border-color:#3dadfc;box-shadow:0 0 3px #3dadfc;cursor:pointer;}.at3ImgSpanOuter .atImgLB{display:block;position:absolute;top:0;bottom:0;left:0;right:0;z-index:1;background-color:rgba(0,0,0,.8);background-repeat:no-repeat;background-position:center center;}#at3lb{position:fixed;top:0;right:0;left:0;bottom:0;z-index:16777270;display:none;}.at3lblight{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABtJREFUeNpizCuu/sRABGBiIBKMKqSOQoAAAwC8KgJipENhxwAAAABJRU5ErkJggg==);background:rgba(110,115,123,.65);}.at3lbdark{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABtJREFUeNpiZGBg2M9ABGBiIBKMKqSOQoAAAwBAlwDTJEe1aAAAAABJRU5ErkJggg==);background:rgba(0,0,0,.5);}.at3lbnone{background:rgba(255,255,255,0);}#at3win{position:fixed;_position:absolute;top:15%;left:50%;margin-left:-320px;background:#fff;border:1px solid #d2d2d1;width:640px;-webkit-box-shadow:0 0 8px 4px rgba(0,0,0,0.25);-moz-box-shadow:0 0 8px 4px rgba(0,0,0,0.25);box-shadow:0 0 8px 4px rgba(0,0,0,0.25);font-family:"helvetica neue",helvetica,arial,sans-serif;z-index:16777271;display:none;overflow:hidden;}#at3win #at3winheader{position:relative;border-bottom:1px solid #d2d2d1;background:#f1f1f1;height:49px;cursor:default;}#at3win #at3winheader p{position:absolute;top:16px;left:100px;width:475px;padding:0;margin:0;font-size:14px;line-height:18px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}#at3win #at3winheader h3{height:49px;text-align:left;line-height:49px;margin:0 50px 0 22px;border:0;padding:0 20px;font-size:16px;font-family:"helvetica neue",helvetica,arial,sans-serif;font-weight:bold;text-shadow:0 1px #fff;color:#333;direction:ltr;}#at3win #at3winheader h3.logoaddthis{padding-left:22px;}#at3win #at3winheader .at3winheadersvc{display:inline-block;position:absolute;top:15px;left:20px;cursor:default!important;opacity:1!important;}#at3win #at3winheader #at3winheaderclose{display:block;position:absolute;top:0;right:0;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2tpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjQwNzc2QTQ5Qjk1RDExRTFCMkE4OEUxNTUwRjMwREY0IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjQwNzc2QTQ4Qjk1RDExRTFCMkE4OEUxNTUwRjMwREY0IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InV1aWQ6OEE1QUU0REMzMEU4REYxMUJCNzJGQkJCQzlBM0Y1RkMiIHN0UmVmOmRvY3VtZW50SUQ9InV1aWQ6M0M5RkJGRTEyQUU4REYxMUJCNzJGQkJCQzlBM0Y1RkMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz78RHhFAAAApUlEQVR42rxTiQnAIAxU6QAdxRW6iZ1EnKRu4gqO0g1sCilEvT7Q0kBQ9O4wl6hLKepNGPUyhmMTQhhpSZTZez8jMGEWWizlRJi1fUHiS8dARHaMSaiELPaViCB3WC1NBMB4CMozWaJuuwBE1BkZdoEB8Qn5kzaaC7fbgN0xN+TYlNOJmCvyXjPwpBKRL7BnhgERiwQmHhDothDJjMVz8Ptv3AQYAJWjVVdnlDZCAAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:center center;border-left:1px solid #d2d2d1;width:49px;height:49px;line-height:49px;overflow:hidden;text-indent:-9999px;text-shadow:none;cursor:pointer;}#at3win #at3winheader #at3winheaderclose:hover{background-color:#DEDEDE;}#at3win #at3wincontent{height:440px;position:relative;}#at3winshare,#at3wincopy,#at3winemail{height:440px;}#at3wincontent,#at20mc{-moz-box-sizing:content-box;-webkit-box-sizing:content-box;-o-box-sizing:content-box;box-sizing:content-box;}#at3win #at3wincontent.at3nowin{position:relative;height:400px;padding:20px;overflow:auto;}#at3winfooter{position:relative;background:#fff;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;-o-box-sizing:content-box;box-sizing:content-box;border-top:1px solid #d2d2d1;height:11px;_height:20px;line-height:11px;padding:5px 20px;font-size:11px;color:#666;}#at3winfooter a{margin-right:10px;text-decoration:none;color:#666;float:left;}#at3winfooter a:hover{text-decoration:none;color:#000;}#at3logo{background:url(//s7.addthis.com/static/t00/at3logo-sm.gif) no-repeat left center!important;padding-left:10px;}#at3privacy{position:absolute;top:5px;right:10px;background:url(//s7.addthis.com/static/t00/at3-privacy.gif) no-repeat right center!important;padding-right:14px;}#at3winfilter{background:#f1f1f1;border-top:1px solid #fff;border-bottom:1px solid #d2d2d1;padding:13px 0;text-align:center;}#at3winsvc-filter{background-repeat:no-repeat;background-position:right;background-image:url(data:image/gif;base64,R0lGODlhHgAUALMAAJiYmHV1deTk5Kmpqbe3t9nZ2Y2Njfn5+fT09Ozs7MnJyYGBgWpqav39/WZmZv///yH5BAAAAAAALAAAAAAeABQAAASi8MlXxgoLqDa7/xICOGTpLAKoTshCMsZgBG+6gqNjJA93DAxH4HDzCEgGTqdBIBGKnSYjoewcXAvoZJRVDUhErcEBWClIPC1X1fg6ENrHl4GoThquQJxCKn+kA3sPY2QHSkwMQQJ2Nw0INEIABBYmATZxCQtBJpyWgg0KBkEMCwQKm0KXgoYTBaiegh8NriUBabFLtH24Hg2zm368HgULKDcRADs=);border:1px solid #d2d2d1;padding:15px 38px 15px 12px;margin:0 auto;width:374px;text-align:left;font-size:18px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;box-shadow:inset 0 1px 2px rgba(0,0,0,.1);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);color:#666;}#service-filter:hover{border-color:#9c9c9c;}#service-filter:focus{border-color:#3dadfc;box-shadow:0 0 4px rgba(61,173,252,.8);-webkit-box-shadow:0 0 4px rgba(61,173,252,.8);-moz-box-shadow:0 0 4px rgba(61,173,252,.8);outline:none;}#at3wintoolbox{margin:0 0 0 20px;height:340px;overflow:auto;padding:10px 0;}#at3wintoolbox a{display:block;float:left;width:180px;padding:4px;margin-bottom:10px;text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-radius:4px;-moz-border-radius:4px;-webkit-border-radius:4px;font-size:16px;color:#235dab;}#at3wintoolbox a:hover,#at3wintoolbox a:focus{background-color:#2095f0;text-decoration:none;color:#fff;font-weight:normal;text-shadow:none;opacity:1;filter:alpha(opacity=100);cursor:pointer;}#at3wintoolbox span:hover{text-decoration:none;color:#fff;font-weight:normal;text-shadow:none;opacity:1;filter:alpha(opacity=100);cursor:pointer;}#at3wintoolbox span{display:block;height:32px;line-height:32px;padding-left:38px!important;width:auto!important;}.service-icon{padding:4px 8px;}.service-icon:hover{background:#2095f0;color:#fff;}.service-icon span{padding-left:20px;}#at3winssi{position:absolute;right:50px;top:0;height:50px;display:block;}.at-quickshare-header-peep{position:absolute;top:0;right:34px;height:16px;padding:6px;border-left:1px solid #dedede;cursor:pointer;}.at-quickshare-header-peep.peep-active{background:#dedede;cursor:default;}.at-quickshare-header-peep span{display:inline-block;background:url(data:image/gif;base64,R0lGODlhBwAEAIABALm5uf///yH5BAEAAAEALAAAAAAHAAQAAAIIhA+BGWoNWSgAOw==) no-repeat right;padding-right:11px;}.at-quickshare-header-peep span img{display:block;background:#ccc;width:16px;height:16px;line-height:20px;overflow:hidden;text-indent:-9999px;border:1px solid #bbb;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;}.at-quickshare-header-peep ul{position:absolute;top:25px;left:-75px;width:140px;background:#fff;border:1px solid #bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;box-shadow:0 1px 4px rgba(102,102,102,.8);margin:0;padding:0;font-weight:normal;z-index:1100;}.at-quickshare-header-peep ul li{list-style:none;font-size:12px;padding:0;margin:0;text-align:left;}.at-quickshare-menu{outline:none;}.at-quickshare-menu li.at-quickshare-menu-sep{border-bottom:1px solid #dedede;}.at-quickshare-header-peep ul li a{display:block;padding:5px 10px;text-decoration:none;color:#666;}.at-quickshare-header-peep ul li a:hover{background:#0d98fb;text-decoration:none;color:#fff;}#at_auth{position:relative;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;-o-box-sizing:content-box;border-top:1px solid #d5d6d6!important;padding:10px 10px 7px;line-height:16px;height:16px;}#atic_signin{text-decoration:none;cursor:pointer;}#atic_signin:hover{text-decoration:none;}#atic_signin #at_auth:hover{background:#2095f0;text-decoration:none;color:#fff!important;}#atic_usersettings{cursor:pointer;}#atic_usersettings:hover{text-decoration:underline;}#atic_usersignout{font-size:11px;position:absolute;top:10px;right:10px;cursor:pointer;}#atic_usersignout:hover{text-decoration:underline;}#at_auth img{width:16px;height:16px;overflow:hidden;border:none;padding:0;margin:0 5px 0 0;float:left;}#at_auth a{text-decoration:none;}.addthis_20x20_style .at15t_100zakladok{background-position:0 -0px!important;}.hi20 .addthis_20x20_style .dummy .at15t_100zakladok{background-position:0 -0px!important;}.addthis_20x20_style .at15t_addthis{background-position:0 -20px!important;}.hi20 .addthis_20x20_style .dummy .at15t_addthis{background-position:0 -20px!important;}.addthis_20x20_style .at15t_adifni{background-position:0 -40px!important;}.hi20 .addthis_20x20_style .dummy .at15t_adifni{background-position:0 -40px!important;}.addthis_20x20_style .at15t_aim{background-position:0 -60px!important;}.hi20 .addthis_20x20_style .dummy .at15t_aim{background-position:0 -60px!important;}.addthis_20x20_style .at15t_amazonwishlist{background-position:0 -80px!important;}.hi20 .addthis_20x20_style .dummy .at15t_amazonwishlist{background-position:0 -80px!important;}.addthis_20x20_style .at15t_arto{background-position:0 -100px!important;}.hi20 .addthis_20x20_style .dummy .at15t_arto{background-position:0 -100px!important;}.addthis_20x20_style .at15t_baidu{background-position:0 -120px!important;}.hi20 .addthis_20x20_style .dummy .at15t_baidu{background-position:0 -120px!important;}.addthis_20x20_style .at15t_bitly{background-position:0 -140px!important;}.hi20 .addthis_20x20_style .dummy .at15t_bitly{background-position:0 -140px!important;}.addthis_20x20_style .at15t_blogger{background-position:0 -160px!important;}.hi20 .addthis_20x20_style .dummy .at15t_blogger{background-position:0 -160px!important;}.addthis_20x20_style .at15t_bloggy{background-position:0 -180px!important;}.hi20 .addthis_20x20_style .dummy .at15t_bloggy{background-position:0 -180px!important;}.addthis_20x20_style .at15t_bobrdobr{background-position:0 -200px!important;}.hi20 .addthis_20x20_style .dummy .at15t_bobrdobr{background-position:0 -200px!important;}.addthis_20x20_style .at15t_delicious{background-position:0 -220px!important;}.hi20 .addthis_20x20_style .dummy .at15t_delicious{background-position:0 -220px!important;}.addthis_20x20_style .at15t_digg{background-position:0 -240px!important;}.hi20 .addthis_20x20_style .dummy .at15t_digg{background-position:0 -240px!important;}.addthis_20x20_style .at15t_diggita{background-position:0 -260px!important;}.hi20 .addthis_20x20_style .dummy .at15t_diggita{background-position:0 -260px!important;}.addthis_20x20_style .at15t_draugiem{background-position:0 -280px!important;}.hi20 .addthis_20x20_style .dummy .at15t_draugiem{background-position:0 -280px!important;}.addthis_20x20_style .at15t_ekudos{background-position:0 -300px!important;}.hi20 .addthis_20x20_style .dummy .at15t_ekudos{background-position:0 -300px!important;}.addthis_20x20_style .at15t_email{background-position:0 -320px!important;}.hi20 .addthis_20x20_style .dummy .at15t_email{background-position:0 -320px!important;}.addthis_20x20_style .at15t_facebook{background-position:0 -340px!important;}.hi20 .addthis_20x20_style .dummy .at15t_facebook{background-position:0 -340px!important;}.addthis_20x20_style .at15t_favorites{background-position:0 -360px!important;}.hi20 .addthis_20x20_style .dummy .at15t_favorites{background-position:0 -360px!important;}.addthis_20x20_style .at15t_friendfeed{background-position:0 -380px!important;}.hi20 .addthis_20x20_style .dummy .at15t_friendfeed{background-position:0 -380px!important;}.addthis_20x20_style .at15t_gmail{background-position:0 -400px!important;}.hi20 .addthis_20x20_style .dummy .at15t_gmail{background-position:0 -400px!important;}.addthis_20x20_style .at15t_google{background-position:0 -420px!important;}.hi20 .addthis_20x20_style .dummy .at15t_google{background-position:0 -420px!important;}.addthis_20x20_style .at15t_google_plusone_share{background-position:0 -440px!important;}.hi20 .addthis_20x20_style .dummy .at15t_google_plusone_share{background-position:0 -440px!important;}.addthis_20x20_style .at15t_hatena{background-position:0 -460px!important;}.hi20 .addthis_20x20_style .dummy .at15t_hatena{background-position:0 -460px!important;}.addthis_20x20_style .at15t_hotmail{background-position:0 -480px!important;}.hi20 .addthis_20x20_style .dummy .at15t_hotmail{background-position:0 -480px!important;}.addthis_20x20_style .at15t_jappy{background-position:0 -500px!important;}.hi20 .addthis_20x20_style .dummy .at15t_jappy{background-position:0 -500px!important;}.addthis_20x20_style .at15t_linkedin{background-position:0 -520px!important;}.hi20 .addthis_20x20_style .dummy .at15t_linkedin{background-position:0 -520px!important;}.addthis_20x20_style .at15t_live{background-position:0 -540px!important;}.hi20 .addthis_20x20_style .dummy .at15t_live{background-position:0 -540px!important;}.addthis_20x20_style .at15t_livejournal{background-position:0 -560px!important;}.hi20 .addthis_20x20_style .dummy .at15t_livejournal{background-position:0 -560px!important;}.addthis_20x20_style .at15t_mailto{background-position:0 -580px!important;}.hi20 .addthis_20x20_style .dummy .at15t_mailto{background-position:0 -580px!important;}.addthis_20x20_style .at15t_meinvz{background-position:0 -600px!important;}.hi20 .addthis_20x20_style .dummy .at15t_meinvz{background-position:0 -600px!important;}.addthis_20x20_style .at15t_meneame{background-position:0 -620px!important;}.hi20 .addthis_20x20_style .dummy .at15t_meneame{background-position:0 -620px!important;}.addthis_20x20_style .at15t_misterwong{background-position:0 -640px!important;}.hi20 .addthis_20x20_style .dummy .at15t_misterwong{background-position:0 -640px!important;}.addthis_20x20_style .at15t_more{background-position:0 -660px!important;}.hi20 .addthis_20x20_style .dummy .at15t_more{background-position:0 -660px!important;}.addthis_20x20_style .at15t_mymailru{background-position:0 -680px!important;}.hi20 .addthis_20x20_style .dummy .at15t_mymailru{background-position:0 -680px!important;}.addthis_20x20_style .at15t_myspace{background-position:0 -700px!important;}.hi20 .addthis_20x20_style .dummy .at15t_myspace{background-position:0 -700px!important;}.addthis_20x20_style .at15t_netlog{background-position:0 -720px!important;}.hi20 .addthis_20x20_style .dummy .at15t_netlog{background-position:0 -720px!important;}.addthis_20x20_style .at15t_nujij{background-position:0 -740px!important;}.hi20 .addthis_20x20_style .dummy .at15t_nujij{background-position:0 -740px!important;}.addthis_20x20_style .at15t_oknotizie{background-position:0 -760px!important;}.hi20 .addthis_20x20_style .dummy .at15t_oknotizie{background-position:0 -760px!important;}.addthis_20x20_style .at15t_orkut{background-position:0 -780px!important;}.hi20 .addthis_20x20_style .dummy .at15t_orkut{background-position:0 -780px!important;}.addthis_20x20_style .at15t_oyyla{background-position:0 -800px!important;}.hi20 .addthis_20x20_style .dummy .at15t_oyyla{background-position:0 -800px!important;}.addthis_20x20_style .at15t_pinterest_share{background-position:0 -820px!important;}.hi20 .addthis_20x20_style .dummy .at15t_pinterest_share{background-position:0 -820px!important;}.addthis_20x20_style .at15t_plurk{background-position:0 -840px!important;}.hi20 .addthis_20x20_style .dummy .at15t_plurk{background-position:0 -840px!important;}.addthis_20x20_style .at15t_print{background-position:0 -860px!important;}.hi20 .addthis_20x20_style .dummy .at15t_print{background-position:0 -860px!important;}.addthis_20x20_style .at15t_pusha{background-position:0 -880px!important;}.hi20 .addthis_20x20_style .dummy .at15t_pusha{background-position:0 -880px!important;}.addthis_20x20_style .at15t_reddit{background-position:0 -900px!important;}.hi20 .addthis_20x20_style .dummy .at15t_reddit{background-position:0 -900px!important;}.addthis_20x20_style .at15t_settings{background-position:0 -920px!important;}.hi20 .addthis_20x20_style .dummy .at15t_settings{background-position:0 -920px!important;}.addthis_20x20_style .at15t_sonico{background-position:0 -940px!important;}.hi20 .addthis_20x20_style .dummy .at15t_sonico{background-position:0 -940px!important;}.addthis_20x20_style .at15t_studivz{background-position:0 -960px!important;}.hi20 .addthis_20x20_style .dummy .at15t_studivz{background-position:0 -960px!important;}.addthis_20x20_style .at15t_stumbleupon{background-position:0 -980px!important;}.hi20 .addthis_20x20_style .dummy .at15t_stumbleupon{background-position:0 -980px!important;}.addthis_20x20_style .at15t_tuenti{background-position:0 -1000px!important;}.hi20 .addthis_20x20_style .dummy .at15t_tuenti{background-position:0 -1000px!important;}.addthis_20x20_style .at15t_tumblr{background-position:0 -1020px!important;}.hi20 .addthis_20x20_style .dummy .at15t_tumblr{background-position:0 -1020px!important;}.addthis_20x20_style .at15t_twitter{background-position:0 -1040px!important;}.hi20 .addthis_20x20_style .dummy .at15t_twitter{background-position:0 -1040px!important;}.addthis_20x20_style .at15t_viadeo{background-position:0 -1060px!important;}.hi20 .addthis_20x20_style .dummy .at15t_viadeo{background-position:0 -1060px!important;}.addthis_20x20_style .at15t_vk{background-position:0 -1080px!important;}.hi20 .addthis_20x20_style .dummy .at15t_vk{background-position:0 -1080px!important;}.addthis_20x20_style .at15t_wordpress{background-position:0 -1100px!important;}.hi20 .addthis_20x20_style .dummy .at15t_wordpress{background-position:0 -1100px!important;}.addthis_20x20_style .at15t_wykop{background-position:0 -1120px!important;}.hi20 .addthis_20x20_style .dummy .at15t_wykop{background-position:0 -1120px!important;}.addthis_20x20_style .at15t_xing{background-position:0 -1140px!important;}.hi20 .addthis_20x20_style .dummy .at15t_xing{background-position:0 -1140px!important;}.addthis_20x20_style .at15t_yahoobkm{background-position:0 -1160px!important;}.hi20 .addthis_20x20_style .dummy .at15t_yahoobkm{background-position:0 -1160px!important;}.addthis_20x20_style .at15t_yahoomail{background-position:0 -1180px!important;}.hi20 .addthis_20x20_style .dummy .at15t_yahoomail{background-position:0 -1180px!important;}.addthis_20x20_style .at15t_yorumcuyum{background-position:0 -1200px!important;}.hi20 .addthis_20x20_style .dummy .at15t_yorumcuyum{background-position:0 -1200px!important;}.addthis_20x20_style .at15t_compact{background-position:0 -660px!important;}.hi20 .addthis_20x20_style .dummy .at15t_compact{background-position:0 -660px!important;}.addthis_20x20_style .at15t_expanded{background-position:0 -660px!important;}.hi20 .addthis_20x20_style .dummy .at15t_expanded{background-position:0 -660px!important;}@media screen and/*!YUI Compressor */(max-width:680px){#at3win{width:95%;left:auto;margin-left:auto;}} diff --git a/Chapter 7/serving_files/bin/packages/http_server/http_server.dart b/Chapter 7/serving_files/bin/packages/http_server/http_server.dart new file mode 100644 index 0000000..afaf71c --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/http_server/http_server.dart @@ -0,0 +1,90 @@ +// Copyright (c) 2013, 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. + +/** + * A library for serving HTTP requests and resources. + * + * ## Installing ## + * + * Use [pub][] to install this package. Add the following to your + * `pubspec.yaml` file. + * + * dependencies: + * http_server: any + * + * Then run `pub install`. + * + * For more information, see the + * [http_server package on pub.dartlang.org][pub]. + * + * ## Basic usage + * + * Here is a short example of how to serve all files from the current + * directory. + * + * import 'dart:io'; + * import 'dart:async'; + * import 'package:http_server/http_server.dart'; + * + * void main() { + * var staticFiles = new VirtualDirectory('.') + * ..allowDirectoryListing = true; + * + * runZoned(() { + * HttpServer.bind('0.0.0.0', 7777).then((server) { + * print('Server running'); + * server.listen(staticFiles.serveRequest); + * }); + * }, + * onError: (e, stackTrace) => print('Oh noes! $e $stackTrace')); + * } + * + * ## Virtual directory + * + * The [VirtualDirectory] class makes it easy to serve static content + * from the file system. It supports: + * + * * Range-based requests. + * * If-Modified-Since based caching. + * * Automatic GZip-compression of content. + * * Following symlinks, either throughout the system or inside + * a jailed root. + * * Directory listing. + * + * See [VirtualDirectory] for more information. + * + * ## Virtual host + * + * The [VirtualHost] class helps to serve multiple hosts on the same + * address, by using the `Host` field of the incoming requests. It also + * works with wildcards for sub-domains. + * + * var virtualHost = new VirtualHost(server); + * // Filter out on a specific host + * var stream1 = virtualServer.addHost('static.myserver.com'); + * // Wildcard for any other sub-domains. + * var stream2 = virtualServer.addHost('*.myserver.com'); + * // Requets not matching any hosts. + * var stream3 = virtualServer.unhandled; + * + * See [VirtualHost] for more information. + * + * [pub]: http://pub.dartlang.org/packages/http_server + */ +library http_server; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:mime/mime.dart'; +import "package:path/path.dart"; + +part 'src/http_body.dart'; +part 'src/http_body_impl.dart'; +part 'src/http_multipart_form_data.dart'; +part 'src/http_multipart_form_data_impl.dart'; +part 'src/virtual_directory.dart'; +part 'src/virtual_host.dart'; + diff --git a/Chapter 7/serving_files/bin/packages/http_server/src/http_body.dart b/Chapter 7/serving_files/bin/packages/http_server/src/http_body.dart new file mode 100644 index 0000000..6eb4d31 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/http_server/src/http_body.dart @@ -0,0 +1,206 @@ +// Copyright (c) 2013, 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. + +part of http_server; + + +/** + * [HttpBodyHandler] is a helper class for processing and collecting + * HTTP message data in an easy-to-use [HttpBody] object. The content + * body is parsed, depending on the `Content-Type` header field. When + * the full body is read and parsed the body content is made + * available. The class can be used to process both server requests + * and client responses. + * + * The following content types are recognized: + * + * text/ * + * application/json + * application/x-www-form-urlencoded + * multipart/form-data + * + * For content type `text/\*` the body is decoded into a string. The + * 'charset' parameter of the content type specifies the encoding + * used for decoding. If no 'charset' is present the default encoding + * of ISO-8859-1 is used. + * + * For content type `application/json` the body is decoded into a + * string which is then parsed as JSON. The resulting body is a + * [Map]. The 'charset' parameter of the content type specifies the + * encoding used for decoding. If no 'charset' is present the default + * encoding of UTF-8 is used. + * + * For content type `application/x-www-form-urlencoded` the body is a + * query string which is then split according to the rules for + * splitting a query string. The resulting body is a `Map`. If the same name is present several times in the query + * string, then the last value seen for this name will be in the + * resulting map. The encoding US-ASCII is always used for decoding + * the body. + * + * For content type `multipart/form-data` the body is parsed into + * it's different fields. The resulting body is a `Map`, where the value is a [String] for normal fields and a + * [HttpBodyFileUpload] instance for file upload fields. If the same + * name is present several times, then the last value seen for this + * name will be in the resulting map. + * + * When using content type `multipart/form-data` the encoding of + * fields with [String] values is determined by the browser sending + * the HTTP request with the form data. The encoding is specified + * either by the attribute `accept-charset` on the HTML form, or by + * the content type of the web page containing the form. If the HTML + * form has an `accept-charset` attribute the browser will use the + * encoding specified there. If the HTML form has no `accept-charset` + * attribute the browser determines the encoding from the content + * type of the web page containing the form. Using a content type of + * `text/html; charset=utf-8` for the page and setting + * `accept-charset` on the HTML form to `utf-8` is recommended as the + * default for [HttpBodyHandler] is UTF-8. It is important to get + * these encoding values right, as the actual `multipart/form-data` + * HTTP request sent by the browser does _not_ contain any + * information on the encoding. If something else than UTF-8 is used + * `defaultEncoding` needs to be set in the [HttpBodyHandler] + * constructor and calls to [processRequest] and [processResponse]. + * + * For all other content types the body will be treated as + * uninterpreted binary data. The resulting body will be of type + * `List`. + * + * To use with the [HttpServer] for request messages, [HttpBodyHandler] can be + * used as either a [StreamTransformer] or as a per-request handler (see + * [processRequest]). + * + * HttpServer server = ... + * server.transform(new HttpBodyHandler()) + * .listen((HttpRequestBody body) { + * ... + * }); + * + * To use with the [HttpClient] for response messages, [HttpBodyHandler] can be + * used as a per-request handler (see [processResponse]). + * + * HttpClient client = ... + * client.get(...) + * .then((HttpClientRequest response) => response.close()) + * .then(HttpBodyHandler.processResponse) + * .then((HttpClientResponseBody body) { + * ... + * }); + * + */ +class HttpBodyHandler + implements StreamTransformer { + var _transformer; + + /** + * Create a new [HttpBodyHandler] to be used with a [Stream]<[HttpRequest]>, + * e.g. a [HttpServer]. + * + * If the page is served using different encoding than UTF-8, set + * [defaultEncoding] accordingly. This is required for parsing + * `multipart/form-data` content correctly. See the class comment + * for more information on `multipart/form-data`. + */ + HttpBodyHandler({Encoding defaultEncoding: UTF8}) + : _transformer = new _HttpBodyHandlerTransformer(defaultEncoding); + + /** + * Process and parse an incoming [HttpRequest]. The returned [HttpRequestBody] + * contains a [response] field for accessing the [HttpResponse]. + * + * See [HttpBodyHandler] constructor for more info on [defaultEncoding]. + */ + static Future processRequest( + HttpRequest request, + {Encoding defaultEncoding: UTF8}) { + return _HttpBodyHandler.processRequest(request, defaultEncoding); + } + + /** + * Process and parse an incoming [HttpClientResponse]. + * + * See [HttpBodyHandler] constructor for more info on [defaultEncoding]. + */ + static Future processResponse( + HttpClientResponse response, + {Encoding defaultEncoding: UTF8}) { + return _HttpBodyHandler.processResponse(response, defaultEncoding); + } + + Stream bind(Stream stream) { + return _transformer.bind(stream); + } +} + + +/** + * A HTTP content body produced by [HttpBodyHandler] for either [HttpRequest] + * or [HttpClientResponse]. + */ +abstract class HttpBody { + /** + * A high-level type value, that reflects how the body was parsed, e.g. + * "text", "binary" and "json". + */ + String get type; + + /** + * The actual body. The type depends on [type]. + */ + dynamic get body; +} + + +/** + * The [HttpBody] of a [HttpClientResponse] will be of type + * [HttpClientResponseBody]. It contains the [HttpClientResponse] object + * for access to the headers. + */ +abstract class HttpClientResponseBody extends HttpBody { + /** + * The [HttpClientResponse] from which the [HttpClientResponseBody] was + * created. + */ + HttpClientResponse get response; +} + + +/** + * The [HttpBody] of a [HttpRequest] will be of type [HttpRequestBody]. It + * provides access to the request, for reading all request header information + * and responding to the client. + */ +abstract class HttpRequestBody extends HttpBody { + /** + * The [HttpRequest] from which the [HttpRequestBody] was created. + * + * Note that the [HttpRequest] is already drained at this point, so the + * `Stream` methods cannot be used. + */ + HttpRequest get request; +} + + +/** + * A [HttpBodyFileUpload] object wraps a file upload, presenting a way for + * extracting filename, contentType and the data of the uploaded file. + */ +abstract class HttpBodyFileUpload { + /** + * The filename of the uploaded file. + */ + String get filename; + + /** + * The [ContentType] of the uploaded file. For 'text/\*' and + * 'application/json' the [data] field will a String. + */ + ContentType get contentType; + + /** + * The content of the file. Either a [String] or a [List]. + */ + dynamic get content; +} diff --git a/Chapter 7/serving_files/bin/packages/http_server/src/http_body_impl.dart b/Chapter 7/serving_files/bin/packages/http_server/src/http_body_impl.dart new file mode 100644 index 0000000..7682902 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/http_server/src/http_body_impl.dart @@ -0,0 +1,205 @@ +// Copyright (c) 2013, 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. + +part of http_server; + +class _HttpBodyHandlerTransformer + implements StreamTransformer { + final Encoding _defaultEncoding; + + const _HttpBodyHandlerTransformer(this._defaultEncoding); + + Stream bind(Stream stream) { + return new Stream.eventTransformed( + stream, + (EventSink sink) => + new _HttpBodyHandlerTransformerSink(_defaultEncoding, sink)); + } +} + +class _HttpBodyHandlerTransformerSink implements EventSink { + final Encoding _defaultEncoding; + final EventSink _outSink; + int _pending = 0; + bool _closed = false; + + _HttpBodyHandlerTransformerSink(this._defaultEncoding, this._outSink); + + void add(HttpRequest request) { + _pending++; + _HttpBodyHandler.processRequest(request, _defaultEncoding) + .then(_outSink.add, onError: _outSink.addError) + .whenComplete(() { + _pending--; + if (_closed && _pending == 0) _outSink.close(); + }); + } + void addError(Object error, [StackTrace stackTrace]) { + _outSink.addError(error, stackTrace); + } + void close() { + _closed = true; + if (_pending == 0) _outSink.close(); + } +} + +class _HttpBodyHandler { + static Future processRequest( + HttpRequest request, + Encoding defaultEncoding) { + return process(request, request.headers, defaultEncoding) + .then((body) => new _HttpRequestBody(request, body), + onError: (error) { + // Try to send BAD_REQUEST response. + request.response.statusCode = HttpStatus.BAD_REQUEST; + request.response.close(); + throw error; + }); + } + + static Future processResponse( + HttpClientResponse response, + Encoding defaultEncoding) { + return process(response, response.headers, defaultEncoding) + .then((body) => new _HttpClientResponseBody(response, body)); + } + + static Future process(Stream> stream, + HttpHeaders headers, + Encoding defaultEncoding) { + ContentType contentType = headers.contentType; + + Future asBinary() { + return stream + .fold(new BytesBuilder(), (builder, data) => builder..add(data)) + .then((builder) => new _HttpBody("binary", builder.takeBytes())); + } + + Future asText(Encoding defaultEncoding) { + var encoding; + var charset = contentType.charset; + if (charset != null) encoding = Encoding.getByName(charset); + if (encoding == null) encoding = defaultEncoding; + return stream + .transform(encoding.decoder) + .fold(new StringBuffer(), (buffer, data) => buffer..write(data)) + .then((buffer) => new _HttpBody("text", buffer.toString())); + } + + Future asFormData() { + return stream + .transform(new MimeMultipartTransformer( + contentType.parameters['boundary'])) + .map((part) => HttpMultipartFormData.parse( + part, defaultEncoding: defaultEncoding)) + .map((multipart) { + var future; + if (multipart.isText) { + future = multipart + .fold(new StringBuffer(), (b, s) => b..write(s)) + .then((b) => b.toString()); + } else { + future = multipart + .fold(new BytesBuilder(), (b, d) => b..add(d)) + .then((b) => b.takeBytes()); + } + return future.then((data) { + var filename = + multipart.contentDisposition.parameters['filename']; + if (filename != null) { + data = new _HttpBodyFileUpload(multipart.contentType, + filename, + data); + } + return [multipart.contentDisposition.parameters['name'], data]; + }); + }) + .fold([], (l, f) => l..add(f)) + .then(Future.wait) + .then((parts) { + Map map = new Map(); + for (var part in parts) { + map[part[0]] = part[1]; // Override existing entries. + } + return new _HttpBody('form', map); + }); + } + + if (contentType == null) { + return asBinary(); + } + + switch (contentType.primaryType) { + case "text": + return asText(defaultEncoding); + + case "application": + switch (contentType.subType) { + case "json": + return asText(UTF8) + .then((body) => new _HttpBody("json", JSON.decode(body.body))); + + case "x-www-form-urlencoded": + return asText(ASCII) + .then((body) { + var map = Uri.splitQueryString(body.body, + encoding: defaultEncoding); + var result = {}; + for (var key in map.keys) { + result[key] = map[key]; + } + return new _HttpBody("form", result); + }); + + default: + break; + } + break; + + case "multipart": + switch (contentType.subType) { + case "form-data": + return asFormData(); + + default: + break; + } + break; + + default: + break; + } + + return asBinary(); + } +} + +class _HttpBodyFileUpload implements HttpBodyFileUpload { + final ContentType contentType; + final String filename; + final dynamic content; + _HttpBodyFileUpload(this.contentType, this.filename, this.content); +} + +class _HttpBody implements HttpBody { + final String type; + final dynamic body; + + _HttpBody(this.type, this.body); +} + +class _HttpRequestBody extends _HttpBody implements HttpRequestBody { + final HttpRequest request; + + _HttpRequestBody(this.request, HttpBody body) + : super(body.type, body.body); +} + +class _HttpClientResponseBody + extends _HttpBody implements HttpClientResponseBody { + final HttpClientResponse response; + + _HttpClientResponseBody(this.response, HttpBody body) + : super(body.type, body.body); +} diff --git a/Chapter 7/serving_files/bin/packages/http_server/src/http_multipart_form_data.dart b/Chapter 7/serving_files/bin/packages/http_server/src/http_multipart_form_data.dart new file mode 100644 index 0000000..eae575b --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/http_server/src/http_multipart_form_data.dart @@ -0,0 +1,80 @@ +// Copyright (c) 2012, 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. + +part of http_server; + + +/** + * [:HttpMultipartFormData:] class used for 'upgrading' a [MimeMultipart] by + * parsing it as a 'multipart/form-data' part. The following code shows how + * it can be used. + * + * HttpServer server = ...; + * server.listen((request) { + * String boundary = request.headers.contentType.parameters['boundary']; + * request + * .transform(new MimeMultipartTransformer(boundary)) + * .map(HttpMultipartFormData.parse) + * .map((HttpMultipartFormData formData) { + * // form data object available here. + * }); + * + * [:HttpMultipartFormData:] is a Stream, serving either bytes or decoded + * Strings. Use [isText] or [isBinary] to see what type of data is provided. + */ +abstract class HttpMultipartFormData implements Stream { + /** + * The parsed [:Content-Type:] header of the [:HttpMultipartFormData:]. + * Returns [:null:] if not present. + */ + ContentType get contentType; + + /** + * The parsed [:Content-Disposition:] header of the [:HttpMultipartFormData:]. + * This field is always present. Use this to extract e.g. name(form field + * name)and filename (client provided name of uploaded file) parameters. + */ + HeaderValue get contentDisposition; + + /** + * The parsed [:Content-Transfer-Encoding:] header of the + * [:HttpMultipartFormData:]. This field is used to determine how to decode + * the data. Returns [:null:] if not present. + */ + HeaderValue get contentTransferEncoding; + + /** + * Returns [:true:] if the data is decoded as [String]. + */ + bool get isText; + + /** + * Returns [:true:] if the data is raw bytes. + */ + bool get isBinary; + + /** + * Returns the value for the header named [name]. If there + * is no header with the provided name, [:null:] will be returned. + * + * Use this method to index other headers available in the original + * [MimeMultipart]. + */ + String value(String name); + + /** + * Parse a [MimeMultipart] and return a [HttpMultipartFormData]. If the + * [:Content-Disposition:] header is missing or invalid, a [HttpException] is + * thrown. + * + * If the [MimeMultipart] is identified as text, and the [:Content-Type:] + * header is missing, the data is decoded using [defaultEncoding]. See more + * information in the + * [HTML5 spec](http://dev.w3.org/html5/spec-preview/ + * constraints.html#multipart-form-data). + */ + static HttpMultipartFormData parse(MimeMultipart multipart, + {Encoding defaultEncoding: UTF8}) + => _HttpMultipartFormData.parse(multipart, defaultEncoding); +} diff --git a/Chapter 7/serving_files/bin/packages/http_server/src/http_multipart_form_data_impl.dart b/Chapter 7/serving_files/bin/packages/http_server/src/http_multipart_form_data_impl.dart new file mode 100644 index 0000000..daebeec --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/http_server/src/http_multipart_form_data_impl.dart @@ -0,0 +1,142 @@ +// Copyright (c) 2012, 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. + +part of http_server; + + +class _HttpMultipartFormData extends Stream implements HttpMultipartFormData { + final ContentType contentType; + final HeaderValue contentDisposition; + final HeaderValue contentTransferEncoding; + + final MimeMultipart _mimeMultipart; + + bool _isText = false; + + Stream _stream; + + _HttpMultipartFormData(ContentType this.contentType, + HeaderValue this.contentDisposition, + HeaderValue this.contentTransferEncoding, + MimeMultipart this._mimeMultipart, + Encoding defaultEncoding) { + _stream = _mimeMultipart; + if (contentTransferEncoding != null) { + // TODO(ajohnsen): Support BASE64, etc. + throw new HttpException("Unsupported contentTransferEncoding: " + "${contentTransferEncoding.value}"); + } + + if (contentType == null || + contentType.primaryType == 'text' || + contentType.mimeType == 'application/json') { + _isText = true; + StringBuffer buffer = new StringBuffer(); + Encoding encoding; + if (contentType != null) { + encoding = Encoding.getByName(contentType.charset); + } + if (encoding == null) encoding = defaultEncoding; + _stream = _stream + .transform(encoding.decoder) + .expand((data) { + buffer.write(data); + var out = _decodeHttpEntityString(buffer.toString()); + if (out != null) { + buffer.clear(); + return [out]; + } + return const []; + }); + } + } + + bool get isText => _isText; + bool get isBinary => !_isText; + + static HttpMultipartFormData parse(MimeMultipart multipart, + Encoding defaultEncoding) { + var type; + var encoding; + var disposition; + var remaining = new Map(); + for (String key in multipart.headers.keys) { + switch (key) { + case 'content-type': + type = ContentType.parse(multipart.headers[key]); + break; + + case 'content-transfer-encoding': + encoding = HeaderValue.parse(multipart.headers[key]); + break; + + case 'content-disposition': + disposition = HeaderValue.parse(multipart.headers[key], + preserveBackslash: true); + break; + + default: + remaining[key] = multipart.headers[key]; + break; + } + } + if (disposition == null) { + throw new HttpException( + "Mime Multipart doesn't contain a Content-Disposition header value"); + } + return new _HttpMultipartFormData( + type, disposition, encoding, multipart, defaultEncoding); + } + + StreamSubscription listen(void onData(data), + {void onDone(), + Function onError, + bool cancelOnError}) { + return _stream.listen(onData, + onDone: onDone, + onError: onError, + cancelOnError: cancelOnError); + } + + String value(String name) { + return _mimeMultipart.headers[name]; + } + + // Decode a string with HTTP entities. Returns null if the string ends in the + // middle of a http entity. + static String _decodeHttpEntityString(String input) { + int amp = input.lastIndexOf('&'); + if (amp < 0) return input; + int end = input.lastIndexOf(';'); + if (end < amp) return null; + + var buffer = new StringBuffer(); + int offset = 0; + + parse(amp, end) { + switch (input[amp + 1]) { + case '#': + if (input[amp + 2] == 'x') { + buffer.writeCharCode( + int.parse(input.substring(amp + 3, end), radix: 16)); + } else { + buffer.writeCharCode(int.parse(input.substring(amp + 2, end))); + } + break; + + default: + throw new HttpException('Unhandled HTTP entity token'); + } + } + + while ((amp = input.indexOf('&', offset)) >= 0) { + buffer.write(input.substring(offset, amp)); + int end = input.indexOf(';', amp); + parse(amp, end); + offset = end + 1; + } + buffer.write(input.substring(offset)); + return buffer.toString(); + } +} diff --git a/Chapter 7/serving_files/bin/packages/http_server/src/virtual_directory.dart b/Chapter 7/serving_files/bin/packages/http_server/src/virtual_directory.dart new file mode 100644 index 0000000..6eef0b4 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/http_server/src/virtual_directory.dart @@ -0,0 +1,410 @@ +// Copyright (c) 2013, 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. + +part of http_server; + + +// Used for signal a directory redirecting, where a tailing slash is missing. +class _DirectoryRedirect { + const _DirectoryRedirect(); +} + +typedef dynamic _DirCallback(Directory dir, HttpRequest request); +typedef dynamic _ErrorCallback(HttpRequest request); + +/** + * A [VirtualDirectory] can serve files and directory-listing from a root path, + * to [HttpRequest]s. + * + * The [VirtualDirectory] providing secure handling of request uris and + * file-system links, correct mime-types and custom error pages. + */ +class VirtualDirectory { + final String root; + + /** + * Set or get if the [VirtualDirectory] should list the content of + * directories. + */ + bool allowDirectoryListing = false; + + /** + * Set or get if the [VirtualDirectory] should follow links, that point + * to other resources within the [root] directory. + */ + bool followLinks = true; + + /** + * Set or get if the [VirtualDirectory] should jail the root. When the root is + * not jailed, links can be followed to outside the [root] directory. + */ + bool jailRoot = true; + + final RegExp _invalidPathRegExp = new RegExp("[\\\/\x00]"); + + _ErrorCallback _errorCallback; + _DirCallback _dirCallback; + + /* + * Create a new [VirtualDirectory] for serving static file content of + * the path [root]. + * + * The [root] is not required to exist. If the [root] doesn't exist at time of + * a request, a 404 is generated. + */ + VirtualDirectory(this.root); + + /** + * Serve a [Stream] of [HttpRequest]s, in this [VirtualDirectory]. + */ + StreamSubscription serve(Stream requests) => + requests.listen(serveRequest); + + /** + * Serve a single [HttpRequest], in this [VirtualDirectory]. + */ + Future serveRequest(HttpRequest request) { + return _locateResource('.', request.uri.pathSegments.iterator..moveNext()) + .then((entity) { + if (entity is File) { + serveFile(entity, request); + } else if (entity is Directory) { + if (allowDirectoryListing) { + _serveDirectory(entity, request); + } else { + _serveErrorPage(HttpStatus.NOT_FOUND, request); + } + } else if (entity is _DirectoryRedirect) { + // TODO(ajohnsen): Use HttpRequest.requestedUri once 1.2 is out. + request.response.redirect(Uri.parse('${request.uri}/'), + status: HttpStatus.MOVED_PERMANENTLY); + } else { + assert(entity == null); + _serveErrorPage(HttpStatus.NOT_FOUND, request); + } + return request.response.done; + }); + } + + /** + * Set the [callback] to override the default directory listing. The + * [callback] will be called with the [Directory] to be listed and the + * [HttpRequest]. + */ + void set directoryHandler(void callback(Directory dir, HttpRequest request)) { + _dirCallback = callback; + } + + /** + * Set the [callback] to override the error page handler. When [callback] is + * invoked, the `statusCode` property of the response is set. + */ + void set errorPageHandler(void callback(HttpRequest request)) { + _errorCallback = callback; + } + + Future _locateResource(String path, Iterator segments) { + // Don't allow navigating up paths. + if (segments.current == "..") return new Future.value(null); + path = normalize(path); + // If we jail to root, the relative path can never go up. + if (jailRoot && split(path).first == "..") return new Future.value(null); + String fullPath() => join(root, path); + return FileSystemEntity.type(fullPath(), followLinks: false) + .then((type) { + switch (type) { + case FileSystemEntityType.FILE: + if (segments.current == null) { + return new File(fullPath()); + } + break; + + case FileSystemEntityType.DIRECTORY: + String dirFullPath() => '${fullPath()}$separator'; + var current = segments.current; + if (current == null) { + if (path == '.') return new Directory(dirFullPath()); + return const _DirectoryRedirect(); + } + bool hasNext = segments.moveNext(); + if (!hasNext && current == "") { + return new Directory(dirFullPath()); + } else { + if (_invalidPathRegExp.hasMatch(current)) break; + return _locateResource(join(path, current), segments); + } + break; + + case FileSystemEntityType.LINK: + if (followLinks) { + return new Link(fullPath()).target() + .then((target) { + String targetPath = normalize(target); + if (isAbsolute(targetPath)) { + // If we jail to root, the path can never be absolute. + if (jailRoot) return null; + return _locateResource(targetPath, segments); + } else { + targetPath = join(dirname(path), targetPath); + return _locateResource(targetPath, segments); + } + }); + } + break; + } + // Return `null` on fall-through, to indicate NOT_FOUND. + return null; + }); + } + + /** + * Serve the content of [file] to [request]. + * + * This is usefull when e.g. overriding [directoryHandler] to redirect to + * some index file. + * + * In the request contains the [HttpStatus.IF_MODIFIED_SINCE] header, + * [serveFile] will send a [HttpStatus.NOT_MODIFIED] response if the file + * was not changed. + * + * Note that if it was unabled to read from [file], the [request]s response + * is closed with error-code [HttpStatus.NOT_FOUND]. + */ + void serveFile(File file, HttpRequest request) { + var response = request.response; + // TODO(ajohnsen): Set up Zone support for these errors. + file.lastModified().then((lastModified) { + if (request.headers.ifModifiedSince != null && + !lastModified.isAfter(request.headers.ifModifiedSince)) { + response.statusCode = HttpStatus.NOT_MODIFIED; + response.close(); + return null; + } + + response.headers.set(HttpHeaders.LAST_MODIFIED, lastModified); + response.headers.set(HttpHeaders.ACCEPT_RANGES, "bytes"); + + if (request.method == 'HEAD') { + response.close(); + return null; + } + + return file.length().then((length) { + String range = request.headers.value("range"); + if (range != null) { + // We only support one range, where the standard support several. + Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range); + // If the range header have the right format, handle it. + if (matches != null) { + // Serve sub-range. + int start; + int end; + if (matches[1].isEmpty) { + start = matches[2].isEmpty ? + length : + length - int.parse(matches[2]); + end = length; + } else { + start = int.parse(matches[1]); + end = matches[2].isEmpty ? length : int.parse(matches[2]) + 1; + } + + // Override Content-Length with the actual bytes sent. + response.headers.set(HttpHeaders.CONTENT_LENGTH, end - start); + + // Set 'Partial Content' status code. + response.statusCode = HttpStatus.PARTIAL_CONTENT; + response.headers.set(HttpHeaders.CONTENT_RANGE, + "bytes $start-${end - 1}/$length"); + + // Pipe the 'range' of the file. + file.openRead(start, end) + .pipe(new _VirtualDirectoryFileStream(response, file.path)) + .catchError((_) { + // TODO(kevmoo): log errors + }); + return; + } + } + + file.openRead() + .pipe(new _VirtualDirectoryFileStream(response, file.path)) + .catchError((_) { + // TODO(kevmoo): log errors + }); + }); + }).catchError((_) { + response.statusCode = HttpStatus.NOT_FOUND; + response.close(); + }); + } + + void _serveDirectory(Directory dir, HttpRequest request) { + if (_dirCallback != null) { + _dirCallback(dir, request); + return; + } + var response = request.response; + dir.stat().then((stats) { + if (request.headers.ifModifiedSince != null && + !stats.modified.isAfter(request.headers.ifModifiedSince)) { + response.statusCode = HttpStatus.NOT_MODIFIED; + response.close(); + return; + } + + response.headers.set(HttpHeaders.LAST_MODIFIED, stats.modified); + var path = request.uri.path; + var header = +''' + + +Index of $path + + +

    Index of $path

    + + + + + + +'''; + var server = response.headers.value(HttpHeaders.SERVER); + if (server == null) server = ""; + var footer = +'''
    NameLast modifiedSize
    +$server + + +'''; + + response.write(header); + + void add(String name, String modified, var size) { + if (size == null) size = "-"; + if (modified == null) modified = ""; + var p = normalize(join(path, name)); + var entry = +''' + $name + $modified + $size + '''; + response.write(entry); + } + + if (path != '/') { + add('../', null, null); + } + + dir.list(followLinks: true).listen((entity) { + if (entity is File) { + var stat = entity.statSync(); + add(basename(entity.path), + stat.modified.toString(), + stat.size); + } else if (entity is Directory) { + add(basename(entity.path) + '/', + entity.statSync().modified.toString(), + null); + } + }, onError: (e) { + // TODO(kevmoo): log error + }, onDone: () { + response.write(footer); + response.close(); + }); + }, onError: (e) { + // TODO(kevmoo): log error + response.close(); + }); + } + + void _serveErrorPage(int error, HttpRequest request) { + var response = request.response; + response.statusCode = error; + if (_errorCallback != null) { + _errorCallback(request); + return; + } + // Default error page. + var path = request.uri.path; + var reason = response.reasonPhrase; + + var server = response.headers.value(HttpHeaders.SERVER); + if (server == null) server = ""; + var page = +''' + + +$reason: $path + + +

    Error $error at \'$path\': $reason

    +$server + +'''; + response.write(page); + response.close(); + } +} + +class _VirtualDirectoryFileStream extends StreamConsumer> { + final HttpResponse response; + final String path; + List buffer = []; + + _VirtualDirectoryFileStream(HttpResponse this.response, String this.path); + + Future addStream(Stream> stream) { + stream.listen( + (data) { + if (buffer == null) { + response.add(data); + return; + } + if (buffer.length == 0) { + if (data.length >= defaultMagicNumbersMaxLength) { + setMimeType(data); + response.add(data); + buffer = null; + } else { + buffer.addAll(data); + } + } else { + buffer.addAll(data); + if (buffer.length >= defaultMagicNumbersMaxLength) { + setMimeType(buffer); + response.add(buffer); + buffer = null; + } + } + }, + onDone: () { + if (buffer != null) { + if (buffer.length == 0) { + setMimeType(null); + } else { + setMimeType(buffer); + response.add(buffer); + } + } + response.close(); + }, + onError: response.addError); + return response.done; + } + + Future close() => new Future.value(); + + void setMimeType(List bytes) { + var mimeType = lookupMimeType(path, headerBytes: bytes); + if (mimeType != null) { + response.headers.contentType = ContentType.parse(mimeType); + } + } +} diff --git a/Chapter 7/serving_files/bin/packages/http_server/src/virtual_host.dart b/Chapter 7/serving_files/bin/packages/http_server/src/virtual_host.dart new file mode 100644 index 0000000..4c67eca --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/http_server/src/virtual_host.dart @@ -0,0 +1,149 @@ +// Copyright (c) 2013, 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. + +part of http_server; + + +/** + * The [VirtualHost] class is a utility class for handling multiple hosts on + * multiple sources, by using a named-based approach. + */ +abstract class VirtualHost { + /** + * Get the [Stream] of [HttpRequest]s, not matching any hosts. If unused, the + * default implementation will result in a [HttpHeaders.FORBIDDEN] response. + */ + Stream get unhandled; + + /** + * Construct a new [VirtualHost]. + * + * The optional [source] is a shortcut for calling [addSource]. + * + * Example of usage: + * + * HttpServer.bind(..., 80).then((server) { + * var virtualHost = new VirtualHost(server); + * virtualServer.addHost('static.myserver.com') + * .listen(...); + * virtualServer.addHost('cache.myserver.com') + * .listen(...); + * }) + */ + factory VirtualHost([Stream source]) => new _VirtualHost(source); + + /** + * Provide another source of [HttpRequest]s in the form of a [Stream]. + */ + void addSource(Stream source); + + + /** + * Add a host to the [VirtualHost] instance. The host can be either a specific + * domain (`my.domain.name`) or a wildcard-based domain name + * (`*.domain.name`). The former will only match the specific domain name + * while the latter will match any series of sub-domains. + * + * If both `my.domain.name` and `*.domain.name` is specified, the most + * qualified will take precedence, `my.domain.name` in this case. + */ + Stream addHost(String host); +} + + +class _VirtualHostDomain { + StreamController any; + StreamController exact; + Map subDomains = {}; +} + + +class _VirtualHost implements VirtualHost { + final _VirtualHostDomain _topDomain = new _VirtualHostDomain(); + StreamController _unhandledController; + + Stream get unhandled { + if (_unhandledController == null) { + _unhandledController = new StreamController(); + } + return _unhandledController.stream; + } + + _VirtualHost([Stream source]) { + if (source != null) addSource(source); + } + + void addSource(Stream source) { + source.listen((request) { + var host = request.headers.host; + if (host == null) { + _unhandled(request); + return; + } + var domains = host.split('.'); + var current = _topDomain; + var any; + for (var i = domains.length - 1; i >= 0; i--) { + if (current.any != null) any = current.any; + if (i == 0) { + var last = current.subDomains[domains[i]]; + if (last != null && last.exact != null) { + last.exact.add(request); + return; + } + } else { + if (!current.subDomains.containsKey(domains[i])) { + break; + } + current = current.subDomains[domains[i]]; + } + } + if (any != null) { + any.add(request); + return; + } + _unhandled(request); + }); + } + + Stream addHost(String host) { + if (host.lastIndexOf('*') > 0) { + throw new ArgumentError( + 'Wildcards are only allowed in the beginning of a host'); + } + var controller = new StreamController(); + var domains = host.split('.'); + var current = _topDomain; + for (var i = domains.length - 1; i >= 0; i--) { + if (domains[i] == '*') { + if (current.any != null) { + throw new ArgumentError('Host is already provided'); + } + current.any = controller; + } else { + if (!current.subDomains.containsKey(domains[i])) { + current.subDomains[domains[i]] = new _VirtualHostDomain(); + } + if (i > 0) { + current = current.subDomains[domains[i]]; + } else { + if (current.subDomains[domains[i]].exact != null) { + throw new ArgumentError('Host is already provided'); + } + current.subDomains[domains[i]].exact = controller; + } + } + } + return controller.stream; + } + + void _unhandled(HttpRequest request) { + if (_unhandledController != null) { + _unhandledController.add(request); + return; + } + request.response.statusCode = HttpStatus.FORBIDDEN; + request.response.close(); + } +} diff --git a/Chapter 7/serving_files/bin/packages/mime/mime.dart b/Chapter 7/serving_files/bin/packages/mime/mime.dart new file mode 100644 index 0000000..dce6774 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/mime/mime.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2013, 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. + +/** + * Help for working with file format identifiers + * such as `text/html` and `image/png`. + * + * More details, including a list of types, are in the Wikipedia article + * [Internet media type](http://en.wikipedia.org/wiki/Internet_media_type). + * For information on installing and importing this library, see the + * [mime package on pub.dartlang.org] + * (http://pub.dartlang.org/packages/mime). + */ +library mime; + +export 'src/mime_multipart_transformer.dart'; +export 'src/mime_shared.dart'; +export 'src/mime_type.dart'; diff --git a/Chapter 7/serving_files/bin/packages/mime/src/bound_multipart_stream.dart b/Chapter 7/serving_files/bin/packages/mime/src/bound_multipart_stream.dart new file mode 100644 index 0000000..d470034 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/mime/src/bound_multipart_stream.dart @@ -0,0 +1,375 @@ +// Copyright (c) 2014, 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. +library mime.bound_multipart_stream; + +import 'dart:async'; +import 'dart:convert'; + +import 'mime_shared.dart'; +import 'char_code.dart'; + +// Bytes for '()<>@,;:\\"/[]?={} \t'. +const _SEPARATORS = const [40, 41, 60, 62, 64, 44, 59, 58, 92, 34, 47, 91, 93, + 63, 61, 123, 125, 32, 9]; + +bool _isTokenChar(int byte) { + return byte > 31 && byte < 128 && _SEPARATORS.indexOf(byte) == -1; +} + +int _toLowerCase(int byte) { + const delta = CharCode.LOWER_A - CharCode.UPPER_A; + return (CharCode.UPPER_A <= byte && byte <= CharCode.UPPER_Z) ? + byte + delta : byte; +} + +void _expectByteValue(int val1, int val2) { + if (val1 != val2) { + throw new MimeMultipartException("Failed to parse multipart mime 1"); + } +} + +void _expectWhitespace(int byte) { + if (byte != CharCode.SP && byte != CharCode.HT) { + throw new MimeMultipartException("Failed to parse multipart mime 2"); + } +} + +class _MimeMultipart extends MimeMultipart { + final Map headers; + final Stream> _stream; + + _MimeMultipart(this.headers, this._stream); + + StreamSubscription> listen(void onData(List data), + {void onDone(), + Function onError, + bool cancelOnError}) { + return _stream.listen(onData, + onDone: onDone, + onError: onError, + cancelOnError: cancelOnError); + } +} + +class BoundMultipartStream { + static const int _START = 0; + static const int _FIRST_BOUNDARY_ENDING = 111; + static const int _FIRST_BOUNDARY_END = 112; + static const int _BOUNDARY_ENDING = 1; + static const int _BOUNDARY_END = 2; + static const int _HEADER_START = 3; + static const int _HEADER_FIELD = 4; + static const int _HEADER_VALUE_START = 5; + static const int _HEADER_VALUE = 6; + static const int _HEADER_VALUE_FOLDING_OR_ENDING = 7; + static const int _HEADER_VALUE_FOLD_OR_END = 8; + static const int _HEADER_ENDING = 9; + static const int _CONTENT = 10; + static const int _LAST_BOUNDARY_DASH2 = 11; + static const int _LAST_BOUNDARY_ENDING = 12; + static const int _LAST_BOUNDARY_END = 13; + static const int _DONE = 14; + static const int _FAIL = 15; + + final List _boundary; + final List _headerField = []; + final List _headerValue = []; + + StreamController _controller; + + Stream get stream => _controller.stream; + + StreamSubscription _subscription; + + StreamController _multipartController; + Map _headers; + + int _state = _START; + int _boundaryIndex = 2; + + // Current index in the data buffer. If index is negative then it + // is the index into the artificial prefix of the boundary string. + int _index; + List _buffer; + + BoundMultipartStream(this._boundary, Stream> stream) { + _controller = new StreamController( + sync: true, + onPause: _pauseStream, + onResume:_resumeStream, + onCancel: () { + _subscription.cancel(); + }, + onListen: () { + _subscription = stream.listen( + (data) { + assert(_buffer == null); + _pauseStream(); + _buffer = data; + _index = 0; + _parse(); + }, + onDone: () { + if (_state != _DONE) { + _controller.addError( + new MimeMultipartException("Bad multipart ending")); + } + _controller.close(); + }, + onError: _controller.addError); + }); + } + + void _resumeStream() { + _subscription.resume(); + } + + void _pauseStream() { + _subscription.pause(); + } + + + void _parse() { + // Number of boundary bytes to artificially place before the supplied data. + int boundaryPrefix = 0; + // Position where content starts. Will be null if no known content + // start exists. Will be negative of the content starts in the + // boundary prefix. Will be zero or position if the content starts + // in the current buffer. + int contentStartIndex; + + // Function to report content data for the current part. The data + // reported is from the current content start index up til the + // current index. As the data can be artificially prefixed with a + // prefix of the boundary both the content start index and index + // can be negative. + void reportData() { + if (contentStartIndex < 0) { + var contentLength = boundaryPrefix + _index - _boundaryIndex; + if (contentLength <= boundaryPrefix) { + _multipartController.add( + _boundary.sublist(0, contentLength)); + } else { + _multipartController.add( + _boundary.sublist(0, boundaryPrefix)); + _multipartController.add( + _buffer.sublist(0, contentLength - boundaryPrefix)); + } + } else { + var contentEndIndex = _index - _boundaryIndex; + _multipartController.add( + _buffer.sublist(contentStartIndex, contentEndIndex)); + } + } + + if (_state == _CONTENT && _boundaryIndex == 0) { + contentStartIndex = 0; + } else { + contentStartIndex = null; + } + // The data to parse might be "artificially" prefixed with a + // partial match of the boundary. + boundaryPrefix = _boundaryIndex; + + while ((_index < _buffer.length) && _state != _FAIL && _state != _DONE) { + if (_multipartController != null && _multipartController.isPaused) { + return; + } + int byte; + if (_index < 0) { + byte = _boundary[boundaryPrefix + _index]; + } else { + byte = _buffer[_index]; + } + switch (_state) { + case _START: + if (byte == _boundary[_boundaryIndex]) { + _boundaryIndex++; + if (_boundaryIndex == _boundary.length) { + _state = _FIRST_BOUNDARY_ENDING; + _boundaryIndex = 0; + } + } else { + // Restart matching of the boundary. + _index = _index - _boundaryIndex; + _boundaryIndex = 0; + } + break; + + case _FIRST_BOUNDARY_ENDING: + if (byte == CharCode.CR) { + _state = _FIRST_BOUNDARY_END; + } else { + _expectWhitespace(byte); + } + break; + + case _FIRST_BOUNDARY_END: + _expectByteValue(byte, CharCode.LF); + _state = _HEADER_START; + break; + + case _BOUNDARY_ENDING: + if (byte == CharCode.CR) { + _state = _BOUNDARY_END; + } else if (byte == CharCode.DASH) { + _state = _LAST_BOUNDARY_DASH2; + } else { + _expectWhitespace(byte); + } + break; + + case _BOUNDARY_END: + _expectByteValue(byte, CharCode.LF); + _multipartController.close(); + _multipartController = null; + _state = _HEADER_START; + break; + + case _HEADER_START: + _headers = new Map(); + if (byte == CharCode.CR) { + _state = _HEADER_ENDING; + } else { + // Start of new header field. + _headerField.add(_toLowerCase(byte)); + _state = _HEADER_FIELD; + } + break; + + case _HEADER_FIELD: + if (byte == CharCode.COLON) { + _state = _HEADER_VALUE_START; + } else { + if (!_isTokenChar(byte)) { + throw new MimeMultipartException("Invalid header field name"); + } + _headerField.add(_toLowerCase(byte)); + } + break; + + case _HEADER_VALUE_START: + if (byte == CharCode.CR) { + _state = _HEADER_VALUE_FOLDING_OR_ENDING; + } else if (byte != CharCode.SP && byte != CharCode.HT) { + // Start of new header value. + _headerValue.add(byte); + _state = _HEADER_VALUE; + } + break; + + case _HEADER_VALUE: + if (byte == CharCode.CR) { + _state = _HEADER_VALUE_FOLDING_OR_ENDING; + } else { + _headerValue.add(byte); + } + break; + + case _HEADER_VALUE_FOLDING_OR_ENDING: + _expectByteValue(byte, CharCode.LF); + _state = _HEADER_VALUE_FOLD_OR_END; + break; + + case _HEADER_VALUE_FOLD_OR_END: + if (byte == CharCode.SP || byte == CharCode.HT) { + _state = _HEADER_VALUE_START; + } else { + String headerField = UTF8.decode(_headerField); + String headerValue = UTF8.decode(_headerValue); + _headers[headerField.toLowerCase()] = headerValue; + _headerField.clear(); + _headerValue.clear(); + if (byte == CharCode.CR) { + _state = _HEADER_ENDING; + } else { + // Start of new header field. + _headerField.add(_toLowerCase(byte)); + _state = _HEADER_FIELD; + } + } + break; + + case _HEADER_ENDING: + _expectByteValue(byte, CharCode.LF); + _multipartController = new StreamController( + sync: true, + onPause: () { + _pauseStream(); + }, + onResume: () { + _resumeStream(); + _parse(); + }); + _controller.add( + new _MimeMultipart(_headers, _multipartController.stream)); + _headers = null; + _state = _CONTENT; + contentStartIndex = _index + 1; + break; + + case _CONTENT: + if (byte == _boundary[_boundaryIndex]) { + _boundaryIndex++; + if (_boundaryIndex == _boundary.length) { + if (contentStartIndex != null) { + _index++; + reportData(); + _index--; + } + _multipartController.close(); + _boundaryIndex = 0; + _state = _BOUNDARY_ENDING; + } + } else { + // Restart matching of the boundary. + _index = _index - _boundaryIndex; + if (contentStartIndex == null) contentStartIndex = _index; + _boundaryIndex = 0; + } + break; + + case _LAST_BOUNDARY_DASH2: + _expectByteValue(byte, CharCode.DASH); + _state = _LAST_BOUNDARY_ENDING; + break; + + case _LAST_BOUNDARY_ENDING: + if (byte == CharCode.CR) { + _state = _LAST_BOUNDARY_END; + } else { + _expectWhitespace(byte); + } + break; + + case _LAST_BOUNDARY_END: + _expectByteValue(byte, CharCode.LF); + _multipartController.close(); + _multipartController = null; + _state = _DONE; + break; + + default: + // Should be unreachable. + assert(false); + break; + } + + // Move to the next byte. + _index++; + } + + // Report any known content. + if (_state == _CONTENT && contentStartIndex != null) { + reportData(); + } + + // Resume if at end. + if (_index == _buffer.length) { + _buffer = null; + _index = null; + _resumeStream(); + } + } +} diff --git a/Chapter 7/serving_files/bin/packages/mime/src/char_code.dart b/Chapter 7/serving_files/bin/packages/mime/src/char_code.dart new file mode 100644 index 0000000..f455e68 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/mime/src/char_code.dart @@ -0,0 +1,16 @@ +// Copyright (c) 2014, 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. +library mime.char_code; + +class CharCode { + static const int HT = 9; + static const int LF = 10; + static const int CR = 13; + static const int SP = 32; + static const int DASH = 45; + static const int COLON = 58; + static const int UPPER_A = 65; + static const int UPPER_Z = 90; + static const int LOWER_A = 97; +} diff --git a/Chapter 7/serving_files/bin/packages/mime/src/default_extension_map.dart b/Chapter 7/serving_files/bin/packages/mime/src/default_extension_map.dart new file mode 100644 index 0000000..ae0d7df --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/mime/src/default_extension_map.dart @@ -0,0 +1,990 @@ +// Copyright (c) 2013, 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. + +library mime.extension_map; + +// TODO(ajohnsen): Use const map once Issue 7559 is fixed. +final Map defaultExtensionMap = { +'123':'application/vnd.lotus-1-2-3', +'3dml':'text/vnd.in3d.3dml', +'3ds':'image/x-3ds', +'3g2':'video/3gpp2', +'3gp':'video/3gpp', +'7z':'application/x-7z-compressed', +'aab':'application/x-authorware-bin', +'aac':'audio/x-aac', +'aam':'application/x-authorware-map', +'aas':'application/x-authorware-seg', +'abw':'application/x-abiword', +'ac':'application/pkix-attr-cert', +'acc':'application/vnd.americandynamics.acc', +'ace':'application/x-ace-compressed', +'acu':'application/vnd.acucobol', +'acutc':'application/vnd.acucorp', +'adp':'audio/adpcm', +'aep':'application/vnd.audiograph', +'afm':'application/x-font-type1', +'afp':'application/vnd.ibm.modcap', +'ahead':'application/vnd.ahead.space', +'ai':'application/postscript', +'aif':'audio/x-aiff', +'aifc':'audio/x-aiff', +'aiff':'audio/x-aiff', +'air':'application/vnd.adobe.air-application-installer-package+zip', +'ait':'application/vnd.dvb.ait', +'ami':'application/vnd.amiga.ami', +'apk':'application/vnd.android.package-archive', +'appcache':'text/cache-manifest', +'application':'application/x-ms-application', +'apr':'application/vnd.lotus-approach', +'arc':'application/x-freearc', +'asc':'application/pgp-signature', +'asf':'video/x-ms-asf', +'asm':'text/x-asm', +'aso':'application/vnd.accpac.simply.aso', +'asx':'video/x-ms-asf', +'atc':'application/vnd.acucorp', +'atom':'application/atom+xml', +'atomcat':'application/atomcat+xml', +'atomsvc':'application/atomsvc+xml', +'atx':'application/vnd.antix.game-component', +'au':'audio/basic', +'avi':'video/x-msvideo', +'aw':'application/applixware', +'azf':'application/vnd.airzip.filesecure.azf', +'azs':'application/vnd.airzip.filesecure.azs', +'azw':'application/vnd.amazon.ebook', +'bat':'application/x-msdownload', +'bcpio':'application/x-bcpio', +'bdf':'application/x-font-bdf', +'bdm':'application/vnd.syncml.dm+wbxml', +'bed':'application/vnd.realvnc.bed', +'bh2':'application/vnd.fujitsu.oasysprs', +'bin':'application/octet-stream', +'blb':'application/x-blorb', +'blorb':'application/x-blorb', +'bmi':'application/vnd.bmi', +'bmp':'image/bmp', +'book':'application/vnd.framemaker', +'box':'application/vnd.previewsystems.box', +'boz':'application/x-bzip2', +'bpk':'application/octet-stream', +'btif':'image/prs.btif', +'bz':'application/x-bzip', +'bz2':'application/x-bzip2', +'c':'text/x-c', +'c11amc':'application/vnd.cluetrust.cartomobile-config', +'c11amz':'application/vnd.cluetrust.cartomobile-config-pkg', +'c4d':'application/vnd.clonk.c4group', +'c4f':'application/vnd.clonk.c4group', +'c4g':'application/vnd.clonk.c4group', +'c4p':'application/vnd.clonk.c4group', +'c4u':'application/vnd.clonk.c4group', +'cab':'application/vnd.ms-cab-compressed', +'caf':'audio/x-caf', +'cap':'application/vnd.tcpdump.pcap', +'car':'application/vnd.curl.car', +'cat':'application/vnd.ms-pki.seccat', +'cb7':'application/x-cbr', +'cba':'application/x-cbr', +'cbr':'application/x-cbr', +'cbt':'application/x-cbr', +'cbz':'application/x-cbr', +'cc':'text/x-c', +'cct':'application/x-director', +'ccxml':'application/ccxml+xml', +'cdbcmsg':'application/vnd.contact.cmsg', +'cdf':'application/x-netcdf', +'cdkey':'application/vnd.mediastation.cdkey', +'cdmia':'application/cdmi-capability', +'cdmic':'application/cdmi-container', +'cdmid':'application/cdmi-domain', +'cdmio':'application/cdmi-object', +'cdmiq':'application/cdmi-queue', +'cdx':'chemical/x-cdx', +'cdxml':'application/vnd.chemdraw+xml', +'cdy':'application/vnd.cinderella', +'cer':'application/pkix-cert', +'cfs':'application/x-cfs-compressed', +'cgm':'image/cgm', +'chat':'application/x-chat', +'chm':'application/vnd.ms-htmlhelp', +'chrt':'application/vnd.kde.kchart', +'cif':'chemical/x-cif', +'cii':'application/vnd.anser-web-certificate-issue-initiation', +'cil':'application/vnd.ms-artgalry', +'cla':'application/vnd.claymore', +'class':'application/java-vm', +'clkk':'application/vnd.crick.clicker.keyboard', +'clkp':'application/vnd.crick.clicker.palette', +'clkt':'application/vnd.crick.clicker.template', +'clkw':'application/vnd.crick.clicker.wordbank', +'clkx':'application/vnd.crick.clicker', +'clp':'application/x-msclip', +'cmc':'application/vnd.cosmocaller', +'cmdf':'chemical/x-cmdf', +'cml':'chemical/x-cml', +'cmp':'application/vnd.yellowriver-custom-menu', +'cmx':'image/x-cmx', +'cod':'application/vnd.rim.cod', +'com':'application/x-msdownload', +'conf':'text/plain', +'cpio':'application/x-cpio', +'cpp':'text/x-c', +'cpt':'application/mac-compactpro', +'crd':'application/x-mscardfile', +'crl':'application/pkix-crl', +'crt':'application/x-x509-ca-cert', +'cryptonote':'application/vnd.rig.cryptonote', +'csh':'application/x-csh', +'csml':'chemical/x-csml', +'csp':'application/vnd.commonspace', +'css':'text/css', +'cst':'application/x-director', +'csv':'text/csv', +'cu':'application/cu-seeme', +'curl':'text/vnd.curl', +'cww':'application/prs.cww', +'cxt':'application/x-director', +'cxx':'text/x-c', +'dae':'model/vnd.collada+xml', +'daf':'application/vnd.mobius.daf', +'dart':'application/dart', +'dataless':'application/vnd.fdsn.seed', +'davmount':'application/davmount+xml', +'dbk':'application/docbook+xml', +'dcr':'application/x-director', +'dcurl':'text/vnd.curl.dcurl', +'dd2':'application/vnd.oma.dd2+xml', +'ddd':'application/vnd.fujixerox.ddd', +'deb':'application/x-debian-package', +'def':'text/plain', +'deploy':'application/octet-stream', +'der':'application/x-x509-ca-cert', +'dfac':'application/vnd.dreamfactory', +'dgc':'application/x-dgc-compressed', +'dic':'text/x-c', +'dir':'application/x-director', +'dis':'application/vnd.mobius.dis', +'dist':'application/octet-stream', +'distz':'application/octet-stream', +'djv':'image/vnd.djvu', +'djvu':'image/vnd.djvu', +'dll':'application/x-msdownload', +'dmg':'application/x-apple-diskimage', +'dmp':'application/vnd.tcpdump.pcap', +'dms':'application/octet-stream', +'dna':'application/vnd.dna', +'doc':'application/msword', +'docm':'application/vnd.ms-word.document.macroenabled.12', +'docx':'application/vnd.openxmlformats-officedocument.wordprocessingml.document', +'dot':'application/msword', +'dotm':'application/vnd.ms-word.template.macroenabled.12', +'dotx':'application/vnd.openxmlformats-officedocument.wordprocessingml.template', +'dp':'application/vnd.osgi.dp', +'dpg':'application/vnd.dpgraph', +'dra':'audio/vnd.dra', +'dsc':'text/prs.lines.tag', +'dssc':'application/dssc+der', +'dtb':'application/x-dtbook+xml', +'dtd':'application/xml-dtd', +'dts':'audio/vnd.dts', +'dtshd':'audio/vnd.dts.hd', +'dump':'application/octet-stream', +'dvb':'video/vnd.dvb.file', +'dvi':'application/x-dvi', +'dwf':'model/vnd.dwf', +'dwg':'image/vnd.dwg', +'dxf':'image/vnd.dxf', +'dxp':'application/vnd.spotfire.dxp', +'dxr':'application/x-director', +'ecelp4800':'audio/vnd.nuera.ecelp4800', +'ecelp7470':'audio/vnd.nuera.ecelp7470', +'ecelp9600':'audio/vnd.nuera.ecelp9600', +'ecma':'application/ecmascript', +'edm':'application/vnd.novadigm.edm', +'edx':'application/vnd.novadigm.edx', +'efif':'application/vnd.picsel', +'ei6':'application/vnd.pg.osasli', +'elc':'application/octet-stream', +'emf':'application/x-msmetafile', +'eml':'message/rfc822', +'emma':'application/emma+xml', +'emz':'application/x-msmetafile', +'eol':'audio/vnd.digital-winds', +'eot':'application/vnd.ms-fontobject', +'eps':'application/postscript', +'epub':'application/epub+zip', +'es3':'application/vnd.eszigno3+xml', +'esa':'application/vnd.osgi.subsystem', +'esf':'application/vnd.epson.esf', +'et3':'application/vnd.eszigno3+xml', +'etx':'text/x-setext', +'eva':'application/x-eva', +'evy':'application/x-envoy', +'exe':'application/x-msdownload', +'exi':'application/exi', +'ext':'application/vnd.novadigm.ext', +'ez':'application/andrew-inset', +'ez2':'application/vnd.ezpix-album', +'ez3':'application/vnd.ezpix-package', +'f':'text/x-fortran', +'f4v':'video/x-f4v', +'f77':'text/x-fortran', +'f90':'text/x-fortran', +'fbs':'image/vnd.fastbidsheet', +'fcdt':'application/vnd.adobe.formscentral.fcdt', +'fcs':'application/vnd.isac.fcs', +'fdf':'application/vnd.fdf', +'fe_launch':'application/vnd.denovo.fcselayout-link', +'fg5':'application/vnd.fujitsu.oasysgp', +'fgd':'application/x-director', +'fh':'image/x-freehand', +'fh4':'image/x-freehand', +'fh5':'image/x-freehand', +'fh7':'image/x-freehand', +'fhc':'image/x-freehand', +'fig':'application/x-xfig', +'flac':'audio/x-flac', +'fli':'video/x-fli', +'flo':'application/vnd.micrografx.flo', +'flv':'video/x-flv', +'flw':'application/vnd.kde.kivio', +'flx':'text/vnd.fmi.flexstor', +'fly':'text/vnd.fly', +'fm':'application/vnd.framemaker', +'fnc':'application/vnd.frogans.fnc', +'for':'text/x-fortran', +'fpx':'image/vnd.fpx', +'frame':'application/vnd.framemaker', +'fsc':'application/vnd.fsc.weblaunch', +'fst':'image/vnd.fst', +'ftc':'application/vnd.fluxtime.clip', +'fti':'application/vnd.anser-web-funds-transfer-initiation', +'fvt':'video/vnd.fvt', +'fxp':'application/vnd.adobe.fxp', +'fxpl':'application/vnd.adobe.fxp', +'fzs':'application/vnd.fuzzysheet', +'g2w':'application/vnd.geoplan', +'g3':'image/g3fax', +'g3w':'application/vnd.geospace', +'gac':'application/vnd.groove-account', +'gam':'application/x-tads', +'gbr':'application/rpki-ghostbusters', +'gca':'application/x-gca-compressed', +'gdl':'model/vnd.gdl', +'geo':'application/vnd.dynageo', +'gex':'application/vnd.geometry-explorer', +'ggb':'application/vnd.geogebra.file', +'ggt':'application/vnd.geogebra.tool', +'ghf':'application/vnd.groove-help', +'gif':'image/gif', +'gim':'application/vnd.groove-identity-message', +'gml':'application/gml+xml', +'gmx':'application/vnd.gmx', +'gnumeric':'application/x-gnumeric', +'gph':'application/vnd.flographit', +'gpx':'application/gpx+xml', +'gqf':'application/vnd.grafeq', +'gqs':'application/vnd.grafeq', +'gram':'application/srgs', +'gramps':'application/x-gramps-xml', +'gre':'application/vnd.geometry-explorer', +'grv':'application/vnd.groove-injector', +'grxml':'application/srgs+xml', +'gsf':'application/x-font-ghostscript', +'gtar':'application/x-gtar', +'gtm':'application/vnd.groove-tool-message', +'gtw':'model/vnd.gtw', +'gv':'text/vnd.graphviz', +'gxf':'application/gxf', +'gxt':'application/vnd.geonext', +'h':'text/x-c', +'h261':'video/h261', +'h263':'video/h263', +'h264':'video/h264', +'hal':'application/vnd.hal+xml', +'hbci':'application/vnd.hbci', +'hdf':'application/x-hdf', +'hh':'text/x-c', +'hlp':'application/winhlp', +'hpgl':'application/vnd.hp-hpgl', +'hpid':'application/vnd.hp-hpid', +'hps':'application/vnd.hp-hps', +'hqx':'application/mac-binhex40', +'htke':'application/vnd.kenameaapp', +'htm':'text/html', +'html':'text/html', +'hvd':'application/vnd.yamaha.hv-dic', +'hvp':'application/vnd.yamaha.hv-voice', +'hvs':'application/vnd.yamaha.hv-script', +'i2g':'application/vnd.intergeo', +'icc':'application/vnd.iccprofile', +'ice':'x-conference/x-cooltalk', +'icm':'application/vnd.iccprofile', +'ico':'image/x-icon', +'ics':'text/calendar', +'ief':'image/ief', +'ifb':'text/calendar', +'ifm':'application/vnd.shana.informed.formdata', +'iges':'model/iges', +'igl':'application/vnd.igloader', +'igm':'application/vnd.insors.igm', +'igs':'model/iges', +'igx':'application/vnd.micrografx.igx', +'iif':'application/vnd.shana.informed.interchange', +'imp':'application/vnd.accpac.simply.imp', +'ims':'application/vnd.ms-ims', +'in':'text/plain', +'ink':'application/inkml+xml', +'inkml':'application/inkml+xml', +'install':'application/x-install-instructions', +'iota':'application/vnd.astraea-software.iota', +'ipfix':'application/ipfix', +'ipk':'application/vnd.shana.informed.package', +'irm':'application/vnd.ibm.rights-management', +'irp':'application/vnd.irepository.package+xml', +'iso':'application/x-iso9660-image', +'itp':'application/vnd.shana.informed.formtemplate', +'ivp':'application/vnd.immervision-ivp', +'ivu':'application/vnd.immervision-ivu', +'jad':'text/vnd.sun.j2me.app-descriptor', +'jam':'application/vnd.jam', +'jar':'application/java-archive', +'java':'text/x-java-source', +'jisp':'application/vnd.jisp', +'jlt':'application/vnd.hp-jlyt', +'jnlp':'application/x-java-jnlp-file', +'joda':'application/vnd.joost.joda-archive', +'jpe':'image/jpeg', +'jpeg':'image/jpeg', +'jpg':'image/jpeg', +'jpgm':'video/jpm', +'jpgv':'video/jpeg', +'jpm':'video/jpm', +'js':'application/javascript', +'json':'application/json', +'jsonml':'application/jsonml+json', +'kar':'audio/midi', +'karbon':'application/vnd.kde.karbon', +'kfo':'application/vnd.kde.kformula', +'kia':'application/vnd.kidspiration', +'kml':'application/vnd.google-earth.kml+xml', +'kmz':'application/vnd.google-earth.kmz', +'kne':'application/vnd.kinar', +'knp':'application/vnd.kinar', +'kon':'application/vnd.kde.kontour', +'kpr':'application/vnd.kde.kpresenter', +'kpt':'application/vnd.kde.kpresenter', +'kpxx':'application/vnd.ds-keypoint', +'ksp':'application/vnd.kde.kspread', +'ktr':'application/vnd.kahootz', +'ktx':'image/ktx', +'ktz':'application/vnd.kahootz', +'kwd':'application/vnd.kde.kword', +'kwt':'application/vnd.kde.kword', +'lasxml':'application/vnd.las.las+xml', +'latex':'application/x-latex', +'lbd':'application/vnd.llamagraphics.life-balance.desktop', +'lbe':'application/vnd.llamagraphics.life-balance.exchange+xml', +'les':'application/vnd.hhe.lesson-player', +'lha':'application/x-lzh-compressed', +'link66':'application/vnd.route66.link66+xml', +'list':'text/plain', +'list3820':'application/vnd.ibm.modcap', +'listafp':'application/vnd.ibm.modcap', +'lnk':'application/x-ms-shortcut', +'log':'text/plain', +'lostxml':'application/lost+xml', +'lrf':'application/octet-stream', +'lrm':'application/vnd.ms-lrm', +'ltf':'application/vnd.frogans.ltf', +'lvp':'audio/vnd.lucent.voice', +'lwp':'application/vnd.lotus-wordpro', +'lzh':'application/x-lzh-compressed', +'m13':'application/x-msmediaview', +'m14':'application/x-msmediaview', +'m1v':'video/mpeg', +'m21':'application/mp21', +'m2a':'audio/mpeg', +'m2v':'video/mpeg', +'m3a':'audio/mpeg', +'m3u':'audio/x-mpegurl', +'m3u8':'application/vnd.apple.mpegurl', +'m4u':'video/vnd.mpegurl', +'m4v':'video/x-m4v', +'ma':'application/mathematica', +'mads':'application/mads+xml', +'mag':'application/vnd.ecowin.chart', +'maker':'application/vnd.framemaker', +'man':'text/troff', +'mar':'application/octet-stream', +'mathml':'application/mathml+xml', +'mb':'application/mathematica', +'mbk':'application/vnd.mobius.mbk', +'mbox':'application/mbox', +'mc1':'application/vnd.medcalcdata', +'mcd':'application/vnd.mcd', +'mcurl':'text/vnd.curl.mcurl', +'mdb':'application/x-msaccess', +'mdi':'image/vnd.ms-modi', +'me':'text/troff', +'mesh':'model/mesh', +'meta4':'application/metalink4+xml', +'metalink':'application/metalink+xml', +'mets':'application/mets+xml', +'mfm':'application/vnd.mfmp', +'mft':'application/rpki-manifest', +'mgp':'application/vnd.osgeo.mapguide.package', +'mgz':'application/vnd.proteus.magazine', +'mid':'audio/midi', +'midi':'audio/midi', +'mie':'application/x-mie', +'mif':'application/vnd.mif', +'mime':'message/rfc822', +'mj2':'video/mj2', +'mjp2':'video/mj2', +'mk3d':'video/x-matroska', +'mka':'audio/x-matroska', +'mks':'video/x-matroska', +'mkv':'video/x-matroska', +'mlp':'application/vnd.dolby.mlp', +'mmd':'application/vnd.chipnuts.karaoke-mmd', +'mmf':'application/vnd.smaf', +'mmr':'image/vnd.fujixerox.edmics-mmr', +'mng':'video/x-mng', +'mny':'application/x-msmoney', +'mobi':'application/x-mobipocket-ebook', +'mods':'application/mods+xml', +'mov':'video/quicktime', +'movie':'video/x-sgi-movie', +'mp2':'audio/mpeg', +'mp21':'application/mp21', +'mp2a':'audio/mpeg', +'mp3':'audio/mpeg', +'mp4':'video/mp4', +'mp4a':'audio/mp4', +'mp4s':'application/mp4', +'mp4v':'video/mp4', +'mpc':'application/vnd.mophun.certificate', +'mpe':'video/mpeg', +'mpeg':'video/mpeg', +'mpg':'video/mpeg', +'mpg4':'video/mp4', +'mpga':'audio/mpeg', +'mpkg':'application/vnd.apple.installer+xml', +'mpm':'application/vnd.blueice.multipass', +'mpn':'application/vnd.mophun.application', +'mpp':'application/vnd.ms-project', +'mpt':'application/vnd.ms-project', +'mpy':'application/vnd.ibm.minipay', +'mqy':'application/vnd.mobius.mqy', +'mrc':'application/marc', +'mrcx':'application/marcxml+xml', +'ms':'text/troff', +'mscml':'application/mediaservercontrol+xml', +'mseed':'application/vnd.fdsn.mseed', +'mseq':'application/vnd.mseq', +'msf':'application/vnd.epson.msf', +'msh':'model/mesh', +'msi':'application/x-msdownload', +'msl':'application/vnd.mobius.msl', +'msty':'application/vnd.muvee.style', +'mts':'model/vnd.mts', +'mus':'application/vnd.musician', +'musicxml':'application/vnd.recordare.musicxml+xml', +'mvb':'application/x-msmediaview', +'mwf':'application/vnd.mfer', +'mxf':'application/mxf', +'mxl':'application/vnd.recordare.musicxml', +'mxml':'application/xv+xml', +'mxs':'application/vnd.triscape.mxs', +'mxu':'video/vnd.mpegurl', +'n-gage':'application/vnd.nokia.n-gage.symbian.install', +'n3':'text/n3', +'nb':'application/mathematica', +'nbp':'application/vnd.wolfram.player', +'nc':'application/x-netcdf', +'ncx':'application/x-dtbncx+xml', +'nfo':'text/x-nfo', +'ngdat':'application/vnd.nokia.n-gage.data', +'nitf':'application/vnd.nitf', +'nlu':'application/vnd.neurolanguage.nlu', +'nml':'application/vnd.enliven', +'nnd':'application/vnd.noblenet-directory', +'nns':'application/vnd.noblenet-sealer', +'nnw':'application/vnd.noblenet-web', +'npx':'image/vnd.net-fpx', +'nsc':'application/x-conference', +'nsf':'application/vnd.lotus-notes', +'ntf':'application/vnd.nitf', +'nzb':'application/x-nzb', +'oa2':'application/vnd.fujitsu.oasys2', +'oa3':'application/vnd.fujitsu.oasys3', +'oas':'application/vnd.fujitsu.oasys', +'obd':'application/x-msbinder', +'obj':'application/x-tgif', +'oda':'application/oda', +'odb':'application/vnd.oasis.opendocument.database', +'odc':'application/vnd.oasis.opendocument.chart', +'odf':'application/vnd.oasis.opendocument.formula', +'odft':'application/vnd.oasis.opendocument.formula-template', +'odg':'application/vnd.oasis.opendocument.graphics', +'odi':'application/vnd.oasis.opendocument.image', +'odm':'application/vnd.oasis.opendocument.text-master', +'odp':'application/vnd.oasis.opendocument.presentation', +'ods':'application/vnd.oasis.opendocument.spreadsheet', +'odt':'application/vnd.oasis.opendocument.text', +'oga':'audio/ogg', +'ogg':'audio/ogg', +'ogv':'video/ogg', +'ogx':'application/ogg', +'omdoc':'application/omdoc+xml', +'onepkg':'application/onenote', +'onetmp':'application/onenote', +'onetoc':'application/onenote', +'onetoc2':'application/onenote', +'opf':'application/oebps-package+xml', +'opml':'text/x-opml', +'oprc':'application/vnd.palm', +'org':'application/vnd.lotus-organizer', +'osf':'application/vnd.yamaha.openscoreformat', +'osfpvg':'application/vnd.yamaha.openscoreformat.osfpvg+xml', +'otc':'application/vnd.oasis.opendocument.chart-template', +'otf':'application/x-font-otf', +'otg':'application/vnd.oasis.opendocument.graphics-template', +'oth':'application/vnd.oasis.opendocument.text-web', +'oti':'application/vnd.oasis.opendocument.image-template', +'otp':'application/vnd.oasis.opendocument.presentation-template', +'ots':'application/vnd.oasis.opendocument.spreadsheet-template', +'ott':'application/vnd.oasis.opendocument.text-template', +'oxps':'application/oxps', +'oxt':'application/vnd.openofficeorg.extension', +'p':'text/x-pascal', +'p10':'application/pkcs10', +'p12':'application/x-pkcs12', +'p7b':'application/x-pkcs7-certificates', +'p7c':'application/pkcs7-mime', +'p7m':'application/pkcs7-mime', +'p7r':'application/x-pkcs7-certreqresp', +'p7s':'application/pkcs7-signature', +'p8':'application/pkcs8', +'pas':'text/x-pascal', +'paw':'application/vnd.pawaafile', +'pbd':'application/vnd.powerbuilder6', +'pbm':'image/x-portable-bitmap', +'pcap':'application/vnd.tcpdump.pcap', +'pcf':'application/x-font-pcf', +'pcl':'application/vnd.hp-pcl', +'pclxl':'application/vnd.hp-pclxl', +'pct':'image/x-pict', +'pcurl':'application/vnd.curl.pcurl', +'pcx':'image/x-pcx', +'pdb':'application/vnd.palm', +'pdf':'application/pdf', +'pfa':'application/x-font-type1', +'pfb':'application/x-font-type1', +'pfm':'application/x-font-type1', +'pfr':'application/font-tdpfr', +'pfx':'application/x-pkcs12', +'pgm':'image/x-portable-graymap', +'pgn':'application/x-chess-pgn', +'pgp':'application/pgp-encrypted', +'pic':'image/x-pict', +'pkg':'application/octet-stream', +'pki':'application/pkixcmp', +'pkipath':'application/pkix-pkipath', +'plb':'application/vnd.3gpp.pic-bw-large', +'plc':'application/vnd.mobius.plc', +'plf':'application/vnd.pocketlearn', +'pls':'application/pls+xml', +'pml':'application/vnd.ctc-posml', +'png':'image/png', +'pnm':'image/x-portable-anymap', +'portpkg':'application/vnd.macports.portpkg', +'pot':'application/vnd.ms-powerpoint', +'potm':'application/vnd.ms-powerpoint.template.macroenabled.12', +'potx':'application/vnd.openxmlformats-officedocument.presentationml.template', +'ppam':'application/vnd.ms-powerpoint.addin.macroenabled.12', +'ppd':'application/vnd.cups-ppd', +'ppm':'image/x-portable-pixmap', +'pps':'application/vnd.ms-powerpoint', +'ppsm':'application/vnd.ms-powerpoint.slideshow.macroenabled.12', +'ppsx':'application/vnd.openxmlformats-officedocument.presentationml.slideshow', +'ppt':'application/vnd.ms-powerpoint', +'pptm':'application/vnd.ms-powerpoint.presentation.macroenabled.12', +'pptx':'application/vnd.openxmlformats-officedocument.presentationml.presentation', +'pqa':'application/vnd.palm', +'prc':'application/x-mobipocket-ebook', +'pre':'application/vnd.lotus-freelance', +'prf':'application/pics-rules', +'ps':'application/postscript', +'psb':'application/vnd.3gpp.pic-bw-small', +'psd':'image/vnd.adobe.photoshop', +'psf':'application/x-font-linux-psf', +'pskcxml':'application/pskc+xml', +'ptid':'application/vnd.pvi.ptid1', +'pub':'application/x-mspublisher', +'pvb':'application/vnd.3gpp.pic-bw-var', +'pwn':'application/vnd.3m.post-it-notes', +'pya':'audio/vnd.ms-playready.media.pya', +'pyv':'video/vnd.ms-playready.media.pyv', +'qam':'application/vnd.epson.quickanime', +'qbo':'application/vnd.intu.qbo', +'qfx':'application/vnd.intu.qfx', +'qps':'application/vnd.publishare-delta-tree', +'qt':'video/quicktime', +'qwd':'application/vnd.quark.quarkxpress', +'qwt':'application/vnd.quark.quarkxpress', +'qxb':'application/vnd.quark.quarkxpress', +'qxd':'application/vnd.quark.quarkxpress', +'qxl':'application/vnd.quark.quarkxpress', +'qxt':'application/vnd.quark.quarkxpress', +'ra':'audio/x-pn-realaudio', +'ram':'audio/x-pn-realaudio', +'rar':'application/x-rar-compressed', +'ras':'image/x-cmu-raster', +'rcprofile':'application/vnd.ipunplugged.rcprofile', +'rdf':'application/rdf+xml', +'rdz':'application/vnd.data-vision.rdz', +'rep':'application/vnd.businessobjects', +'res':'application/x-dtbresource+xml', +'rgb':'image/x-rgb', +'rif':'application/reginfo+xml', +'rip':'audio/vnd.rip', +'ris':'application/x-research-info-systems', +'rl':'application/resource-lists+xml', +'rlc':'image/vnd.fujixerox.edmics-rlc', +'rld':'application/resource-lists-diff+xml', +'rm':'application/vnd.rn-realmedia', +'rmi':'audio/midi', +'rmp':'audio/x-pn-realaudio-plugin', +'rms':'application/vnd.jcp.javame.midlet-rms', +'rmvb':'application/vnd.rn-realmedia-vbr', +'rnc':'application/relax-ng-compact-syntax', +'roa':'application/rpki-roa', +'roff':'text/troff', +'rp9':'application/vnd.cloanto.rp9', +'rpss':'application/vnd.nokia.radio-presets', +'rpst':'application/vnd.nokia.radio-preset', +'rq':'application/sparql-query', +'rs':'application/rls-services+xml', +'rsd':'application/rsd+xml', +'rss':'application/rss+xml', +'rtf':'application/rtf', +'rtx':'text/richtext', +'s':'text/x-asm', +'s3m':'audio/s3m', +'saf':'application/vnd.yamaha.smaf-audio', +'sbml':'application/sbml+xml', +'sc':'application/vnd.ibm.secure-container', +'scd':'application/x-msschedule', +'scm':'application/vnd.lotus-screencam', +'scq':'application/scvp-cv-request', +'scs':'application/scvp-cv-response', +'scurl':'text/vnd.curl.scurl', +'sda':'application/vnd.stardivision.draw', +'sdc':'application/vnd.stardivision.calc', +'sdd':'application/vnd.stardivision.impress', +'sdkd':'application/vnd.solent.sdkm+xml', +'sdkm':'application/vnd.solent.sdkm+xml', +'sdp':'application/sdp', +'sdw':'application/vnd.stardivision.writer', +'see':'application/vnd.seemail', +'seed':'application/vnd.fdsn.seed', +'sema':'application/vnd.sema', +'semd':'application/vnd.semd', +'semf':'application/vnd.semf', +'ser':'application/java-serialized-object', +'setpay':'application/set-payment-initiation', +'setreg':'application/set-registration-initiation', +'sfd-hdstx':'application/vnd.hydrostatix.sof-data', +'sfs':'application/vnd.spotfire.sfs', +'sfv':'text/x-sfv', +'sgi':'image/sgi', +'sgl':'application/vnd.stardivision.writer-global', +'sgm':'text/sgml', +'sgml':'text/sgml', +'sh':'application/x-sh', +'shar':'application/x-shar', +'shf':'application/shf+xml', +'sid':'image/x-mrsid-image', +'sig':'application/pgp-signature', +'sil':'audio/silk', +'silo':'model/mesh', +'sis':'application/vnd.symbian.install', +'sisx':'application/vnd.symbian.install', +'sit':'application/x-stuffit', +'sitx':'application/x-stuffitx', +'skd':'application/vnd.koan', +'skm':'application/vnd.koan', +'skp':'application/vnd.koan', +'skt':'application/vnd.koan', +'sldm':'application/vnd.ms-powerpoint.slide.macroenabled.12', +'sldx':'application/vnd.openxmlformats-officedocument.presentationml.slide', +'slt':'application/vnd.epson.salt', +'sm':'application/vnd.stepmania.stepchart', +'smf':'application/vnd.stardivision.math', +'smi':'application/smil+xml', +'smil':'application/smil+xml', +'smv':'video/x-smv', +'smzip':'application/vnd.stepmania.package', +'snd':'audio/basic', +'snf':'application/x-font-snf', +'so':'application/octet-stream', +'spc':'application/x-pkcs7-certificates', +'spf':'application/vnd.yamaha.smaf-phrase', +'spl':'application/x-futuresplash', +'spot':'text/vnd.in3d.spot', +'spp':'application/scvp-vp-response', +'spq':'application/scvp-vp-request', +'spx':'audio/ogg', +'sql':'application/x-sql', +'src':'application/x-wais-source', +'srt':'application/x-subrip', +'sru':'application/sru+xml', +'srx':'application/sparql-results+xml', +'ssdl':'application/ssdl+xml', +'sse':'application/vnd.kodak-descriptor', +'ssf':'application/vnd.epson.ssf', +'ssml':'application/ssml+xml', +'st':'application/vnd.sailingtracker.track', +'stc':'application/vnd.sun.xml.calc.template', +'std':'application/vnd.sun.xml.draw.template', +'stf':'application/vnd.wt.stf', +'sti':'application/vnd.sun.xml.impress.template', +'stk':'application/hyperstudio', +'stl':'application/vnd.ms-pki.stl', +'str':'application/vnd.pg.format', +'stw':'application/vnd.sun.xml.writer.template', +'sub':'text/vnd.dvb.subtitle', +'sus':'application/vnd.sus-calendar', +'susp':'application/vnd.sus-calendar', +'sv4cpio':'application/x-sv4cpio', +'sv4crc':'application/x-sv4crc', +'svc':'application/vnd.dvb.service', +'svd':'application/vnd.svd', +'svg':'image/svg+xml', +'svgz':'image/svg+xml', +'swa':'application/x-director', +'swf':'application/x-shockwave-flash', +'swi':'application/vnd.aristanetworks.swi', +'sxc':'application/vnd.sun.xml.calc', +'sxd':'application/vnd.sun.xml.draw', +'sxg':'application/vnd.sun.xml.writer.global', +'sxi':'application/vnd.sun.xml.impress', +'sxm':'application/vnd.sun.xml.math', +'sxw':'application/vnd.sun.xml.writer', +'t':'text/troff', +'t3':'application/x-t3vm-image', +'taglet':'application/vnd.mynfc', +'tao':'application/vnd.tao.intent-module-archive', +'tar':'application/x-tar', +'tcap':'application/vnd.3gpp2.tcap', +'tcl':'application/x-tcl', +'teacher':'application/vnd.smart.teacher', +'tei':'application/tei+xml', +'teicorpus':'application/tei+xml', +'tex':'application/x-tex', +'texi':'application/x-texinfo', +'texinfo':'application/x-texinfo', +'text':'text/plain', +'tfi':'application/thraud+xml', +'tfm':'application/x-tex-tfm', +'tga':'image/x-tga', +'thmx':'application/vnd.ms-officetheme', +'tif':'image/tiff', +'tiff':'image/tiff', +'tmo':'application/vnd.tmobile-livetv', +'torrent':'application/x-bittorrent', +'tpl':'application/vnd.groove-tool-template', +'tpt':'application/vnd.trid.tpt', +'tr':'text/troff', +'tra':'application/vnd.trueapp', +'trm':'application/x-msterminal', +'tsd':'application/timestamped-data', +'tsv':'text/tab-separated-values', +'ttc':'application/x-font-ttf', +'ttf':'application/x-font-ttf', +'ttl':'text/turtle', +'twd':'application/vnd.simtech-mindmapper', +'twds':'application/vnd.simtech-mindmapper', +'txd':'application/vnd.genomatix.tuxedo', +'txf':'application/vnd.mobius.txf', +'txt':'text/plain', +'u32':'application/x-authorware-bin', +'udeb':'application/x-debian-package', +'ufd':'application/vnd.ufdl', +'ufdl':'application/vnd.ufdl', +'ulx':'application/x-glulx', +'umj':'application/vnd.umajin', +'unityweb':'application/vnd.unity', +'uoml':'application/vnd.uoml+xml', +'uri':'text/uri-list', +'uris':'text/uri-list', +'urls':'text/uri-list', +'ustar':'application/x-ustar', +'utz':'application/vnd.uiq.theme', +'uu':'text/x-uuencode', +'uva':'audio/vnd.dece.audio', +'uvd':'application/vnd.dece.data', +'uvf':'application/vnd.dece.data', +'uvg':'image/vnd.dece.graphic', +'uvh':'video/vnd.dece.hd', +'uvi':'image/vnd.dece.graphic', +'uvm':'video/vnd.dece.mobile', +'uvp':'video/vnd.dece.pd', +'uvs':'video/vnd.dece.sd', +'uvt':'application/vnd.dece.ttml+xml', +'uvu':'video/vnd.uvvu.mp4', +'uvv':'video/vnd.dece.video', +'uvva':'audio/vnd.dece.audio', +'uvvd':'application/vnd.dece.data', +'uvvf':'application/vnd.dece.data', +'uvvg':'image/vnd.dece.graphic', +'uvvh':'video/vnd.dece.hd', +'uvvi':'image/vnd.dece.graphic', +'uvvm':'video/vnd.dece.mobile', +'uvvp':'video/vnd.dece.pd', +'uvvs':'video/vnd.dece.sd', +'uvvt':'application/vnd.dece.ttml+xml', +'uvvu':'video/vnd.uvvu.mp4', +'uvvv':'video/vnd.dece.video', +'uvvx':'application/vnd.dece.unspecified', +'uvvz':'application/vnd.dece.zip', +'uvx':'application/vnd.dece.unspecified', +'uvz':'application/vnd.dece.zip', +'vcard':'text/vcard', +'vcd':'application/x-cdlink', +'vcf':'text/x-vcard', +'vcg':'application/vnd.groove-vcard', +'vcs':'text/x-vcalendar', +'vcx':'application/vnd.vcx', +'vis':'application/vnd.visionary', +'viv':'video/vnd.vivo', +'vob':'video/x-ms-vob', +'vor':'application/vnd.stardivision.writer', +'vox':'application/x-authorware-bin', +'vrml':'model/vrml', +'vsd':'application/vnd.visio', +'vsf':'application/vnd.vsf', +'vss':'application/vnd.visio', +'vst':'application/vnd.visio', +'vsw':'application/vnd.visio', +'vtu':'model/vnd.vtu', +'vxml':'application/voicexml+xml', +'w3d':'application/x-director', +'wad':'application/x-doom', +'wav':'audio/x-wav', +'wax':'audio/x-ms-wax', +'wbmp':'image/vnd.wap.wbmp', +'wbs':'application/vnd.criticaltools.wbs+xml', +'wbxml':'application/vnd.wap.wbxml', +'wcm':'application/vnd.ms-works', +'wdb':'application/vnd.ms-works', +'wdp':'image/vnd.ms-photo', +'weba':'audio/webm', +'webm':'video/webm', +'webp':'image/webp', +'wg':'application/vnd.pmi.widget', +'wgt':'application/widget', +'wks':'application/vnd.ms-works', +'wm':'video/x-ms-wm', +'wma':'audio/x-ms-wma', +'wmd':'application/x-ms-wmd', +'wmf':'application/x-msmetafile', +'wml':'text/vnd.wap.wml', +'wmlc':'application/vnd.wap.wmlc', +'wmls':'text/vnd.wap.wmlscript', +'wmlsc':'application/vnd.wap.wmlscriptc', +'wmv':'video/x-ms-wmv', +'wmx':'video/x-ms-wmx', +'wmz':'application/x-ms-wmz', +'woff':'application/x-font-woff', +'wpd':'application/vnd.wordperfect', +'wpl':'application/vnd.ms-wpl', +'wps':'application/vnd.ms-works', +'wqd':'application/vnd.wqd', +'wri':'application/x-mswrite', +'wrl':'model/vrml', +'wsdl':'application/wsdl+xml', +'wspolicy':'application/wspolicy+xml', +'wtb':'application/vnd.webturbo', +'wvx':'video/x-ms-wvx', +'x32':'application/x-authorware-bin', +'x3d':'model/x3d+xml', +'x3db':'model/x3d+binary', +'x3dbz':'model/x3d+binary', +'x3dv':'model/x3d+vrml', +'x3dvz':'model/x3d+vrml', +'x3dz':'model/x3d+xml', +'xaml':'application/xaml+xml', +'xap':'application/x-silverlight-app', +'xar':'application/vnd.xara', +'xbap':'application/x-ms-xbap', +'xbd':'application/vnd.fujixerox.docuworks.binder', +'xbm':'image/x-xbitmap', +'xdf':'application/xcap-diff+xml', +'xdm':'application/vnd.syncml.dm+xml', +'xdp':'application/vnd.adobe.xdp+xml', +'xdssc':'application/dssc+xml', +'xdw':'application/vnd.fujixerox.docuworks', +'xenc':'application/xenc+xml', +'xer':'application/patch-ops-error+xml', +'xfdf':'application/vnd.adobe.xfdf', +'xfdl':'application/vnd.xfdl', +'xht':'application/xhtml+xml', +'xhtml':'application/xhtml+xml', +'xhvml':'application/xv+xml', +'xif':'image/vnd.xiff', +'xla':'application/vnd.ms-excel', +'xlam':'application/vnd.ms-excel.addin.macroenabled.12', +'xlc':'application/vnd.ms-excel', +'xlf':'application/x-xliff+xml', +'xlm':'application/vnd.ms-excel', +'xls':'application/vnd.ms-excel', +'xlsb':'application/vnd.ms-excel.sheet.binary.macroenabled.12', +'xlsm':'application/vnd.ms-excel.sheet.macroenabled.12', +'xlsx':'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', +'xlt':'application/vnd.ms-excel', +'xltm':'application/vnd.ms-excel.template.macroenabled.12', +'xltx':'application/vnd.openxmlformats-officedocument.spreadsheetml.template', +'xlw':'application/vnd.ms-excel', +'xm':'audio/xm', +'xml':'application/xml', +'xo':'application/vnd.olpc-sugar', +'xop':'application/xop+xml', +'xpi':'application/x-xpinstall', +'xpl':'application/xproc+xml', +'xpm':'image/x-xpixmap', +'xpr':'application/vnd.is-xpr', +'xps':'application/vnd.ms-xpsdocument', +'xpw':'application/vnd.intercon.formnet', +'xpx':'application/vnd.intercon.formnet', +'xsl':'application/xml', +'xslt':'application/xslt+xml', +'xsm':'application/vnd.syncml+xml', +'xspf':'application/xspf+xml', +'xul':'application/vnd.mozilla.xul+xml', +'xvm':'application/xv+xml', +'xvml':'application/xv+xml', +'xwd':'image/x-xwindowdump', +'xyz':'chemical/x-xyz', +'xz':'application/x-xz', +'yang':'application/yang', +'yin':'application/yin+xml', +'z1':'application/x-zmachine', +'z2':'application/x-zmachine', +'z3':'application/x-zmachine', +'z4':'application/x-zmachine', +'z5':'application/x-zmachine', +'z6':'application/x-zmachine', +'z7':'application/x-zmachine', +'z8':'application/x-zmachine', +'zaz':'application/vnd.zzazz.deck+xml', +'zip':'application/zip', +'zir':'application/vnd.zul', +'zirz':'application/vnd.zul', +'zmm':'application/vnd.handheld-entertainment+xml', +}; diff --git a/Chapter 7/serving_files/bin/packages/mime/src/magic_number.dart b/Chapter 7/serving_files/bin/packages/mime/src/magic_number.dart new file mode 100644 index 0000000..3efb2ee --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/mime/src/magic_number.dart @@ -0,0 +1,48 @@ +// Copyright (c) 2013, 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. + +library mime.magic_number; + +class MagicNumber { + final String mimeType; + final List numbers; + final List mask; + + const MagicNumber(this.mimeType, this.numbers, {this.mask}); + + bool matches(List header) { + if (header.length < numbers.length) return false; + + for (int i = 0; i < numbers.length; i++) { + if (mask != null) { + if ((mask[i] & numbers[i]) != (mask[i] & header[i])) return false; + } else { + if (numbers[i] != header[i]) return false; + } + } + + return true; + } + +} + +const int DEFAULT_MAGIC_NUMBERS_MAX_LENGTH = 12; + +const List DEFAULT_MAGIC_NUMBERS = const [ + const MagicNumber('application/pdf', const [0x25, 0x50, 0x44, 0x46]), + const MagicNumber('application/postscript', const [0x25, 0x51]), + const MagicNumber('image/gif', const [0x47, 0x49, 0x46, 0x38, 0x37, 0x61]), + const MagicNumber('image/gif', const [0x47, 0x49, 0x46, 0x38, 0x39, 0x61]), + const MagicNumber('image/jpeg', const [0xFF, 0xD8]), + const MagicNumber('image/png', + const [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]), + const MagicNumber('image/tiff', const [0x49, 0x49, 0x2A, 0x00]), + const MagicNumber('image/tiff', const [0x4D, 0x4D, 0x00, 0x2A]), + const MagicNumber( + 'video/mp4', + const [0x00, 0x00, 0x00, 0x00, 0x66, 0x74, + 0x79, 0x70, 0x33, 0x67, 0x70, 0x35], + mask: const [0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) +]; diff --git a/Chapter 7/serving_files/bin/packages/mime/src/mime_multipart_transformer.dart b/Chapter 7/serving_files/bin/packages/mime/src/mime_multipart_transformer.dart new file mode 100644 index 0000000..3afff2d --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/mime/src/mime_multipart_transformer.dart @@ -0,0 +1,49 @@ +// Copyright (c) 2012, 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. +library mime.multipart_transformer; + +import 'dart:async'; +import 'dart:typed_data'; + +import 'bound_multipart_stream.dart'; +import 'mime_shared.dart'; +import 'char_code.dart'; + + +Uint8List _getBoundary(String boundary) { + var charCodes = boundary.codeUnits; + + var boundaryList = new Uint8List(4 + charCodes.length); + // Set-up the matching boundary preceding it with CRLF and two + // dashes. + boundaryList[0] = CharCode.CR; + boundaryList[1] = CharCode.LF; + boundaryList[2] = CharCode.DASH; + boundaryList[3] = CharCode.DASH; + boundaryList.setRange(4, 4 + charCodes.length, charCodes); + return boundaryList; +} + +/** + * Parser for MIME multipart types of data as described in RFC 2046 + * section 5.1.1. The data is transformed into [MimeMultipart] objects, each + * of them streaming the multipart data. + */ +class MimeMultipartTransformer + implements StreamTransformer, MimeMultipart> { + + final List _boundary; + + /** + * Construct a new MIME multipart parser with the boundary + * [boundary]. The boundary should be as specified in the content + * type parameter, that is without the -- prefix. + */ + MimeMultipartTransformer(String boundary) + : _boundary = _getBoundary(boundary); + + Stream bind(Stream> stream) { + return new BoundMultipartStream(_boundary, stream).stream; + } +} diff --git a/Chapter 7/serving_files/bin/packages/mime/src/mime_shared.dart b/Chapter 7/serving_files/bin/packages/mime/src/mime_shared.dart new file mode 100644 index 0000000..6d14e0a --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/mime/src/mime_shared.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2014, 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. +library mime.shared; + +import 'dart:async'; + +class MimeMultipartException implements Exception { + final String message; + + const MimeMultipartException([String this.message = ""]); + + String toString() => "MimeMultipartException: $message"; +} + +/** + * A Mime Multipart class representing each part parsed by + * [MimeMultipartTransformer]. The data is streamed in as it become available. + */ +abstract class MimeMultipart extends Stream> { + Map get headers; +} diff --git a/Chapter 7/serving_files/bin/packages/mime/src/mime_type.dart b/Chapter 7/serving_files/bin/packages/mime/src/mime_type.dart new file mode 100644 index 0000000..744eead --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/mime/src/mime_type.dart @@ -0,0 +1,128 @@ +// Copyright (c) 2013, 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. + +library mime.mime_type; + +import 'default_extension_map.dart'; +import 'magic_number.dart'; + +final MimeTypeResolver _globalResolver = new MimeTypeResolver(); + +/** + * The maximum number of bytes needed, to match all default magic-numbers. + */ +int get defaultMagicNumbersMaxLength => _globalResolver.magicNumbersMaxLength; + +/** + * Extract the extension from [path] and use that for MIME-type lookup, using + * the default extension map. + * + * If no matching MIME-type was found, `null` is returned. + * + * If [headerBytes] is present, a match for known magic-numbers will be + * performed first. This allows the correct mime-type to be found, even though + * a file have been saved using the wrong file-name extension. If less than + * [defaultMagicNumbersMaxLength] bytes was provided, some magic-numbers won't + * be matched against. + */ +String lookupMimeType(String path, {List headerBytes}) => + _globalResolver.lookup(path, headerBytes: headerBytes); + +/** + * MIME-type resolver class, used to customize the lookup of mime-types. + */ +class MimeTypeResolver { + final Map _extensionMap = {}; + final List _magicNumbers = []; + final bool _useDefault; + int _magicNumbersMaxLength; + + /** + * Create a new empty [MimeTypeResolver]. + */ + MimeTypeResolver.empty() : _useDefault = false, _magicNumbersMaxLength = 0; + + /** + * Create a new [MimeTypeResolver] containing the default scope. + */ + MimeTypeResolver() : + _useDefault = true, + _magicNumbersMaxLength = DEFAULT_MAGIC_NUMBERS_MAX_LENGTH; + + /** + * Get the maximum number of bytes required to match all magic numbers, when + * performing [lookup] with headerBytes present. + */ + int get magicNumbersMaxLength => _magicNumbersMaxLength; + + /** + * Extract the extension from [path] and use that for MIME-type lookup. + * + * If no matching MIME-type was found, `null` is returned. + * + * If [headerBytes] is present, a match for known magic-numbers will be + * performed first. This allows the correct mime-type to be found, even though + * a file have been saved using the wrong file-name extension. If less than + * [magicNumbersMaxLength] bytes was provided, some magic-numbers won't + * be matched against. + */ + String lookup(String path, {List headerBytes}) { + String result; + if (headerBytes != null) { + result = _matchMagic(headerBytes, _magicNumbers); + if (result != null) return result; + if (_useDefault) { + result = _matchMagic(headerBytes, DEFAULT_MAGIC_NUMBERS); + if (result != null) return result; + } + } + var ext = _ext(path); + result = _extensionMap[ext]; + if (result != null) return result; + if (_useDefault) { + result = defaultExtensionMap[ext]; + if (result != null) return result; + } + return null; + } + + /** + * Add a new MIME-type mapping to the [MimeTypeResolver]. If the [extension] + * is already present in the [MimeTypeResolver], it'll be overwritten. + */ + void addExtension(String extension, String mimeType) { + _extensionMap[extension] = mimeType; + } + + /** + * Add a new magic-number mapping to the [MimeTypeResolver]. + * + * If [mask] is present,the [mask] is used to only perform matching on + * selective bits. The [mask] must have the same length as [bytes]. + */ + void addMagicNumber(List bytes, String mimeType, {List mask}) { + if (mask != null && bytes.length != mask.length) { + throw new ArgumentError('Bytes and mask are of different lengths'); + } + if (bytes.length > _magicNumbersMaxLength) { + _magicNumbersMaxLength = bytes.length; + } + _magicNumbers.add(new MagicNumber(mimeType, bytes, mask: mask)); + } + + static String _matchMagic(List headerBytes, + List magicNumbers) { + for (var mn in magicNumbers) { + if (mn.matches(headerBytes)) return mn.mimeType; + } + return null; + } + + static String _ext(String path) { + int index = path.lastIndexOf('.'); + if (index < 0 || index + 1 >= path.length) return path; + return path.substring(index + 1).toLowerCase(); + } +} + diff --git a/Chapter 7/serving_files/bin/packages/path/path.dart b/Chapter 7/serving_files/bin/packages/path/path.dart new file mode 100644 index 0000000..599a351 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/path/path.dart @@ -0,0 +1,391 @@ +// Copyright (c) 2012, 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. + +/// A comprehensive, cross-platform path manipulation library. +/// +/// ## Installing ## +/// +/// Use [pub][] to install this package. Add the following to your +/// `pubspec.yaml` file. +/// +/// dependencies: +/// path: any +/// +/// Then run `pub install`. +/// +/// For more information, see the [path package on pub.dartlang.org][pkg]. +/// +/// [pub]: http://pub.dartlang.org +/// [pkg]: http://pub.dartlang.org/packages/path +/// +/// ## Usage ## +/// +/// The path library was designed to be imported with a prefix, though you don't +/// have to if you don't want to: +/// +/// import 'package:path/path.dart' as path; +/// +/// The most common way to use the library is through the top-level functions. +/// These manipulate path strings based on your current working directory and +/// the path style (POSIX, Windows, or URLs) of the host platform. For example: +/// +/// path.join("directory", "file.txt"); +/// +/// This calls the top-level [join] function to join "directory" and "file.txt" +/// using the current platform's directory separator. +/// +/// If you want to work with paths for a specific platform regardless of the +/// underlying platform that the program is running on, you can create a +/// [Context] and give it an explicit [Style]: +/// +/// var context = new path.Context(style: Style.windows); +/// context.join("directory", "file.txt"); +/// +/// This will join "directory" and "file.txt" using the Windows path separator, +/// even when the program is run on a POSIX machine. +library path; + +import 'src/context.dart'; +import 'src/style.dart'; + +export 'src/context.dart'; +export 'src/path_exception.dart'; +export 'src/style.dart'; + +/// A default context for manipulating POSIX paths. +final posix = new Context(style: Style.posix); + +/// A default context for manipulating Windows paths. +final windows = new Context(style: Style.windows); + +/// A default context for manipulating URLs. +final url = new Context(style: Style.url); + +/// The result of [Uri.base] last time the current working directory was +/// calculated. +/// +/// This is used to invalidate [_cachedContext] when the working directory has +/// changed since the last time a function was called. +Uri _lastBaseUri; + +/// An internal context for the current OS so we can provide a straight +/// functional interface and not require users to create one. +Context get _context { + if (_cachedContext != null && Uri.base == _lastBaseUri) return _cachedContext; + _lastBaseUri = Uri.base; + _cachedContext = new Context(); + return _cachedContext; +} +Context _cachedContext; + +/// Returns the [Style] of the current context. +/// +/// This is the style that all top-level path functions will use. +Style get style => _context.style; + +/// Gets the path to the current working directory. +/// +/// In the browser, this means the current URL, without the last file segment. +String get current { + var uri = Uri.base; + if (Style.platform == Style.url) { + return uri.resolve('.').toString(); + } else { + var path = uri.toFilePath(); + // Remove trailing '/' or '\'. + int lastIndex = path.length - 1; + assert(path[lastIndex] == '/' || path[lastIndex] == '\\'); + return path.substring(0, lastIndex); + } +} + +/// Gets the path separator for the current platform. This is `\` on Windows +/// and `/` on other platforms (including the browser). +String get separator => _context.separator; + +/// Creates a new path by appending the given path parts to [current]. +/// Equivalent to [join()] with [current] as the first argument. Example: +/// +/// path.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo' +String absolute(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7]) => + _context.absolute(part1, part2, part3, part4, part5, part6, part7); + +/// Gets the part of [path] after the last separator. +/// +/// path.basename('path/to/foo.dart'); // -> 'foo.dart' +/// path.basename('path/to'); // -> 'to' +/// +/// Trailing separators are ignored. +/// +/// path.basename('path/to/'); // -> 'to' +String basename(String path) => _context.basename(path); + +/// Gets the part of [path] after the last separator, and without any trailing +/// file extension. +/// +/// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' +/// +/// Trailing separators are ignored. +/// +/// path.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' +String basenameWithoutExtension(String path) => + _context.basenameWithoutExtension(path); + +/// Gets the part of [path] before the last separator. +/// +/// path.dirname('path/to/foo.dart'); // -> 'path/to' +/// path.dirname('path/to'); // -> 'path' +/// +/// Trailing separators are ignored. +/// +/// path.dirname('path/to/'); // -> 'path' +/// +/// If an absolute path contains no directories, only a root, then the root +/// is returned. +/// +/// path.dirname('/'); // -> '/' (posix) +/// path.dirname('c:\'); // -> 'c:\' (windows) +/// +/// If a relative path has no directories, then '.' is returned. +/// +/// path.dirname('foo'); // -> '.' +/// path.dirname(''); // -> '.' +String dirname(String path) => _context.dirname(path); + +/// Gets the file extension of [path]: the portion of [basename] from the last +/// `.` to the end (including the `.` itself). +/// +/// path.extension('path/to/foo.dart'); // -> '.dart' +/// path.extension('path/to/foo'); // -> '' +/// path.extension('path.to/foo'); // -> '' +/// path.extension('path/to/foo.dart.js'); // -> '.js' +/// +/// If the file name starts with a `.`, then that is not considered the +/// extension: +/// +/// path.extension('~/.bashrc'); // -> '' +/// path.extension('~/.notes.txt'); // -> '.txt' +String extension(String path) => _context.extension(path); + +// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. +/// Returns the root of [path], if it's absolute, or the empty string if it's +/// relative. +/// +/// // Unix +/// path.rootPrefix('path/to/foo'); // -> '' +/// path.rootPrefix('/path/to/foo'); // -> '/' +/// +/// // Windows +/// path.rootPrefix(r'path\to\foo'); // -> '' +/// path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' +/// +/// // URL +/// path.rootPrefix('path/to/foo'); // -> '' +/// path.rootPrefix('http://dartlang.org/path/to/foo'); +/// // -> 'http://dartlang.org' +String rootPrefix(String path) => _context.rootPrefix(path); + +/// Returns `true` if [path] is an absolute path and `false` if it is a +/// relative path. +/// +/// On POSIX systems, absolute paths start with a `/` (forward slash). On +/// Windows, an absolute path starts with `\\`, or a drive letter followed by +/// `:/` or `:\`. For URLs, absolute paths either start with a protocol and +/// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. +/// +/// URLs that start with `/` are known as "root-relative", since they're +/// relative to the root of the current URL. Since root-relative paths are still +/// absolute in every other sense, [isAbsolute] will return true for them. They +/// can be detected using [isRootRelative]. +bool isAbsolute(String path) => _context.isAbsolute(path); + +/// Returns `true` if [path] is a relative path and `false` if it is absolute. +/// On POSIX systems, absolute paths start with a `/` (forward slash). On +/// Windows, an absolute path starts with `\\`, or a drive letter followed by +/// `:/` or `:\`. +bool isRelative(String path) => _context.isRelative(path); + +/// Returns `true` if [path] is a root-relative path and `false` if it's not. +/// +/// URLs that start with `/` are known as "root-relative", since they're +/// relative to the root of the current URL. Since root-relative paths are still +/// absolute in every other sense, [isAbsolute] will return true for them. They +/// can be detected using [isRootRelative]. +/// +/// No POSIX and Windows paths are root-relative. +bool isRootRelative(String path) => _context.isRootRelative(path); + +/// Joins the given path parts into a single path using the current platform's +/// [separator]. Example: +/// +/// path.join('path', 'to', 'foo'); // -> 'path/to/foo' +/// +/// If any part ends in a path separator, then a redundant separator will not +/// be added: +/// +/// path.join('path/', 'to', 'foo'); // -> 'path/to/foo +/// +/// If a part is an absolute path, then anything before that will be ignored: +/// +/// path.join('path', '/to', 'foo'); // -> '/to/foo' +String join(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7, String part8]) => + _context.join(part1, part2, part3, part4, part5, part6, part7, part8); + +/// Joins the given path parts into a single path using the current platform's +/// [separator]. Example: +/// +/// path.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' +/// +/// If any part ends in a path separator, then a redundant separator will not +/// be added: +/// +/// path.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo +/// +/// If a part is an absolute path, then anything before that will be ignored: +/// +/// path.joinAll(['path', '/to', 'foo']); // -> '/to/foo' +/// +/// For a fixed number of parts, [join] is usually terser. +String joinAll(Iterable parts) => _context.joinAll(parts); + +// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. +/// Splits [path] into its components using the current platform's [separator]. +/// +/// path.split('path/to/foo'); // -> ['path', 'to', 'foo'] +/// +/// The path will *not* be normalized before splitting. +/// +/// path.split('path/../foo'); // -> ['path', '..', 'foo'] +/// +/// If [path] is absolute, the root directory will be the first element in the +/// array. Example: +/// +/// // Unix +/// path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] +/// +/// // Windows +/// path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] +/// +/// // Browser +/// path.split('http://dartlang.org/path/to/foo'); +/// // -> ['http://dartlang.org', 'path', 'to', 'foo'] +List split(String path) => _context.split(path); + +/// Normalizes [path], simplifying it by handling `..`, and `.`, and +/// removing redundant path separators whenever possible. +/// +/// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' +String normalize(String path) => _context.normalize(path); + +/// Attempts to convert [path] to an equivalent relative path from the current +/// directory. +/// +/// // Given current directory is /root/path: +/// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart' +/// path.relative('/root/other.dart'); // -> '../other.dart' +/// +/// If the [from] argument is passed, [path] is made relative to that instead. +/// +/// path.relative('/root/path/a/b.dart', +/// from: '/root/path'); // -> 'a/b.dart' +/// path.relative('/root/other.dart', +/// from: '/root/path'); // -> '../other.dart' +/// +/// If [path] and/or [from] are relative paths, they are assumed to be relative +/// to the current directory. +/// +/// Since there is no relative path from one drive letter to another on Windows, +/// or from one hostname to another for URLs, this will return an absolute path +/// in those cases. +/// +/// // Windows +/// path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' +/// +/// // URL +/// path.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); +/// // -> 'http://dartlang.org' +String relative(String path, {String from}) => + _context.relative(path, from: from); + +/// Returns `true` if [child] is a path beneath `parent`, and `false` otherwise. +/// +/// path.isWithin('/root/path', '/root/path/a'); // -> true +/// path.isWithin('/root/path', '/root/other'); // -> false +/// path.isWithin('/root/path', '/root/path') // -> false +bool isWithin(String parent, String child) => _context.isWithin(parent, child); + +/// Removes a trailing extension from the last part of [path]. +/// +/// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' +String withoutExtension(String path) => _context.withoutExtension(path); + +/// Returns the path represented by [uri], which may be a [String] or a [Uri]. +/// +/// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL +/// style, this will just convert [uri] to a string. +/// +/// // POSIX +/// context.fromUri('file:///path/to/foo') +/// // -> '/path/to/foo' +/// +/// // Windows +/// context.fromUri('file:///C:/path/to/foo') +/// // -> r'C:\path\to\foo' +/// +/// // URL +/// context.fromUri('http://dartlang.org/path/to/foo') +/// // -> 'http://dartlang.org/path/to/foo' +/// +/// If [uri] is relative, a relative path will be returned. +/// +/// path.fromUri('path/to/foo'); // -> 'path/to/foo' +String fromUri(uri) => _context.fromUri(uri); + +/// Returns the URI that represents [path]. +/// +/// For POSIX and Windows styles, this will return a `file:` URI. For the URL +/// style, this will just convert [path] to a [Uri]. +/// +/// // POSIX +/// path.toUri('/path/to/foo') +/// // -> Uri.parse('file:///path/to/foo') +/// +/// // Windows +/// path.toUri(r'C:\path\to\foo') +/// // -> Uri.parse('file:///C:/path/to/foo') +/// +/// // URL +/// path.toUri('http://dartlang.org/path/to/foo') +/// // -> Uri.parse('http://dartlang.org/path/to/foo') +/// +/// If [path] is relative, a relative URI will be returned. +/// +/// path.toUri('path/to/foo') +/// // -> Uri.parse('path/to/foo') +Uri toUri(String path) => _context.toUri(path); + +/// Returns a terse, human-readable representation of [uri]. +/// +/// [uri] can be a [String] or a [Uri]. If it can be made relative to the +/// current working directory, that's done. Otherwise, it's returned as-is. This +/// gracefully handles non-`file:` URIs for [Style.posix] and [Style.windows]. +/// +/// The returned value is meant for human consumption, and may be either URI- +/// or path-formatted. +/// +/// // POSIX at "/root/path" +/// path.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart' +/// path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' +/// +/// // Windows at "C:\root\path" +/// path.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart' +/// path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' +/// +/// // URL at "http://dartlang.org/root/path" +/// path.prettyUri('http://dartlang.org/root/path/a/b.dart'); +/// // -> r'a/b.dart' +/// path.prettyUri('file:///root/path'); // -> 'file:///root/path' +String prettyUri(uri) => _context.prettyUri(uri); diff --git a/Chapter 7/serving_files/bin/packages/path/src/characters.dart b/Chapter 7/serving_files/bin/packages/path/src/characters.dart new file mode 100644 index 0000000..ff196a6 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/path/src/characters.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2014, 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. + +/// This library contains character-code definitions. +library path.characters; + +const PLUS = 0x2b; +const MINUS = 0x2d; +const PERIOD = 0x2e; +const SLASH = 0x2f; +const ZERO = 0x30; +const NINE = 0x39; +const COLON = 0x3a; +const UPPER_A = 0x41; +const UPPER_Z = 0x5a; +const LOWER_A = 0x61; +const LOWER_Z = 0x7a; +const BACKSLASH = 0x5c; diff --git a/Chapter 7/serving_files/bin/packages/path/src/context.dart b/Chapter 7/serving_files/bin/packages/path/src/context.dart new file mode 100644 index 0000000..823e0c2 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/path/src/context.dart @@ -0,0 +1,547 @@ +// Copyright (c) 2013, 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. + +library path.context; + +import 'internal_style.dart'; +import 'style.dart'; +import 'parsed_path.dart'; +import 'path_exception.dart'; +import '../path.dart' as p; + +/// An instantiable class for manipulating paths. Unlike the top-level +/// functions, this lets you explicitly select what platform the paths will use. +class Context { + /// Creates a new path context for the given style and current directory. + /// + /// If [style] is omitted, it uses the host operating system's path style. If + /// only [current] is omitted, it defaults ".". If *both* [style] and + /// [current] are omitted, [current] defaults to the real current working + /// directory. + /// + /// On the browser, [style] defaults to [Style.url] and [current] defaults to + /// the current URL. + factory Context({Style style, String current}) { + if (current == null) { + if (style == null) { + current = p.current; + } else { + current = "."; + } + } + + if (style == null) { + style = Style.platform; + } else if (style is! InternalStyle) { + throw new ArgumentError("Only styles defined by the path package are " + "allowed."); + } + + return new Context._(style, current); + } + + Context._(this.style, this.current); + + /// The style of path that this context works with. + final InternalStyle style; + + /// The current directory that relative paths will be relative to. + final String current; + + /// Gets the path separator for the context's [style]. On Mac and Linux, + /// this is `/`. On Windows, it's `\`. + String get separator => style.separator; + + /// Creates a new path by appending the given path parts to [current]. + /// Equivalent to [join()] with [current] as the first argument. Example: + /// + /// var context = new Context(current: '/root'); + /// context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo' + /// + /// If [current] isn't absolute, this won't return an absolute path. + String absolute(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7]) { + return join(current, part1, part2, part3, part4, part5, part6, part7); + } + + /// Gets the part of [path] after the last separator on the context's + /// platform. + /// + /// context.basename('path/to/foo.dart'); // -> 'foo.dart' + /// context.basename('path/to'); // -> 'to' + /// + /// Trailing separators are ignored. + /// + /// context.basename('path/to/'); // -> 'to' + String basename(String path) => _parse(path).basename; + + /// Gets the part of [path] after the last separator on the context's + /// platform, and without any trailing file extension. + /// + /// context.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' + /// + /// Trailing separators are ignored. + /// + /// context.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' + String basenameWithoutExtension(String path) => + _parse(path).basenameWithoutExtension; + + /// Gets the part of [path] before the last separator. + /// + /// context.dirname('path/to/foo.dart'); // -> 'path/to' + /// context.dirname('path/to'); // -> 'path' + /// + /// Trailing separators are ignored. + /// + /// context.dirname('path/to/'); // -> 'path' + String dirname(String path) { + var parsed = _parse(path); + parsed.removeTrailingSeparators(); + if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; + if (parsed.parts.length == 1) { + return parsed.root == null ? '.' : parsed.root; + } + parsed.parts.removeLast(); + parsed.separators.removeLast(); + parsed.removeTrailingSeparators(); + return parsed.toString(); + } + + /// Gets the file extension of [path]: the portion of [basename] from the last + /// `.` to the end (including the `.` itself). + /// + /// context.extension('path/to/foo.dart'); // -> '.dart' + /// context.extension('path/to/foo'); // -> '' + /// context.extension('path.to/foo'); // -> '' + /// context.extension('path/to/foo.dart.js'); // -> '.js' + /// + /// If the file name starts with a `.`, then it is not considered an + /// extension: + /// + /// context.extension('~/.bashrc'); // -> '' + /// context.extension('~/.notes.txt'); // -> '.txt' + String extension(String path) => _parse(path).extension; + + // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. + /// Returns the root of [path] if it's absolute, or an empty string if it's + /// relative. + /// + /// // Unix + /// context.rootPrefix('path/to/foo'); // -> '' + /// context.rootPrefix('/path/to/foo'); // -> '/' + /// + /// // Windows + /// context.rootPrefix(r'path\to\foo'); // -> '' + /// context.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' + /// + /// // URL + /// context.rootPrefix('path/to/foo'); // -> '' + /// context.rootPrefix('http://dartlang.org/path/to/foo'); + /// // -> 'http://dartlang.org' + String rootPrefix(String path) { + var root = _parse(path).root; + return root == null ? '' : root; + } + + /// Returns `true` if [path] is an absolute path and `false` if it is a + /// relative path. + /// + /// On POSIX systems, absolute paths start with a `/` (forward slash). On + /// Windows, an absolute path starts with `\\`, or a drive letter followed by + /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and + /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. + /// + /// URLs that start with `/` are known as "root-relative", since they're + /// relative to the root of the current URL. Since root-relative paths are + /// still absolute in every other sense, [isAbsolute] will return true for + /// them. They can be detected using [isRootRelative]. + bool isAbsolute(String path) => _parse(path).isAbsolute; + + /// Returns `true` if [path] is a relative path and `false` if it is absolute. + /// On POSIX systems, absolute paths start with a `/` (forward slash). On + /// Windows, an absolute path starts with `\\`, or a drive letter followed by + /// `:/` or `:\`. + bool isRelative(String path) => !this.isAbsolute(path); + + /// Returns `true` if [path] is a root-relative path and `false` if it's not. + /// + /// URLs that start with `/` are known as "root-relative", since they're + /// relative to the root of the current URL. Since root-relative paths are + /// still absolute in every other sense, [isAbsolute] will return true for + /// them. They can be detected using [isRootRelative]. + /// + /// No POSIX and Windows paths are root-relative. + bool isRootRelative(String path) => _parse(path).isRootRelative; + + /// Joins the given path parts into a single path. Example: + /// + /// context.join('path', 'to', 'foo'); // -> 'path/to/foo' + /// + /// If any part ends in a path separator, then a redundant separator will not + /// be added: + /// + /// context.join('path/', 'to', 'foo'); // -> 'path/to/foo + /// + /// If a part is an absolute path, then anything before that will be ignored: + /// + /// context.join('path', '/to', 'foo'); // -> '/to/foo' + /// + String join(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7, String part8]) { + var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; + _validateArgList("join", parts); + return joinAll(parts.where((part) => part != null)); + } + + /// Joins the given path parts into a single path. Example: + /// + /// context.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' + /// + /// If any part ends in a path separator, then a redundant separator will not + /// be added: + /// + /// context.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo + /// + /// If a part is an absolute path, then anything before that will be ignored: + /// + /// context.joinAll(['path', '/to', 'foo']); // -> '/to/foo' + /// + /// For a fixed number of parts, [join] is usually terser. + String joinAll(Iterable parts) { + var buffer = new StringBuffer(); + var needsSeparator = false; + var isAbsoluteAndNotRootRelative = false; + + for (var part in parts.where((part) => part != '')) { + if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) { + // If the new part is root-relative, it preserves the previous root but + // replaces the path after it. + var parsed = _parse(part); + parsed.root = this.rootPrefix(buffer.toString()); + if (style.needsSeparator(parsed.root)) { + parsed.separators[0] = style.separator; + } + buffer.clear(); + buffer.write(parsed.toString()); + } else if (this.isAbsolute(part)) { + isAbsoluteAndNotRootRelative = !this.isRootRelative(part); + // An absolute path discards everything before it. + buffer.clear(); + buffer.write(part); + } else { + if (part.length > 0 && style.containsSeparator(part[0])) { + // The part starts with a separator, so we don't need to add one. + } else if (needsSeparator) { + buffer.write(separator); + } + + buffer.write(part); + } + + // Unless this part ends with a separator, we'll need to add one before + // the next part. + needsSeparator = style.needsSeparator(part); + } + + return buffer.toString(); + } + + // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. + /// Splits [path] into its components using the current platform's + /// [separator]. Example: + /// + /// context.split('path/to/foo'); // -> ['path', 'to', 'foo'] + /// + /// The path will *not* be normalized before splitting. + /// + /// context.split('path/../foo'); // -> ['path', '..', 'foo'] + /// + /// If [path] is absolute, the root directory will be the first element in the + /// array. Example: + /// + /// // Unix + /// context.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] + /// + /// // Windows + /// context.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] + List split(String path) { + var parsed = _parse(path); + // Filter out empty parts that exist due to multiple separators in a row. + parsed.parts = parsed.parts.where((part) => !part.isEmpty) + .toList(); + if (parsed.root != null) parsed.parts.insert(0, parsed.root); + return parsed.parts; + } + + /// Normalizes [path], simplifying it by handling `..`, and `.`, and + /// removing redundant path separators whenever possible. + /// + /// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt' + String normalize(String path) { + var parsed = _parse(path); + parsed.normalize(); + return parsed.toString(); + } + + /// Attempts to convert [path] to an equivalent relative path relative to + /// [root]. + /// + /// var context = new Context(current: '/root/path'); + /// context.relative('/root/path/a/b.dart'); // -> 'a/b.dart' + /// context.relative('/root/other.dart'); // -> '../other.dart' + /// + /// If the [from] argument is passed, [path] is made relative to that instead. + /// + /// context.relative('/root/path/a/b.dart', + /// from: '/root/path'); // -> 'a/b.dart' + /// context.relative('/root/other.dart', + /// from: '/root/path'); // -> '../other.dart' + /// + /// If [path] and/or [from] are relative paths, they are assumed to be + /// relative to [current]. + /// + /// Since there is no relative path from one drive letter to another on + /// Windows, this will return an absolute path in that case. + /// + /// context.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other' + /// + /// This will also return an absolute path if an absolute [path] is passed to + /// a context with a relative path for [current]. + /// + /// var context = new Context(r'some/relative/path'); + /// context.relative(r'/absolute/path'); // -> '/absolute/path' + /// + /// If [root] is relative, it may be impossible to determine a path from + /// [from] to [path]. For example, if [root] and [path] are "." and [from] is + /// "/", no path can be determined. In this case, a [PathException] will be + /// thrown. + String relative(String path, {String from}) { + from = from == null ? current : this.join(current, from); + + // We can't determine the path from a relative path to an absolute path. + if (this.isRelative(from) && this.isAbsolute(path)) { + return this.normalize(path); + } + + // If the given path is relative, resolve it relative to the context's + // current directory. + if (this.isRelative(path) || this.isRootRelative(path)) { + path = this.absolute(path); + } + + // If the path is still relative and `from` is absolute, we're unable to + // find a path from `from` to `path`. + if (this.isRelative(path) && this.isAbsolute(from)) { + throw new PathException('Unable to find a path to "$path" from "$from".'); + } + + var fromParsed = _parse(from)..normalize(); + var pathParsed = _parse(path)..normalize(); + + if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') { + return pathParsed.toString(); + } + + // If the root prefixes don't match (for example, different drive letters + // on Windows), then there is no relative path, so just return the absolute + // one. In Windows, drive letters are case-insenstive and we allow + // calculation of relative paths, even if a path has not been normalized. + if (fromParsed.root != pathParsed.root && + ((fromParsed.root == null || pathParsed.root == null) || + fromParsed.root.toLowerCase().replaceAll('/', '\\') != + pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { + return pathParsed.toString(); + } + + // Strip off their common prefix. + while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && + fromParsed.parts[0] == pathParsed.parts[0]) { + fromParsed.parts.removeAt(0); + fromParsed.separators.removeAt(1); + pathParsed.parts.removeAt(0); + pathParsed.separators.removeAt(1); + } + + // If there are any directories left in the from path, we need to walk up + // out of them. If a directory left in the from path is '..', it cannot + // be cancelled by adding a '..'. + if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { + throw new PathException('Unable to find a path to "$path" from "$from".'); + } + pathParsed.parts.insertAll(0, + new List.filled(fromParsed.parts.length, '..')); + pathParsed.separators[0] = ''; + pathParsed.separators.insertAll(1, + new List.filled(fromParsed.parts.length, style.separator)); + + // Corner case: the paths completely collapsed. + if (pathParsed.parts.length == 0) return '.'; + + // Corner case: path was '.' and some '..' directories were added in front. + // Don't add a final '/.' in that case. + if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') { + pathParsed.parts.removeLast(); + pathParsed.separators..removeLast()..removeLast()..add(''); + } + + // Make it relative. + pathParsed.root = ''; + pathParsed.removeTrailingSeparators(); + + return pathParsed.toString(); + } + + /// Returns `true` if [child] is a path beneath `parent`, and `false` + /// otherwise. + /// + /// path.isWithin('/root/path', '/root/path/a'); // -> true + /// path.isWithin('/root/path', '/root/other'); // -> false + /// path.isWithin('/root/path', '/root/path'); // -> false + bool isWithin(String parent, String child) { + var relative; + try { + relative = this.relative(child, from: parent); + } on PathException catch (_) { + // If no relative path from [parent] to [child] is found, [child] + // definitely isn't a child of [parent]. + return false; + } + + var parts = this.split(relative); + return this.isRelative(relative) && parts.first != '..' && + parts.first != '.'; + } + + /// Removes a trailing extension from the last part of [path]. + /// + /// context.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' + String withoutExtension(String path) { + var parsed = _parse(path); + + for (var i = parsed.parts.length - 1; i >= 0; i--) { + if (!parsed.parts[i].isEmpty) { + parsed.parts[i] = parsed.basenameWithoutExtension; + break; + } + } + + return parsed.toString(); + } + + /// Returns the path represented by [uri], which may be a [String] or a [Uri]. + /// + /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL + /// style, this will just convert [uri] to a string. + /// + /// // POSIX + /// context.fromUri('file:///path/to/foo') + /// // -> '/path/to/foo' + /// + /// // Windows + /// context.fromUri('file:///C:/path/to/foo') + /// // -> r'C:\path\to\foo' + /// + /// // URL + /// context.fromUri('http://dartlang.org/path/to/foo') + /// // -> 'http://dartlang.org/path/to/foo' + /// + /// If [uri] is relative, a relative path will be returned. + /// + /// path.fromUri('path/to/foo'); // -> 'path/to/foo' + String fromUri(uri) { + if (uri is String) uri = Uri.parse(uri); + return style.pathFromUri(uri); + } + + /// Returns the URI that represents [path]. + /// + /// For POSIX and Windows styles, this will return a `file:` URI. For the URL + /// style, this will just convert [path] to a [Uri]. + /// + /// // POSIX + /// context.toUri('/path/to/foo') + /// // -> Uri.parse('file:///path/to/foo') + /// + /// // Windows + /// context.toUri(r'C:\path\to\foo') + /// // -> Uri.parse('file:///C:/path/to/foo') + /// + /// // URL + /// context.toUri('http://dartlang.org/path/to/foo') + /// // -> Uri.parse('http://dartlang.org/path/to/foo') + Uri toUri(String path) { + if (isRelative(path)) { + return style.relativePathToUri(path); + } else { + return style.absolutePathToUri(join(current, path)); + } + } + + /// Returns a terse, human-readable representation of [uri]. + /// + /// [uri] can be a [String] or a [Uri]. If it can be made relative to the + /// current working directory, that's done. Otherwise, it's returned as-is. + /// This gracefully handles non-`file:` URIs for [Style.posix] and + /// [Style.windows]. + /// + /// The returned value is meant for human consumption, and may be either URI- + /// or path-formatted. + /// + /// // POSIX + /// var context = new Context(current: '/root/path'); + /// context.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart' + /// context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' + /// + /// // Windows + /// var context = new Context(current: r'C:\root\path'); + /// context.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart' + /// context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' + /// + /// // URL + /// var context = new Context(current: 'http://dartlang.org/root/path'); + /// context.prettyUri('http://dartlang.org/root/path/a/b.dart'); + /// // -> r'a/b.dart' + /// context.prettyUri('file:///root/path'); // -> 'file:///root/path' + String prettyUri(uri) { + if (uri is String) uri = Uri.parse(uri); + if (uri.scheme == 'file' && style == Style.url) return uri.toString(); + if (uri.scheme != 'file' && uri.scheme != '' && style != Style.url) { + return uri.toString(); + } + + var path = normalize(fromUri(uri)); + var rel = relative(path); + var components = split(rel); + + // Only return a relative path if it's actually shorter than the absolute + // path. This avoids ugly things like long "../" chains to get to the root + // and then go back down. + return split(rel).length > split(path).length ? path : rel; + } + + ParsedPath _parse(String path) => new ParsedPath.parse(path, style); +} + +/// Validates that there are no non-null arguments following a null one and +/// throws an appropriate [ArgumentError] on failure. +_validateArgList(String method, List args) { + for (var i = 1; i < args.length; i++) { + // Ignore nulls hanging off the end. + if (args[i] == null || args[i - 1] != null) continue; + + var numArgs; + for (numArgs = args.length; numArgs >= 1; numArgs--) { + if (args[numArgs - 1] != null) break; + } + + // Show the arguments. + var message = new StringBuffer(); + message.write("$method("); + message.write(args.take(numArgs) + .map((arg) => arg == null ? "null" : '"$arg"') + .join(", ")); + message.write("): part ${i - 1} was null, but part $i was not."); + throw new ArgumentError(message.toString()); + } +} diff --git a/Chapter 7/serving_files/bin/packages/path/src/internal_style.dart b/Chapter 7/serving_files/bin/packages/path/src/internal_style.dart new file mode 100644 index 0000000..67b5d34 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/path/src/internal_style.dart @@ -0,0 +1,54 @@ +// Copyright (c) 2014, 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. + +library path.internal_style; + +import 'context.dart'; +import 'style.dart'; + +/// The internal interface for the [Style] type. +/// +/// Users should be able to pass around instances of [Style] like an enum, but +/// the members that [Context] uses should be hidden from them. Those members +/// are defined on this class instead. +abstract class InternalStyle extends Style { + /// The default path separator for this style. + /// + /// On POSIX, this is `/`. On Windows, it's `\`. + String get separator; + + /// Returns whether [path] contains a separator. + bool containsSeparator(String path); + + /// Returns whether [codeUnit] is the character code of a separator. + bool isSeparator(int codeUnit); + + /// Returns whether this path component needs a separator after it. + /// + /// Windows and POSIX styles just need separators when the previous component + /// doesn't already end in a separator, but the URL always needs to place a + /// separator between the root and the first component, even if the root + /// already ends in a separator character. For example, to join "file://" and + /// "usr", an additional "/" is needed (making "file:///usr"). + bool needsSeparator(String path); + + /// Gets the root prefix of [path] if path is absolute. If [path] is relative, + /// returns `null`. + String getRoot(String path); + + /// Gets the root prefix of [path] if it's root-relative. + /// + /// If [path] is relative or absolute and not root-relative, returns `null`. + String getRelativeRoot(String path); + + /// Returns the path represented by [uri] in this style. + String pathFromUri(Uri uri); + + /// Returns the URI that represents the relative path made of [parts]. + Uri relativePathToUri(String path) => + new Uri(pathSegments: context.split(path)); + + /// Returns the URI that represents [path], which is assumed to be absolute. + Uri absolutePathToUri(String path); +} diff --git a/Chapter 7/serving_files/bin/packages/path/src/parsed_path.dart b/Chapter 7/serving_files/bin/packages/path/src/parsed_path.dart new file mode 100644 index 0000000..57773ee --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/path/src/parsed_path.dart @@ -0,0 +1,187 @@ +// Copyright (c) 2013, 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. + +library path.parsed_path; + +import 'internal_style.dart'; +import 'style.dart'; + +class ParsedPath { + /// The [InternalStyle] that was used to parse this path. + InternalStyle style; + + /// The absolute root portion of the path, or `null` if the path is relative. + /// On POSIX systems, this will be `null` or "/". On Windows, it can be + /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive + /// letters. + String root; + + /// Whether this path is root-relative. + /// + /// See [Context.isRootRelative]. + bool isRootRelative; + + /// The path-separated parts of the path. All but the last will be + /// directories. + List parts; + + /// The path separators preceding each part. + /// + /// The first one will be an empty string unless the root requires a separator + /// between it and the path. The last one will be an empty string unless the + /// path ends with a trailing separator. + List separators; + + /// The file extension of the last non-empty part, or "" if it doesn't have + /// one. + String get extension => _splitExtension()[1]; + + /// `true` if this is an absolute path. + bool get isAbsolute => root != null; + + factory ParsedPath.parse(String path, InternalStyle style) { + var before = path; + + // Remove the root prefix, if any. + var root = style.getRoot(path); + var isRootRelative = style.getRelativeRoot(path) != null; + if (root != null) path = path.substring(root.length); + + // Split the parts on path separators. + var parts = []; + var separators = []; + + var start = 0; + + if (path.isNotEmpty && style.isSeparator(path.codeUnitAt(0))) { + separators.add(path[0]); + start = 1; + } else { + separators.add(''); + } + + for (var i = start; i < path.length; i++) { + if (style.isSeparator(path.codeUnitAt(i))) { + parts.add(path.substring(start, i)); + separators.add(path[i]); + start = i + 1; + } + } + + // Add the final part, if any. + if (start < path.length) { + parts.add(path.substring(start)); + separators.add(''); + } + + return new ParsedPath._(style, root, isRootRelative, parts, separators); + } + + ParsedPath._(this.style, this.root, this.isRootRelative, this.parts, + this.separators); + + String get basename { + var copy = this.clone(); + copy.removeTrailingSeparators(); + if (copy.parts.isEmpty) return root == null ? '' : root; + return copy.parts.last; + } + + String get basenameWithoutExtension => _splitExtension()[0]; + + bool get hasTrailingSeparator => + !parts.isEmpty && (parts.last == '' || separators.last != ''); + + void removeTrailingSeparators() { + while (!parts.isEmpty && parts.last == '') { + parts.removeLast(); + separators.removeLast(); + } + if (separators.length > 0) separators[separators.length - 1] = ''; + } + + void normalize() { + // Handle '.', '..', and empty parts. + var leadingDoubles = 0; + var newParts = []; + for (var part in parts) { + if (part == '.' || part == '') { + // Do nothing. Ignore it. + } else if (part == '..') { + // Pop the last part off. + if (newParts.length > 0) { + newParts.removeLast(); + } else { + // Backed out past the beginning, so preserve the "..". + leadingDoubles++; + } + } else { + newParts.add(part); + } + } + + // A relative path can back out from the start directory. + if (!isAbsolute) { + newParts.insertAll(0, new List.filled(leadingDoubles, '..')); + } + + // If we collapsed down to nothing, do ".". + if (newParts.length == 0 && !isAbsolute) { + newParts.add('.'); + } + + // Canonicalize separators. + var newSeparators = new List.generate( + newParts.length, (_) => style.separator, growable: true); + newSeparators.insert(0, + isAbsolute && newParts.length > 0 && style.needsSeparator(root) ? + style.separator : ''); + + parts = newParts; + separators = newSeparators; + + // Normalize the Windows root if needed. + if (root != null && style == Style.windows) { + root = root.replaceAll('/', '\\'); + } + removeTrailingSeparators(); + } + + String toString() { + var builder = new StringBuffer(); + if (root != null) builder.write(root); + for (var i = 0; i < parts.length; i++) { + builder.write(separators[i]); + builder.write(parts[i]); + } + builder.write(separators.last); + + return builder.toString(); + } + + /// Splits the last non-empty part of the path into a `[basename, extension`] + /// pair. + /// + /// Returns a two-element list. The first is the name of the file without any + /// extension. The second is the extension or "" if it has none. + List _splitExtension() { + var file = parts.lastWhere((p) => p != '', orElse: () => null); + + if (file == null) return ['', '']; + if (file == '..') return ['..', '']; + + var lastDot = file.lastIndexOf('.'); + + // If there is no dot, or it's the first character, like '.bashrc', it + // doesn't count. + if (lastDot <= 0) return [file, '']; + + return [file.substring(0, lastDot), file.substring(lastDot)]; + } + + ParsedPath clone() => new ParsedPath._( + style, root, isRootRelative, + new List.from(parts), new List.from(separators)); +} + diff --git a/Chapter 7/serving_files/bin/packages/path/src/path_exception.dart b/Chapter 7/serving_files/bin/packages/path/src/path_exception.dart new file mode 100644 index 0000000..49bd268 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/path/src/path_exception.dart @@ -0,0 +1,15 @@ +// Copyright (c) 2013, 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. + +library path.path_exception; + +/// An exception class that's thrown when a path operation is unable to be +/// computed accurately. +class PathException implements Exception { + String message; + + PathException(this.message); + + String toString() => "PathException: $message"; +} diff --git a/Chapter 7/serving_files/bin/packages/path/src/style.dart b/Chapter 7/serving_files/bin/packages/path/src/style.dart new file mode 100644 index 0000000..a94ec68 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/path/src/style.dart @@ -0,0 +1,88 @@ +// Copyright (c) 2013, 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. + +library path.style; + +import 'context.dart'; +import 'style/posix.dart'; +import 'style/url.dart'; +import 'style/windows.dart'; + +/// An enum type describing a "flavor" of path. +abstract class Style { + /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths + /// start with "/". Used by UNIX, Linux, Mac OS X, and others. + static final posix = new PosixStyle(); + + /// Windows paths use "\" (backslash) as separators. Absolute paths start with + /// a drive letter followed by a colon (example, "C:") or two backslashes + /// ("\\") for UNC paths. + // TODO(rnystrom): The UNC root prefix should include the drive name too, not + // just the "\\". + static final windows = new WindowsStyle(); + + /// URLs aren't filesystem paths, but they're supported to make it easier to + /// manipulate URL paths in the browser. + /// + /// URLs use "/" (forward slash) as separators. Absolute paths either start + /// with a protocol and optional hostname (e.g. `http://dartlang.org`, + /// `file://`) or with "/". + static final url = new UrlStyle(); + + /// The style of the host platform. + /// + /// When running on the command line, this will be [windows] or [posix] based + /// on the host operating system. On a browser, this will be [url]. + static final platform = _getPlatformStyle(); + + /// Gets the type of the host platform. + static Style _getPlatformStyle() { + // If we're running a Dart file in the browser from a `file:` URI, + // [Uri.base] will point to a file. If we're running on the standalone, + // it will point to a directory. We can use that fact to determine which + // style to use. + if (Uri.base.scheme != 'file') return Style.url; + if (!Uri.base.path.endsWith('/')) return Style.url; + if (new Uri(path: 'a/b').toFilePath() == 'a\\b') return Style.windows; + return Style.posix; + } + + /// The name of this path style. Will be "posix" or "windows". + String get name; + + /// A [Context] that uses this style. + Context get context => new Context(style: this); + + @Deprecated("Most Style members will be removed in path 2.0.") + String get separator; + + @Deprecated("Most Style members will be removed in path 2.0.") + Pattern get separatorPattern; + + @Deprecated("Most Style members will be removed in path 2.0.") + Pattern get needsSeparatorPattern; + + @Deprecated("Most Style members will be removed in path 2.0.") + Pattern get rootPattern; + + @Deprecated("Most Style members will be removed in path 2.0.") + Pattern get relativeRootPattern; + + @Deprecated("Most style members will be removed in path 2.0.") + String getRoot(String path); + + @Deprecated("Most style members will be removed in path 2.0.") + String getRelativeRoot(String path); + + @Deprecated("Most style members will be removed in path 2.0.") + String pathFromUri(Uri uri); + + @Deprecated("Most style members will be removed in path 2.0.") + Uri relativePathToUri(String path); + + @Deprecated("Most style members will be removed in path 2.0.") + Uri absolutePathToUri(String path); + + String toString() => name; +} diff --git a/Chapter 7/serving_files/bin/packages/path/src/style/posix.dart b/Chapter 7/serving_files/bin/packages/path/src/style/posix.dart new file mode 100644 index 0000000..b8b82b4 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/path/src/style/posix.dart @@ -0,0 +1,62 @@ +// Copyright (c) 2013, 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. + +library path.style.posix; + +import '../characters.dart' as chars; +import '../parsed_path.dart'; +import '../internal_style.dart'; + +/// The style for POSIX paths. +class PosixStyle extends InternalStyle { + PosixStyle(); + + final name = 'posix'; + final separator = '/'; + final separators = const ['/']; + + // Deprecated properties. + + final separatorPattern = new RegExp(r'/'); + final needsSeparatorPattern = new RegExp(r'[^/]$'); + final rootPattern = new RegExp(r'^/'); + final relativeRootPattern = null; + + bool containsSeparator(String path) => path.contains('/'); + + bool isSeparator(int codeUnit) => codeUnit == chars.SLASH; + + bool needsSeparator(String path) => + path.isNotEmpty && !isSeparator(path.codeUnitAt(path.length - 1)); + + String getRoot(String path) { + if (path.isNotEmpty && isSeparator(path.codeUnitAt(0))) return '/'; + return null; + } + + String getRelativeRoot(String path) => null; + + String pathFromUri(Uri uri) { + if (uri.scheme == '' || uri.scheme == 'file') { + return Uri.decodeComponent(uri.path); + } + throw new ArgumentError("Uri $uri must have scheme 'file:'."); + } + + Uri absolutePathToUri(String path) { + var parsed = new ParsedPath.parse(path, this); + if (parsed.parts.isEmpty) { + // If the path is a bare root (e.g. "/"), [components] will + // currently be empty. We add two empty components so the URL constructor + // produces "file:///", with a trailing slash. + parsed.parts.addAll(["", ""]); + } else if (parsed.hasTrailingSeparator) { + // If the path has a trailing slash, add a single empty component so the + // URI has a trailing slash as well. + parsed.parts.add(""); + } + + return new Uri(scheme: 'file', pathSegments: parsed.parts); + } +} diff --git a/Chapter 7/serving_files/bin/packages/path/src/style/url.dart b/Chapter 7/serving_files/bin/packages/path/src/style/url.dart new file mode 100644 index 0000000..f383923 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/path/src/style/url.dart @@ -0,0 +1,88 @@ +// Copyright (c) 2013, 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. + +library path.style.url; + +import '../characters.dart' as chars; +import '../internal_style.dart'; +import '../utils.dart'; + +/// The style for URL paths. +class UrlStyle extends InternalStyle { + UrlStyle(); + + final name = 'url'; + final separator = '/'; + final separators = const ['/']; + + // Deprecated properties. + + final separatorPattern = new RegExp(r'/'); + final needsSeparatorPattern = new RegExp( + r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); + final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); + final relativeRootPattern = new RegExp(r"^/"); + + bool containsSeparator(String path) => path.contains('/'); + + bool isSeparator(int codeUnit) => codeUnit == chars.SLASH; + + bool needsSeparator(String path) { + if (path.isEmpty) return false; + + // A URL that doesn't end in "/" always needs a separator. + if (!isSeparator(path.codeUnitAt(path.length - 1))) return true; + + // A URI that's just "scheme://" needs an extra separator, despite ending + // with "/". + var root = _getRoot(path); + return root != null && root.endsWith('://'); + } + + String getRoot(String path) { + var root = _getRoot(path); + return root == null ? getRelativeRoot(path) : root; + } + + String getRelativeRoot(String path) { + if (path.isEmpty) return null; + return isSeparator(path.codeUnitAt(0)) ? "/" : null; + } + + String pathFromUri(Uri uri) => uri.toString(); + + Uri relativePathToUri(String path) => Uri.parse(path); + Uri absolutePathToUri(String path) => Uri.parse(path); + + // A helper method for [getRoot] that doesn't handle relative roots. + String _getRoot(String path) { + if (path.isEmpty) return null; + + // We aren't using a RegExp for this because they're slow (issue 19090). If + // we could, we'd match against r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*". + + if (!isAlphabetic(path.codeUnitAt(0))) return null; + var start = 1; + for (; start < path.length; start++) { + var char = path.codeUnitAt(start); + if (isAlphabetic(char)) continue; + if (isNumeric(char)) continue; + if (char == chars.MINUS || char == chars.PLUS || char == chars.PERIOD) { + continue; + } + + break; + } + + if (start + 3 > path.length) return null; + if (path.substring(start, start + 3) != '://') return null; + start += 3; + + // A URL root can end with a non-"/" prefix. + while (start < path.length && !isSeparator(path.codeUnitAt(start))) { + start++; + } + return path.substring(0, start); + } +} diff --git a/Chapter 7/serving_files/bin/packages/path/src/style/windows.dart b/Chapter 7/serving_files/bin/packages/path/src/style/windows.dart new file mode 100644 index 0000000..2965f1e --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/path/src/style/windows.dart @@ -0,0 +1,138 @@ +// Copyright (c) 2013, 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. + +library path.style.windows; + +import '../characters.dart' as chars; +import '../internal_style.dart'; +import '../parsed_path.dart'; +import '../utils.dart'; + +/// The style for Windows paths. +class WindowsStyle extends InternalStyle { + WindowsStyle(); + + final name = 'windows'; + final separator = '\\'; + final separators = const ['/', '\\']; + + // Deprecated properties. + + final separatorPattern = new RegExp(r'[/\\]'); + final needsSeparatorPattern = new RegExp(r'[^/\\]$'); + final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); + final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])"); + + bool containsSeparator(String path) => path.contains('/'); + + bool isSeparator(int codeUnit) => + codeUnit == chars.SLASH || codeUnit == chars.BACKSLASH; + + bool needsSeparator(String path) { + if (path.isEmpty) return false; + return !isSeparator(path.codeUnitAt(path.length - 1)); + } + + String getRoot(String path) { + var root = _getRoot(path); + return root == null ? getRelativeRoot(path) : root; + } + + String getRelativeRoot(String path) { + if (path.isEmpty) return null; + if (!isSeparator(path.codeUnitAt(0))) return null; + if (path.length > 1 && isSeparator(path.codeUnitAt(1))) return null; + return path[0]; + } + + String pathFromUri(Uri uri) { + if (uri.scheme != '' && uri.scheme != 'file') { + throw new ArgumentError("Uri $uri must have scheme 'file:'."); + } + + var path = uri.path; + if (uri.host == '') { + // Drive-letter paths look like "file:///C:/path/to/file". The + // replaceFirst removes the extra initial slash. + if (path.startsWith('/')) path = path.replaceFirst("/", ""); + } else { + // Network paths look like "file://hostname/path/to/file". + path = '\\\\${uri.host}$path'; + } + return Uri.decodeComponent(path.replaceAll("/", "\\")); + } + + Uri absolutePathToUri(String path) { + var parsed = new ParsedPath.parse(path, this); + if (parsed.root.startsWith(r'\\')) { + // Network paths become "file://server/share/path/to/file". + + // The root is of the form "\\server\share". We want "server" to be the + // URI host, and "share" to be the first element of the path. + var rootParts = parsed.root.split('\\').where((part) => part != ''); + parsed.parts.insert(0, rootParts.last); + + if (parsed.hasTrailingSeparator) { + // If the path has a trailing slash, add a single empty component so the + // URI has a trailing slash as well. + parsed.parts.add(""); + } + + return new Uri(scheme: 'file', host: rootParts.first, + pathSegments: parsed.parts); + } else { + // Drive-letter paths become "file:///C:/path/to/file". + + // If the path is a bare root (e.g. "C:\"), [parsed.parts] will currently + // be empty. We add an empty component so the URL constructor produces + // "file:///C:/", with a trailing slash. We also add an empty component if + // the URL otherwise has a trailing slash. + if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) { + parsed.parts.add(""); + } + + // Get rid of the trailing "\" in "C:\" because the URI constructor will + // add a separator on its own. + parsed.parts.insert(0, + parsed.root.replaceAll("/", "").replaceAll("\\", "")); + + return new Uri(scheme: 'file', pathSegments: parsed.parts); + } + } + + // A helper method for [getRoot] that doesn't handle relative roots. + String _getRoot(String path) { + if (path.length < 3) return null; + + // We aren't using a RegExp for this because they're slow (issue 19090). If + // we could, we'd match against r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'. + + // Try roots like "C:\". + if (isAlphabetic(path.codeUnitAt(0))) { + if (path.codeUnitAt(1) != chars.COLON) return null; + if (!isSeparator(path.codeUnitAt(2))) return null; + return path.substring(0, 3); + } + + // Try roots like "\\server\share". + if (!path.startsWith('\\\\')) return null; + + var start = 2; + // The server is one or more non-"\" characters. + while (start < path.length && path.codeUnitAt(start) != chars.BACKSLASH) { + start++; + } + if (start == 2 || start == path.length) return null; + + // The share is one or more non-"\" characters. + start += 1; + if (path.codeUnitAt(start) == chars.BACKSLASH) return null; + start += 1; + while (start < path.length && path.codeUnitAt(start) != chars.BACKSLASH) { + start++; + } + + return path.substring(0, start); + } +} \ No newline at end of file diff --git a/Chapter 7/serving_files/bin/packages/path/src/utils.dart b/Chapter 7/serving_files/bin/packages/path/src/utils.dart new file mode 100644 index 0000000..0636261 --- /dev/null +++ b/Chapter 7/serving_files/bin/packages/path/src/utils.dart @@ -0,0 +1,16 @@ +// Copyright (c) 2014, 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. + +library path.utils; + +import 'characters.dart' as chars; + +/// Returns whether [char] is the code for an ASCII letter (uppercase or +/// lowercase). +bool isAlphabetic(int char) => + (char >= chars.UPPER_A && char <= chars.UPPER_Z) || + (char >= chars.LOWER_A && char <= chars.LOWER_Z); + +/// Returns whether [char] is the code for an ASCII digit. +bool isNumeric(int char) => char >= chars.ZERO && char <= chars.NINE; diff --git a/Chapter 7/serving_files/bin/serving_curdir.dart b/Chapter 7/serving_files/bin/serving_curdir.dart new file mode 100644 index 0000000..20ef22b --- /dev/null +++ b/Chapter 7/serving_files/bin/serving_curdir.dart @@ -0,0 +1,24 @@ +import 'dart:io'; +import 'dart:async'; +import 'package:http_server/http_server.dart'; + +InternetAddress HOST = InternetAddress.LOOPBACK_IP_V6; +const PORT = 8080; +VirtualDirectory staticFiles; + +void main() { + staticFiles = new VirtualDirectory('.') + ..allowDirectoryListing = true; + + runZoned( startServer, onError: handleError); +} + +startServer() { + HttpServer.bind(HOST, PORT).then((server) { + server.listen(staticFiles.serveRequest); + }); +} + +handleError(e, stackTrace) { + print('An error occurred: $e $stackTrace'); +} \ No newline at end of file diff --git a/Chapter 7/serving_files/bin/serving_file.dart b/Chapter 7/serving_files/bin/serving_file.dart new file mode 100644 index 0000000..8143e47 --- /dev/null +++ b/Chapter 7/serving_files/bin/serving_file.dart @@ -0,0 +1,14 @@ +import 'dart:io'; +import 'package:http_server/http_server.dart'; + +InternetAddress HOST = InternetAddress.LOOPBACK_IP_V6; +const PORT = 8080; + +void main() { + VirtualDirectory staticFiles = new VirtualDirectory('.'); + HttpServer.bind(HOST, PORT).then((server) { + server.listen((req) { + staticFiles.serveFile(new File('Learning Dart Packt Publishing.html'), req); + }); + }); +} diff --git a/Chapter 7/serving_files/packages/http_server/http_server.dart b/Chapter 7/serving_files/packages/http_server/http_server.dart new file mode 100644 index 0000000..afaf71c --- /dev/null +++ b/Chapter 7/serving_files/packages/http_server/http_server.dart @@ -0,0 +1,90 @@ +// Copyright (c) 2013, 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. + +/** + * A library for serving HTTP requests and resources. + * + * ## Installing ## + * + * Use [pub][] to install this package. Add the following to your + * `pubspec.yaml` file. + * + * dependencies: + * http_server: any + * + * Then run `pub install`. + * + * For more information, see the + * [http_server package on pub.dartlang.org][pub]. + * + * ## Basic usage + * + * Here is a short example of how to serve all files from the current + * directory. + * + * import 'dart:io'; + * import 'dart:async'; + * import 'package:http_server/http_server.dart'; + * + * void main() { + * var staticFiles = new VirtualDirectory('.') + * ..allowDirectoryListing = true; + * + * runZoned(() { + * HttpServer.bind('0.0.0.0', 7777).then((server) { + * print('Server running'); + * server.listen(staticFiles.serveRequest); + * }); + * }, + * onError: (e, stackTrace) => print('Oh noes! $e $stackTrace')); + * } + * + * ## Virtual directory + * + * The [VirtualDirectory] class makes it easy to serve static content + * from the file system. It supports: + * + * * Range-based requests. + * * If-Modified-Since based caching. + * * Automatic GZip-compression of content. + * * Following symlinks, either throughout the system or inside + * a jailed root. + * * Directory listing. + * + * See [VirtualDirectory] for more information. + * + * ## Virtual host + * + * The [VirtualHost] class helps to serve multiple hosts on the same + * address, by using the `Host` field of the incoming requests. It also + * works with wildcards for sub-domains. + * + * var virtualHost = new VirtualHost(server); + * // Filter out on a specific host + * var stream1 = virtualServer.addHost('static.myserver.com'); + * // Wildcard for any other sub-domains. + * var stream2 = virtualServer.addHost('*.myserver.com'); + * // Requets not matching any hosts. + * var stream3 = virtualServer.unhandled; + * + * See [VirtualHost] for more information. + * + * [pub]: http://pub.dartlang.org/packages/http_server + */ +library http_server; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:mime/mime.dart'; +import "package:path/path.dart"; + +part 'src/http_body.dart'; +part 'src/http_body_impl.dart'; +part 'src/http_multipart_form_data.dart'; +part 'src/http_multipart_form_data_impl.dart'; +part 'src/virtual_directory.dart'; +part 'src/virtual_host.dart'; + diff --git a/Chapter 7/serving_files/packages/http_server/src/http_body.dart b/Chapter 7/serving_files/packages/http_server/src/http_body.dart new file mode 100644 index 0000000..6eb4d31 --- /dev/null +++ b/Chapter 7/serving_files/packages/http_server/src/http_body.dart @@ -0,0 +1,206 @@ +// Copyright (c) 2013, 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. + +part of http_server; + + +/** + * [HttpBodyHandler] is a helper class for processing and collecting + * HTTP message data in an easy-to-use [HttpBody] object. The content + * body is parsed, depending on the `Content-Type` header field. When + * the full body is read and parsed the body content is made + * available. The class can be used to process both server requests + * and client responses. + * + * The following content types are recognized: + * + * text/ * + * application/json + * application/x-www-form-urlencoded + * multipart/form-data + * + * For content type `text/\*` the body is decoded into a string. The + * 'charset' parameter of the content type specifies the encoding + * used for decoding. If no 'charset' is present the default encoding + * of ISO-8859-1 is used. + * + * For content type `application/json` the body is decoded into a + * string which is then parsed as JSON. The resulting body is a + * [Map]. The 'charset' parameter of the content type specifies the + * encoding used for decoding. If no 'charset' is present the default + * encoding of UTF-8 is used. + * + * For content type `application/x-www-form-urlencoded` the body is a + * query string which is then split according to the rules for + * splitting a query string. The resulting body is a `Map`. If the same name is present several times in the query + * string, then the last value seen for this name will be in the + * resulting map. The encoding US-ASCII is always used for decoding + * the body. + * + * For content type `multipart/form-data` the body is parsed into + * it's different fields. The resulting body is a `Map`, where the value is a [String] for normal fields and a + * [HttpBodyFileUpload] instance for file upload fields. If the same + * name is present several times, then the last value seen for this + * name will be in the resulting map. + * + * When using content type `multipart/form-data` the encoding of + * fields with [String] values is determined by the browser sending + * the HTTP request with the form data. The encoding is specified + * either by the attribute `accept-charset` on the HTML form, or by + * the content type of the web page containing the form. If the HTML + * form has an `accept-charset` attribute the browser will use the + * encoding specified there. If the HTML form has no `accept-charset` + * attribute the browser determines the encoding from the content + * type of the web page containing the form. Using a content type of + * `text/html; charset=utf-8` for the page and setting + * `accept-charset` on the HTML form to `utf-8` is recommended as the + * default for [HttpBodyHandler] is UTF-8. It is important to get + * these encoding values right, as the actual `multipart/form-data` + * HTTP request sent by the browser does _not_ contain any + * information on the encoding. If something else than UTF-8 is used + * `defaultEncoding` needs to be set in the [HttpBodyHandler] + * constructor and calls to [processRequest] and [processResponse]. + * + * For all other content types the body will be treated as + * uninterpreted binary data. The resulting body will be of type + * `List`. + * + * To use with the [HttpServer] for request messages, [HttpBodyHandler] can be + * used as either a [StreamTransformer] or as a per-request handler (see + * [processRequest]). + * + * HttpServer server = ... + * server.transform(new HttpBodyHandler()) + * .listen((HttpRequestBody body) { + * ... + * }); + * + * To use with the [HttpClient] for response messages, [HttpBodyHandler] can be + * used as a per-request handler (see [processResponse]). + * + * HttpClient client = ... + * client.get(...) + * .then((HttpClientRequest response) => response.close()) + * .then(HttpBodyHandler.processResponse) + * .then((HttpClientResponseBody body) { + * ... + * }); + * + */ +class HttpBodyHandler + implements StreamTransformer { + var _transformer; + + /** + * Create a new [HttpBodyHandler] to be used with a [Stream]<[HttpRequest]>, + * e.g. a [HttpServer]. + * + * If the page is served using different encoding than UTF-8, set + * [defaultEncoding] accordingly. This is required for parsing + * `multipart/form-data` content correctly. See the class comment + * for more information on `multipart/form-data`. + */ + HttpBodyHandler({Encoding defaultEncoding: UTF8}) + : _transformer = new _HttpBodyHandlerTransformer(defaultEncoding); + + /** + * Process and parse an incoming [HttpRequest]. The returned [HttpRequestBody] + * contains a [response] field for accessing the [HttpResponse]. + * + * See [HttpBodyHandler] constructor for more info on [defaultEncoding]. + */ + static Future processRequest( + HttpRequest request, + {Encoding defaultEncoding: UTF8}) { + return _HttpBodyHandler.processRequest(request, defaultEncoding); + } + + /** + * Process and parse an incoming [HttpClientResponse]. + * + * See [HttpBodyHandler] constructor for more info on [defaultEncoding]. + */ + static Future processResponse( + HttpClientResponse response, + {Encoding defaultEncoding: UTF8}) { + return _HttpBodyHandler.processResponse(response, defaultEncoding); + } + + Stream bind(Stream stream) { + return _transformer.bind(stream); + } +} + + +/** + * A HTTP content body produced by [HttpBodyHandler] for either [HttpRequest] + * or [HttpClientResponse]. + */ +abstract class HttpBody { + /** + * A high-level type value, that reflects how the body was parsed, e.g. + * "text", "binary" and "json". + */ + String get type; + + /** + * The actual body. The type depends on [type]. + */ + dynamic get body; +} + + +/** + * The [HttpBody] of a [HttpClientResponse] will be of type + * [HttpClientResponseBody]. It contains the [HttpClientResponse] object + * for access to the headers. + */ +abstract class HttpClientResponseBody extends HttpBody { + /** + * The [HttpClientResponse] from which the [HttpClientResponseBody] was + * created. + */ + HttpClientResponse get response; +} + + +/** + * The [HttpBody] of a [HttpRequest] will be of type [HttpRequestBody]. It + * provides access to the request, for reading all request header information + * and responding to the client. + */ +abstract class HttpRequestBody extends HttpBody { + /** + * The [HttpRequest] from which the [HttpRequestBody] was created. + * + * Note that the [HttpRequest] is already drained at this point, so the + * `Stream` methods cannot be used. + */ + HttpRequest get request; +} + + +/** + * A [HttpBodyFileUpload] object wraps a file upload, presenting a way for + * extracting filename, contentType and the data of the uploaded file. + */ +abstract class HttpBodyFileUpload { + /** + * The filename of the uploaded file. + */ + String get filename; + + /** + * The [ContentType] of the uploaded file. For 'text/\*' and + * 'application/json' the [data] field will a String. + */ + ContentType get contentType; + + /** + * The content of the file. Either a [String] or a [List]. + */ + dynamic get content; +} diff --git a/Chapter 7/serving_files/packages/http_server/src/http_body_impl.dart b/Chapter 7/serving_files/packages/http_server/src/http_body_impl.dart new file mode 100644 index 0000000..7682902 --- /dev/null +++ b/Chapter 7/serving_files/packages/http_server/src/http_body_impl.dart @@ -0,0 +1,205 @@ +// Copyright (c) 2013, 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. + +part of http_server; + +class _HttpBodyHandlerTransformer + implements StreamTransformer { + final Encoding _defaultEncoding; + + const _HttpBodyHandlerTransformer(this._defaultEncoding); + + Stream bind(Stream stream) { + return new Stream.eventTransformed( + stream, + (EventSink sink) => + new _HttpBodyHandlerTransformerSink(_defaultEncoding, sink)); + } +} + +class _HttpBodyHandlerTransformerSink implements EventSink { + final Encoding _defaultEncoding; + final EventSink _outSink; + int _pending = 0; + bool _closed = false; + + _HttpBodyHandlerTransformerSink(this._defaultEncoding, this._outSink); + + void add(HttpRequest request) { + _pending++; + _HttpBodyHandler.processRequest(request, _defaultEncoding) + .then(_outSink.add, onError: _outSink.addError) + .whenComplete(() { + _pending--; + if (_closed && _pending == 0) _outSink.close(); + }); + } + void addError(Object error, [StackTrace stackTrace]) { + _outSink.addError(error, stackTrace); + } + void close() { + _closed = true; + if (_pending == 0) _outSink.close(); + } +} + +class _HttpBodyHandler { + static Future processRequest( + HttpRequest request, + Encoding defaultEncoding) { + return process(request, request.headers, defaultEncoding) + .then((body) => new _HttpRequestBody(request, body), + onError: (error) { + // Try to send BAD_REQUEST response. + request.response.statusCode = HttpStatus.BAD_REQUEST; + request.response.close(); + throw error; + }); + } + + static Future processResponse( + HttpClientResponse response, + Encoding defaultEncoding) { + return process(response, response.headers, defaultEncoding) + .then((body) => new _HttpClientResponseBody(response, body)); + } + + static Future process(Stream> stream, + HttpHeaders headers, + Encoding defaultEncoding) { + ContentType contentType = headers.contentType; + + Future asBinary() { + return stream + .fold(new BytesBuilder(), (builder, data) => builder..add(data)) + .then((builder) => new _HttpBody("binary", builder.takeBytes())); + } + + Future asText(Encoding defaultEncoding) { + var encoding; + var charset = contentType.charset; + if (charset != null) encoding = Encoding.getByName(charset); + if (encoding == null) encoding = defaultEncoding; + return stream + .transform(encoding.decoder) + .fold(new StringBuffer(), (buffer, data) => buffer..write(data)) + .then((buffer) => new _HttpBody("text", buffer.toString())); + } + + Future asFormData() { + return stream + .transform(new MimeMultipartTransformer( + contentType.parameters['boundary'])) + .map((part) => HttpMultipartFormData.parse( + part, defaultEncoding: defaultEncoding)) + .map((multipart) { + var future; + if (multipart.isText) { + future = multipart + .fold(new StringBuffer(), (b, s) => b..write(s)) + .then((b) => b.toString()); + } else { + future = multipart + .fold(new BytesBuilder(), (b, d) => b..add(d)) + .then((b) => b.takeBytes()); + } + return future.then((data) { + var filename = + multipart.contentDisposition.parameters['filename']; + if (filename != null) { + data = new _HttpBodyFileUpload(multipart.contentType, + filename, + data); + } + return [multipart.contentDisposition.parameters['name'], data]; + }); + }) + .fold([], (l, f) => l..add(f)) + .then(Future.wait) + .then((parts) { + Map map = new Map(); + for (var part in parts) { + map[part[0]] = part[1]; // Override existing entries. + } + return new _HttpBody('form', map); + }); + } + + if (contentType == null) { + return asBinary(); + } + + switch (contentType.primaryType) { + case "text": + return asText(defaultEncoding); + + case "application": + switch (contentType.subType) { + case "json": + return asText(UTF8) + .then((body) => new _HttpBody("json", JSON.decode(body.body))); + + case "x-www-form-urlencoded": + return asText(ASCII) + .then((body) { + var map = Uri.splitQueryString(body.body, + encoding: defaultEncoding); + var result = {}; + for (var key in map.keys) { + result[key] = map[key]; + } + return new _HttpBody("form", result); + }); + + default: + break; + } + break; + + case "multipart": + switch (contentType.subType) { + case "form-data": + return asFormData(); + + default: + break; + } + break; + + default: + break; + } + + return asBinary(); + } +} + +class _HttpBodyFileUpload implements HttpBodyFileUpload { + final ContentType contentType; + final String filename; + final dynamic content; + _HttpBodyFileUpload(this.contentType, this.filename, this.content); +} + +class _HttpBody implements HttpBody { + final String type; + final dynamic body; + + _HttpBody(this.type, this.body); +} + +class _HttpRequestBody extends _HttpBody implements HttpRequestBody { + final HttpRequest request; + + _HttpRequestBody(this.request, HttpBody body) + : super(body.type, body.body); +} + +class _HttpClientResponseBody + extends _HttpBody implements HttpClientResponseBody { + final HttpClientResponse response; + + _HttpClientResponseBody(this.response, HttpBody body) + : super(body.type, body.body); +} diff --git a/Chapter 7/serving_files/packages/http_server/src/http_multipart_form_data.dart b/Chapter 7/serving_files/packages/http_server/src/http_multipart_form_data.dart new file mode 100644 index 0000000..eae575b --- /dev/null +++ b/Chapter 7/serving_files/packages/http_server/src/http_multipart_form_data.dart @@ -0,0 +1,80 @@ +// Copyright (c) 2012, 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. + +part of http_server; + + +/** + * [:HttpMultipartFormData:] class used for 'upgrading' a [MimeMultipart] by + * parsing it as a 'multipart/form-data' part. The following code shows how + * it can be used. + * + * HttpServer server = ...; + * server.listen((request) { + * String boundary = request.headers.contentType.parameters['boundary']; + * request + * .transform(new MimeMultipartTransformer(boundary)) + * .map(HttpMultipartFormData.parse) + * .map((HttpMultipartFormData formData) { + * // form data object available here. + * }); + * + * [:HttpMultipartFormData:] is a Stream, serving either bytes or decoded + * Strings. Use [isText] or [isBinary] to see what type of data is provided. + */ +abstract class HttpMultipartFormData implements Stream { + /** + * The parsed [:Content-Type:] header of the [:HttpMultipartFormData:]. + * Returns [:null:] if not present. + */ + ContentType get contentType; + + /** + * The parsed [:Content-Disposition:] header of the [:HttpMultipartFormData:]. + * This field is always present. Use this to extract e.g. name(form field + * name)and filename (client provided name of uploaded file) parameters. + */ + HeaderValue get contentDisposition; + + /** + * The parsed [:Content-Transfer-Encoding:] header of the + * [:HttpMultipartFormData:]. This field is used to determine how to decode + * the data. Returns [:null:] if not present. + */ + HeaderValue get contentTransferEncoding; + + /** + * Returns [:true:] if the data is decoded as [String]. + */ + bool get isText; + + /** + * Returns [:true:] if the data is raw bytes. + */ + bool get isBinary; + + /** + * Returns the value for the header named [name]. If there + * is no header with the provided name, [:null:] will be returned. + * + * Use this method to index other headers available in the original + * [MimeMultipart]. + */ + String value(String name); + + /** + * Parse a [MimeMultipart] and return a [HttpMultipartFormData]. If the + * [:Content-Disposition:] header is missing or invalid, a [HttpException] is + * thrown. + * + * If the [MimeMultipart] is identified as text, and the [:Content-Type:] + * header is missing, the data is decoded using [defaultEncoding]. See more + * information in the + * [HTML5 spec](http://dev.w3.org/html5/spec-preview/ + * constraints.html#multipart-form-data). + */ + static HttpMultipartFormData parse(MimeMultipart multipart, + {Encoding defaultEncoding: UTF8}) + => _HttpMultipartFormData.parse(multipart, defaultEncoding); +} diff --git a/Chapter 7/serving_files/packages/http_server/src/http_multipart_form_data_impl.dart b/Chapter 7/serving_files/packages/http_server/src/http_multipart_form_data_impl.dart new file mode 100644 index 0000000..daebeec --- /dev/null +++ b/Chapter 7/serving_files/packages/http_server/src/http_multipart_form_data_impl.dart @@ -0,0 +1,142 @@ +// Copyright (c) 2012, 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. + +part of http_server; + + +class _HttpMultipartFormData extends Stream implements HttpMultipartFormData { + final ContentType contentType; + final HeaderValue contentDisposition; + final HeaderValue contentTransferEncoding; + + final MimeMultipart _mimeMultipart; + + bool _isText = false; + + Stream _stream; + + _HttpMultipartFormData(ContentType this.contentType, + HeaderValue this.contentDisposition, + HeaderValue this.contentTransferEncoding, + MimeMultipart this._mimeMultipart, + Encoding defaultEncoding) { + _stream = _mimeMultipart; + if (contentTransferEncoding != null) { + // TODO(ajohnsen): Support BASE64, etc. + throw new HttpException("Unsupported contentTransferEncoding: " + "${contentTransferEncoding.value}"); + } + + if (contentType == null || + contentType.primaryType == 'text' || + contentType.mimeType == 'application/json') { + _isText = true; + StringBuffer buffer = new StringBuffer(); + Encoding encoding; + if (contentType != null) { + encoding = Encoding.getByName(contentType.charset); + } + if (encoding == null) encoding = defaultEncoding; + _stream = _stream + .transform(encoding.decoder) + .expand((data) { + buffer.write(data); + var out = _decodeHttpEntityString(buffer.toString()); + if (out != null) { + buffer.clear(); + return [out]; + } + return const []; + }); + } + } + + bool get isText => _isText; + bool get isBinary => !_isText; + + static HttpMultipartFormData parse(MimeMultipart multipart, + Encoding defaultEncoding) { + var type; + var encoding; + var disposition; + var remaining = new Map(); + for (String key in multipart.headers.keys) { + switch (key) { + case 'content-type': + type = ContentType.parse(multipart.headers[key]); + break; + + case 'content-transfer-encoding': + encoding = HeaderValue.parse(multipart.headers[key]); + break; + + case 'content-disposition': + disposition = HeaderValue.parse(multipart.headers[key], + preserveBackslash: true); + break; + + default: + remaining[key] = multipart.headers[key]; + break; + } + } + if (disposition == null) { + throw new HttpException( + "Mime Multipart doesn't contain a Content-Disposition header value"); + } + return new _HttpMultipartFormData( + type, disposition, encoding, multipart, defaultEncoding); + } + + StreamSubscription listen(void onData(data), + {void onDone(), + Function onError, + bool cancelOnError}) { + return _stream.listen(onData, + onDone: onDone, + onError: onError, + cancelOnError: cancelOnError); + } + + String value(String name) { + return _mimeMultipart.headers[name]; + } + + // Decode a string with HTTP entities. Returns null if the string ends in the + // middle of a http entity. + static String _decodeHttpEntityString(String input) { + int amp = input.lastIndexOf('&'); + if (amp < 0) return input; + int end = input.lastIndexOf(';'); + if (end < amp) return null; + + var buffer = new StringBuffer(); + int offset = 0; + + parse(amp, end) { + switch (input[amp + 1]) { + case '#': + if (input[amp + 2] == 'x') { + buffer.writeCharCode( + int.parse(input.substring(amp + 3, end), radix: 16)); + } else { + buffer.writeCharCode(int.parse(input.substring(amp + 2, end))); + } + break; + + default: + throw new HttpException('Unhandled HTTP entity token'); + } + } + + while ((amp = input.indexOf('&', offset)) >= 0) { + buffer.write(input.substring(offset, amp)); + int end = input.indexOf(';', amp); + parse(amp, end); + offset = end + 1; + } + buffer.write(input.substring(offset)); + return buffer.toString(); + } +} diff --git a/Chapter 7/serving_files/packages/http_server/src/virtual_directory.dart b/Chapter 7/serving_files/packages/http_server/src/virtual_directory.dart new file mode 100644 index 0000000..6eef0b4 --- /dev/null +++ b/Chapter 7/serving_files/packages/http_server/src/virtual_directory.dart @@ -0,0 +1,410 @@ +// Copyright (c) 2013, 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. + +part of http_server; + + +// Used for signal a directory redirecting, where a tailing slash is missing. +class _DirectoryRedirect { + const _DirectoryRedirect(); +} + +typedef dynamic _DirCallback(Directory dir, HttpRequest request); +typedef dynamic _ErrorCallback(HttpRequest request); + +/** + * A [VirtualDirectory] can serve files and directory-listing from a root path, + * to [HttpRequest]s. + * + * The [VirtualDirectory] providing secure handling of request uris and + * file-system links, correct mime-types and custom error pages. + */ +class VirtualDirectory { + final String root; + + /** + * Set or get if the [VirtualDirectory] should list the content of + * directories. + */ + bool allowDirectoryListing = false; + + /** + * Set or get if the [VirtualDirectory] should follow links, that point + * to other resources within the [root] directory. + */ + bool followLinks = true; + + /** + * Set or get if the [VirtualDirectory] should jail the root. When the root is + * not jailed, links can be followed to outside the [root] directory. + */ + bool jailRoot = true; + + final RegExp _invalidPathRegExp = new RegExp("[\\\/\x00]"); + + _ErrorCallback _errorCallback; + _DirCallback _dirCallback; + + /* + * Create a new [VirtualDirectory] for serving static file content of + * the path [root]. + * + * The [root] is not required to exist. If the [root] doesn't exist at time of + * a request, a 404 is generated. + */ + VirtualDirectory(this.root); + + /** + * Serve a [Stream] of [HttpRequest]s, in this [VirtualDirectory]. + */ + StreamSubscription serve(Stream requests) => + requests.listen(serveRequest); + + /** + * Serve a single [HttpRequest], in this [VirtualDirectory]. + */ + Future serveRequest(HttpRequest request) { + return _locateResource('.', request.uri.pathSegments.iterator..moveNext()) + .then((entity) { + if (entity is File) { + serveFile(entity, request); + } else if (entity is Directory) { + if (allowDirectoryListing) { + _serveDirectory(entity, request); + } else { + _serveErrorPage(HttpStatus.NOT_FOUND, request); + } + } else if (entity is _DirectoryRedirect) { + // TODO(ajohnsen): Use HttpRequest.requestedUri once 1.2 is out. + request.response.redirect(Uri.parse('${request.uri}/'), + status: HttpStatus.MOVED_PERMANENTLY); + } else { + assert(entity == null); + _serveErrorPage(HttpStatus.NOT_FOUND, request); + } + return request.response.done; + }); + } + + /** + * Set the [callback] to override the default directory listing. The + * [callback] will be called with the [Directory] to be listed and the + * [HttpRequest]. + */ + void set directoryHandler(void callback(Directory dir, HttpRequest request)) { + _dirCallback = callback; + } + + /** + * Set the [callback] to override the error page handler. When [callback] is + * invoked, the `statusCode` property of the response is set. + */ + void set errorPageHandler(void callback(HttpRequest request)) { + _errorCallback = callback; + } + + Future _locateResource(String path, Iterator segments) { + // Don't allow navigating up paths. + if (segments.current == "..") return new Future.value(null); + path = normalize(path); + // If we jail to root, the relative path can never go up. + if (jailRoot && split(path).first == "..") return new Future.value(null); + String fullPath() => join(root, path); + return FileSystemEntity.type(fullPath(), followLinks: false) + .then((type) { + switch (type) { + case FileSystemEntityType.FILE: + if (segments.current == null) { + return new File(fullPath()); + } + break; + + case FileSystemEntityType.DIRECTORY: + String dirFullPath() => '${fullPath()}$separator'; + var current = segments.current; + if (current == null) { + if (path == '.') return new Directory(dirFullPath()); + return const _DirectoryRedirect(); + } + bool hasNext = segments.moveNext(); + if (!hasNext && current == "") { + return new Directory(dirFullPath()); + } else { + if (_invalidPathRegExp.hasMatch(current)) break; + return _locateResource(join(path, current), segments); + } + break; + + case FileSystemEntityType.LINK: + if (followLinks) { + return new Link(fullPath()).target() + .then((target) { + String targetPath = normalize(target); + if (isAbsolute(targetPath)) { + // If we jail to root, the path can never be absolute. + if (jailRoot) return null; + return _locateResource(targetPath, segments); + } else { + targetPath = join(dirname(path), targetPath); + return _locateResource(targetPath, segments); + } + }); + } + break; + } + // Return `null` on fall-through, to indicate NOT_FOUND. + return null; + }); + } + + /** + * Serve the content of [file] to [request]. + * + * This is usefull when e.g. overriding [directoryHandler] to redirect to + * some index file. + * + * In the request contains the [HttpStatus.IF_MODIFIED_SINCE] header, + * [serveFile] will send a [HttpStatus.NOT_MODIFIED] response if the file + * was not changed. + * + * Note that if it was unabled to read from [file], the [request]s response + * is closed with error-code [HttpStatus.NOT_FOUND]. + */ + void serveFile(File file, HttpRequest request) { + var response = request.response; + // TODO(ajohnsen): Set up Zone support for these errors. + file.lastModified().then((lastModified) { + if (request.headers.ifModifiedSince != null && + !lastModified.isAfter(request.headers.ifModifiedSince)) { + response.statusCode = HttpStatus.NOT_MODIFIED; + response.close(); + return null; + } + + response.headers.set(HttpHeaders.LAST_MODIFIED, lastModified); + response.headers.set(HttpHeaders.ACCEPT_RANGES, "bytes"); + + if (request.method == 'HEAD') { + response.close(); + return null; + } + + return file.length().then((length) { + String range = request.headers.value("range"); + if (range != null) { + // We only support one range, where the standard support several. + Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range); + // If the range header have the right format, handle it. + if (matches != null) { + // Serve sub-range. + int start; + int end; + if (matches[1].isEmpty) { + start = matches[2].isEmpty ? + length : + length - int.parse(matches[2]); + end = length; + } else { + start = int.parse(matches[1]); + end = matches[2].isEmpty ? length : int.parse(matches[2]) + 1; + } + + // Override Content-Length with the actual bytes sent. + response.headers.set(HttpHeaders.CONTENT_LENGTH, end - start); + + // Set 'Partial Content' status code. + response.statusCode = HttpStatus.PARTIAL_CONTENT; + response.headers.set(HttpHeaders.CONTENT_RANGE, + "bytes $start-${end - 1}/$length"); + + // Pipe the 'range' of the file. + file.openRead(start, end) + .pipe(new _VirtualDirectoryFileStream(response, file.path)) + .catchError((_) { + // TODO(kevmoo): log errors + }); + return; + } + } + + file.openRead() + .pipe(new _VirtualDirectoryFileStream(response, file.path)) + .catchError((_) { + // TODO(kevmoo): log errors + }); + }); + }).catchError((_) { + response.statusCode = HttpStatus.NOT_FOUND; + response.close(); + }); + } + + void _serveDirectory(Directory dir, HttpRequest request) { + if (_dirCallback != null) { + _dirCallback(dir, request); + return; + } + var response = request.response; + dir.stat().then((stats) { + if (request.headers.ifModifiedSince != null && + !stats.modified.isAfter(request.headers.ifModifiedSince)) { + response.statusCode = HttpStatus.NOT_MODIFIED; + response.close(); + return; + } + + response.headers.set(HttpHeaders.LAST_MODIFIED, stats.modified); + var path = request.uri.path; + var header = +''' + + +Index of $path + + +

    Index of $path

    + + + + + + +'''; + var server = response.headers.value(HttpHeaders.SERVER); + if (server == null) server = ""; + var footer = +'''
    NameLast modifiedSize
    +$server + + +'''; + + response.write(header); + + void add(String name, String modified, var size) { + if (size == null) size = "-"; + if (modified == null) modified = ""; + var p = normalize(join(path, name)); + var entry = +''' + $name + $modified + $size + '''; + response.write(entry); + } + + if (path != '/') { + add('../', null, null); + } + + dir.list(followLinks: true).listen((entity) { + if (entity is File) { + var stat = entity.statSync(); + add(basename(entity.path), + stat.modified.toString(), + stat.size); + } else if (entity is Directory) { + add(basename(entity.path) + '/', + entity.statSync().modified.toString(), + null); + } + }, onError: (e) { + // TODO(kevmoo): log error + }, onDone: () { + response.write(footer); + response.close(); + }); + }, onError: (e) { + // TODO(kevmoo): log error + response.close(); + }); + } + + void _serveErrorPage(int error, HttpRequest request) { + var response = request.response; + response.statusCode = error; + if (_errorCallback != null) { + _errorCallback(request); + return; + } + // Default error page. + var path = request.uri.path; + var reason = response.reasonPhrase; + + var server = response.headers.value(HttpHeaders.SERVER); + if (server == null) server = ""; + var page = +''' + + +$reason: $path + + +

    Error $error at \'$path\': $reason

    +$server + +'''; + response.write(page); + response.close(); + } +} + +class _VirtualDirectoryFileStream extends StreamConsumer> { + final HttpResponse response; + final String path; + List buffer = []; + + _VirtualDirectoryFileStream(HttpResponse this.response, String this.path); + + Future addStream(Stream> stream) { + stream.listen( + (data) { + if (buffer == null) { + response.add(data); + return; + } + if (buffer.length == 0) { + if (data.length >= defaultMagicNumbersMaxLength) { + setMimeType(data); + response.add(data); + buffer = null; + } else { + buffer.addAll(data); + } + } else { + buffer.addAll(data); + if (buffer.length >= defaultMagicNumbersMaxLength) { + setMimeType(buffer); + response.add(buffer); + buffer = null; + } + } + }, + onDone: () { + if (buffer != null) { + if (buffer.length == 0) { + setMimeType(null); + } else { + setMimeType(buffer); + response.add(buffer); + } + } + response.close(); + }, + onError: response.addError); + return response.done; + } + + Future close() => new Future.value(); + + void setMimeType(List bytes) { + var mimeType = lookupMimeType(path, headerBytes: bytes); + if (mimeType != null) { + response.headers.contentType = ContentType.parse(mimeType); + } + } +} diff --git a/Chapter 7/serving_files/packages/http_server/src/virtual_host.dart b/Chapter 7/serving_files/packages/http_server/src/virtual_host.dart new file mode 100644 index 0000000..4c67eca --- /dev/null +++ b/Chapter 7/serving_files/packages/http_server/src/virtual_host.dart @@ -0,0 +1,149 @@ +// Copyright (c) 2013, 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. + +part of http_server; + + +/** + * The [VirtualHost] class is a utility class for handling multiple hosts on + * multiple sources, by using a named-based approach. + */ +abstract class VirtualHost { + /** + * Get the [Stream] of [HttpRequest]s, not matching any hosts. If unused, the + * default implementation will result in a [HttpHeaders.FORBIDDEN] response. + */ + Stream get unhandled; + + /** + * Construct a new [VirtualHost]. + * + * The optional [source] is a shortcut for calling [addSource]. + * + * Example of usage: + * + * HttpServer.bind(..., 80).then((server) { + * var virtualHost = new VirtualHost(server); + * virtualServer.addHost('static.myserver.com') + * .listen(...); + * virtualServer.addHost('cache.myserver.com') + * .listen(...); + * }) + */ + factory VirtualHost([Stream source]) => new _VirtualHost(source); + + /** + * Provide another source of [HttpRequest]s in the form of a [Stream]. + */ + void addSource(Stream source); + + + /** + * Add a host to the [VirtualHost] instance. The host can be either a specific + * domain (`my.domain.name`) or a wildcard-based domain name + * (`*.domain.name`). The former will only match the specific domain name + * while the latter will match any series of sub-domains. + * + * If both `my.domain.name` and `*.domain.name` is specified, the most + * qualified will take precedence, `my.domain.name` in this case. + */ + Stream addHost(String host); +} + + +class _VirtualHostDomain { + StreamController any; + StreamController exact; + Map subDomains = {}; +} + + +class _VirtualHost implements VirtualHost { + final _VirtualHostDomain _topDomain = new _VirtualHostDomain(); + StreamController _unhandledController; + + Stream get unhandled { + if (_unhandledController == null) { + _unhandledController = new StreamController(); + } + return _unhandledController.stream; + } + + _VirtualHost([Stream source]) { + if (source != null) addSource(source); + } + + void addSource(Stream source) { + source.listen((request) { + var host = request.headers.host; + if (host == null) { + _unhandled(request); + return; + } + var domains = host.split('.'); + var current = _topDomain; + var any; + for (var i = domains.length - 1; i >= 0; i--) { + if (current.any != null) any = current.any; + if (i == 0) { + var last = current.subDomains[domains[i]]; + if (last != null && last.exact != null) { + last.exact.add(request); + return; + } + } else { + if (!current.subDomains.containsKey(domains[i])) { + break; + } + current = current.subDomains[domains[i]]; + } + } + if (any != null) { + any.add(request); + return; + } + _unhandled(request); + }); + } + + Stream addHost(String host) { + if (host.lastIndexOf('*') > 0) { + throw new ArgumentError( + 'Wildcards are only allowed in the beginning of a host'); + } + var controller = new StreamController(); + var domains = host.split('.'); + var current = _topDomain; + for (var i = domains.length - 1; i >= 0; i--) { + if (domains[i] == '*') { + if (current.any != null) { + throw new ArgumentError('Host is already provided'); + } + current.any = controller; + } else { + if (!current.subDomains.containsKey(domains[i])) { + current.subDomains[domains[i]] = new _VirtualHostDomain(); + } + if (i > 0) { + current = current.subDomains[domains[i]]; + } else { + if (current.subDomains[domains[i]].exact != null) { + throw new ArgumentError('Host is already provided'); + } + current.subDomains[domains[i]].exact = controller; + } + } + } + return controller.stream; + } + + void _unhandled(HttpRequest request) { + if (_unhandledController != null) { + _unhandledController.add(request); + return; + } + request.response.statusCode = HttpStatus.FORBIDDEN; + request.response.close(); + } +} diff --git a/Chapter 7/serving_files/packages/mime/mime.dart b/Chapter 7/serving_files/packages/mime/mime.dart new file mode 100644 index 0000000..dce6774 --- /dev/null +++ b/Chapter 7/serving_files/packages/mime/mime.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2013, 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. + +/** + * Help for working with file format identifiers + * such as `text/html` and `image/png`. + * + * More details, including a list of types, are in the Wikipedia article + * [Internet media type](http://en.wikipedia.org/wiki/Internet_media_type). + * For information on installing and importing this library, see the + * [mime package on pub.dartlang.org] + * (http://pub.dartlang.org/packages/mime). + */ +library mime; + +export 'src/mime_multipart_transformer.dart'; +export 'src/mime_shared.dart'; +export 'src/mime_type.dart'; diff --git a/Chapter 7/serving_files/packages/mime/src/bound_multipart_stream.dart b/Chapter 7/serving_files/packages/mime/src/bound_multipart_stream.dart new file mode 100644 index 0000000..d470034 --- /dev/null +++ b/Chapter 7/serving_files/packages/mime/src/bound_multipart_stream.dart @@ -0,0 +1,375 @@ +// Copyright (c) 2014, 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. +library mime.bound_multipart_stream; + +import 'dart:async'; +import 'dart:convert'; + +import 'mime_shared.dart'; +import 'char_code.dart'; + +// Bytes for '()<>@,;:\\"/[]?={} \t'. +const _SEPARATORS = const [40, 41, 60, 62, 64, 44, 59, 58, 92, 34, 47, 91, 93, + 63, 61, 123, 125, 32, 9]; + +bool _isTokenChar(int byte) { + return byte > 31 && byte < 128 && _SEPARATORS.indexOf(byte) == -1; +} + +int _toLowerCase(int byte) { + const delta = CharCode.LOWER_A - CharCode.UPPER_A; + return (CharCode.UPPER_A <= byte && byte <= CharCode.UPPER_Z) ? + byte + delta : byte; +} + +void _expectByteValue(int val1, int val2) { + if (val1 != val2) { + throw new MimeMultipartException("Failed to parse multipart mime 1"); + } +} + +void _expectWhitespace(int byte) { + if (byte != CharCode.SP && byte != CharCode.HT) { + throw new MimeMultipartException("Failed to parse multipart mime 2"); + } +} + +class _MimeMultipart extends MimeMultipart { + final Map headers; + final Stream> _stream; + + _MimeMultipart(this.headers, this._stream); + + StreamSubscription> listen(void onData(List data), + {void onDone(), + Function onError, + bool cancelOnError}) { + return _stream.listen(onData, + onDone: onDone, + onError: onError, + cancelOnError: cancelOnError); + } +} + +class BoundMultipartStream { + static const int _START = 0; + static const int _FIRST_BOUNDARY_ENDING = 111; + static const int _FIRST_BOUNDARY_END = 112; + static const int _BOUNDARY_ENDING = 1; + static const int _BOUNDARY_END = 2; + static const int _HEADER_START = 3; + static const int _HEADER_FIELD = 4; + static const int _HEADER_VALUE_START = 5; + static const int _HEADER_VALUE = 6; + static const int _HEADER_VALUE_FOLDING_OR_ENDING = 7; + static const int _HEADER_VALUE_FOLD_OR_END = 8; + static const int _HEADER_ENDING = 9; + static const int _CONTENT = 10; + static const int _LAST_BOUNDARY_DASH2 = 11; + static const int _LAST_BOUNDARY_ENDING = 12; + static const int _LAST_BOUNDARY_END = 13; + static const int _DONE = 14; + static const int _FAIL = 15; + + final List _boundary; + final List _headerField = []; + final List _headerValue = []; + + StreamController _controller; + + Stream get stream => _controller.stream; + + StreamSubscription _subscription; + + StreamController _multipartController; + Map _headers; + + int _state = _START; + int _boundaryIndex = 2; + + // Current index in the data buffer. If index is negative then it + // is the index into the artificial prefix of the boundary string. + int _index; + List _buffer; + + BoundMultipartStream(this._boundary, Stream> stream) { + _controller = new StreamController( + sync: true, + onPause: _pauseStream, + onResume:_resumeStream, + onCancel: () { + _subscription.cancel(); + }, + onListen: () { + _subscription = stream.listen( + (data) { + assert(_buffer == null); + _pauseStream(); + _buffer = data; + _index = 0; + _parse(); + }, + onDone: () { + if (_state != _DONE) { + _controller.addError( + new MimeMultipartException("Bad multipart ending")); + } + _controller.close(); + }, + onError: _controller.addError); + }); + } + + void _resumeStream() { + _subscription.resume(); + } + + void _pauseStream() { + _subscription.pause(); + } + + + void _parse() { + // Number of boundary bytes to artificially place before the supplied data. + int boundaryPrefix = 0; + // Position where content starts. Will be null if no known content + // start exists. Will be negative of the content starts in the + // boundary prefix. Will be zero or position if the content starts + // in the current buffer. + int contentStartIndex; + + // Function to report content data for the current part. The data + // reported is from the current content start index up til the + // current index. As the data can be artificially prefixed with a + // prefix of the boundary both the content start index and index + // can be negative. + void reportData() { + if (contentStartIndex < 0) { + var contentLength = boundaryPrefix + _index - _boundaryIndex; + if (contentLength <= boundaryPrefix) { + _multipartController.add( + _boundary.sublist(0, contentLength)); + } else { + _multipartController.add( + _boundary.sublist(0, boundaryPrefix)); + _multipartController.add( + _buffer.sublist(0, contentLength - boundaryPrefix)); + } + } else { + var contentEndIndex = _index - _boundaryIndex; + _multipartController.add( + _buffer.sublist(contentStartIndex, contentEndIndex)); + } + } + + if (_state == _CONTENT && _boundaryIndex == 0) { + contentStartIndex = 0; + } else { + contentStartIndex = null; + } + // The data to parse might be "artificially" prefixed with a + // partial match of the boundary. + boundaryPrefix = _boundaryIndex; + + while ((_index < _buffer.length) && _state != _FAIL && _state != _DONE) { + if (_multipartController != null && _multipartController.isPaused) { + return; + } + int byte; + if (_index < 0) { + byte = _boundary[boundaryPrefix + _index]; + } else { + byte = _buffer[_index]; + } + switch (_state) { + case _START: + if (byte == _boundary[_boundaryIndex]) { + _boundaryIndex++; + if (_boundaryIndex == _boundary.length) { + _state = _FIRST_BOUNDARY_ENDING; + _boundaryIndex = 0; + } + } else { + // Restart matching of the boundary. + _index = _index - _boundaryIndex; + _boundaryIndex = 0; + } + break; + + case _FIRST_BOUNDARY_ENDING: + if (byte == CharCode.CR) { + _state = _FIRST_BOUNDARY_END; + } else { + _expectWhitespace(byte); + } + break; + + case _FIRST_BOUNDARY_END: + _expectByteValue(byte, CharCode.LF); + _state = _HEADER_START; + break; + + case _BOUNDARY_ENDING: + if (byte == CharCode.CR) { + _state = _BOUNDARY_END; + } else if (byte == CharCode.DASH) { + _state = _LAST_BOUNDARY_DASH2; + } else { + _expectWhitespace(byte); + } + break; + + case _BOUNDARY_END: + _expectByteValue(byte, CharCode.LF); + _multipartController.close(); + _multipartController = null; + _state = _HEADER_START; + break; + + case _HEADER_START: + _headers = new Map(); + if (byte == CharCode.CR) { + _state = _HEADER_ENDING; + } else { + // Start of new header field. + _headerField.add(_toLowerCase(byte)); + _state = _HEADER_FIELD; + } + break; + + case _HEADER_FIELD: + if (byte == CharCode.COLON) { + _state = _HEADER_VALUE_START; + } else { + if (!_isTokenChar(byte)) { + throw new MimeMultipartException("Invalid header field name"); + } + _headerField.add(_toLowerCase(byte)); + } + break; + + case _HEADER_VALUE_START: + if (byte == CharCode.CR) { + _state = _HEADER_VALUE_FOLDING_OR_ENDING; + } else if (byte != CharCode.SP && byte != CharCode.HT) { + // Start of new header value. + _headerValue.add(byte); + _state = _HEADER_VALUE; + } + break; + + case _HEADER_VALUE: + if (byte == CharCode.CR) { + _state = _HEADER_VALUE_FOLDING_OR_ENDING; + } else { + _headerValue.add(byte); + } + break; + + case _HEADER_VALUE_FOLDING_OR_ENDING: + _expectByteValue(byte, CharCode.LF); + _state = _HEADER_VALUE_FOLD_OR_END; + break; + + case _HEADER_VALUE_FOLD_OR_END: + if (byte == CharCode.SP || byte == CharCode.HT) { + _state = _HEADER_VALUE_START; + } else { + String headerField = UTF8.decode(_headerField); + String headerValue = UTF8.decode(_headerValue); + _headers[headerField.toLowerCase()] = headerValue; + _headerField.clear(); + _headerValue.clear(); + if (byte == CharCode.CR) { + _state = _HEADER_ENDING; + } else { + // Start of new header field. + _headerField.add(_toLowerCase(byte)); + _state = _HEADER_FIELD; + } + } + break; + + case _HEADER_ENDING: + _expectByteValue(byte, CharCode.LF); + _multipartController = new StreamController( + sync: true, + onPause: () { + _pauseStream(); + }, + onResume: () { + _resumeStream(); + _parse(); + }); + _controller.add( + new _MimeMultipart(_headers, _multipartController.stream)); + _headers = null; + _state = _CONTENT; + contentStartIndex = _index + 1; + break; + + case _CONTENT: + if (byte == _boundary[_boundaryIndex]) { + _boundaryIndex++; + if (_boundaryIndex == _boundary.length) { + if (contentStartIndex != null) { + _index++; + reportData(); + _index--; + } + _multipartController.close(); + _boundaryIndex = 0; + _state = _BOUNDARY_ENDING; + } + } else { + // Restart matching of the boundary. + _index = _index - _boundaryIndex; + if (contentStartIndex == null) contentStartIndex = _index; + _boundaryIndex = 0; + } + break; + + case _LAST_BOUNDARY_DASH2: + _expectByteValue(byte, CharCode.DASH); + _state = _LAST_BOUNDARY_ENDING; + break; + + case _LAST_BOUNDARY_ENDING: + if (byte == CharCode.CR) { + _state = _LAST_BOUNDARY_END; + } else { + _expectWhitespace(byte); + } + break; + + case _LAST_BOUNDARY_END: + _expectByteValue(byte, CharCode.LF); + _multipartController.close(); + _multipartController = null; + _state = _DONE; + break; + + default: + // Should be unreachable. + assert(false); + break; + } + + // Move to the next byte. + _index++; + } + + // Report any known content. + if (_state == _CONTENT && contentStartIndex != null) { + reportData(); + } + + // Resume if at end. + if (_index == _buffer.length) { + _buffer = null; + _index = null; + _resumeStream(); + } + } +} diff --git a/Chapter 7/serving_files/packages/mime/src/char_code.dart b/Chapter 7/serving_files/packages/mime/src/char_code.dart new file mode 100644 index 0000000..f455e68 --- /dev/null +++ b/Chapter 7/serving_files/packages/mime/src/char_code.dart @@ -0,0 +1,16 @@ +// Copyright (c) 2014, 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. +library mime.char_code; + +class CharCode { + static const int HT = 9; + static const int LF = 10; + static const int CR = 13; + static const int SP = 32; + static const int DASH = 45; + static const int COLON = 58; + static const int UPPER_A = 65; + static const int UPPER_Z = 90; + static const int LOWER_A = 97; +} diff --git a/Chapter 7/serving_files/packages/mime/src/default_extension_map.dart b/Chapter 7/serving_files/packages/mime/src/default_extension_map.dart new file mode 100644 index 0000000..ae0d7df --- /dev/null +++ b/Chapter 7/serving_files/packages/mime/src/default_extension_map.dart @@ -0,0 +1,990 @@ +// Copyright (c) 2013, 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. + +library mime.extension_map; + +// TODO(ajohnsen): Use const map once Issue 7559 is fixed. +final Map defaultExtensionMap = { +'123':'application/vnd.lotus-1-2-3', +'3dml':'text/vnd.in3d.3dml', +'3ds':'image/x-3ds', +'3g2':'video/3gpp2', +'3gp':'video/3gpp', +'7z':'application/x-7z-compressed', +'aab':'application/x-authorware-bin', +'aac':'audio/x-aac', +'aam':'application/x-authorware-map', +'aas':'application/x-authorware-seg', +'abw':'application/x-abiword', +'ac':'application/pkix-attr-cert', +'acc':'application/vnd.americandynamics.acc', +'ace':'application/x-ace-compressed', +'acu':'application/vnd.acucobol', +'acutc':'application/vnd.acucorp', +'adp':'audio/adpcm', +'aep':'application/vnd.audiograph', +'afm':'application/x-font-type1', +'afp':'application/vnd.ibm.modcap', +'ahead':'application/vnd.ahead.space', +'ai':'application/postscript', +'aif':'audio/x-aiff', +'aifc':'audio/x-aiff', +'aiff':'audio/x-aiff', +'air':'application/vnd.adobe.air-application-installer-package+zip', +'ait':'application/vnd.dvb.ait', +'ami':'application/vnd.amiga.ami', +'apk':'application/vnd.android.package-archive', +'appcache':'text/cache-manifest', +'application':'application/x-ms-application', +'apr':'application/vnd.lotus-approach', +'arc':'application/x-freearc', +'asc':'application/pgp-signature', +'asf':'video/x-ms-asf', +'asm':'text/x-asm', +'aso':'application/vnd.accpac.simply.aso', +'asx':'video/x-ms-asf', +'atc':'application/vnd.acucorp', +'atom':'application/atom+xml', +'atomcat':'application/atomcat+xml', +'atomsvc':'application/atomsvc+xml', +'atx':'application/vnd.antix.game-component', +'au':'audio/basic', +'avi':'video/x-msvideo', +'aw':'application/applixware', +'azf':'application/vnd.airzip.filesecure.azf', +'azs':'application/vnd.airzip.filesecure.azs', +'azw':'application/vnd.amazon.ebook', +'bat':'application/x-msdownload', +'bcpio':'application/x-bcpio', +'bdf':'application/x-font-bdf', +'bdm':'application/vnd.syncml.dm+wbxml', +'bed':'application/vnd.realvnc.bed', +'bh2':'application/vnd.fujitsu.oasysprs', +'bin':'application/octet-stream', +'blb':'application/x-blorb', +'blorb':'application/x-blorb', +'bmi':'application/vnd.bmi', +'bmp':'image/bmp', +'book':'application/vnd.framemaker', +'box':'application/vnd.previewsystems.box', +'boz':'application/x-bzip2', +'bpk':'application/octet-stream', +'btif':'image/prs.btif', +'bz':'application/x-bzip', +'bz2':'application/x-bzip2', +'c':'text/x-c', +'c11amc':'application/vnd.cluetrust.cartomobile-config', +'c11amz':'application/vnd.cluetrust.cartomobile-config-pkg', +'c4d':'application/vnd.clonk.c4group', +'c4f':'application/vnd.clonk.c4group', +'c4g':'application/vnd.clonk.c4group', +'c4p':'application/vnd.clonk.c4group', +'c4u':'application/vnd.clonk.c4group', +'cab':'application/vnd.ms-cab-compressed', +'caf':'audio/x-caf', +'cap':'application/vnd.tcpdump.pcap', +'car':'application/vnd.curl.car', +'cat':'application/vnd.ms-pki.seccat', +'cb7':'application/x-cbr', +'cba':'application/x-cbr', +'cbr':'application/x-cbr', +'cbt':'application/x-cbr', +'cbz':'application/x-cbr', +'cc':'text/x-c', +'cct':'application/x-director', +'ccxml':'application/ccxml+xml', +'cdbcmsg':'application/vnd.contact.cmsg', +'cdf':'application/x-netcdf', +'cdkey':'application/vnd.mediastation.cdkey', +'cdmia':'application/cdmi-capability', +'cdmic':'application/cdmi-container', +'cdmid':'application/cdmi-domain', +'cdmio':'application/cdmi-object', +'cdmiq':'application/cdmi-queue', +'cdx':'chemical/x-cdx', +'cdxml':'application/vnd.chemdraw+xml', +'cdy':'application/vnd.cinderella', +'cer':'application/pkix-cert', +'cfs':'application/x-cfs-compressed', +'cgm':'image/cgm', +'chat':'application/x-chat', +'chm':'application/vnd.ms-htmlhelp', +'chrt':'application/vnd.kde.kchart', +'cif':'chemical/x-cif', +'cii':'application/vnd.anser-web-certificate-issue-initiation', +'cil':'application/vnd.ms-artgalry', +'cla':'application/vnd.claymore', +'class':'application/java-vm', +'clkk':'application/vnd.crick.clicker.keyboard', +'clkp':'application/vnd.crick.clicker.palette', +'clkt':'application/vnd.crick.clicker.template', +'clkw':'application/vnd.crick.clicker.wordbank', +'clkx':'application/vnd.crick.clicker', +'clp':'application/x-msclip', +'cmc':'application/vnd.cosmocaller', +'cmdf':'chemical/x-cmdf', +'cml':'chemical/x-cml', +'cmp':'application/vnd.yellowriver-custom-menu', +'cmx':'image/x-cmx', +'cod':'application/vnd.rim.cod', +'com':'application/x-msdownload', +'conf':'text/plain', +'cpio':'application/x-cpio', +'cpp':'text/x-c', +'cpt':'application/mac-compactpro', +'crd':'application/x-mscardfile', +'crl':'application/pkix-crl', +'crt':'application/x-x509-ca-cert', +'cryptonote':'application/vnd.rig.cryptonote', +'csh':'application/x-csh', +'csml':'chemical/x-csml', +'csp':'application/vnd.commonspace', +'css':'text/css', +'cst':'application/x-director', +'csv':'text/csv', +'cu':'application/cu-seeme', +'curl':'text/vnd.curl', +'cww':'application/prs.cww', +'cxt':'application/x-director', +'cxx':'text/x-c', +'dae':'model/vnd.collada+xml', +'daf':'application/vnd.mobius.daf', +'dart':'application/dart', +'dataless':'application/vnd.fdsn.seed', +'davmount':'application/davmount+xml', +'dbk':'application/docbook+xml', +'dcr':'application/x-director', +'dcurl':'text/vnd.curl.dcurl', +'dd2':'application/vnd.oma.dd2+xml', +'ddd':'application/vnd.fujixerox.ddd', +'deb':'application/x-debian-package', +'def':'text/plain', +'deploy':'application/octet-stream', +'der':'application/x-x509-ca-cert', +'dfac':'application/vnd.dreamfactory', +'dgc':'application/x-dgc-compressed', +'dic':'text/x-c', +'dir':'application/x-director', +'dis':'application/vnd.mobius.dis', +'dist':'application/octet-stream', +'distz':'application/octet-stream', +'djv':'image/vnd.djvu', +'djvu':'image/vnd.djvu', +'dll':'application/x-msdownload', +'dmg':'application/x-apple-diskimage', +'dmp':'application/vnd.tcpdump.pcap', +'dms':'application/octet-stream', +'dna':'application/vnd.dna', +'doc':'application/msword', +'docm':'application/vnd.ms-word.document.macroenabled.12', +'docx':'application/vnd.openxmlformats-officedocument.wordprocessingml.document', +'dot':'application/msword', +'dotm':'application/vnd.ms-word.template.macroenabled.12', +'dotx':'application/vnd.openxmlformats-officedocument.wordprocessingml.template', +'dp':'application/vnd.osgi.dp', +'dpg':'application/vnd.dpgraph', +'dra':'audio/vnd.dra', +'dsc':'text/prs.lines.tag', +'dssc':'application/dssc+der', +'dtb':'application/x-dtbook+xml', +'dtd':'application/xml-dtd', +'dts':'audio/vnd.dts', +'dtshd':'audio/vnd.dts.hd', +'dump':'application/octet-stream', +'dvb':'video/vnd.dvb.file', +'dvi':'application/x-dvi', +'dwf':'model/vnd.dwf', +'dwg':'image/vnd.dwg', +'dxf':'image/vnd.dxf', +'dxp':'application/vnd.spotfire.dxp', +'dxr':'application/x-director', +'ecelp4800':'audio/vnd.nuera.ecelp4800', +'ecelp7470':'audio/vnd.nuera.ecelp7470', +'ecelp9600':'audio/vnd.nuera.ecelp9600', +'ecma':'application/ecmascript', +'edm':'application/vnd.novadigm.edm', +'edx':'application/vnd.novadigm.edx', +'efif':'application/vnd.picsel', +'ei6':'application/vnd.pg.osasli', +'elc':'application/octet-stream', +'emf':'application/x-msmetafile', +'eml':'message/rfc822', +'emma':'application/emma+xml', +'emz':'application/x-msmetafile', +'eol':'audio/vnd.digital-winds', +'eot':'application/vnd.ms-fontobject', +'eps':'application/postscript', +'epub':'application/epub+zip', +'es3':'application/vnd.eszigno3+xml', +'esa':'application/vnd.osgi.subsystem', +'esf':'application/vnd.epson.esf', +'et3':'application/vnd.eszigno3+xml', +'etx':'text/x-setext', +'eva':'application/x-eva', +'evy':'application/x-envoy', +'exe':'application/x-msdownload', +'exi':'application/exi', +'ext':'application/vnd.novadigm.ext', +'ez':'application/andrew-inset', +'ez2':'application/vnd.ezpix-album', +'ez3':'application/vnd.ezpix-package', +'f':'text/x-fortran', +'f4v':'video/x-f4v', +'f77':'text/x-fortran', +'f90':'text/x-fortran', +'fbs':'image/vnd.fastbidsheet', +'fcdt':'application/vnd.adobe.formscentral.fcdt', +'fcs':'application/vnd.isac.fcs', +'fdf':'application/vnd.fdf', +'fe_launch':'application/vnd.denovo.fcselayout-link', +'fg5':'application/vnd.fujitsu.oasysgp', +'fgd':'application/x-director', +'fh':'image/x-freehand', +'fh4':'image/x-freehand', +'fh5':'image/x-freehand', +'fh7':'image/x-freehand', +'fhc':'image/x-freehand', +'fig':'application/x-xfig', +'flac':'audio/x-flac', +'fli':'video/x-fli', +'flo':'application/vnd.micrografx.flo', +'flv':'video/x-flv', +'flw':'application/vnd.kde.kivio', +'flx':'text/vnd.fmi.flexstor', +'fly':'text/vnd.fly', +'fm':'application/vnd.framemaker', +'fnc':'application/vnd.frogans.fnc', +'for':'text/x-fortran', +'fpx':'image/vnd.fpx', +'frame':'application/vnd.framemaker', +'fsc':'application/vnd.fsc.weblaunch', +'fst':'image/vnd.fst', +'ftc':'application/vnd.fluxtime.clip', +'fti':'application/vnd.anser-web-funds-transfer-initiation', +'fvt':'video/vnd.fvt', +'fxp':'application/vnd.adobe.fxp', +'fxpl':'application/vnd.adobe.fxp', +'fzs':'application/vnd.fuzzysheet', +'g2w':'application/vnd.geoplan', +'g3':'image/g3fax', +'g3w':'application/vnd.geospace', +'gac':'application/vnd.groove-account', +'gam':'application/x-tads', +'gbr':'application/rpki-ghostbusters', +'gca':'application/x-gca-compressed', +'gdl':'model/vnd.gdl', +'geo':'application/vnd.dynageo', +'gex':'application/vnd.geometry-explorer', +'ggb':'application/vnd.geogebra.file', +'ggt':'application/vnd.geogebra.tool', +'ghf':'application/vnd.groove-help', +'gif':'image/gif', +'gim':'application/vnd.groove-identity-message', +'gml':'application/gml+xml', +'gmx':'application/vnd.gmx', +'gnumeric':'application/x-gnumeric', +'gph':'application/vnd.flographit', +'gpx':'application/gpx+xml', +'gqf':'application/vnd.grafeq', +'gqs':'application/vnd.grafeq', +'gram':'application/srgs', +'gramps':'application/x-gramps-xml', +'gre':'application/vnd.geometry-explorer', +'grv':'application/vnd.groove-injector', +'grxml':'application/srgs+xml', +'gsf':'application/x-font-ghostscript', +'gtar':'application/x-gtar', +'gtm':'application/vnd.groove-tool-message', +'gtw':'model/vnd.gtw', +'gv':'text/vnd.graphviz', +'gxf':'application/gxf', +'gxt':'application/vnd.geonext', +'h':'text/x-c', +'h261':'video/h261', +'h263':'video/h263', +'h264':'video/h264', +'hal':'application/vnd.hal+xml', +'hbci':'application/vnd.hbci', +'hdf':'application/x-hdf', +'hh':'text/x-c', +'hlp':'application/winhlp', +'hpgl':'application/vnd.hp-hpgl', +'hpid':'application/vnd.hp-hpid', +'hps':'application/vnd.hp-hps', +'hqx':'application/mac-binhex40', +'htke':'application/vnd.kenameaapp', +'htm':'text/html', +'html':'text/html', +'hvd':'application/vnd.yamaha.hv-dic', +'hvp':'application/vnd.yamaha.hv-voice', +'hvs':'application/vnd.yamaha.hv-script', +'i2g':'application/vnd.intergeo', +'icc':'application/vnd.iccprofile', +'ice':'x-conference/x-cooltalk', +'icm':'application/vnd.iccprofile', +'ico':'image/x-icon', +'ics':'text/calendar', +'ief':'image/ief', +'ifb':'text/calendar', +'ifm':'application/vnd.shana.informed.formdata', +'iges':'model/iges', +'igl':'application/vnd.igloader', +'igm':'application/vnd.insors.igm', +'igs':'model/iges', +'igx':'application/vnd.micrografx.igx', +'iif':'application/vnd.shana.informed.interchange', +'imp':'application/vnd.accpac.simply.imp', +'ims':'application/vnd.ms-ims', +'in':'text/plain', +'ink':'application/inkml+xml', +'inkml':'application/inkml+xml', +'install':'application/x-install-instructions', +'iota':'application/vnd.astraea-software.iota', +'ipfix':'application/ipfix', +'ipk':'application/vnd.shana.informed.package', +'irm':'application/vnd.ibm.rights-management', +'irp':'application/vnd.irepository.package+xml', +'iso':'application/x-iso9660-image', +'itp':'application/vnd.shana.informed.formtemplate', +'ivp':'application/vnd.immervision-ivp', +'ivu':'application/vnd.immervision-ivu', +'jad':'text/vnd.sun.j2me.app-descriptor', +'jam':'application/vnd.jam', +'jar':'application/java-archive', +'java':'text/x-java-source', +'jisp':'application/vnd.jisp', +'jlt':'application/vnd.hp-jlyt', +'jnlp':'application/x-java-jnlp-file', +'joda':'application/vnd.joost.joda-archive', +'jpe':'image/jpeg', +'jpeg':'image/jpeg', +'jpg':'image/jpeg', +'jpgm':'video/jpm', +'jpgv':'video/jpeg', +'jpm':'video/jpm', +'js':'application/javascript', +'json':'application/json', +'jsonml':'application/jsonml+json', +'kar':'audio/midi', +'karbon':'application/vnd.kde.karbon', +'kfo':'application/vnd.kde.kformula', +'kia':'application/vnd.kidspiration', +'kml':'application/vnd.google-earth.kml+xml', +'kmz':'application/vnd.google-earth.kmz', +'kne':'application/vnd.kinar', +'knp':'application/vnd.kinar', +'kon':'application/vnd.kde.kontour', +'kpr':'application/vnd.kde.kpresenter', +'kpt':'application/vnd.kde.kpresenter', +'kpxx':'application/vnd.ds-keypoint', +'ksp':'application/vnd.kde.kspread', +'ktr':'application/vnd.kahootz', +'ktx':'image/ktx', +'ktz':'application/vnd.kahootz', +'kwd':'application/vnd.kde.kword', +'kwt':'application/vnd.kde.kword', +'lasxml':'application/vnd.las.las+xml', +'latex':'application/x-latex', +'lbd':'application/vnd.llamagraphics.life-balance.desktop', +'lbe':'application/vnd.llamagraphics.life-balance.exchange+xml', +'les':'application/vnd.hhe.lesson-player', +'lha':'application/x-lzh-compressed', +'link66':'application/vnd.route66.link66+xml', +'list':'text/plain', +'list3820':'application/vnd.ibm.modcap', +'listafp':'application/vnd.ibm.modcap', +'lnk':'application/x-ms-shortcut', +'log':'text/plain', +'lostxml':'application/lost+xml', +'lrf':'application/octet-stream', +'lrm':'application/vnd.ms-lrm', +'ltf':'application/vnd.frogans.ltf', +'lvp':'audio/vnd.lucent.voice', +'lwp':'application/vnd.lotus-wordpro', +'lzh':'application/x-lzh-compressed', +'m13':'application/x-msmediaview', +'m14':'application/x-msmediaview', +'m1v':'video/mpeg', +'m21':'application/mp21', +'m2a':'audio/mpeg', +'m2v':'video/mpeg', +'m3a':'audio/mpeg', +'m3u':'audio/x-mpegurl', +'m3u8':'application/vnd.apple.mpegurl', +'m4u':'video/vnd.mpegurl', +'m4v':'video/x-m4v', +'ma':'application/mathematica', +'mads':'application/mads+xml', +'mag':'application/vnd.ecowin.chart', +'maker':'application/vnd.framemaker', +'man':'text/troff', +'mar':'application/octet-stream', +'mathml':'application/mathml+xml', +'mb':'application/mathematica', +'mbk':'application/vnd.mobius.mbk', +'mbox':'application/mbox', +'mc1':'application/vnd.medcalcdata', +'mcd':'application/vnd.mcd', +'mcurl':'text/vnd.curl.mcurl', +'mdb':'application/x-msaccess', +'mdi':'image/vnd.ms-modi', +'me':'text/troff', +'mesh':'model/mesh', +'meta4':'application/metalink4+xml', +'metalink':'application/metalink+xml', +'mets':'application/mets+xml', +'mfm':'application/vnd.mfmp', +'mft':'application/rpki-manifest', +'mgp':'application/vnd.osgeo.mapguide.package', +'mgz':'application/vnd.proteus.magazine', +'mid':'audio/midi', +'midi':'audio/midi', +'mie':'application/x-mie', +'mif':'application/vnd.mif', +'mime':'message/rfc822', +'mj2':'video/mj2', +'mjp2':'video/mj2', +'mk3d':'video/x-matroska', +'mka':'audio/x-matroska', +'mks':'video/x-matroska', +'mkv':'video/x-matroska', +'mlp':'application/vnd.dolby.mlp', +'mmd':'application/vnd.chipnuts.karaoke-mmd', +'mmf':'application/vnd.smaf', +'mmr':'image/vnd.fujixerox.edmics-mmr', +'mng':'video/x-mng', +'mny':'application/x-msmoney', +'mobi':'application/x-mobipocket-ebook', +'mods':'application/mods+xml', +'mov':'video/quicktime', +'movie':'video/x-sgi-movie', +'mp2':'audio/mpeg', +'mp21':'application/mp21', +'mp2a':'audio/mpeg', +'mp3':'audio/mpeg', +'mp4':'video/mp4', +'mp4a':'audio/mp4', +'mp4s':'application/mp4', +'mp4v':'video/mp4', +'mpc':'application/vnd.mophun.certificate', +'mpe':'video/mpeg', +'mpeg':'video/mpeg', +'mpg':'video/mpeg', +'mpg4':'video/mp4', +'mpga':'audio/mpeg', +'mpkg':'application/vnd.apple.installer+xml', +'mpm':'application/vnd.blueice.multipass', +'mpn':'application/vnd.mophun.application', +'mpp':'application/vnd.ms-project', +'mpt':'application/vnd.ms-project', +'mpy':'application/vnd.ibm.minipay', +'mqy':'application/vnd.mobius.mqy', +'mrc':'application/marc', +'mrcx':'application/marcxml+xml', +'ms':'text/troff', +'mscml':'application/mediaservercontrol+xml', +'mseed':'application/vnd.fdsn.mseed', +'mseq':'application/vnd.mseq', +'msf':'application/vnd.epson.msf', +'msh':'model/mesh', +'msi':'application/x-msdownload', +'msl':'application/vnd.mobius.msl', +'msty':'application/vnd.muvee.style', +'mts':'model/vnd.mts', +'mus':'application/vnd.musician', +'musicxml':'application/vnd.recordare.musicxml+xml', +'mvb':'application/x-msmediaview', +'mwf':'application/vnd.mfer', +'mxf':'application/mxf', +'mxl':'application/vnd.recordare.musicxml', +'mxml':'application/xv+xml', +'mxs':'application/vnd.triscape.mxs', +'mxu':'video/vnd.mpegurl', +'n-gage':'application/vnd.nokia.n-gage.symbian.install', +'n3':'text/n3', +'nb':'application/mathematica', +'nbp':'application/vnd.wolfram.player', +'nc':'application/x-netcdf', +'ncx':'application/x-dtbncx+xml', +'nfo':'text/x-nfo', +'ngdat':'application/vnd.nokia.n-gage.data', +'nitf':'application/vnd.nitf', +'nlu':'application/vnd.neurolanguage.nlu', +'nml':'application/vnd.enliven', +'nnd':'application/vnd.noblenet-directory', +'nns':'application/vnd.noblenet-sealer', +'nnw':'application/vnd.noblenet-web', +'npx':'image/vnd.net-fpx', +'nsc':'application/x-conference', +'nsf':'application/vnd.lotus-notes', +'ntf':'application/vnd.nitf', +'nzb':'application/x-nzb', +'oa2':'application/vnd.fujitsu.oasys2', +'oa3':'application/vnd.fujitsu.oasys3', +'oas':'application/vnd.fujitsu.oasys', +'obd':'application/x-msbinder', +'obj':'application/x-tgif', +'oda':'application/oda', +'odb':'application/vnd.oasis.opendocument.database', +'odc':'application/vnd.oasis.opendocument.chart', +'odf':'application/vnd.oasis.opendocument.formula', +'odft':'application/vnd.oasis.opendocument.formula-template', +'odg':'application/vnd.oasis.opendocument.graphics', +'odi':'application/vnd.oasis.opendocument.image', +'odm':'application/vnd.oasis.opendocument.text-master', +'odp':'application/vnd.oasis.opendocument.presentation', +'ods':'application/vnd.oasis.opendocument.spreadsheet', +'odt':'application/vnd.oasis.opendocument.text', +'oga':'audio/ogg', +'ogg':'audio/ogg', +'ogv':'video/ogg', +'ogx':'application/ogg', +'omdoc':'application/omdoc+xml', +'onepkg':'application/onenote', +'onetmp':'application/onenote', +'onetoc':'application/onenote', +'onetoc2':'application/onenote', +'opf':'application/oebps-package+xml', +'opml':'text/x-opml', +'oprc':'application/vnd.palm', +'org':'application/vnd.lotus-organizer', +'osf':'application/vnd.yamaha.openscoreformat', +'osfpvg':'application/vnd.yamaha.openscoreformat.osfpvg+xml', +'otc':'application/vnd.oasis.opendocument.chart-template', +'otf':'application/x-font-otf', +'otg':'application/vnd.oasis.opendocument.graphics-template', +'oth':'application/vnd.oasis.opendocument.text-web', +'oti':'application/vnd.oasis.opendocument.image-template', +'otp':'application/vnd.oasis.opendocument.presentation-template', +'ots':'application/vnd.oasis.opendocument.spreadsheet-template', +'ott':'application/vnd.oasis.opendocument.text-template', +'oxps':'application/oxps', +'oxt':'application/vnd.openofficeorg.extension', +'p':'text/x-pascal', +'p10':'application/pkcs10', +'p12':'application/x-pkcs12', +'p7b':'application/x-pkcs7-certificates', +'p7c':'application/pkcs7-mime', +'p7m':'application/pkcs7-mime', +'p7r':'application/x-pkcs7-certreqresp', +'p7s':'application/pkcs7-signature', +'p8':'application/pkcs8', +'pas':'text/x-pascal', +'paw':'application/vnd.pawaafile', +'pbd':'application/vnd.powerbuilder6', +'pbm':'image/x-portable-bitmap', +'pcap':'application/vnd.tcpdump.pcap', +'pcf':'application/x-font-pcf', +'pcl':'application/vnd.hp-pcl', +'pclxl':'application/vnd.hp-pclxl', +'pct':'image/x-pict', +'pcurl':'application/vnd.curl.pcurl', +'pcx':'image/x-pcx', +'pdb':'application/vnd.palm', +'pdf':'application/pdf', +'pfa':'application/x-font-type1', +'pfb':'application/x-font-type1', +'pfm':'application/x-font-type1', +'pfr':'application/font-tdpfr', +'pfx':'application/x-pkcs12', +'pgm':'image/x-portable-graymap', +'pgn':'application/x-chess-pgn', +'pgp':'application/pgp-encrypted', +'pic':'image/x-pict', +'pkg':'application/octet-stream', +'pki':'application/pkixcmp', +'pkipath':'application/pkix-pkipath', +'plb':'application/vnd.3gpp.pic-bw-large', +'plc':'application/vnd.mobius.plc', +'plf':'application/vnd.pocketlearn', +'pls':'application/pls+xml', +'pml':'application/vnd.ctc-posml', +'png':'image/png', +'pnm':'image/x-portable-anymap', +'portpkg':'application/vnd.macports.portpkg', +'pot':'application/vnd.ms-powerpoint', +'potm':'application/vnd.ms-powerpoint.template.macroenabled.12', +'potx':'application/vnd.openxmlformats-officedocument.presentationml.template', +'ppam':'application/vnd.ms-powerpoint.addin.macroenabled.12', +'ppd':'application/vnd.cups-ppd', +'ppm':'image/x-portable-pixmap', +'pps':'application/vnd.ms-powerpoint', +'ppsm':'application/vnd.ms-powerpoint.slideshow.macroenabled.12', +'ppsx':'application/vnd.openxmlformats-officedocument.presentationml.slideshow', +'ppt':'application/vnd.ms-powerpoint', +'pptm':'application/vnd.ms-powerpoint.presentation.macroenabled.12', +'pptx':'application/vnd.openxmlformats-officedocument.presentationml.presentation', +'pqa':'application/vnd.palm', +'prc':'application/x-mobipocket-ebook', +'pre':'application/vnd.lotus-freelance', +'prf':'application/pics-rules', +'ps':'application/postscript', +'psb':'application/vnd.3gpp.pic-bw-small', +'psd':'image/vnd.adobe.photoshop', +'psf':'application/x-font-linux-psf', +'pskcxml':'application/pskc+xml', +'ptid':'application/vnd.pvi.ptid1', +'pub':'application/x-mspublisher', +'pvb':'application/vnd.3gpp.pic-bw-var', +'pwn':'application/vnd.3m.post-it-notes', +'pya':'audio/vnd.ms-playready.media.pya', +'pyv':'video/vnd.ms-playready.media.pyv', +'qam':'application/vnd.epson.quickanime', +'qbo':'application/vnd.intu.qbo', +'qfx':'application/vnd.intu.qfx', +'qps':'application/vnd.publishare-delta-tree', +'qt':'video/quicktime', +'qwd':'application/vnd.quark.quarkxpress', +'qwt':'application/vnd.quark.quarkxpress', +'qxb':'application/vnd.quark.quarkxpress', +'qxd':'application/vnd.quark.quarkxpress', +'qxl':'application/vnd.quark.quarkxpress', +'qxt':'application/vnd.quark.quarkxpress', +'ra':'audio/x-pn-realaudio', +'ram':'audio/x-pn-realaudio', +'rar':'application/x-rar-compressed', +'ras':'image/x-cmu-raster', +'rcprofile':'application/vnd.ipunplugged.rcprofile', +'rdf':'application/rdf+xml', +'rdz':'application/vnd.data-vision.rdz', +'rep':'application/vnd.businessobjects', +'res':'application/x-dtbresource+xml', +'rgb':'image/x-rgb', +'rif':'application/reginfo+xml', +'rip':'audio/vnd.rip', +'ris':'application/x-research-info-systems', +'rl':'application/resource-lists+xml', +'rlc':'image/vnd.fujixerox.edmics-rlc', +'rld':'application/resource-lists-diff+xml', +'rm':'application/vnd.rn-realmedia', +'rmi':'audio/midi', +'rmp':'audio/x-pn-realaudio-plugin', +'rms':'application/vnd.jcp.javame.midlet-rms', +'rmvb':'application/vnd.rn-realmedia-vbr', +'rnc':'application/relax-ng-compact-syntax', +'roa':'application/rpki-roa', +'roff':'text/troff', +'rp9':'application/vnd.cloanto.rp9', +'rpss':'application/vnd.nokia.radio-presets', +'rpst':'application/vnd.nokia.radio-preset', +'rq':'application/sparql-query', +'rs':'application/rls-services+xml', +'rsd':'application/rsd+xml', +'rss':'application/rss+xml', +'rtf':'application/rtf', +'rtx':'text/richtext', +'s':'text/x-asm', +'s3m':'audio/s3m', +'saf':'application/vnd.yamaha.smaf-audio', +'sbml':'application/sbml+xml', +'sc':'application/vnd.ibm.secure-container', +'scd':'application/x-msschedule', +'scm':'application/vnd.lotus-screencam', +'scq':'application/scvp-cv-request', +'scs':'application/scvp-cv-response', +'scurl':'text/vnd.curl.scurl', +'sda':'application/vnd.stardivision.draw', +'sdc':'application/vnd.stardivision.calc', +'sdd':'application/vnd.stardivision.impress', +'sdkd':'application/vnd.solent.sdkm+xml', +'sdkm':'application/vnd.solent.sdkm+xml', +'sdp':'application/sdp', +'sdw':'application/vnd.stardivision.writer', +'see':'application/vnd.seemail', +'seed':'application/vnd.fdsn.seed', +'sema':'application/vnd.sema', +'semd':'application/vnd.semd', +'semf':'application/vnd.semf', +'ser':'application/java-serialized-object', +'setpay':'application/set-payment-initiation', +'setreg':'application/set-registration-initiation', +'sfd-hdstx':'application/vnd.hydrostatix.sof-data', +'sfs':'application/vnd.spotfire.sfs', +'sfv':'text/x-sfv', +'sgi':'image/sgi', +'sgl':'application/vnd.stardivision.writer-global', +'sgm':'text/sgml', +'sgml':'text/sgml', +'sh':'application/x-sh', +'shar':'application/x-shar', +'shf':'application/shf+xml', +'sid':'image/x-mrsid-image', +'sig':'application/pgp-signature', +'sil':'audio/silk', +'silo':'model/mesh', +'sis':'application/vnd.symbian.install', +'sisx':'application/vnd.symbian.install', +'sit':'application/x-stuffit', +'sitx':'application/x-stuffitx', +'skd':'application/vnd.koan', +'skm':'application/vnd.koan', +'skp':'application/vnd.koan', +'skt':'application/vnd.koan', +'sldm':'application/vnd.ms-powerpoint.slide.macroenabled.12', +'sldx':'application/vnd.openxmlformats-officedocument.presentationml.slide', +'slt':'application/vnd.epson.salt', +'sm':'application/vnd.stepmania.stepchart', +'smf':'application/vnd.stardivision.math', +'smi':'application/smil+xml', +'smil':'application/smil+xml', +'smv':'video/x-smv', +'smzip':'application/vnd.stepmania.package', +'snd':'audio/basic', +'snf':'application/x-font-snf', +'so':'application/octet-stream', +'spc':'application/x-pkcs7-certificates', +'spf':'application/vnd.yamaha.smaf-phrase', +'spl':'application/x-futuresplash', +'spot':'text/vnd.in3d.spot', +'spp':'application/scvp-vp-response', +'spq':'application/scvp-vp-request', +'spx':'audio/ogg', +'sql':'application/x-sql', +'src':'application/x-wais-source', +'srt':'application/x-subrip', +'sru':'application/sru+xml', +'srx':'application/sparql-results+xml', +'ssdl':'application/ssdl+xml', +'sse':'application/vnd.kodak-descriptor', +'ssf':'application/vnd.epson.ssf', +'ssml':'application/ssml+xml', +'st':'application/vnd.sailingtracker.track', +'stc':'application/vnd.sun.xml.calc.template', +'std':'application/vnd.sun.xml.draw.template', +'stf':'application/vnd.wt.stf', +'sti':'application/vnd.sun.xml.impress.template', +'stk':'application/hyperstudio', +'stl':'application/vnd.ms-pki.stl', +'str':'application/vnd.pg.format', +'stw':'application/vnd.sun.xml.writer.template', +'sub':'text/vnd.dvb.subtitle', +'sus':'application/vnd.sus-calendar', +'susp':'application/vnd.sus-calendar', +'sv4cpio':'application/x-sv4cpio', +'sv4crc':'application/x-sv4crc', +'svc':'application/vnd.dvb.service', +'svd':'application/vnd.svd', +'svg':'image/svg+xml', +'svgz':'image/svg+xml', +'swa':'application/x-director', +'swf':'application/x-shockwave-flash', +'swi':'application/vnd.aristanetworks.swi', +'sxc':'application/vnd.sun.xml.calc', +'sxd':'application/vnd.sun.xml.draw', +'sxg':'application/vnd.sun.xml.writer.global', +'sxi':'application/vnd.sun.xml.impress', +'sxm':'application/vnd.sun.xml.math', +'sxw':'application/vnd.sun.xml.writer', +'t':'text/troff', +'t3':'application/x-t3vm-image', +'taglet':'application/vnd.mynfc', +'tao':'application/vnd.tao.intent-module-archive', +'tar':'application/x-tar', +'tcap':'application/vnd.3gpp2.tcap', +'tcl':'application/x-tcl', +'teacher':'application/vnd.smart.teacher', +'tei':'application/tei+xml', +'teicorpus':'application/tei+xml', +'tex':'application/x-tex', +'texi':'application/x-texinfo', +'texinfo':'application/x-texinfo', +'text':'text/plain', +'tfi':'application/thraud+xml', +'tfm':'application/x-tex-tfm', +'tga':'image/x-tga', +'thmx':'application/vnd.ms-officetheme', +'tif':'image/tiff', +'tiff':'image/tiff', +'tmo':'application/vnd.tmobile-livetv', +'torrent':'application/x-bittorrent', +'tpl':'application/vnd.groove-tool-template', +'tpt':'application/vnd.trid.tpt', +'tr':'text/troff', +'tra':'application/vnd.trueapp', +'trm':'application/x-msterminal', +'tsd':'application/timestamped-data', +'tsv':'text/tab-separated-values', +'ttc':'application/x-font-ttf', +'ttf':'application/x-font-ttf', +'ttl':'text/turtle', +'twd':'application/vnd.simtech-mindmapper', +'twds':'application/vnd.simtech-mindmapper', +'txd':'application/vnd.genomatix.tuxedo', +'txf':'application/vnd.mobius.txf', +'txt':'text/plain', +'u32':'application/x-authorware-bin', +'udeb':'application/x-debian-package', +'ufd':'application/vnd.ufdl', +'ufdl':'application/vnd.ufdl', +'ulx':'application/x-glulx', +'umj':'application/vnd.umajin', +'unityweb':'application/vnd.unity', +'uoml':'application/vnd.uoml+xml', +'uri':'text/uri-list', +'uris':'text/uri-list', +'urls':'text/uri-list', +'ustar':'application/x-ustar', +'utz':'application/vnd.uiq.theme', +'uu':'text/x-uuencode', +'uva':'audio/vnd.dece.audio', +'uvd':'application/vnd.dece.data', +'uvf':'application/vnd.dece.data', +'uvg':'image/vnd.dece.graphic', +'uvh':'video/vnd.dece.hd', +'uvi':'image/vnd.dece.graphic', +'uvm':'video/vnd.dece.mobile', +'uvp':'video/vnd.dece.pd', +'uvs':'video/vnd.dece.sd', +'uvt':'application/vnd.dece.ttml+xml', +'uvu':'video/vnd.uvvu.mp4', +'uvv':'video/vnd.dece.video', +'uvva':'audio/vnd.dece.audio', +'uvvd':'application/vnd.dece.data', +'uvvf':'application/vnd.dece.data', +'uvvg':'image/vnd.dece.graphic', +'uvvh':'video/vnd.dece.hd', +'uvvi':'image/vnd.dece.graphic', +'uvvm':'video/vnd.dece.mobile', +'uvvp':'video/vnd.dece.pd', +'uvvs':'video/vnd.dece.sd', +'uvvt':'application/vnd.dece.ttml+xml', +'uvvu':'video/vnd.uvvu.mp4', +'uvvv':'video/vnd.dece.video', +'uvvx':'application/vnd.dece.unspecified', +'uvvz':'application/vnd.dece.zip', +'uvx':'application/vnd.dece.unspecified', +'uvz':'application/vnd.dece.zip', +'vcard':'text/vcard', +'vcd':'application/x-cdlink', +'vcf':'text/x-vcard', +'vcg':'application/vnd.groove-vcard', +'vcs':'text/x-vcalendar', +'vcx':'application/vnd.vcx', +'vis':'application/vnd.visionary', +'viv':'video/vnd.vivo', +'vob':'video/x-ms-vob', +'vor':'application/vnd.stardivision.writer', +'vox':'application/x-authorware-bin', +'vrml':'model/vrml', +'vsd':'application/vnd.visio', +'vsf':'application/vnd.vsf', +'vss':'application/vnd.visio', +'vst':'application/vnd.visio', +'vsw':'application/vnd.visio', +'vtu':'model/vnd.vtu', +'vxml':'application/voicexml+xml', +'w3d':'application/x-director', +'wad':'application/x-doom', +'wav':'audio/x-wav', +'wax':'audio/x-ms-wax', +'wbmp':'image/vnd.wap.wbmp', +'wbs':'application/vnd.criticaltools.wbs+xml', +'wbxml':'application/vnd.wap.wbxml', +'wcm':'application/vnd.ms-works', +'wdb':'application/vnd.ms-works', +'wdp':'image/vnd.ms-photo', +'weba':'audio/webm', +'webm':'video/webm', +'webp':'image/webp', +'wg':'application/vnd.pmi.widget', +'wgt':'application/widget', +'wks':'application/vnd.ms-works', +'wm':'video/x-ms-wm', +'wma':'audio/x-ms-wma', +'wmd':'application/x-ms-wmd', +'wmf':'application/x-msmetafile', +'wml':'text/vnd.wap.wml', +'wmlc':'application/vnd.wap.wmlc', +'wmls':'text/vnd.wap.wmlscript', +'wmlsc':'application/vnd.wap.wmlscriptc', +'wmv':'video/x-ms-wmv', +'wmx':'video/x-ms-wmx', +'wmz':'application/x-ms-wmz', +'woff':'application/x-font-woff', +'wpd':'application/vnd.wordperfect', +'wpl':'application/vnd.ms-wpl', +'wps':'application/vnd.ms-works', +'wqd':'application/vnd.wqd', +'wri':'application/x-mswrite', +'wrl':'model/vrml', +'wsdl':'application/wsdl+xml', +'wspolicy':'application/wspolicy+xml', +'wtb':'application/vnd.webturbo', +'wvx':'video/x-ms-wvx', +'x32':'application/x-authorware-bin', +'x3d':'model/x3d+xml', +'x3db':'model/x3d+binary', +'x3dbz':'model/x3d+binary', +'x3dv':'model/x3d+vrml', +'x3dvz':'model/x3d+vrml', +'x3dz':'model/x3d+xml', +'xaml':'application/xaml+xml', +'xap':'application/x-silverlight-app', +'xar':'application/vnd.xara', +'xbap':'application/x-ms-xbap', +'xbd':'application/vnd.fujixerox.docuworks.binder', +'xbm':'image/x-xbitmap', +'xdf':'application/xcap-diff+xml', +'xdm':'application/vnd.syncml.dm+xml', +'xdp':'application/vnd.adobe.xdp+xml', +'xdssc':'application/dssc+xml', +'xdw':'application/vnd.fujixerox.docuworks', +'xenc':'application/xenc+xml', +'xer':'application/patch-ops-error+xml', +'xfdf':'application/vnd.adobe.xfdf', +'xfdl':'application/vnd.xfdl', +'xht':'application/xhtml+xml', +'xhtml':'application/xhtml+xml', +'xhvml':'application/xv+xml', +'xif':'image/vnd.xiff', +'xla':'application/vnd.ms-excel', +'xlam':'application/vnd.ms-excel.addin.macroenabled.12', +'xlc':'application/vnd.ms-excel', +'xlf':'application/x-xliff+xml', +'xlm':'application/vnd.ms-excel', +'xls':'application/vnd.ms-excel', +'xlsb':'application/vnd.ms-excel.sheet.binary.macroenabled.12', +'xlsm':'application/vnd.ms-excel.sheet.macroenabled.12', +'xlsx':'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', +'xlt':'application/vnd.ms-excel', +'xltm':'application/vnd.ms-excel.template.macroenabled.12', +'xltx':'application/vnd.openxmlformats-officedocument.spreadsheetml.template', +'xlw':'application/vnd.ms-excel', +'xm':'audio/xm', +'xml':'application/xml', +'xo':'application/vnd.olpc-sugar', +'xop':'application/xop+xml', +'xpi':'application/x-xpinstall', +'xpl':'application/xproc+xml', +'xpm':'image/x-xpixmap', +'xpr':'application/vnd.is-xpr', +'xps':'application/vnd.ms-xpsdocument', +'xpw':'application/vnd.intercon.formnet', +'xpx':'application/vnd.intercon.formnet', +'xsl':'application/xml', +'xslt':'application/xslt+xml', +'xsm':'application/vnd.syncml+xml', +'xspf':'application/xspf+xml', +'xul':'application/vnd.mozilla.xul+xml', +'xvm':'application/xv+xml', +'xvml':'application/xv+xml', +'xwd':'image/x-xwindowdump', +'xyz':'chemical/x-xyz', +'xz':'application/x-xz', +'yang':'application/yang', +'yin':'application/yin+xml', +'z1':'application/x-zmachine', +'z2':'application/x-zmachine', +'z3':'application/x-zmachine', +'z4':'application/x-zmachine', +'z5':'application/x-zmachine', +'z6':'application/x-zmachine', +'z7':'application/x-zmachine', +'z8':'application/x-zmachine', +'zaz':'application/vnd.zzazz.deck+xml', +'zip':'application/zip', +'zir':'application/vnd.zul', +'zirz':'application/vnd.zul', +'zmm':'application/vnd.handheld-entertainment+xml', +}; diff --git a/Chapter 7/serving_files/packages/mime/src/magic_number.dart b/Chapter 7/serving_files/packages/mime/src/magic_number.dart new file mode 100644 index 0000000..3efb2ee --- /dev/null +++ b/Chapter 7/serving_files/packages/mime/src/magic_number.dart @@ -0,0 +1,48 @@ +// Copyright (c) 2013, 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. + +library mime.magic_number; + +class MagicNumber { + final String mimeType; + final List numbers; + final List mask; + + const MagicNumber(this.mimeType, this.numbers, {this.mask}); + + bool matches(List header) { + if (header.length < numbers.length) return false; + + for (int i = 0; i < numbers.length; i++) { + if (mask != null) { + if ((mask[i] & numbers[i]) != (mask[i] & header[i])) return false; + } else { + if (numbers[i] != header[i]) return false; + } + } + + return true; + } + +} + +const int DEFAULT_MAGIC_NUMBERS_MAX_LENGTH = 12; + +const List DEFAULT_MAGIC_NUMBERS = const [ + const MagicNumber('application/pdf', const [0x25, 0x50, 0x44, 0x46]), + const MagicNumber('application/postscript', const [0x25, 0x51]), + const MagicNumber('image/gif', const [0x47, 0x49, 0x46, 0x38, 0x37, 0x61]), + const MagicNumber('image/gif', const [0x47, 0x49, 0x46, 0x38, 0x39, 0x61]), + const MagicNumber('image/jpeg', const [0xFF, 0xD8]), + const MagicNumber('image/png', + const [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]), + const MagicNumber('image/tiff', const [0x49, 0x49, 0x2A, 0x00]), + const MagicNumber('image/tiff', const [0x4D, 0x4D, 0x00, 0x2A]), + const MagicNumber( + 'video/mp4', + const [0x00, 0x00, 0x00, 0x00, 0x66, 0x74, + 0x79, 0x70, 0x33, 0x67, 0x70, 0x35], + mask: const [0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) +]; diff --git a/Chapter 7/serving_files/packages/mime/src/mime_multipart_transformer.dart b/Chapter 7/serving_files/packages/mime/src/mime_multipart_transformer.dart new file mode 100644 index 0000000..3afff2d --- /dev/null +++ b/Chapter 7/serving_files/packages/mime/src/mime_multipart_transformer.dart @@ -0,0 +1,49 @@ +// Copyright (c) 2012, 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. +library mime.multipart_transformer; + +import 'dart:async'; +import 'dart:typed_data'; + +import 'bound_multipart_stream.dart'; +import 'mime_shared.dart'; +import 'char_code.dart'; + + +Uint8List _getBoundary(String boundary) { + var charCodes = boundary.codeUnits; + + var boundaryList = new Uint8List(4 + charCodes.length); + // Set-up the matching boundary preceding it with CRLF and two + // dashes. + boundaryList[0] = CharCode.CR; + boundaryList[1] = CharCode.LF; + boundaryList[2] = CharCode.DASH; + boundaryList[3] = CharCode.DASH; + boundaryList.setRange(4, 4 + charCodes.length, charCodes); + return boundaryList; +} + +/** + * Parser for MIME multipart types of data as described in RFC 2046 + * section 5.1.1. The data is transformed into [MimeMultipart] objects, each + * of them streaming the multipart data. + */ +class MimeMultipartTransformer + implements StreamTransformer, MimeMultipart> { + + final List _boundary; + + /** + * Construct a new MIME multipart parser with the boundary + * [boundary]. The boundary should be as specified in the content + * type parameter, that is without the -- prefix. + */ + MimeMultipartTransformer(String boundary) + : _boundary = _getBoundary(boundary); + + Stream bind(Stream> stream) { + return new BoundMultipartStream(_boundary, stream).stream; + } +} diff --git a/Chapter 7/serving_files/packages/mime/src/mime_shared.dart b/Chapter 7/serving_files/packages/mime/src/mime_shared.dart new file mode 100644 index 0000000..6d14e0a --- /dev/null +++ b/Chapter 7/serving_files/packages/mime/src/mime_shared.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2014, 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. +library mime.shared; + +import 'dart:async'; + +class MimeMultipartException implements Exception { + final String message; + + const MimeMultipartException([String this.message = ""]); + + String toString() => "MimeMultipartException: $message"; +} + +/** + * A Mime Multipart class representing each part parsed by + * [MimeMultipartTransformer]. The data is streamed in as it become available. + */ +abstract class MimeMultipart extends Stream> { + Map get headers; +} diff --git a/Chapter 7/serving_files/packages/mime/src/mime_type.dart b/Chapter 7/serving_files/packages/mime/src/mime_type.dart new file mode 100644 index 0000000..744eead --- /dev/null +++ b/Chapter 7/serving_files/packages/mime/src/mime_type.dart @@ -0,0 +1,128 @@ +// Copyright (c) 2013, 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. + +library mime.mime_type; + +import 'default_extension_map.dart'; +import 'magic_number.dart'; + +final MimeTypeResolver _globalResolver = new MimeTypeResolver(); + +/** + * The maximum number of bytes needed, to match all default magic-numbers. + */ +int get defaultMagicNumbersMaxLength => _globalResolver.magicNumbersMaxLength; + +/** + * Extract the extension from [path] and use that for MIME-type lookup, using + * the default extension map. + * + * If no matching MIME-type was found, `null` is returned. + * + * If [headerBytes] is present, a match for known magic-numbers will be + * performed first. This allows the correct mime-type to be found, even though + * a file have been saved using the wrong file-name extension. If less than + * [defaultMagicNumbersMaxLength] bytes was provided, some magic-numbers won't + * be matched against. + */ +String lookupMimeType(String path, {List headerBytes}) => + _globalResolver.lookup(path, headerBytes: headerBytes); + +/** + * MIME-type resolver class, used to customize the lookup of mime-types. + */ +class MimeTypeResolver { + final Map _extensionMap = {}; + final List _magicNumbers = []; + final bool _useDefault; + int _magicNumbersMaxLength; + + /** + * Create a new empty [MimeTypeResolver]. + */ + MimeTypeResolver.empty() : _useDefault = false, _magicNumbersMaxLength = 0; + + /** + * Create a new [MimeTypeResolver] containing the default scope. + */ + MimeTypeResolver() : + _useDefault = true, + _magicNumbersMaxLength = DEFAULT_MAGIC_NUMBERS_MAX_LENGTH; + + /** + * Get the maximum number of bytes required to match all magic numbers, when + * performing [lookup] with headerBytes present. + */ + int get magicNumbersMaxLength => _magicNumbersMaxLength; + + /** + * Extract the extension from [path] and use that for MIME-type lookup. + * + * If no matching MIME-type was found, `null` is returned. + * + * If [headerBytes] is present, a match for known magic-numbers will be + * performed first. This allows the correct mime-type to be found, even though + * a file have been saved using the wrong file-name extension. If less than + * [magicNumbersMaxLength] bytes was provided, some magic-numbers won't + * be matched against. + */ + String lookup(String path, {List headerBytes}) { + String result; + if (headerBytes != null) { + result = _matchMagic(headerBytes, _magicNumbers); + if (result != null) return result; + if (_useDefault) { + result = _matchMagic(headerBytes, DEFAULT_MAGIC_NUMBERS); + if (result != null) return result; + } + } + var ext = _ext(path); + result = _extensionMap[ext]; + if (result != null) return result; + if (_useDefault) { + result = defaultExtensionMap[ext]; + if (result != null) return result; + } + return null; + } + + /** + * Add a new MIME-type mapping to the [MimeTypeResolver]. If the [extension] + * is already present in the [MimeTypeResolver], it'll be overwritten. + */ + void addExtension(String extension, String mimeType) { + _extensionMap[extension] = mimeType; + } + + /** + * Add a new magic-number mapping to the [MimeTypeResolver]. + * + * If [mask] is present,the [mask] is used to only perform matching on + * selective bits. The [mask] must have the same length as [bytes]. + */ + void addMagicNumber(List bytes, String mimeType, {List mask}) { + if (mask != null && bytes.length != mask.length) { + throw new ArgumentError('Bytes and mask are of different lengths'); + } + if (bytes.length > _magicNumbersMaxLength) { + _magicNumbersMaxLength = bytes.length; + } + _magicNumbers.add(new MagicNumber(mimeType, bytes, mask: mask)); + } + + static String _matchMagic(List headerBytes, + List magicNumbers) { + for (var mn in magicNumbers) { + if (mn.matches(headerBytes)) return mn.mimeType; + } + return null; + } + + static String _ext(String path) { + int index = path.lastIndexOf('.'); + if (index < 0 || index + 1 >= path.length) return path; + return path.substring(index + 1).toLowerCase(); + } +} + diff --git a/Chapter 7/serving_files/packages/path/path.dart b/Chapter 7/serving_files/packages/path/path.dart new file mode 100644 index 0000000..599a351 --- /dev/null +++ b/Chapter 7/serving_files/packages/path/path.dart @@ -0,0 +1,391 @@ +// Copyright (c) 2012, 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. + +/// A comprehensive, cross-platform path manipulation library. +/// +/// ## Installing ## +/// +/// Use [pub][] to install this package. Add the following to your +/// `pubspec.yaml` file. +/// +/// dependencies: +/// path: any +/// +/// Then run `pub install`. +/// +/// For more information, see the [path package on pub.dartlang.org][pkg]. +/// +/// [pub]: http://pub.dartlang.org +/// [pkg]: http://pub.dartlang.org/packages/path +/// +/// ## Usage ## +/// +/// The path library was designed to be imported with a prefix, though you don't +/// have to if you don't want to: +/// +/// import 'package:path/path.dart' as path; +/// +/// The most common way to use the library is through the top-level functions. +/// These manipulate path strings based on your current working directory and +/// the path style (POSIX, Windows, or URLs) of the host platform. For example: +/// +/// path.join("directory", "file.txt"); +/// +/// This calls the top-level [join] function to join "directory" and "file.txt" +/// using the current platform's directory separator. +/// +/// If you want to work with paths for a specific platform regardless of the +/// underlying platform that the program is running on, you can create a +/// [Context] and give it an explicit [Style]: +/// +/// var context = new path.Context(style: Style.windows); +/// context.join("directory", "file.txt"); +/// +/// This will join "directory" and "file.txt" using the Windows path separator, +/// even when the program is run on a POSIX machine. +library path; + +import 'src/context.dart'; +import 'src/style.dart'; + +export 'src/context.dart'; +export 'src/path_exception.dart'; +export 'src/style.dart'; + +/// A default context for manipulating POSIX paths. +final posix = new Context(style: Style.posix); + +/// A default context for manipulating Windows paths. +final windows = new Context(style: Style.windows); + +/// A default context for manipulating URLs. +final url = new Context(style: Style.url); + +/// The result of [Uri.base] last time the current working directory was +/// calculated. +/// +/// This is used to invalidate [_cachedContext] when the working directory has +/// changed since the last time a function was called. +Uri _lastBaseUri; + +/// An internal context for the current OS so we can provide a straight +/// functional interface and not require users to create one. +Context get _context { + if (_cachedContext != null && Uri.base == _lastBaseUri) return _cachedContext; + _lastBaseUri = Uri.base; + _cachedContext = new Context(); + return _cachedContext; +} +Context _cachedContext; + +/// Returns the [Style] of the current context. +/// +/// This is the style that all top-level path functions will use. +Style get style => _context.style; + +/// Gets the path to the current working directory. +/// +/// In the browser, this means the current URL, without the last file segment. +String get current { + var uri = Uri.base; + if (Style.platform == Style.url) { + return uri.resolve('.').toString(); + } else { + var path = uri.toFilePath(); + // Remove trailing '/' or '\'. + int lastIndex = path.length - 1; + assert(path[lastIndex] == '/' || path[lastIndex] == '\\'); + return path.substring(0, lastIndex); + } +} + +/// Gets the path separator for the current platform. This is `\` on Windows +/// and `/` on other platforms (including the browser). +String get separator => _context.separator; + +/// Creates a new path by appending the given path parts to [current]. +/// Equivalent to [join()] with [current] as the first argument. Example: +/// +/// path.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo' +String absolute(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7]) => + _context.absolute(part1, part2, part3, part4, part5, part6, part7); + +/// Gets the part of [path] after the last separator. +/// +/// path.basename('path/to/foo.dart'); // -> 'foo.dart' +/// path.basename('path/to'); // -> 'to' +/// +/// Trailing separators are ignored. +/// +/// path.basename('path/to/'); // -> 'to' +String basename(String path) => _context.basename(path); + +/// Gets the part of [path] after the last separator, and without any trailing +/// file extension. +/// +/// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' +/// +/// Trailing separators are ignored. +/// +/// path.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' +String basenameWithoutExtension(String path) => + _context.basenameWithoutExtension(path); + +/// Gets the part of [path] before the last separator. +/// +/// path.dirname('path/to/foo.dart'); // -> 'path/to' +/// path.dirname('path/to'); // -> 'path' +/// +/// Trailing separators are ignored. +/// +/// path.dirname('path/to/'); // -> 'path' +/// +/// If an absolute path contains no directories, only a root, then the root +/// is returned. +/// +/// path.dirname('/'); // -> '/' (posix) +/// path.dirname('c:\'); // -> 'c:\' (windows) +/// +/// If a relative path has no directories, then '.' is returned. +/// +/// path.dirname('foo'); // -> '.' +/// path.dirname(''); // -> '.' +String dirname(String path) => _context.dirname(path); + +/// Gets the file extension of [path]: the portion of [basename] from the last +/// `.` to the end (including the `.` itself). +/// +/// path.extension('path/to/foo.dart'); // -> '.dart' +/// path.extension('path/to/foo'); // -> '' +/// path.extension('path.to/foo'); // -> '' +/// path.extension('path/to/foo.dart.js'); // -> '.js' +/// +/// If the file name starts with a `.`, then that is not considered the +/// extension: +/// +/// path.extension('~/.bashrc'); // -> '' +/// path.extension('~/.notes.txt'); // -> '.txt' +String extension(String path) => _context.extension(path); + +// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. +/// Returns the root of [path], if it's absolute, or the empty string if it's +/// relative. +/// +/// // Unix +/// path.rootPrefix('path/to/foo'); // -> '' +/// path.rootPrefix('/path/to/foo'); // -> '/' +/// +/// // Windows +/// path.rootPrefix(r'path\to\foo'); // -> '' +/// path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' +/// +/// // URL +/// path.rootPrefix('path/to/foo'); // -> '' +/// path.rootPrefix('http://dartlang.org/path/to/foo'); +/// // -> 'http://dartlang.org' +String rootPrefix(String path) => _context.rootPrefix(path); + +/// Returns `true` if [path] is an absolute path and `false` if it is a +/// relative path. +/// +/// On POSIX systems, absolute paths start with a `/` (forward slash). On +/// Windows, an absolute path starts with `\\`, or a drive letter followed by +/// `:/` or `:\`. For URLs, absolute paths either start with a protocol and +/// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. +/// +/// URLs that start with `/` are known as "root-relative", since they're +/// relative to the root of the current URL. Since root-relative paths are still +/// absolute in every other sense, [isAbsolute] will return true for them. They +/// can be detected using [isRootRelative]. +bool isAbsolute(String path) => _context.isAbsolute(path); + +/// Returns `true` if [path] is a relative path and `false` if it is absolute. +/// On POSIX systems, absolute paths start with a `/` (forward slash). On +/// Windows, an absolute path starts with `\\`, or a drive letter followed by +/// `:/` or `:\`. +bool isRelative(String path) => _context.isRelative(path); + +/// Returns `true` if [path] is a root-relative path and `false` if it's not. +/// +/// URLs that start with `/` are known as "root-relative", since they're +/// relative to the root of the current URL. Since root-relative paths are still +/// absolute in every other sense, [isAbsolute] will return true for them. They +/// can be detected using [isRootRelative]. +/// +/// No POSIX and Windows paths are root-relative. +bool isRootRelative(String path) => _context.isRootRelative(path); + +/// Joins the given path parts into a single path using the current platform's +/// [separator]. Example: +/// +/// path.join('path', 'to', 'foo'); // -> 'path/to/foo' +/// +/// If any part ends in a path separator, then a redundant separator will not +/// be added: +/// +/// path.join('path/', 'to', 'foo'); // -> 'path/to/foo +/// +/// If a part is an absolute path, then anything before that will be ignored: +/// +/// path.join('path', '/to', 'foo'); // -> '/to/foo' +String join(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7, String part8]) => + _context.join(part1, part2, part3, part4, part5, part6, part7, part8); + +/// Joins the given path parts into a single path using the current platform's +/// [separator]. Example: +/// +/// path.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' +/// +/// If any part ends in a path separator, then a redundant separator will not +/// be added: +/// +/// path.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo +/// +/// If a part is an absolute path, then anything before that will be ignored: +/// +/// path.joinAll(['path', '/to', 'foo']); // -> '/to/foo' +/// +/// For a fixed number of parts, [join] is usually terser. +String joinAll(Iterable parts) => _context.joinAll(parts); + +// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. +/// Splits [path] into its components using the current platform's [separator]. +/// +/// path.split('path/to/foo'); // -> ['path', 'to', 'foo'] +/// +/// The path will *not* be normalized before splitting. +/// +/// path.split('path/../foo'); // -> ['path', '..', 'foo'] +/// +/// If [path] is absolute, the root directory will be the first element in the +/// array. Example: +/// +/// // Unix +/// path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] +/// +/// // Windows +/// path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] +/// +/// // Browser +/// path.split('http://dartlang.org/path/to/foo'); +/// // -> ['http://dartlang.org', 'path', 'to', 'foo'] +List split(String path) => _context.split(path); + +/// Normalizes [path], simplifying it by handling `..`, and `.`, and +/// removing redundant path separators whenever possible. +/// +/// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' +String normalize(String path) => _context.normalize(path); + +/// Attempts to convert [path] to an equivalent relative path from the current +/// directory. +/// +/// // Given current directory is /root/path: +/// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart' +/// path.relative('/root/other.dart'); // -> '../other.dart' +/// +/// If the [from] argument is passed, [path] is made relative to that instead. +/// +/// path.relative('/root/path/a/b.dart', +/// from: '/root/path'); // -> 'a/b.dart' +/// path.relative('/root/other.dart', +/// from: '/root/path'); // -> '../other.dart' +/// +/// If [path] and/or [from] are relative paths, they are assumed to be relative +/// to the current directory. +/// +/// Since there is no relative path from one drive letter to another on Windows, +/// or from one hostname to another for URLs, this will return an absolute path +/// in those cases. +/// +/// // Windows +/// path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' +/// +/// // URL +/// path.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); +/// // -> 'http://dartlang.org' +String relative(String path, {String from}) => + _context.relative(path, from: from); + +/// Returns `true` if [child] is a path beneath `parent`, and `false` otherwise. +/// +/// path.isWithin('/root/path', '/root/path/a'); // -> true +/// path.isWithin('/root/path', '/root/other'); // -> false +/// path.isWithin('/root/path', '/root/path') // -> false +bool isWithin(String parent, String child) => _context.isWithin(parent, child); + +/// Removes a trailing extension from the last part of [path]. +/// +/// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' +String withoutExtension(String path) => _context.withoutExtension(path); + +/// Returns the path represented by [uri], which may be a [String] or a [Uri]. +/// +/// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL +/// style, this will just convert [uri] to a string. +/// +/// // POSIX +/// context.fromUri('file:///path/to/foo') +/// // -> '/path/to/foo' +/// +/// // Windows +/// context.fromUri('file:///C:/path/to/foo') +/// // -> r'C:\path\to\foo' +/// +/// // URL +/// context.fromUri('http://dartlang.org/path/to/foo') +/// // -> 'http://dartlang.org/path/to/foo' +/// +/// If [uri] is relative, a relative path will be returned. +/// +/// path.fromUri('path/to/foo'); // -> 'path/to/foo' +String fromUri(uri) => _context.fromUri(uri); + +/// Returns the URI that represents [path]. +/// +/// For POSIX and Windows styles, this will return a `file:` URI. For the URL +/// style, this will just convert [path] to a [Uri]. +/// +/// // POSIX +/// path.toUri('/path/to/foo') +/// // -> Uri.parse('file:///path/to/foo') +/// +/// // Windows +/// path.toUri(r'C:\path\to\foo') +/// // -> Uri.parse('file:///C:/path/to/foo') +/// +/// // URL +/// path.toUri('http://dartlang.org/path/to/foo') +/// // -> Uri.parse('http://dartlang.org/path/to/foo') +/// +/// If [path] is relative, a relative URI will be returned. +/// +/// path.toUri('path/to/foo') +/// // -> Uri.parse('path/to/foo') +Uri toUri(String path) => _context.toUri(path); + +/// Returns a terse, human-readable representation of [uri]. +/// +/// [uri] can be a [String] or a [Uri]. If it can be made relative to the +/// current working directory, that's done. Otherwise, it's returned as-is. This +/// gracefully handles non-`file:` URIs for [Style.posix] and [Style.windows]. +/// +/// The returned value is meant for human consumption, and may be either URI- +/// or path-formatted. +/// +/// // POSIX at "/root/path" +/// path.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart' +/// path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' +/// +/// // Windows at "C:\root\path" +/// path.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart' +/// path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' +/// +/// // URL at "http://dartlang.org/root/path" +/// path.prettyUri('http://dartlang.org/root/path/a/b.dart'); +/// // -> r'a/b.dart' +/// path.prettyUri('file:///root/path'); // -> 'file:///root/path' +String prettyUri(uri) => _context.prettyUri(uri); diff --git a/Chapter 7/serving_files/packages/path/src/characters.dart b/Chapter 7/serving_files/packages/path/src/characters.dart new file mode 100644 index 0000000..ff196a6 --- /dev/null +++ b/Chapter 7/serving_files/packages/path/src/characters.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2014, 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. + +/// This library contains character-code definitions. +library path.characters; + +const PLUS = 0x2b; +const MINUS = 0x2d; +const PERIOD = 0x2e; +const SLASH = 0x2f; +const ZERO = 0x30; +const NINE = 0x39; +const COLON = 0x3a; +const UPPER_A = 0x41; +const UPPER_Z = 0x5a; +const LOWER_A = 0x61; +const LOWER_Z = 0x7a; +const BACKSLASH = 0x5c; diff --git a/Chapter 7/serving_files/packages/path/src/context.dart b/Chapter 7/serving_files/packages/path/src/context.dart new file mode 100644 index 0000000..823e0c2 --- /dev/null +++ b/Chapter 7/serving_files/packages/path/src/context.dart @@ -0,0 +1,547 @@ +// Copyright (c) 2013, 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. + +library path.context; + +import 'internal_style.dart'; +import 'style.dart'; +import 'parsed_path.dart'; +import 'path_exception.dart'; +import '../path.dart' as p; + +/// An instantiable class for manipulating paths. Unlike the top-level +/// functions, this lets you explicitly select what platform the paths will use. +class Context { + /// Creates a new path context for the given style and current directory. + /// + /// If [style] is omitted, it uses the host operating system's path style. If + /// only [current] is omitted, it defaults ".". If *both* [style] and + /// [current] are omitted, [current] defaults to the real current working + /// directory. + /// + /// On the browser, [style] defaults to [Style.url] and [current] defaults to + /// the current URL. + factory Context({Style style, String current}) { + if (current == null) { + if (style == null) { + current = p.current; + } else { + current = "."; + } + } + + if (style == null) { + style = Style.platform; + } else if (style is! InternalStyle) { + throw new ArgumentError("Only styles defined by the path package are " + "allowed."); + } + + return new Context._(style, current); + } + + Context._(this.style, this.current); + + /// The style of path that this context works with. + final InternalStyle style; + + /// The current directory that relative paths will be relative to. + final String current; + + /// Gets the path separator for the context's [style]. On Mac and Linux, + /// this is `/`. On Windows, it's `\`. + String get separator => style.separator; + + /// Creates a new path by appending the given path parts to [current]. + /// Equivalent to [join()] with [current] as the first argument. Example: + /// + /// var context = new Context(current: '/root'); + /// context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo' + /// + /// If [current] isn't absolute, this won't return an absolute path. + String absolute(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7]) { + return join(current, part1, part2, part3, part4, part5, part6, part7); + } + + /// Gets the part of [path] after the last separator on the context's + /// platform. + /// + /// context.basename('path/to/foo.dart'); // -> 'foo.dart' + /// context.basename('path/to'); // -> 'to' + /// + /// Trailing separators are ignored. + /// + /// context.basename('path/to/'); // -> 'to' + String basename(String path) => _parse(path).basename; + + /// Gets the part of [path] after the last separator on the context's + /// platform, and without any trailing file extension. + /// + /// context.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' + /// + /// Trailing separators are ignored. + /// + /// context.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' + String basenameWithoutExtension(String path) => + _parse(path).basenameWithoutExtension; + + /// Gets the part of [path] before the last separator. + /// + /// context.dirname('path/to/foo.dart'); // -> 'path/to' + /// context.dirname('path/to'); // -> 'path' + /// + /// Trailing separators are ignored. + /// + /// context.dirname('path/to/'); // -> 'path' + String dirname(String path) { + var parsed = _parse(path); + parsed.removeTrailingSeparators(); + if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; + if (parsed.parts.length == 1) { + return parsed.root == null ? '.' : parsed.root; + } + parsed.parts.removeLast(); + parsed.separators.removeLast(); + parsed.removeTrailingSeparators(); + return parsed.toString(); + } + + /// Gets the file extension of [path]: the portion of [basename] from the last + /// `.` to the end (including the `.` itself). + /// + /// context.extension('path/to/foo.dart'); // -> '.dart' + /// context.extension('path/to/foo'); // -> '' + /// context.extension('path.to/foo'); // -> '' + /// context.extension('path/to/foo.dart.js'); // -> '.js' + /// + /// If the file name starts with a `.`, then it is not considered an + /// extension: + /// + /// context.extension('~/.bashrc'); // -> '' + /// context.extension('~/.notes.txt'); // -> '.txt' + String extension(String path) => _parse(path).extension; + + // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. + /// Returns the root of [path] if it's absolute, or an empty string if it's + /// relative. + /// + /// // Unix + /// context.rootPrefix('path/to/foo'); // -> '' + /// context.rootPrefix('/path/to/foo'); // -> '/' + /// + /// // Windows + /// context.rootPrefix(r'path\to\foo'); // -> '' + /// context.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' + /// + /// // URL + /// context.rootPrefix('path/to/foo'); // -> '' + /// context.rootPrefix('http://dartlang.org/path/to/foo'); + /// // -> 'http://dartlang.org' + String rootPrefix(String path) { + var root = _parse(path).root; + return root == null ? '' : root; + } + + /// Returns `true` if [path] is an absolute path and `false` if it is a + /// relative path. + /// + /// On POSIX systems, absolute paths start with a `/` (forward slash). On + /// Windows, an absolute path starts with `\\`, or a drive letter followed by + /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and + /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. + /// + /// URLs that start with `/` are known as "root-relative", since they're + /// relative to the root of the current URL. Since root-relative paths are + /// still absolute in every other sense, [isAbsolute] will return true for + /// them. They can be detected using [isRootRelative]. + bool isAbsolute(String path) => _parse(path).isAbsolute; + + /// Returns `true` if [path] is a relative path and `false` if it is absolute. + /// On POSIX systems, absolute paths start with a `/` (forward slash). On + /// Windows, an absolute path starts with `\\`, or a drive letter followed by + /// `:/` or `:\`. + bool isRelative(String path) => !this.isAbsolute(path); + + /// Returns `true` if [path] is a root-relative path and `false` if it's not. + /// + /// URLs that start with `/` are known as "root-relative", since they're + /// relative to the root of the current URL. Since root-relative paths are + /// still absolute in every other sense, [isAbsolute] will return true for + /// them. They can be detected using [isRootRelative]. + /// + /// No POSIX and Windows paths are root-relative. + bool isRootRelative(String path) => _parse(path).isRootRelative; + + /// Joins the given path parts into a single path. Example: + /// + /// context.join('path', 'to', 'foo'); // -> 'path/to/foo' + /// + /// If any part ends in a path separator, then a redundant separator will not + /// be added: + /// + /// context.join('path/', 'to', 'foo'); // -> 'path/to/foo + /// + /// If a part is an absolute path, then anything before that will be ignored: + /// + /// context.join('path', '/to', 'foo'); // -> '/to/foo' + /// + String join(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7, String part8]) { + var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; + _validateArgList("join", parts); + return joinAll(parts.where((part) => part != null)); + } + + /// Joins the given path parts into a single path. Example: + /// + /// context.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' + /// + /// If any part ends in a path separator, then a redundant separator will not + /// be added: + /// + /// context.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo + /// + /// If a part is an absolute path, then anything before that will be ignored: + /// + /// context.joinAll(['path', '/to', 'foo']); // -> '/to/foo' + /// + /// For a fixed number of parts, [join] is usually terser. + String joinAll(Iterable parts) { + var buffer = new StringBuffer(); + var needsSeparator = false; + var isAbsoluteAndNotRootRelative = false; + + for (var part in parts.where((part) => part != '')) { + if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) { + // If the new part is root-relative, it preserves the previous root but + // replaces the path after it. + var parsed = _parse(part); + parsed.root = this.rootPrefix(buffer.toString()); + if (style.needsSeparator(parsed.root)) { + parsed.separators[0] = style.separator; + } + buffer.clear(); + buffer.write(parsed.toString()); + } else if (this.isAbsolute(part)) { + isAbsoluteAndNotRootRelative = !this.isRootRelative(part); + // An absolute path discards everything before it. + buffer.clear(); + buffer.write(part); + } else { + if (part.length > 0 && style.containsSeparator(part[0])) { + // The part starts with a separator, so we don't need to add one. + } else if (needsSeparator) { + buffer.write(separator); + } + + buffer.write(part); + } + + // Unless this part ends with a separator, we'll need to add one before + // the next part. + needsSeparator = style.needsSeparator(part); + } + + return buffer.toString(); + } + + // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. + /// Splits [path] into its components using the current platform's + /// [separator]. Example: + /// + /// context.split('path/to/foo'); // -> ['path', 'to', 'foo'] + /// + /// The path will *not* be normalized before splitting. + /// + /// context.split('path/../foo'); // -> ['path', '..', 'foo'] + /// + /// If [path] is absolute, the root directory will be the first element in the + /// array. Example: + /// + /// // Unix + /// context.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] + /// + /// // Windows + /// context.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] + List split(String path) { + var parsed = _parse(path); + // Filter out empty parts that exist due to multiple separators in a row. + parsed.parts = parsed.parts.where((part) => !part.isEmpty) + .toList(); + if (parsed.root != null) parsed.parts.insert(0, parsed.root); + return parsed.parts; + } + + /// Normalizes [path], simplifying it by handling `..`, and `.`, and + /// removing redundant path separators whenever possible. + /// + /// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt' + String normalize(String path) { + var parsed = _parse(path); + parsed.normalize(); + return parsed.toString(); + } + + /// Attempts to convert [path] to an equivalent relative path relative to + /// [root]. + /// + /// var context = new Context(current: '/root/path'); + /// context.relative('/root/path/a/b.dart'); // -> 'a/b.dart' + /// context.relative('/root/other.dart'); // -> '../other.dart' + /// + /// If the [from] argument is passed, [path] is made relative to that instead. + /// + /// context.relative('/root/path/a/b.dart', + /// from: '/root/path'); // -> 'a/b.dart' + /// context.relative('/root/other.dart', + /// from: '/root/path'); // -> '../other.dart' + /// + /// If [path] and/or [from] are relative paths, they are assumed to be + /// relative to [current]. + /// + /// Since there is no relative path from one drive letter to another on + /// Windows, this will return an absolute path in that case. + /// + /// context.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other' + /// + /// This will also return an absolute path if an absolute [path] is passed to + /// a context with a relative path for [current]. + /// + /// var context = new Context(r'some/relative/path'); + /// context.relative(r'/absolute/path'); // -> '/absolute/path' + /// + /// If [root] is relative, it may be impossible to determine a path from + /// [from] to [path]. For example, if [root] and [path] are "." and [from] is + /// "/", no path can be determined. In this case, a [PathException] will be + /// thrown. + String relative(String path, {String from}) { + from = from == null ? current : this.join(current, from); + + // We can't determine the path from a relative path to an absolute path. + if (this.isRelative(from) && this.isAbsolute(path)) { + return this.normalize(path); + } + + // If the given path is relative, resolve it relative to the context's + // current directory. + if (this.isRelative(path) || this.isRootRelative(path)) { + path = this.absolute(path); + } + + // If the path is still relative and `from` is absolute, we're unable to + // find a path from `from` to `path`. + if (this.isRelative(path) && this.isAbsolute(from)) { + throw new PathException('Unable to find a path to "$path" from "$from".'); + } + + var fromParsed = _parse(from)..normalize(); + var pathParsed = _parse(path)..normalize(); + + if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') { + return pathParsed.toString(); + } + + // If the root prefixes don't match (for example, different drive letters + // on Windows), then there is no relative path, so just return the absolute + // one. In Windows, drive letters are case-insenstive and we allow + // calculation of relative paths, even if a path has not been normalized. + if (fromParsed.root != pathParsed.root && + ((fromParsed.root == null || pathParsed.root == null) || + fromParsed.root.toLowerCase().replaceAll('/', '\\') != + pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { + return pathParsed.toString(); + } + + // Strip off their common prefix. + while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && + fromParsed.parts[0] == pathParsed.parts[0]) { + fromParsed.parts.removeAt(0); + fromParsed.separators.removeAt(1); + pathParsed.parts.removeAt(0); + pathParsed.separators.removeAt(1); + } + + // If there are any directories left in the from path, we need to walk up + // out of them. If a directory left in the from path is '..', it cannot + // be cancelled by adding a '..'. + if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { + throw new PathException('Unable to find a path to "$path" from "$from".'); + } + pathParsed.parts.insertAll(0, + new List.filled(fromParsed.parts.length, '..')); + pathParsed.separators[0] = ''; + pathParsed.separators.insertAll(1, + new List.filled(fromParsed.parts.length, style.separator)); + + // Corner case: the paths completely collapsed. + if (pathParsed.parts.length == 0) return '.'; + + // Corner case: path was '.' and some '..' directories were added in front. + // Don't add a final '/.' in that case. + if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') { + pathParsed.parts.removeLast(); + pathParsed.separators..removeLast()..removeLast()..add(''); + } + + // Make it relative. + pathParsed.root = ''; + pathParsed.removeTrailingSeparators(); + + return pathParsed.toString(); + } + + /// Returns `true` if [child] is a path beneath `parent`, and `false` + /// otherwise. + /// + /// path.isWithin('/root/path', '/root/path/a'); // -> true + /// path.isWithin('/root/path', '/root/other'); // -> false + /// path.isWithin('/root/path', '/root/path'); // -> false + bool isWithin(String parent, String child) { + var relative; + try { + relative = this.relative(child, from: parent); + } on PathException catch (_) { + // If no relative path from [parent] to [child] is found, [child] + // definitely isn't a child of [parent]. + return false; + } + + var parts = this.split(relative); + return this.isRelative(relative) && parts.first != '..' && + parts.first != '.'; + } + + /// Removes a trailing extension from the last part of [path]. + /// + /// context.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' + String withoutExtension(String path) { + var parsed = _parse(path); + + for (var i = parsed.parts.length - 1; i >= 0; i--) { + if (!parsed.parts[i].isEmpty) { + parsed.parts[i] = parsed.basenameWithoutExtension; + break; + } + } + + return parsed.toString(); + } + + /// Returns the path represented by [uri], which may be a [String] or a [Uri]. + /// + /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL + /// style, this will just convert [uri] to a string. + /// + /// // POSIX + /// context.fromUri('file:///path/to/foo') + /// // -> '/path/to/foo' + /// + /// // Windows + /// context.fromUri('file:///C:/path/to/foo') + /// // -> r'C:\path\to\foo' + /// + /// // URL + /// context.fromUri('http://dartlang.org/path/to/foo') + /// // -> 'http://dartlang.org/path/to/foo' + /// + /// If [uri] is relative, a relative path will be returned. + /// + /// path.fromUri('path/to/foo'); // -> 'path/to/foo' + String fromUri(uri) { + if (uri is String) uri = Uri.parse(uri); + return style.pathFromUri(uri); + } + + /// Returns the URI that represents [path]. + /// + /// For POSIX and Windows styles, this will return a `file:` URI. For the URL + /// style, this will just convert [path] to a [Uri]. + /// + /// // POSIX + /// context.toUri('/path/to/foo') + /// // -> Uri.parse('file:///path/to/foo') + /// + /// // Windows + /// context.toUri(r'C:\path\to\foo') + /// // -> Uri.parse('file:///C:/path/to/foo') + /// + /// // URL + /// context.toUri('http://dartlang.org/path/to/foo') + /// // -> Uri.parse('http://dartlang.org/path/to/foo') + Uri toUri(String path) { + if (isRelative(path)) { + return style.relativePathToUri(path); + } else { + return style.absolutePathToUri(join(current, path)); + } + } + + /// Returns a terse, human-readable representation of [uri]. + /// + /// [uri] can be a [String] or a [Uri]. If it can be made relative to the + /// current working directory, that's done. Otherwise, it's returned as-is. + /// This gracefully handles non-`file:` URIs for [Style.posix] and + /// [Style.windows]. + /// + /// The returned value is meant for human consumption, and may be either URI- + /// or path-formatted. + /// + /// // POSIX + /// var context = new Context(current: '/root/path'); + /// context.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart' + /// context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' + /// + /// // Windows + /// var context = new Context(current: r'C:\root\path'); + /// context.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart' + /// context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' + /// + /// // URL + /// var context = new Context(current: 'http://dartlang.org/root/path'); + /// context.prettyUri('http://dartlang.org/root/path/a/b.dart'); + /// // -> r'a/b.dart' + /// context.prettyUri('file:///root/path'); // -> 'file:///root/path' + String prettyUri(uri) { + if (uri is String) uri = Uri.parse(uri); + if (uri.scheme == 'file' && style == Style.url) return uri.toString(); + if (uri.scheme != 'file' && uri.scheme != '' && style != Style.url) { + return uri.toString(); + } + + var path = normalize(fromUri(uri)); + var rel = relative(path); + var components = split(rel); + + // Only return a relative path if it's actually shorter than the absolute + // path. This avoids ugly things like long "../" chains to get to the root + // and then go back down. + return split(rel).length > split(path).length ? path : rel; + } + + ParsedPath _parse(String path) => new ParsedPath.parse(path, style); +} + +/// Validates that there are no non-null arguments following a null one and +/// throws an appropriate [ArgumentError] on failure. +_validateArgList(String method, List args) { + for (var i = 1; i < args.length; i++) { + // Ignore nulls hanging off the end. + if (args[i] == null || args[i - 1] != null) continue; + + var numArgs; + for (numArgs = args.length; numArgs >= 1; numArgs--) { + if (args[numArgs - 1] != null) break; + } + + // Show the arguments. + var message = new StringBuffer(); + message.write("$method("); + message.write(args.take(numArgs) + .map((arg) => arg == null ? "null" : '"$arg"') + .join(", ")); + message.write("): part ${i - 1} was null, but part $i was not."); + throw new ArgumentError(message.toString()); + } +} diff --git a/Chapter 7/serving_files/packages/path/src/internal_style.dart b/Chapter 7/serving_files/packages/path/src/internal_style.dart new file mode 100644 index 0000000..67b5d34 --- /dev/null +++ b/Chapter 7/serving_files/packages/path/src/internal_style.dart @@ -0,0 +1,54 @@ +// Copyright (c) 2014, 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. + +library path.internal_style; + +import 'context.dart'; +import 'style.dart'; + +/// The internal interface for the [Style] type. +/// +/// Users should be able to pass around instances of [Style] like an enum, but +/// the members that [Context] uses should be hidden from them. Those members +/// are defined on this class instead. +abstract class InternalStyle extends Style { + /// The default path separator for this style. + /// + /// On POSIX, this is `/`. On Windows, it's `\`. + String get separator; + + /// Returns whether [path] contains a separator. + bool containsSeparator(String path); + + /// Returns whether [codeUnit] is the character code of a separator. + bool isSeparator(int codeUnit); + + /// Returns whether this path component needs a separator after it. + /// + /// Windows and POSIX styles just need separators when the previous component + /// doesn't already end in a separator, but the URL always needs to place a + /// separator between the root and the first component, even if the root + /// already ends in a separator character. For example, to join "file://" and + /// "usr", an additional "/" is needed (making "file:///usr"). + bool needsSeparator(String path); + + /// Gets the root prefix of [path] if path is absolute. If [path] is relative, + /// returns `null`. + String getRoot(String path); + + /// Gets the root prefix of [path] if it's root-relative. + /// + /// If [path] is relative or absolute and not root-relative, returns `null`. + String getRelativeRoot(String path); + + /// Returns the path represented by [uri] in this style. + String pathFromUri(Uri uri); + + /// Returns the URI that represents the relative path made of [parts]. + Uri relativePathToUri(String path) => + new Uri(pathSegments: context.split(path)); + + /// Returns the URI that represents [path], which is assumed to be absolute. + Uri absolutePathToUri(String path); +} diff --git a/Chapter 7/serving_files/packages/path/src/parsed_path.dart b/Chapter 7/serving_files/packages/path/src/parsed_path.dart new file mode 100644 index 0000000..57773ee --- /dev/null +++ b/Chapter 7/serving_files/packages/path/src/parsed_path.dart @@ -0,0 +1,187 @@ +// Copyright (c) 2013, 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. + +library path.parsed_path; + +import 'internal_style.dart'; +import 'style.dart'; + +class ParsedPath { + /// The [InternalStyle] that was used to parse this path. + InternalStyle style; + + /// The absolute root portion of the path, or `null` if the path is relative. + /// On POSIX systems, this will be `null` or "/". On Windows, it can be + /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive + /// letters. + String root; + + /// Whether this path is root-relative. + /// + /// See [Context.isRootRelative]. + bool isRootRelative; + + /// The path-separated parts of the path. All but the last will be + /// directories. + List parts; + + /// The path separators preceding each part. + /// + /// The first one will be an empty string unless the root requires a separator + /// between it and the path. The last one will be an empty string unless the + /// path ends with a trailing separator. + List separators; + + /// The file extension of the last non-empty part, or "" if it doesn't have + /// one. + String get extension => _splitExtension()[1]; + + /// `true` if this is an absolute path. + bool get isAbsolute => root != null; + + factory ParsedPath.parse(String path, InternalStyle style) { + var before = path; + + // Remove the root prefix, if any. + var root = style.getRoot(path); + var isRootRelative = style.getRelativeRoot(path) != null; + if (root != null) path = path.substring(root.length); + + // Split the parts on path separators. + var parts = []; + var separators = []; + + var start = 0; + + if (path.isNotEmpty && style.isSeparator(path.codeUnitAt(0))) { + separators.add(path[0]); + start = 1; + } else { + separators.add(''); + } + + for (var i = start; i < path.length; i++) { + if (style.isSeparator(path.codeUnitAt(i))) { + parts.add(path.substring(start, i)); + separators.add(path[i]); + start = i + 1; + } + } + + // Add the final part, if any. + if (start < path.length) { + parts.add(path.substring(start)); + separators.add(''); + } + + return new ParsedPath._(style, root, isRootRelative, parts, separators); + } + + ParsedPath._(this.style, this.root, this.isRootRelative, this.parts, + this.separators); + + String get basename { + var copy = this.clone(); + copy.removeTrailingSeparators(); + if (copy.parts.isEmpty) return root == null ? '' : root; + return copy.parts.last; + } + + String get basenameWithoutExtension => _splitExtension()[0]; + + bool get hasTrailingSeparator => + !parts.isEmpty && (parts.last == '' || separators.last != ''); + + void removeTrailingSeparators() { + while (!parts.isEmpty && parts.last == '') { + parts.removeLast(); + separators.removeLast(); + } + if (separators.length > 0) separators[separators.length - 1] = ''; + } + + void normalize() { + // Handle '.', '..', and empty parts. + var leadingDoubles = 0; + var newParts = []; + for (var part in parts) { + if (part == '.' || part == '') { + // Do nothing. Ignore it. + } else if (part == '..') { + // Pop the last part off. + if (newParts.length > 0) { + newParts.removeLast(); + } else { + // Backed out past the beginning, so preserve the "..". + leadingDoubles++; + } + } else { + newParts.add(part); + } + } + + // A relative path can back out from the start directory. + if (!isAbsolute) { + newParts.insertAll(0, new List.filled(leadingDoubles, '..')); + } + + // If we collapsed down to nothing, do ".". + if (newParts.length == 0 && !isAbsolute) { + newParts.add('.'); + } + + // Canonicalize separators. + var newSeparators = new List.generate( + newParts.length, (_) => style.separator, growable: true); + newSeparators.insert(0, + isAbsolute && newParts.length > 0 && style.needsSeparator(root) ? + style.separator : ''); + + parts = newParts; + separators = newSeparators; + + // Normalize the Windows root if needed. + if (root != null && style == Style.windows) { + root = root.replaceAll('/', '\\'); + } + removeTrailingSeparators(); + } + + String toString() { + var builder = new StringBuffer(); + if (root != null) builder.write(root); + for (var i = 0; i < parts.length; i++) { + builder.write(separators[i]); + builder.write(parts[i]); + } + builder.write(separators.last); + + return builder.toString(); + } + + /// Splits the last non-empty part of the path into a `[basename, extension`] + /// pair. + /// + /// Returns a two-element list. The first is the name of the file without any + /// extension. The second is the extension or "" if it has none. + List _splitExtension() { + var file = parts.lastWhere((p) => p != '', orElse: () => null); + + if (file == null) return ['', '']; + if (file == '..') return ['..', '']; + + var lastDot = file.lastIndexOf('.'); + + // If there is no dot, or it's the first character, like '.bashrc', it + // doesn't count. + if (lastDot <= 0) return [file, '']; + + return [file.substring(0, lastDot), file.substring(lastDot)]; + } + + ParsedPath clone() => new ParsedPath._( + style, root, isRootRelative, + new List.from(parts), new List.from(separators)); +} + diff --git a/Chapter 7/serving_files/packages/path/src/path_exception.dart b/Chapter 7/serving_files/packages/path/src/path_exception.dart new file mode 100644 index 0000000..49bd268 --- /dev/null +++ b/Chapter 7/serving_files/packages/path/src/path_exception.dart @@ -0,0 +1,15 @@ +// Copyright (c) 2013, 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. + +library path.path_exception; + +/// An exception class that's thrown when a path operation is unable to be +/// computed accurately. +class PathException implements Exception { + String message; + + PathException(this.message); + + String toString() => "PathException: $message"; +} diff --git a/Chapter 7/serving_files/packages/path/src/style.dart b/Chapter 7/serving_files/packages/path/src/style.dart new file mode 100644 index 0000000..a94ec68 --- /dev/null +++ b/Chapter 7/serving_files/packages/path/src/style.dart @@ -0,0 +1,88 @@ +// Copyright (c) 2013, 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. + +library path.style; + +import 'context.dart'; +import 'style/posix.dart'; +import 'style/url.dart'; +import 'style/windows.dart'; + +/// An enum type describing a "flavor" of path. +abstract class Style { + /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths + /// start with "/". Used by UNIX, Linux, Mac OS X, and others. + static final posix = new PosixStyle(); + + /// Windows paths use "\" (backslash) as separators. Absolute paths start with + /// a drive letter followed by a colon (example, "C:") or two backslashes + /// ("\\") for UNC paths. + // TODO(rnystrom): The UNC root prefix should include the drive name too, not + // just the "\\". + static final windows = new WindowsStyle(); + + /// URLs aren't filesystem paths, but they're supported to make it easier to + /// manipulate URL paths in the browser. + /// + /// URLs use "/" (forward slash) as separators. Absolute paths either start + /// with a protocol and optional hostname (e.g. `http://dartlang.org`, + /// `file://`) or with "/". + static final url = new UrlStyle(); + + /// The style of the host platform. + /// + /// When running on the command line, this will be [windows] or [posix] based + /// on the host operating system. On a browser, this will be [url]. + static final platform = _getPlatformStyle(); + + /// Gets the type of the host platform. + static Style _getPlatformStyle() { + // If we're running a Dart file in the browser from a `file:` URI, + // [Uri.base] will point to a file. If we're running on the standalone, + // it will point to a directory. We can use that fact to determine which + // style to use. + if (Uri.base.scheme != 'file') return Style.url; + if (!Uri.base.path.endsWith('/')) return Style.url; + if (new Uri(path: 'a/b').toFilePath() == 'a\\b') return Style.windows; + return Style.posix; + } + + /// The name of this path style. Will be "posix" or "windows". + String get name; + + /// A [Context] that uses this style. + Context get context => new Context(style: this); + + @Deprecated("Most Style members will be removed in path 2.0.") + String get separator; + + @Deprecated("Most Style members will be removed in path 2.0.") + Pattern get separatorPattern; + + @Deprecated("Most Style members will be removed in path 2.0.") + Pattern get needsSeparatorPattern; + + @Deprecated("Most Style members will be removed in path 2.0.") + Pattern get rootPattern; + + @Deprecated("Most Style members will be removed in path 2.0.") + Pattern get relativeRootPattern; + + @Deprecated("Most style members will be removed in path 2.0.") + String getRoot(String path); + + @Deprecated("Most style members will be removed in path 2.0.") + String getRelativeRoot(String path); + + @Deprecated("Most style members will be removed in path 2.0.") + String pathFromUri(Uri uri); + + @Deprecated("Most style members will be removed in path 2.0.") + Uri relativePathToUri(String path); + + @Deprecated("Most style members will be removed in path 2.0.") + Uri absolutePathToUri(String path); + + String toString() => name; +} diff --git a/Chapter 7/serving_files/packages/path/src/style/posix.dart b/Chapter 7/serving_files/packages/path/src/style/posix.dart new file mode 100644 index 0000000..b8b82b4 --- /dev/null +++ b/Chapter 7/serving_files/packages/path/src/style/posix.dart @@ -0,0 +1,62 @@ +// Copyright (c) 2013, 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. + +library path.style.posix; + +import '../characters.dart' as chars; +import '../parsed_path.dart'; +import '../internal_style.dart'; + +/// The style for POSIX paths. +class PosixStyle extends InternalStyle { + PosixStyle(); + + final name = 'posix'; + final separator = '/'; + final separators = const ['/']; + + // Deprecated properties. + + final separatorPattern = new RegExp(r'/'); + final needsSeparatorPattern = new RegExp(r'[^/]$'); + final rootPattern = new RegExp(r'^/'); + final relativeRootPattern = null; + + bool containsSeparator(String path) => path.contains('/'); + + bool isSeparator(int codeUnit) => codeUnit == chars.SLASH; + + bool needsSeparator(String path) => + path.isNotEmpty && !isSeparator(path.codeUnitAt(path.length - 1)); + + String getRoot(String path) { + if (path.isNotEmpty && isSeparator(path.codeUnitAt(0))) return '/'; + return null; + } + + String getRelativeRoot(String path) => null; + + String pathFromUri(Uri uri) { + if (uri.scheme == '' || uri.scheme == 'file') { + return Uri.decodeComponent(uri.path); + } + throw new ArgumentError("Uri $uri must have scheme 'file:'."); + } + + Uri absolutePathToUri(String path) { + var parsed = new ParsedPath.parse(path, this); + if (parsed.parts.isEmpty) { + // If the path is a bare root (e.g. "/"), [components] will + // currently be empty. We add two empty components so the URL constructor + // produces "file:///", with a trailing slash. + parsed.parts.addAll(["", ""]); + } else if (parsed.hasTrailingSeparator) { + // If the path has a trailing slash, add a single empty component so the + // URI has a trailing slash as well. + parsed.parts.add(""); + } + + return new Uri(scheme: 'file', pathSegments: parsed.parts); + } +} diff --git a/Chapter 7/serving_files/packages/path/src/style/url.dart b/Chapter 7/serving_files/packages/path/src/style/url.dart new file mode 100644 index 0000000..f383923 --- /dev/null +++ b/Chapter 7/serving_files/packages/path/src/style/url.dart @@ -0,0 +1,88 @@ +// Copyright (c) 2013, 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. + +library path.style.url; + +import '../characters.dart' as chars; +import '../internal_style.dart'; +import '../utils.dart'; + +/// The style for URL paths. +class UrlStyle extends InternalStyle { + UrlStyle(); + + final name = 'url'; + final separator = '/'; + final separators = const ['/']; + + // Deprecated properties. + + final separatorPattern = new RegExp(r'/'); + final needsSeparatorPattern = new RegExp( + r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); + final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); + final relativeRootPattern = new RegExp(r"^/"); + + bool containsSeparator(String path) => path.contains('/'); + + bool isSeparator(int codeUnit) => codeUnit == chars.SLASH; + + bool needsSeparator(String path) { + if (path.isEmpty) return false; + + // A URL that doesn't end in "/" always needs a separator. + if (!isSeparator(path.codeUnitAt(path.length - 1))) return true; + + // A URI that's just "scheme://" needs an extra separator, despite ending + // with "/". + var root = _getRoot(path); + return root != null && root.endsWith('://'); + } + + String getRoot(String path) { + var root = _getRoot(path); + return root == null ? getRelativeRoot(path) : root; + } + + String getRelativeRoot(String path) { + if (path.isEmpty) return null; + return isSeparator(path.codeUnitAt(0)) ? "/" : null; + } + + String pathFromUri(Uri uri) => uri.toString(); + + Uri relativePathToUri(String path) => Uri.parse(path); + Uri absolutePathToUri(String path) => Uri.parse(path); + + // A helper method for [getRoot] that doesn't handle relative roots. + String _getRoot(String path) { + if (path.isEmpty) return null; + + // We aren't using a RegExp for this because they're slow (issue 19090). If + // we could, we'd match against r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*". + + if (!isAlphabetic(path.codeUnitAt(0))) return null; + var start = 1; + for (; start < path.length; start++) { + var char = path.codeUnitAt(start); + if (isAlphabetic(char)) continue; + if (isNumeric(char)) continue; + if (char == chars.MINUS || char == chars.PLUS || char == chars.PERIOD) { + continue; + } + + break; + } + + if (start + 3 > path.length) return null; + if (path.substring(start, start + 3) != '://') return null; + start += 3; + + // A URL root can end with a non-"/" prefix. + while (start < path.length && !isSeparator(path.codeUnitAt(start))) { + start++; + } + return path.substring(0, start); + } +} diff --git a/Chapter 7/serving_files/packages/path/src/style/windows.dart b/Chapter 7/serving_files/packages/path/src/style/windows.dart new file mode 100644 index 0000000..2965f1e --- /dev/null +++ b/Chapter 7/serving_files/packages/path/src/style/windows.dart @@ -0,0 +1,138 @@ +// Copyright (c) 2013, 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. + +library path.style.windows; + +import '../characters.dart' as chars; +import '../internal_style.dart'; +import '../parsed_path.dart'; +import '../utils.dart'; + +/// The style for Windows paths. +class WindowsStyle extends InternalStyle { + WindowsStyle(); + + final name = 'windows'; + final separator = '\\'; + final separators = const ['/', '\\']; + + // Deprecated properties. + + final separatorPattern = new RegExp(r'[/\\]'); + final needsSeparatorPattern = new RegExp(r'[^/\\]$'); + final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); + final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])"); + + bool containsSeparator(String path) => path.contains('/'); + + bool isSeparator(int codeUnit) => + codeUnit == chars.SLASH || codeUnit == chars.BACKSLASH; + + bool needsSeparator(String path) { + if (path.isEmpty) return false; + return !isSeparator(path.codeUnitAt(path.length - 1)); + } + + String getRoot(String path) { + var root = _getRoot(path); + return root == null ? getRelativeRoot(path) : root; + } + + String getRelativeRoot(String path) { + if (path.isEmpty) return null; + if (!isSeparator(path.codeUnitAt(0))) return null; + if (path.length > 1 && isSeparator(path.codeUnitAt(1))) return null; + return path[0]; + } + + String pathFromUri(Uri uri) { + if (uri.scheme != '' && uri.scheme != 'file') { + throw new ArgumentError("Uri $uri must have scheme 'file:'."); + } + + var path = uri.path; + if (uri.host == '') { + // Drive-letter paths look like "file:///C:/path/to/file". The + // replaceFirst removes the extra initial slash. + if (path.startsWith('/')) path = path.replaceFirst("/", ""); + } else { + // Network paths look like "file://hostname/path/to/file". + path = '\\\\${uri.host}$path'; + } + return Uri.decodeComponent(path.replaceAll("/", "\\")); + } + + Uri absolutePathToUri(String path) { + var parsed = new ParsedPath.parse(path, this); + if (parsed.root.startsWith(r'\\')) { + // Network paths become "file://server/share/path/to/file". + + // The root is of the form "\\server\share". We want "server" to be the + // URI host, and "share" to be the first element of the path. + var rootParts = parsed.root.split('\\').where((part) => part != ''); + parsed.parts.insert(0, rootParts.last); + + if (parsed.hasTrailingSeparator) { + // If the path has a trailing slash, add a single empty component so the + // URI has a trailing slash as well. + parsed.parts.add(""); + } + + return new Uri(scheme: 'file', host: rootParts.first, + pathSegments: parsed.parts); + } else { + // Drive-letter paths become "file:///C:/path/to/file". + + // If the path is a bare root (e.g. "C:\"), [parsed.parts] will currently + // be empty. We add an empty component so the URL constructor produces + // "file:///C:/", with a trailing slash. We also add an empty component if + // the URL otherwise has a trailing slash. + if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) { + parsed.parts.add(""); + } + + // Get rid of the trailing "\" in "C:\" because the URI constructor will + // add a separator on its own. + parsed.parts.insert(0, + parsed.root.replaceAll("/", "").replaceAll("\\", "")); + + return new Uri(scheme: 'file', pathSegments: parsed.parts); + } + } + + // A helper method for [getRoot] that doesn't handle relative roots. + String _getRoot(String path) { + if (path.length < 3) return null; + + // We aren't using a RegExp for this because they're slow (issue 19090). If + // we could, we'd match against r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'. + + // Try roots like "C:\". + if (isAlphabetic(path.codeUnitAt(0))) { + if (path.codeUnitAt(1) != chars.COLON) return null; + if (!isSeparator(path.codeUnitAt(2))) return null; + return path.substring(0, 3); + } + + // Try roots like "\\server\share". + if (!path.startsWith('\\\\')) return null; + + var start = 2; + // The server is one or more non-"\" characters. + while (start < path.length && path.codeUnitAt(start) != chars.BACKSLASH) { + start++; + } + if (start == 2 || start == path.length) return null; + + // The share is one or more non-"\" characters. + start += 1; + if (path.codeUnitAt(start) == chars.BACKSLASH) return null; + start += 1; + while (start < path.length && path.codeUnitAt(start) != chars.BACKSLASH) { + start++; + } + + return path.substring(0, start); + } +} \ No newline at end of file diff --git a/Chapter 7/serving_files/packages/path/src/utils.dart b/Chapter 7/serving_files/packages/path/src/utils.dart new file mode 100644 index 0000000..0636261 --- /dev/null +++ b/Chapter 7/serving_files/packages/path/src/utils.dart @@ -0,0 +1,16 @@ +// Copyright (c) 2014, 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. + +library path.utils; + +import 'characters.dart' as chars; + +/// Returns whether [char] is the code for an ASCII letter (uppercase or +/// lowercase). +bool isAlphabetic(int char) => + (char >= chars.UPPER_A && char <= chars.UPPER_Z) || + (char >= chars.LOWER_A && char <= chars.LOWER_Z); + +/// Returns whether [char] is the code for an ASCII digit. +bool isNumeric(int char) => char >= chars.ZERO && char <= chars.NINE; diff --git a/Chapter 7/serving_files/pubspec.lock b/Chapter 7/serving_files/pubspec.lock new file mode 100644 index 0000000..6e45cbb --- /dev/null +++ b/Chapter 7/serving_files/pubspec.lock @@ -0,0 +1,15 @@ +# Generated by pub +# See http://pub.dartlang.org/doc/glossary.html#lockfile +packages: + http_server: + description: http_server + source: hosted + version: "0.9.2" + mime: + description: mime + source: hosted + version: "0.9.0+3" + path: + description: path + source: hosted + version: "1.2.1" diff --git a/Chapter 7/serving_files/pubspec.yaml b/Chapter 7/serving_files/pubspec.yaml new file mode 100644 index 0000000..c264708 --- /dev/null +++ b/Chapter 7/serving_files/pubspec.yaml @@ -0,0 +1,4 @@ +name: serving_files +description: A sample command-line application +dependencies: + http_server: any diff --git a/Chapter 7/simple_webserver/bin/simple_webserver.dart b/Chapter 7/simple_webserver/bin/simple_webserver.dart new file mode 100644 index 0000000..a64fcfc --- /dev/null +++ b/Chapter 7/simple_webserver/bin/simple_webserver.dart @@ -0,0 +1,25 @@ +import 'dart:io'; + +InternetAddress HOST = InternetAddress.LOOPBACK_IP_V6; +const int PORT = 8080; + +main() { + HttpServer.bind(HOST, PORT) + .then((server) { + print('server starts listening on port ${server.port}'); + server.listen(handleRequest); + }) + .catchError(print); +} + +handleRequest(HttpRequest req) { + print('request coming in'); +// print('headers: ${req.headers}'); +// print('uri: ${req.uri}'); +// print('method: ${req.method}'); + req.response + ..headers.contentType = new ContentType("text", "plain", charset: "utf-8") + ..write('I heard you loud and clear.') + ..write(' Send me the data!') + ..close(); +} \ No newline at end of file diff --git a/Chapter 7/simple_webserver/pubspec.lock b/Chapter 7/simple_webserver/pubspec.lock new file mode 100644 index 0000000..e7b03b5 --- /dev/null +++ b/Chapter 7/simple_webserver/pubspec.lock @@ -0,0 +1,3 @@ +# Generated by pub +# See http://pub.dartlang.org/doc/glossary.html#lockfile +packages: {} diff --git a/Chapter 7/simple_webserver/pubspec.yaml b/Chapter 7/simple_webserver/pubspec.yaml new file mode 100644 index 0000000..351430e --- /dev/null +++ b/Chapter 7/simple_webserver/pubspec.yaml @@ -0,0 +1,4 @@ +name: simple_webserver +description: A sample command-line application +#dev_dependencies: +# unittest: any diff --git a/Chapter 7/sockets/bin/socket_client.dart b/Chapter 7/sockets/bin/socket_client.dart new file mode 100644 index 0000000..da843a0 --- /dev/null +++ b/Chapter 7/sockets/bin/socket_client.dart @@ -0,0 +1,25 @@ +import 'dart:io'; + +InternetAddress HOST = InternetAddress.LOOPBACK_IP_V6; +const PORT = 7654; + +void main() { + Socket.connect("google.com", 80).then((socket) { + print('Connected to: ' + '${socket.remoteAddress.address}:${socket.remotePort}'); + socket.destroy(); + }); +// prints: Connected to: 173.194.65.101:80 + + Socket.connect(HOST, PORT).then((socket) { + print(socket.runtimeType); + // data to server: + socket.write('Hello, World from a client!'); + // data from server: + socket.listen(onData); + }); +} + +onData(List data) { + print(new String.fromCharCodes(data)); +} \ No newline at end of file diff --git a/Chapter 7/sockets/bin/socket_server.dart b/Chapter 7/sockets/bin/socket_server.dart new file mode 100644 index 0000000..92ba9d2 --- /dev/null +++ b/Chapter 7/sockets/bin/socket_server.dart @@ -0,0 +1,24 @@ +import 'dart:io'; +import 'dart:convert'; + +InternetAddress HOST = InternetAddress.LOOPBACK_IP_V6; +const PORT = 7654; + +void main() { + ServerSocket.bind(HOST, PORT) + .then((ServerSocket srv) { + print('serversocket is ready'); + srv.listen(handleClient); + } + ); +} + +void handleClient(Socket client){ + print('Connection from: ' + '${client.remoteAddress.address}:${client.remotePort}'); + // data from client: + client.transform(UTF8.decoder).listen(print); + // data to client: + client.write("Hello from Simple Socket Server!\n"); + client.close(); +} \ No newline at end of file diff --git a/Chapter 7/sockets/pubspec.lock b/Chapter 7/sockets/pubspec.lock new file mode 100644 index 0000000..e7b03b5 --- /dev/null +++ b/Chapter 7/sockets/pubspec.lock @@ -0,0 +1,3 @@ +# Generated by pub +# See http://pub.dartlang.org/doc/glossary.html#lockfile +packages: {} diff --git a/Chapter 7/sockets/pubspec.yaml b/Chapter 7/sockets/pubspec.yaml new file mode 100644 index 0000000..0375399 --- /dev/null +++ b/Chapter 7/sockets/pubspec.yaml @@ -0,0 +1,4 @@ +name: sockets +description: A sample command-line application +#dev_dependencies: +# unittest: any