Using dart to create a javascript library - dart

The problem
I'm currently working on a JavaScript library, and in order to reduce the amount of bugs I thought that my library might benefit from using Dart's static typing mechanism. First, because my lib wasn't doing any interop neither with HTML nor with other JavaScript libraries, only pure javascript object manipulation stuff. However I didn't find any info on the net showing how it is possible to build a JS library using dart. So I've tried to do that myself, created initial dart file:
library Repo;
class Type {
final String name;
final TypeCategory category;
Type(String name, TypeCategory category) : name = name, category = category {
category.types[name] = this;
}
}
class TypeCategory {
final String name;
final Map<String, Type> types = new Map();
TypeCategory(this.name);
}
class Branch {
}
class Descriptor {
}
class TableDescriptor extends Descriptor {
TableDescriptor.ctor() {
}
}
class Repo {
Descriptor descriptor(String name) {
}
Branch branch(String name) {
}
void Merge() {
}
}
main() {
return Repo;
}
Compiled it to JavaScript using dart2js to see how I'm doing:
// Generated by dart2js, the Dart to JavaScript compiler version: 1.3.6.
// 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 $$ = {};
(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]);
}
})
([
["Repo", "repo.dart", , F, {
"^": "",
main: function() {
return C.Type_Jeh;
}
},
1],
["_js_helper", "dart:_js_helper", , H, {
"^": "",
createRuntimeType: function($name) {
return new H.TypeImpl($name, null);
},
TypeImpl: {
"^": "Object;_typeName,_unmangledName"
}
}],
["dart.core", "dart:core", , P, {
"^": "",
Null: {
"^": "Object;"
},
Object: {
"^": ";"
}
}],
]);
Isolate.$finishClasses($$, $, null);
$$ = null;
// Runtime type support
// getInterceptor methods
C.Type_Jeh = H.createRuntimeType('Repo');
$.libraries_to_load = {};
$.Closure_functionCounter = 0;
$.BoundClosure_selfFieldNameCache = null;
$.BoundClosure_receiverFieldNameCache = null;
init.functionAliases = {};
;
init.metadata = [];
$ = 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);
// 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(F.main, []);
} else {
F.main([]);
}
});
// 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 (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);
}
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;
return Isolate;
};
}
})()
//# sourceMappingURL=out.js.map
//# sourceMappingURL=out.js.map
And that's it, I've thrown away Dart because I didn't knew what to do with generated JS file, also I was frightened of potentially high amount of time required for keeping the resulting library interface clean and similar to one I'm using with JavaScript.
The question(s)
How do I expose class definitions created in Dart and later use them in JavaScript?
Do you think it's worth it going into Dart when nearly all potential library users will be using JS version instead? (Using dart is already not good for me due to difference in community sizes, this means that less people will find it easy to contribute to my library)
In your opinion, what should I do?

Even though Dart supports this use case, if you target JavaScript developers I would stick with JavaScript.
#AlexandreArdhuin shows in his answer to Expose Dart functions to javascript how you can make a Dart function available to JavaScript.
Under the dart-js-interop are many examples how to do function calls and pass data between Dart and JavaScript.

Wrap dart class into custom element, the Dart object auto expose to javascript.
Assume we have 2 Dart Classes, SlickGrid Class contains Column class in Dart
class SlickGrid{
List<Column> columns;
}
class Column{}
class GridWrap extends HtmlElement {
ShadowRoot shadowRoot;
SlickGrid grid; // here is your cool object
}
compile to javascript, and register custom element, then open javascript console,
//this is SlickGrid object
var grid= document.querySelector('cj-grid').grid;
// this is dart Column Object
var column = grid.columns.$index(0,0);
// call toString function in dart object that produce json string...
column.toString$0()

Related

Dart: Class members seem to be wrong when access from a separate class

Anyone know if this is a Dart bug or is it my misunderstanding of how Dart coding works?
I am learning Dart to investigate feasibility of eventually using Flutter; however, while exploring the language, I found a weird behavior (maybe a bug). I tried repro'ing it by writing a similar pattern of code, but have yet to figure out what causes it. In the attached code, I wrote a quicksort class. In that class, it counts the number of times the "sort" method is recursed and saves the count in a class member called "recurseCount".
From the main() class, if I use the QuickSort class directly, I have no issue getting back the recurseCount member; however, if I call it from a different class (called "Tester"), I do not get the correct value for "recurseCount". Why would calling a class from a separate class cause members to not provide the correct values?
import 'package:test/test.dart';
import 'dart:math' as _math;
// ***********************
enum SortOrder { ascending, descending, unsorted }
class QuickSort {
List list = [];
SortOrder sortOrder = SortOrder.unsorted;
int recurseCount = 0;
QuickSort({this.list}) {
if (list != null && list.length > 1) {
list = sort(useRandomPivot: true);
}
}
List sort(
{List iList,
int leftIndex = 0,
int rightIndex,
bool useRandomPivot = true}) {
if (iList != null) list = iList;
if (list.isEmpty) return [];
rightIndex ??= list.length - 1;
if (rightIndex > list.length - 1) rightIndex = list.length - 1;
if (leftIndex < rightIndex) {
recurseCount++;
var partitionIndex =
_partition(leftIndex, rightIndex, useRandomPivot: useRandomPivot);
if (partitionIndex == -1) {
//already sorted List
if (sortOrder == SortOrder.ascending) {
return list;
} else {
//SortOrder.descending
list = list.reversed.toList(); // Time Complexity of O(n)
sortOrder = SortOrder.ascending;
return list;
}
} else {
sort(leftIndex: leftIndex, rightIndex: partitionIndex - 1);
sort(leftIndex: partitionIndex + 1, rightIndex: rightIndex);
}
} else {
sortOrder = SortOrder.ascending;
}
return list;
}
int _partition(int leftIndex, int rightIndex, {bool useRandomPivot = true}) {
// in case the array is already sorted from the start; only run through the partition'ing one time
// note: regardless of ascending or descending order
if (leftIndex == 0 && rightIndex >= list.length - 1) {
sortOrder = checkSorting(list);
if (sortOrder != SortOrder.unsorted) {
return -1;
}
if (useRandomPivot) {
var random = _math.Random();
var randomIndex = random.nextInt(rightIndex - leftIndex);
_swapElements(randomIndex, rightIndex);
}
}
int pivotVal = list[rightIndex]; //select the last item as the pivot
var headIndex = leftIndex - 1;
for (var scanIndex = leftIndex; scanIndex < rightIndex; scanIndex++) {
if (list[scanIndex] <= pivotVal) {
headIndex++;
_swapElements(headIndex, scanIndex);
}
}
var partitionIndex = headIndex + 1;
_swapElements(partitionIndex, rightIndex);
return partitionIndex;
}
void _swapElements(position1, position2) {
int tempVal = list[position1];
list[position1] = list[position2];
list[position2] = tempVal;
}
SortOrder checkSorting(List arr) {
var isAsc = true;
var isDesc = true;
for (var i = 0; i < arr.length - 1; i++) {
if (arr[i] < arr[i + 1]) isDesc = false;
if (arr[i] > arr[i + 1]) isAsc = false;
if (!isDesc && !isAsc) break; // not sorted Asc or Desc
}
if (isAsc) {
return SortOrder.ascending;
} else if (isDesc) return SortOrder.descending;
return SortOrder.unsorted;
}
}
// ***********************TESTS*********************************
num log2(num n) => _math.log(n) / _math.ln2;
List getRandomIntList({int min = 0, int max = 10000, int len}) {
var random = _math.Random();
var tempList = List(len);
for (var i = 0; i < len; i++) {
tempList[i] = random.nextInt(max - min);
}
return tempList;
}
class Tester {
static num _runControlTest(List list) {
var stopwatch = Stopwatch();
stopwatch.start();
list.sort();
stopwatch.stop();
return stopwatch.elapsedMicroseconds;
}
static void runTests(List list, String groupName,
{bool useRandomPivot = false, bool sortFromConstructor = false}) {
// CONTROL
var controlElapsedTimeMicroSec = 0;
controlElapsedTimeMicroSec = _runControlTest(list);
// END CONTROL
var expectedRecursionCountLogN = log2(list.length).ceil();
var expectedRecursionCountNLogN = list.length * expectedRecursionCountLogN;
var qs = QuickSort();
var stopwatch = Stopwatch();
stopwatch.start();
list =
qs.sort(iList: list, useRandomPivot: useRandomPivot); // METHOD TO TEST
stopwatch.stop();
var elapsedTimeMicroSec = stopwatch.elapsedMicroseconds;
var recursionCount = qs
.recurseCount; //NOTE (BUG in Dart?): unable to get the recurseCount correctly from within this class/method
group(groupName, () {
var testSubject =
'Time Taken: ${controlElapsedTimeMicroSec} microseconds';
var reason =
'The built in sort took ${controlElapsedTimeMicroSec} microseconds, while the test took ${elapsedTimeMicroSec}.';
test(testSubject, () {
expect(
elapsedTimeMicroSec, lessThanOrEqualTo(controlElapsedTimeMicroSec),
reason: reason);
});
testSubject =
'Time Complexity: ${recursionCount} vs ${expectedRecursionCountNLogN}';
reason =
'Time Complexity of ${recursionCount} is greater than either range (LogN) ${expectedRecursionCountLogN} or (N*LogN) ${expectedRecursionCountNLogN}';
test(testSubject, () {
expect(recursionCount, lessThanOrEqualTo(expectedRecursionCountNLogN),
reason: reason);
});
});
}
}
void main() {
var min = 0;
var len = 1000000;
var max = len;
var originalList = getRandomIntList(min: min, max: max, len: len);
var list = List.from(originalList);
// BUG? When called within this Tester.runTests the sort method does NOT return the correct recurseCount
Tester.runTests(list, 'UNSORTED_RIGHT_PIVOT', useRandomPivot: false);
list = List.from(originalList);
var qs = QuickSort();
// When called directly from main() the sort method DOES return the correct recurseCount
var stopwatch = Stopwatch()..start();
qs.sort(iList: list, useRandomPivot: true); //METHOD TO TEST
stopwatch.stop();
group('UNSORTED_RANDOM_PIVOT', () {
test('Time Taken: ${stopwatch.elapsedMicroseconds} microseconds', () {
expect(stopwatch.elapsedMicroseconds,
lessThanOrEqualTo(Tester._runControlTest(originalList)));
});
var nLogN = (list.length * (log2(list.length).ceil()));
test('Time Complexity: ${qs.recurseCount} vs $nLogN', () {
expect(qs.recurseCount, lessThanOrEqualTo(nLogN));
});
});
}

frida - hook socket inputstream in android apk

I found InputStream hook code here! but I add below code in android, then try to hook it with the frida script, it's not been triggered:
Java code (SocketDemo.apk):
InputStream ios = sock.getInputStream();
int len = ios.read(dataBuf);
frida script.js:
function buf2hex(array, readLimit) {
var result = [];
var ascii = ""
readLimit = readLimit || array.length;
for (var i=0; i<readLimit;++i) {
var elem = array[i] & 0xFF
var str = (0x100 + elem).toString(16).substr(1)
result.push(str);
if (elem < 127 && elem >= 32) {
ascii += String.fromCharCode(elem)
} else {
ascii += "."
}
}
return result.join(' ') + " | " + ascii
}
function hookInputStream() {
Java.use('java.net.Socket')['connect'].overload('java.net.SocketAddress').implementation = function(sockaddr) {
console.log("connect")
var retval = this.connect(sockaddr)
return retval
}
Java.use('java.io.InputStream')['read'].overload('[B').implementation = function(data) {
var retval = this.read(data)
console.log("----" + data.length)
var hexstr = buf2hex(data)
console.log(hexstr)
return retval
}
}
Java.perform(hookInputStream);
command:
frida -Uf com.example.socketdemo -l script.js --no-pause

How to print called functions in android?

I'm using frida on an APK and I'm trying to print out which function is being called, and especially which parameters are sent to it. My environment is set correctly as I can print out classes, and perform various actions in accordance to the docs.
This is the closest i've found:
https://codeshare.frida.re/#razaina/get-a-stack-trace-in-your-hook/
But the code gives out errors (ThreadDef undefined and so on)
And the frida docs didn't help me get where I'm trying to.
Any guidance? suggestions? help?
The code from codeshare#razaina has bugs which can be easily fixed (threadef != ThreadDef)
var printBacktrace = function () {
Java.perform(function() {
var JLog = Java.use('android.util.Log'), JException = Java.use('java.lang.Exception');
// getting stacktrace by throwing an exception
console.warn(JLog.getStackTraceString(JException.$new()));
});
};
Just call printBacktrace() w/e you want to see who called your hooked function.
If you want to hook all the methods of Java class you can use this snippet;
var Color = {
RESET: "\x1b[39;49;00m", Black: "0;01", Blue: "4;01", Cyan: "6;01", Gray: "7;11", Green: "2;01", Purple: "5;01", Red: "1;01", Yellow: "3;01",
Light: {
Black: "0;11", Blue: "4;11", Cyan: "6;11", Gray: "7;01", Green: "2;11", Purple: "5;11", Red: "1;11", Yellow: "3;11"
}
};
/**
*
* #param input.
* If an object is passed it will print as json
* #param kwargs options map {
* -l level: string; log/warn/error
* -i indent: boolean; print JSON prettify
* -c color: #see ColorMap
* }
*/
var LOG = function (input, kwargs) {
kwargs = kwargs || {};
var logLevel = kwargs['l'] || 'log', colorPrefix = '\x1b[3', colorSuffix = 'm';
if (typeof input === 'object')
input = JSON.stringify(input, null, kwargs['i'] ? 2 : null);
if (kwargs['c'])
input = colorPrefix + kwargs['c'] + colorSuffix + input + Color.RESET;
console[logLevel](input);
};
var printBacktrace = function () {
Java.perform(function() {
var android_util_Log = Java.use('android.util.Log'), java_lang_Exception = Java.use('java.lang.Exception');
// getting stacktrace by throwing an exception
LOG(android_util_Log.getStackTraceString(java_lang_Exception.$new()), { c: Color.Gray });
});
};
function traceClass(targetClass) {
var hook;
try {
hook = Java.use(targetClass);
} catch (e) {
console.error("trace class failed", e);
return;
}
var methods = hook.class.getDeclaredMethods();
hook.$dispose();
var parsedMethods = [];
methods.forEach(function (method) {
var methodStr = method.toString();
var methodReplace = methodStr.replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1];
parsedMethods.push(methodReplace);
});
uniqBy(parsedMethods, JSON.stringify).forEach(function (targetMethod) {
traceMethod(targetClass + '.' + targetMethod);
});
}
function traceMethod(targetClassMethod) {
var delim = targetClassMethod.lastIndexOf('.');
if (delim === -1)
return;
var targetClass = targetClassMethod.slice(0, delim);
var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length);
var hook = Java.use(targetClass);
var overloadCount = hook[targetMethod].overloads.length;
LOG({ tracing: targetClassMethod, overloaded: overloadCount }, { c: Color.Green });
for (var i = 0; i < overloadCount; i++) {
hook[targetMethod].overloads[i].implementation = function () {
var log = { '#': targetClassMethod, args: [] };
for (var j = 0; j < arguments.length; j++) {
var arg = arguments[j];
// quick&dirty fix for java.io.StringWriter char[].toString() impl because frida prints [object Object]
if (j === 0 && arguments[j]) {
if (arguments[j].toString() === '[object Object]') {
var s = [];
for (var k = 0, l = arguments[j].length; k < l; k++) {
s.push(arguments[j][k]);
}
arg = s.join('');
}
}
log.args.push({ i: j, o: arg, s: arg ? arg.toString(): 'null'});
}
var retval;
try {
retval = this[targetMethod].apply(this, arguments); // might crash (Frida bug?)
log.returns = { val: retval, str: retval ? retval.toString() : null };
} catch (e) {
console.error(e);
}
LOG(log, { c: Color.Blue });
return retval;
}
}
}
// remove duplicates from array
function uniqBy(array, key) {
var seen = {};
return array.filter(function (item) {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
}
var Main = function() {
Java.perform(function () { // avoid java.lang.ClassNotFoundException
[
// "java.io.File",
'java.net.Socket',
'com.package.MyCustomClass'
].forEach(traceClass);
});
};
Java.perform(Main);

How to change get id from url to post method

I have a code like this
var arr = new Array();
var cnt = 0;
var checkboxes = $('.sel');
var emaillist = document.getElementsByName("elist[]");
var selectedContract = [];
var dispflag = false;
var x = 0;
$.each(checkboxes,function(i,r){
if(r.checked){
arr[cnt++] = r.value;
if(emaillist[x].value != "true"){
dispflag = true;
selectedContract.push(r.value);
}
}
x++;
});
var params = selectedContract.join(':');
if(cnt == 0)
{
alert("No contracts selected.");
}
else
{
url = controllerPath + "/getcontract/contract_id/" + arr + "/custom_action/1";
window.open (url,"Contracts","resizable=1,location=1,status=1,scrollbars=1,width=800,height=600");
}
I want to change arr that give result contract_id to POST method cause when the contract_id more than 500 result, the page shows blank due to URL too large/too long.
I still don't understand how to do it.
Anyone can help me to solve it.

Need a map of a map in Dart.lang

Learning Dart.lang . Have a php background. Given this code at this link...
Link to full working php algorithm
or to copy the main part here, this is what i got in PHP and works fine. I need to know how to do it in dart:
foreach ($floors as $x) {
$temp = explode("," , $x);
$floor[$temp[0]][$temp[1]] = $temp[2];
}
library map;
void main() {
var temp = """
1,201,70
1,301,71
3,301,31
1,401,79
2,501,2
1,601,171
2,801,61
2,901,100
5,101,54
4,203,23
3,201,112""";
var values = temp.split('\n');
var floors = new Map<int, Map<int, double>>();
values.forEach((f) {
var v = f.trim().split(',');
var fk = int.parse(v[0]);
var rk = int.parse(v[1]);
var rv = double.parse(v[2]);
if(floors[fk] == null) {
floors[fk] = new Map<int,double>();
}
floors[fk][rk]=rv;
});
floors.forEach((fk, fv) {
var tempSum = 0;
var lowest = null;
for(var rk in fv.keys) {
var temp = fv[rk];
tempSum += temp;
if(lowest == null || (lowest > temp)) {
lowest = temp;
}
}
print("Floor #${fk} has ${fv.length} rooms. The average temp is ${tempSum / fv.length}. The lowest temp is ${lowest}.");
});
}

Resources