www.linux-swt.de

Edwards JavaScript vom wohnungslosen Hartz-IV Empfänger seit 2012 Homepage

Hot Summer

Viel zu heiss, als dass ich zuhause im Wohnheim an meinem uralten gebrauchten PC was programmieren will. Unsere beiden Kaninchen liegen auch rum, weil ihnen warm ist.

Ich sollte die Seite mal wieder kuerzen.

function v( ) { let z = function i() { return 10; }; debugger; let y = z(); return y; }

Zum Schluss fand ich noch heraus, dass die Function- und die GlobalEnvironment nur eine lexEnv besitzen, aber keine varEnv. Jeder neue Context hat die zwei und nur die lexikalische "wandert weiter" oder wird neu gesetzt.. Die Instanziierung der globalen Deklarationen, der eines Blocks, und derer einer Funktion in etwa, werden unterschieden.

Eine Exception wurde geworfen.
Eine Exception wurde geworfen.TypeError
Eine Exception wurde geworfen.TypeErrorexpected function is not callable
Eine Exception wurde geworfen.TypeErrorexpected function is not callableHierk ommt der errstack hin.
Eine Exception wurde geworfen.
Eine Exception wurde geworfen.TypeError
Eine Exception wurde geworfen.TypeErrorexpected function is not callable
Eine Exception wurde geworfen.TypeErrorexpected function is not callableHierk ommt der errstack hin.
Eine Exception wurde geworfen.
Eine Exception wurde geworfen.TypeError
Eine Exception wurde geworfen.TypeErrorexpected function is not callab

Diese "Exception" ein reines console.log auf eine abrupte Completion.

Modules

Cool, ohne es heute geschrieben zu haben, ist die Module und Import Syntax fuer den Parser fuer die Anweisungen bereits korrekt eingebaut. Freut mich. Muss ich fuer das Sample nur die evaluation vervollstaendigen.

    
    module "first" {
	let d = 2;
	let a = 12;
	let b = d + 10;
	export a; 
	export b; 
    }
    
    let a, b;
    import { a, b } from "first";
    a + b;

Um das Zeug zum Laufen zu kriegen, habe ich gerade zwei interessante Artikel gespottet. Ich habe Module Environment auf Google eingegeben und die Wikiseite und die Blogseite vom Rauschma gekriegt. Das reicht auch erstmal und ich sollte die Proposals im Wiki nehmen (grundsaetzlich). (Cool wird es, den Vat einzurichten, man kann Worker und Cluster und Co. nehmen. Und wenn man node hat. Ich haette gerne 4 CPUs um 2*T(n/2) parallel zu verarbeiten, aber egal.)

    let a, b;
    // XHR sync oder suspendieren und returnen (zuerst, dann letzteres)
    import { a, b } from "abmod.js";
    // Mit node.js zuerst das lokale filesystem probieren
    a+b;

http://wiki.ecmascript.org/doku.php?id=harmony:modules

http://2ality.com/2013/07/es6-modules.html

Buggy (on a daily basis)

Wo ich gerade in den Ast geguckt habe, sehe ich, dass "Identifier" mal .name und mal .value hat. Vor zwei Tagen habe ich bereits notiert, von .value auf .name zu transformieren, weil die Mozilla Parser API .name nutzt. Ich moechte hinterher, dass das Ding wirklich den Mozilla AST nutzt und man anstatt syntaxjs.createAst() zum Beispiel esprima.parse() fuer die IR nutzen kann.

function SuperClass (...args) {
    this.name = args[0];
    this.member = "Expression";
}
SuperClass.prototype = { constructor: SuperClass, testme: function (x) { return x; } };


class C extends SuperClass {
    constructor (...args) {
//	super(...args);	
	this.property = "value";
    }
    
    static probe () {
	return "good";
    }
    
    * each (array) {
	let k;
	for (k of array) yield k;
    }
    
    method(a,b,c=3) {
	return a+b+c;
    }
}

let instance = new C("Edward");
instance.method(1,2);

Gestern kam noch sechs raus, und instance.method war eine Function. Morgen ist das behoben, denke ich. Mitsamt this.

Wenn ich this.abc =100 eingebe, hat das globale this die Property. Die ThisValue vom Call scheint noch nicht zu wollen. Sonst haette das hereingegebene Objekt die entsprechenden Variablen. Dabei habe ich gelernt, dass ich extra fuer HasThisBinding und GetThisBinding die Function Environment zwischen zwei Declarative Environment linken kann (per outer ist das wie eine linked list, die traversiert wird, oder die sogenannte "scope chain" halt, while (env = env.outer) {..} Trotzdem muesste die Funktion eine ThisValue (global = global, lexical, das auessere, strict, das was mit thisArg uebergeben wuerde) und zwar das neue Objekt (kreirt in Construct mit constructor[@@create]) parat haben, weil ich in Construct selbiges an Call (fuer den Code) uebergebe...Aber mit OrdinaryConstruct, Construct, und so weiter bin ich noch ein wenig durcheinander. (Ein wenig.)

"constructor" benoetigt natuerlich Special Treatment um ueberhaupt erkannt zu werden. Token, deren Value "constructor" ist, haben komischerweise keinen Type...? Aber das kennt man ja schon. Object.create(null) und (v === "constructor") schafft Abhilfe.

ToValue(AST) sollte 6 ergeben!

Habe zwar im Leistungstest in der Arbeitsagentur super abgeschnitten. Bin aber nicht mehr weiter gekommen, noch was zu schreiben.

Assessment (Jobcenter - Leistungstest mit Bravour bestanden)

Mit weit ueberdurchnittlicher Punktzahl habe ich heute den Leistungstest vom berufspsychologischen Dienst ueberstanden. Dann fing sie an, anhand ihrer Leistung habe ich keine Einwaende wegen ihrer Fortbildung. Dann fing sie an mein Äusseres zu kritisieren. So ist das halt, wenn man ein paar Jährchen im Wohnheim lebt und keine Mittel hat. Man magert ab und trägt alte Klamotten. Das versteht sie nur nicht, weil sie es noch nicht erlebt hat. Das habe ich früher (vorher) auch nicht.

WebWorker

Ich habe noch kein postMessage Interface, um Syntax.js z.B. im Worker an den Texten arbeiten zu lassen, oder die Quellen auszurechnen. Ich denke, mit importScript() geht das einfach. self detecten habe ich letztes Jahr schon probiert (ist wie window und process, typeof !== "undefined"). Dann muss nur noch die GUI mit postMessage arbeiten, dass muss ich auch noch schreiben. Wenn ich nachher oder danach dazu komme.

Uebrigens ist die ganze Juli Seite broken und auf Mai und April laeuft weiter unten auch nichts mehr (ohne auf die Konsole geguckt zu haben...

ExoticWrapperObject + ExoticWrapperFunction

Um ans DOM ranzukommen...

ExoticWrapperObject - Mein Einfall um auf window und process zuzugreifen, ohne auch noch das DOM neu schreiben zu muessen. Sobald man von dem Code aus innerHTML Properties von DOM Nodes setzen kann, und NodeLists traversieren ist das wichtigste erledigt.

start: 185ms
js> process
{ Wrapped: 
   { title: 'node',
     version: 'v0.10.13',
     moduleLoadList: 
      [ 'Binding evals',
        'Binding natives',
        'NativeModule events',
        'NativeModule buffer',
[...]
     mainModule: 
      { id: '.',
        exports: {},
        parent: null,
        filename: '/l/shell.js',
        loaded: true,
        children: [Object],
        paths: [Object] } },
  Symbols: {},
  Prototype: null }
js> 
undefined
js> process.version
v0.10.13
js> 
js> process.stdout
{ Wrapped: 
   { _connecting: false,
     _handle: 
      { fd: 1,

WRONG_THIS_ERR

/* Das wird NICHT im Browser, sondern von syntax.js ausgefuehrt */
let el = window.document.querySelector("#testmich");
el.innerHTML += "exotic wrapped functions + objects + ";

Wenn man auf tovalue drueckt erscheint der Text im DOM. Gerade gab es schon einen WRONG_THIS_ERR (at unwrappedFunction.apply(thisArg, argList)), man muss das Objekt zu dem die Funktion gehoert bei Instanziierung der ExoticWrapperFunction mit angeben. Wird haarig. Weil ich jetzt aber auf window.document.* zugreifen kann, zumindest ein wenig, kann ich dann bessere Beispiele schreiben, wo mal auf ein Element zugegriffen wird. Zur HTML Manipulation reicht es wohl. Ist coool. Ansonsten muss das Backend solche Funktionen bieten :-)

Ein Argument zuviel (bind)

Bei Function.prototype.bind als erste Funktion auf dessen Prototyp wird ein ExoticBoundFunctionObject erzeugt. Jetzt habe ich glatt vergessen, den Function Parameter mit reinzunehmen.

Weiter unten ist die Sequenzexpression abgestuerzt. Ich hatte sie bereits neu begonnen, weil ich die Arrow Function eingebaut habe. Jetzt darf ich den ganzen Code nochmal neu designen. Ich denke, Expression, ExpressionStatement, SequenceExpression, und CoverParenthesizedExpressionAndArrowParameterList darf ich nochmal ueberdenken. Das ist alles andere als stabil.

Ausserdem denke ich, einen anderen Teil der Parserlogik neu zu schreiben. Denn im Draft kommt vor, dass man mal einen String mit dem GoalSymbol xxx parsen soll. Das kann man mit parser["xxx"] erledigen. Aber FunctionBody zum Beispiel erwartet bei mir {} drumrum. Im Draft nicht. Ich denke, die Zeichen, auch FormalParameterList braucht ( und ) bei mir, die muss ich wieder ausbauen.

Hier muss jetzt der (f Parameter weg. Die bind Funktion ist wirklich FunctionPrototype.Bindings["bind"] (=== Function.prototype.bind). Und nicht einfach an f zugewiesen. So ein bisschen funktioniert das schon. Neben all den FAILURES in den Tests. Schade, dass ich nicht mehr die Naechte durchmache, sondern wir frueh einschlafen und die Tage eher kurz sind. Sonst waer ich schon vier Wochen weiter.

js> function f() {}
{ HomeObject: null,
  MethodName: 'f',
  Data: 
   { prototype: 
      { value: [Object],
        writeable: true,
        enumerable: true,
        configurable: true },
     length: 
      { value: 0,
        writeable: false,
        configurable: false,
        enumerable: false },
     name: 
      { value: 'f',
        writeable: false,
        configurable: false,
        enumerable: false } },
  Symbols: {},
  Prototype: 
   { Data: { constructor: [Object], bind: [Object] },
     Symbols: { NaN: null },
     Prototype: null },
  Scope: 
   { envRec: { f: [Object] },
     outer: 
      { outer: null,
        global: [Object],
        lexEnv: [Object],
        VarNames: [Object] } },
  Code: [],
  FormalParameters: [],
  FunctionKind: 'normal',
  Extensible: true,
  Realm: null,
  Strict: false,
  ThisMode: 'global' }
js> let o = {};
undefined
js> f.bind(f, o);
{ HomeObject: null,
  MethodName: null,
  Data: {},
  Symbols: {},
  Prototype: 
   { Data: { constructor: [Object], bind: [Object] },
     Symbols: { NaN: null },
     Prototype: null },
  Scope: null,
  Code: null,
  FormalParameters: [],
  BoundTargetFunction: 
   { HomeObject: null,
     MethodName: 'f',
     Data: { prototype: [Object], length: [Object], name: [Object] },
     Symbols: {},
     Prototype: { Data: [Object], Symbols: [Object], Prototype: null },
     Scope: { envRec: [Object], outer: [Object] },
     Code: [],
     FormalParameters: [],
     FunctionKind: 'normal',
     Extensible: true,
     Realm: null,
     Strict: false,
     ThisMode: 'global' },
  BoundThis: { Data: {}, Symbols: {}, Prototype: null },
  BoundArguments: [],
  Extensible: true,
  Call: [Function] }
js> 

Eine Minute spaeter war klar, das das thisArg nun das globale Objekt ist. Weil die Funktion GetThisResolution() ruft und die auf das globale Objekt statt die ThisValue der eigentlich vorher erreichbaren FunctionEnvironment zeigt.

Das gleiche Problem haben wir mit super() und this.property gerade auch...(noch)

function returns arrow

start: 182ms
js>  function x() { return (a) => { return a; }; }
undefined
js> x()
{ HomeObject: null,
  MethodName: null,
  Data: 
   { caller: 
      { get: [Object],
        set: [Object],
        enumerable: false,
        configurable: false },
     arguments: 
      { get: [Object],
        set: [Object],
        enumerable: false,
        configurable: false },
     length: 
      { value: 1,
        writeable: false,
        configurable: false,
        enumerable: false } },
  Symbols: {},
  Prototype: 
   { Data: { constructor: [Object] },
     Symbols: 
      { '2': [Function],
        '3': null,
        '4': 'Object Function',
        '5': null,
        '6': null,
        '7': null },
     Prototype: null },
  Scope: { envRec: { arguments: [Object] }, outer: null },
  Code: [ { type: 'ReturnStatement', expression: [Object] } ],
  FormalParameters: [ { type: 'Identifier', name: 'a', value: 'a', loc: [Object] } ],
  FunctionKind: 'arrow',
  Extensible: true,
  Construct: null,
  Realm: null,
  Strict: true,
  ThisMode: 'lexical' }
js> 

Gut. Beim returnen von () => {} streikt der Parser aber. Beim () wie ich gerade bemerkt habe. Mit (a) geht das bereits. Er kann uebrigens auch Buchstaben ohne Klammern vorm Pfeil sehen.

Update: Ich vermute, dass er empty ParenthesizedExpressions () gar nicht kann. Dem werde ich nachgehen.

BUG? Ich sehe im Scope das arguments Objekt aus Funktion x(). Das muss in der Arrow Function nicht mehr erreichbar sein. Denke ich. Da thisMode = "lexical" ist, muss die thisValue fuer die Arrow Function die gleiche sein, wie fuer x(). Das ist noch nicht programmiert.

(etwas) Klasse

Aber nur wenig. C ist ein Constructor (was in constructor steht) und die Methoden landen auf dem Prototype des Objects, was der Constructor bei new instanziiert.

Update. Die Kommata kann man weglassen. Anhand des Instanziierungsalgorithmusses kann ich die staerken und Schwaechen meiner AST-Blaetter ausloten. Ob die Sachen in einer Reihenfolge gemacht werden koennen, oder der AST umstaendlich ist. So wird der Constructor extra rausgeliftet und static werde ich auch noch aus den definitions herausheben. Es ist besser, MethodDefinition(parent) statt MethodDefinition() zu nutzen. Desweiteren sollen leere Konstruktorstrings geparsed werden koennen. Das Parser Objekt muss dazu auch nochmal verbessert werden, dass ich Kleinstaufrufe von irgeneiner Funktion korrekt abarbeite.


js>  class C { constructor(...args) {}, method(a,b,c) { return a+b+c; } }
{ HomeObject: 
   { Data: { hasOwnProperty: [Object], constructor: [Object] },
     Symbols: 
      { '2': [Function: ObjectCreate],
        '3': null,
        '4': 'Object Object',
        '5': [Function: OrdinaryHasInstance],
        '6': null,
        '7': null },
     Prototype: null },
  MethodName: null,
  Data: 
   { caller: 
      { get: [Object],
        set: [Object],
        enumerable: false,
        configurable: false },
     arguments: 
      { get: [Object],
        set: [Object],
        enumerable: false,
        configurable: false },
     length: 
      { value: 0,
        writeable: false,
        configurable: false,
        enumerable: false },
     prototype: 
      { value: [Object],
        writeable: false,
        enumerable: false,
        configurable: false } },
  Symbols: {},
  Prototype: 
   { Data: { constructor: [Object] },
     Symbols: 
      { '2': [Function],
        '3': null,
        '4': 'Object Function',
        '5': null,
        '6': null,
        '7': null },
     Prototype: null },
  Scope: 
   { Data: { C: [Object] },
     outer: 
      { outer: null,
        global: [Object],
        lexEnv: [Object],
        VarNames: [Object] } },
  Code: [],
  FormalParameters: [ { type: 'RestParameter', id: [Object] } ],
  FunctionKind: 'normal',
  Extensible: true,
  Construct: [Function],
  Realm: null,
  Strict: true,
  ThisMode: 'strict' }
js> let instance = new C();
undefined
js> instance.method(1,2,3);
6
js> 

Oh, die linked List weiter unten. Ich habe sie gerade das erste mal probiert. Sie hat einen Bug. insertFirst und insertLast waren verkehrt rum. Ich habe die Namen ausgetauscht. Jetzt ist die Liste perfekt.

Erste Arrow Function

Der Syntax Error haette schon bei linux-qc59 sein muessen. /l haette glaube ich auch kein RegExpLiteral nach dem Label sein koennen. Ich glaube, da muss ein Iterationstatement stehen, weil es kein Goto ist. Muss mal bei Labelled Statement und Early Errors gucken. Aber nach einer Subtraktion kann kein Label-Doppelpunkt (:) stehen. Hier muss einer strikter werden.

linux-qc59:/l # n shell
start: 182ms
js> let f = (a) => { return a; }
undefined
js> f(100)
100

:)

js> f
{ HomeObject: null,
  MethodName: null,
  Data: 
   { caller: 
      { get: [Object],
        set: [Object],
        enumerable: false,
        configurable: false },
     arguments: 
      { get: [Object],
        set: [Object],
        enumerable: false,
        configurable: false },
     length: 
      { value: 1,
        writeable: false,
        configurable: false,
        enumerable: false } },
  Symbols: {},
  Prototype: 
   { Data: { constructor: [Object] },
     Symbols: 
      { '2': [Function],
        '3': null,
        '4': 'Object Function',
        '5': null,
        '6': null,
        '7': null },
     Prototype: null },
  Scope: 
   { Data: { f: [Object] },
     outer: 
      { outer: null,
        global: [Object],
        lexEnv: [Object],
        VarNames: [Object] } },
  Code: [ { type: 'ReturnStatement', expression: [Object] } ],
  FormalParameters: [ { type: 'Identifier', name: 'a', value: 'a', loc: [Object] } ],
  FunctionKind: 'arrow',
  Extensible: true,
  Construct: null,
  Realm: null,
  Strict: true,
  ThisMode: 'lexical' }
js> 

Eine Pfeilfunktion. Ohne () und/oder ohne {} erzeugt auch eine Arrow Funktion. Aber (un)logischerweise funktionieren die nicht gleich. -Noch- nicht.

Ist von heute. Dazu habe ich endlich mal wieder den Parser erweitert.

Wenn im Identifier gerade name und value stehen, liegt das daran, dass der Mozilla AST fuer Identifier .name nimmt und ich bislang .value genutzt habe, so dass ich den Code schrittweise (ein paar mal .value in .name aendern) aendern muss. Hinterher soll das ja kompatibel sein.

Error Stack

http://wiki.ecmascript.org/doku.php?id=strawman:error_stack

Ich habe schon ueberlegt, wie ich meinen Stack ausgeben soll, weil es keinen Vorschlag im Draft gibt. Im ES-Wiki gibt es aber einen.

Symbol in Action

Ich hatte ueberlegt, ob ich die Symbol mit O(n) Suche in Arrays speichern wollte. Ich habe mich fuer einen "uniqueSymbolID-Counter" entschlossen, und ein zweites DatenObjekt, damit die ID nicht mit Stringnamen kollidieren kann. In ES5 kann man keine Objekte als Keys nehmen. Symbols sind die neue Form in ES6. Die muss man in ES5 aber simulieren. Wie man im Auszug sehen kann, geht das.

  Symbols: {},
  Prototype: null,
  type: 'object' }
js> var s = new Symbol();
undefined
js> s
{ Data: {},
  Prototype: null,
  Extensible: false,
  uniqueSymbolID: 8,
  Private: true }
js> var o = {};
undefined
js> o[s] = 100;
true
js> o[s]
100
js> o["s"]
undefined
js> 

Ich habe gerade den Code fuer [[Set]], [[Get]] und [[DefineOwnProperty]] und [[GetOwnProperty]] und [[Delete]] und [[HasOwnProperty]] und [[HasProperty]] so veraendert, dass geguckt wird, ob IsPropertyKey(P) ein Symbol ist. Dazu habe ich IsSymbol und GetOwnSymbol und HasOwnSymbol eingefuehrt. Und Get und Set uebrigens dazu geaendert, den Prototype runter zu suchen, ist die Property nicht vorhanden. Denn wenn die auf dem Prototype sitzt, wird die auch benutzt. Koennte jetzt gehen. Ist sie gar nicht vorhanden wird sie im Objekt (nicht im letzten Prototype, ich hab O vorher in o gespeichert, haette Q sagen koennen, gespeichert.)

Ich mach das, weil ich @@create und @@iterator und @@toStringTag und @@hasInstance und @@toPrimitive und dieses neue @@unscopable (fuer with) in die Konstruktoren und Prototypes einbauen will. Jetzt kann man die schliesslich rufen. Ich glaube, man hat sozusagen Object.create abstrahiert und das System was modernisiert. Die @@create Methoden sind die Standardfunktionen, um ein neues Objekt eines bestimmten Typs zu erzeugen. Das Symbol wird im Prototype des Objekts gespeichert. Object.prototype[@@create] oder Array.prototype[@@iterator] geben eine Funktion zum Erzeugen eines Objekts und einen Default Iterator frei, der der (neuen) Array.prototype.values() Funktion entspricht, . Der @@toStringTag ist der StringTag fuer das Objekt. Arrays haben Object Array. @@hasInstance ist OrdinaryHasInstance und @@toPrimitive ist ein Moeglichkeit, ToPrimitive(v, hint) zu ueberschreiben, um das eigene Objekt in einen primitiven Typen zu verwandeln. Der @@iterator von Generator ist die this value, weil Generatoren ja Iteratoren sind...

Ueberhaupt [[Enumerate]] klingt fuer mich nach keys und @@iterator nach values (kann aber wohl key+value, key, value) nach der neueren Logik des Systems.

Siss

       enumerable: false,
        configurable: false } },
  Symbols: {},
  Prototype: null }
js> this
{ Data: 
   { global: 
      { value: [Circular],
        writeable: false,
        enumerable: false,
        configurable: false },
     undefined: 
      { value: undefined,
        writeable: false,
        enumerable: false,
        configurable: false },
     NaN: 
      { value: NaN,
        writeable: false,
        enumerable: false,
        configurable: f

GetThisResolution holt sich mit GetThisEnvironment per HasThisBinding die Environment, und gibt dann GetThisBinding wieder. Anders, als man sich this vorstellt. Aber dies ist das.

Abnormal

Der Completion Record kommt sogar an. In diesem Fall habe ich einen EvalError(cmpl.[[value]]) geworfen, anstatt cmpl.[[value]] zu werfen. Wenn ich cmpl.[[value]] "werfe", muss ich _meinen_ Stack zeigen, und nicht den von Node.js. Anstatt "throw" zu rufen, kann ich printOwnStack schreiben. Dazu habe ich keine Definition gesehen, ich denke, die Art, den Callstack so auszugeben, hat sich so eingepraegt.

linux-qc59:/l # n shell
start: 163ms
js> throw "Abnormal Completion"
Abnormal Completion
EvalError: Abnormal Completion
    at ExecuteTheCode (/l/syntax.js:594:377)
    at /l/shell.js:21:18
    at Interface._onLine (readline.js:200:5)
    at Interface._line (readline.js:531:8)
    at Interface._ttyWrite (readline.js:760:14)
    at ReadStream.onkeypress (readline.js:99:10)
    at ReadStream.EventEmitter.emit (events.js:98:17)
    at emitKey (readline.js:1095:12)
    at ReadStream.onData (readline.js:840:14)
    at ReadStream.EventEmitter.emit (events.js:95:17)
js>

Geistesblitz module

modules sind noch nicht in der ES6-Dings drin. Ich denke sofort dran, dass "module" gut benutzt wird, durch CommonJS, aber da muessen alte Codes halt alte Server nehmen und neue Codes module nicht mehr nutzen. Basta.

modules zu selbst zu definieren, das fiel mir ein. Ich ueberlege nur, ob ich eine ModuleEnvironment definieren will, oder ein OrdinaryModule. Beides zusammen, oder getrennt. Sicher ist. Ich erhalte eine Liste ein Ordinary Object mit [[Exports]]. Die Liste kann ich wiederum in der Environment gebrauchen.

    OrdinaryModule
    {
	[[File]]: ModuleURL
	[[Name]]: ModuleName
	[[Code]]: ModuleBody
	[[Scope]]: Outer Lexical Environment
	[[Exports]]: Alle Exports als Binding 
    }

    ModuleEnvironment (1x Execution per Load)
    {
	[[Module]]: OrdinaryModule
	[[LexEnv]]: LexicalEnvironment
    }
    
    import: resolve [[Module]].[[Exports]]

Dafuer ist spaeter Realm.[[loader]] ? nicht wahr ?

Wenn man den Code ausfuehrt, dann sind export Statements statt return erlaubt. Kein Yield, kein Return, kein Continue, kein Break. Nur export (import geht ueberall). Darin unterscheidet sich [[EvaluateModuleBody]] von einem [[EvaluateFunctionBody]].

Die Frage ist nur, ob man Module und Environment nicht direkt zusammenlegt, und ob ich auf das Interfacing von ueberfluessigen Environmentteilen nicht verzichten kann und einfach HasExportDeclaration und die GetIdentifierReferenceu und GetBindingValue nehme. Irgendwas aehnliches wird da rauskommen. Ich sehe da noch verschiedene Moeglichkeiten, wie man das Modul einbauen kann. Aber ein ist klar. Ein Modul ist normal einzigartig und wird nur einmal geladen...

js> module m {}
undefined
js>  m
m ist noch nicht definiert oder von mir noch nicht gefunden worden.
ReferenceError: m ist noch nicht definiert oder von mir noch nicht gefunden worden.
    at Identifier (/l/syntax.js:555:218)
    at ToValue (/l/syntax.js:604:85)
    at Program (/l/syntax.js:599:124)
    at ToValue (/l/syntax.js:604:85)
    at ExecuteTheCode (/l/syntax.js:605:244)

Noch gibt es keinen Code. Aber wenn das Modul erst erzeugt ist, ist es auch unter seinem Identifier erreichbar.

ToLength

Auf Seite 90 ist sie neu...Die Frage ist, ob ich aus der Funktion CompletionRecords returne oder einfach die Value. Wenn man in einer C Fassung das machen wuerde, wuerde das auch viel mehr kosten, als gemacht wird. Es gibt den Hinweis, das return "Infinity" bedeutet, einen CompletionRecord("normal", Infinity, "") zu returnen, und es wird in jedem Algorithmus ausgepackt. Selbst fuer eine Hochsprache waer mir das eigentlich zu komplex, wo es auch guenstiger geht, als bei jeder Operation Completions zu generieren. Ich bin hier noch ambivalent, weil das allozieren der Completion gar nicht so blitzschnell geht, wenn man sie jede kleine Operation 10x neu alloziert.

function ToLength (V) {
    var len = ToInteger(V);
    if ((len=ifAbrupt(len)) && isAbrupt(len)) return len;
    if (len <= 0) return 0; // new CompletionRecord("normal", 0, "");
    return min(len, (1<<53)-1); // new CompletionRecord("normal", min(len, (1<<53)-1), "");
}

Vielleicht ist die beste Idee meinerseits, den CompletionRecord anstatt ihn immer neu zu generieren, nur neu zu setzen. Das bleibt konform mit dem return "Infinity" Satz, aber spart Speicher und Zeit. Das ewige ein- und auspacken wird Sekunden schlucken, schätze ich. Das hinterher auseinandernehmen um es wieder schnell zu kriegen, das wird Spass machen.

Wie auch immer. Ich weiss noch gar nicht, wozu ToLength gut ist. Bis hier bin ich gerade gekommen.

Papierspende

Das neue Working Draft vom 23.8.2013 ist jetzt online. http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts#august_23_2013_draft.

Ich mach jetzt erstmal meins fertig und fuer das Final bring ich Papierspende mit.

Das geilste ist wohl, dass die behinderte NoIn-Grammatik liquidiert wurde.

es-discuss

Wow, ich verstehe auf einmal, wovon auf es-discuss geredet wird. Ein paar Typos habe ich auch schon gefunden, aber file jetzt keine Bugs fuer, hehe. Eine gute Idee, ist aber, auf einem Zettel zu sammeln.

Auf es-discuss gibt es einen Thread zu @@create. Der hat mir die Augen geoeffnet, wozu die Funktion wirklich gut ist. Ich hatte sowas schon vermutet, aber durch die Mails habe ich das erst verstanden, wie ich @@create richtig einsetzen kann, werde und muss, weil es alle machen..

Die Generatoren habe ich gestern noch nicht abgeschrieben, aber dafuer ein paar Funktionen, die und den Proxy wollte ich mir jetzt vornehmen. Fuer die Runtime, meinem "alten" AST Interpreter (ehemals "engine", jetzt "evaluation") habe ich auch neue Ideen (die richtigen Funktionen vor), weil die Regeln so gut definiert sind.

Generator und Function

Gestern abend wollte ich die Generator Functions einbauen, bin aber vorher eingeschlafen. War da schon hundemüde. Bin gerade dabei, die FunctionCreate, FunctionAllocate, FunctionInitialise zu schreiben. Habe meinem Functionkonstruktor genau deren Aufgaben gegeben gehabt, genaugenommen, fast die gleichen, hier sind natuerlich alle Properties dran. Hoffe, dann gleich noch zu den Generatoren oder irgendwas anderem vernuenftigem zu kommen, bevor ich wieder die Mittagsmuedigkeit erreiche und die 15 Minuten oder halbe Stunde einknacke..

Jobcenter

Naechste Woche habe ich gleich zwei Begutachtungen vom Jobcenter an einem Tag, die sich so ueberschneiden, dass ich eine morgen verschieben muss, vorgestern kam der Brief mit dem 2. Termin an. Dass die auch nichts hinkriegen.

Ich will ne Fortbildung zum Linux-Administrator machen. Beim ersten Besuch freute ich mich, weil sie zusagten, beim zweiten hiess es, ich hatte mich drauf festgelegt, den LPI 1+2 Schein machen zu wollen, hiess es, es kostet ja Geld, wenn ich sowas machen wollen wuerde.

Fakt ist, der deutsche Steuerzahler hat bis heute noch 0 ct und 0 Euro fuer meine Ausbildung berappt und das Arbeitsamt, pardon, die Agentur, sich bis heute nicht drum geschert, dass ich gerne irgendwas gelernt haette.

Neben meiner Aufgabe, ihnen klarzumachen, dass der LPI Schein ein Heimspiel fuer mich ist, habe ich vor, genau das nochmal klarzustellen, und dass es Schikane ist, mich erstmal wieder monatelang zu demuetigen, ob ich denn was lernen moechte, anstatt es mich einfach tun zu lassen.

(Um es mir hinterher zu untersagen, damit wir die naechsten Jahre auch von Pfandflaschen leben koennen. Ich kann meine Familie kaum versorgen. Aber das stoert ja dort niemanden. "Sanktionen kann ich kriegen", sonst aber auch nichts.)

162 Operating Systems and System Programming

Hey, da hatte ich mal Buecher zu. Von der Uni Berkeley gibt es Videolesungen zum Thema. Ich zieh mir das Semester naechsten Monat mal rein. Meine Bytes werden nicht mehr reichen. Diesen Monat habe ich CS61C geguckt. Geile Intro zu Cloud Computing, Parallelen Architekturen, und dem Rechnerstand von heutzutage. Besonders meine Interesse fuer Assembler ist geweckt und mein Verstaendnis hat sich total veraendert.

ArrayCreate

Nachdem erst [] Arrays vorkamen, und letzten Monat das Destructuring mit [] und {} realisert war, nehme ich es in Angriff, die langsamen Datentypen einzubauen. Seit ich gestern das Draft gedruckt gekriegt habe, kann ich es endlich lesen, was ich dazu brauche.

    
linux-qc59:/l # n shell
js> let x = [1,2,3];
undefined
js> x
{ type: 'object',
  Data: 
   { '0': 
      { value: 1,
        writeable: true,
        enumerable: true,
        configurable: true },
     '1': 
      { value: 2,
        writeable: true,
        enumerable: true,
        configurable: true },
     '2': 
      { value: 3,
        writeable: true,
        enumerable: true,
        configurable: true },
     length: 
      { value: 3,
        writeable: true,
        enumerable: false,
        configurable: false } },
  Prototype: { Data: {}, Prototype: null, type: 'object' },
  ArrayInitialisationState: false }
js> x[0]
1
js> x[1]
2
js> 

Cool ist, dass die MemberExpression x[0] und x[1] sofort funktioniert. Man kann ja auch Methoden rufen und so. Nicht viel, aber eine hand voll Befehle. Doch noch cooler ist, dass das Draft die Runtime Semantics komplett erlaeutert. Es ist ein 500 Seiten Handbuch, wie das Programm geschrieben zu werden hat.

Module

Ich hab im ES6 Draft (fast) keine Informationen ueber Module gefunden. Es gibt noch kein Kapitel dazu. Das wird wohl erst demnaechst erscheinen.

Das schlimmste Ding muss der Namenskonflikt "module" mit CommonJS und node.js sein. Ich denke mir, alte node Codes sollen auf altem Node laufen und in Zukunft muessen sich CommonJS Systeme und node.js neu arrangieren, nicht mehr "module" zu nutzen, sondern "moduleRef" oder sowas.

Reflect

ES6 scheint aber wohl auch eine Reflect API zu haben. Das passt sehr gut zu dem AST der Mozilla Parser API (Reflect, das Original) und esprima. Zumindest gibt es die Kapitel dazu bereits. Macht das JavaScript doch irgendwie interoperabler und es leichter, Parser und Code Generatoren zu schreiben, oder?

Hashing im ArrayBuffer

Ich denke, eine Tabelle mit Offsets fuer Key und Value sind der passende Table. Die Allokationsraeume fuer Keys und Values sind damit dynamisch verteilt und der Garbage Collector sollte keine Probleme haben, nicht erreichbare Keys und Values als Garbage zu betrachten, bzw. alles was er nicht markiert hat, auch nicht in den To Space zu kopieren.

Ein Array braucht nur einen IndexTable, indem alle Offsets der Elemente verwaltet werden. Eine gute Idee ist es, vielleicht N als erstes zu speichern, dann die Tabelle mit N moeglichen Element-Offsets und dann die frei allozierten Elemente.

Ich denke mir, ich erweitere die store funktion ausserdem um ein Root-Flag um einen Pointer explizit als Root zu markieren (fuer lokale Variablen, etc.) Neben store gibt es ausserdem jetzt alloc, die einem einfach Speicherplatz von offset bis offset+size-1 garantiert.

(Noch ein gutes Stueck Arbeit.)

Emulieren

Ich denke mir, ArrayBuffer muss ich mit einem ArrayBuffer emulieren, weil ich sonst keinen Buffer habe. Mit Node kann man den mit Buffer realisieren, aber im Browser nicht. Ich denke, hier darf ich wrappern. Einen Proxy zum Beispiel kann ich mit der ecma-api realisieren. Aber binaere Blocks sind mir bis auf den ArrayBuffer versagt.

Math Object

Sehr witzig. Die ganzen Math Funktionen im Ecma-262 Draft sind specification-independent (implementation-dependent) zu realisieren. Es wird nur vorgegeben, wann NaN, +0, -0, Infinity oder -Infinity rauskommen soll. Und die Konstanten sind vordefiniert. Aber die Approximationen, die darf ich selbst vornehmen.

Gut, es wird darauf hingewiesen, dass man zur Implementation die IEEE 754 Approximation Algorithmen oder Math Library empfohlen bekommt. Der Link steht auch dabei, habe ich jetzt nicht im Kopf.

js> Math.floor(13.2)
13
js> Math
{ bindings: 
   { log: 
      { value: [Object],
        writeable: false,
        enumerable: false,
        configurable: false },
     ceil: 
      { value: [Object],
        writeable: false,
        enumerable: false,
        configurable: false },
     floor: 
      { value: [Object],
        writeable: false,
        enumerable: false,
        configurable: false },
     abs: 
      { value: [Object],
        writeable: false,
        enumerable: false,
        configurable: false } },
  Prototype: null,
  Scope: null,
  type: 'object' }
js> Math.floor(13);
13
js> Math.log(234234);
12.364075894672922

Hier rufe ich im Hintergrund Math. Dennoch. floor, ceil, abs, sign, max, die kommen zu Beginn des Docs dran und sind hier auch auf der Seite zu sehen.

Listen

Wenn die Ecma-262 Grammatik FormalParametersList oder StatementList oder IrgendeineList sagt, dann ist speziell die Linked List gemeint. In meiner JavaScript Version durch Arrays realisiert, weil ich denke, dass selbstgeschriebene Listen zu lahm sind. Den Object Pointern zu folgend dauert noch laenger, als einen Array zu nutzen. Dennoch denke ich, hier nochmal zu ueberdenken. Nochmal zu messen. Weil auch hier wichtig wird, ob man das Ding transpilen kann.

Update: Die Liste hatte insertFirst und insertLast getauscht. Ich hatte sie in 5-10 Minuten aus dem Kopf runtergeschrieben, nur kompiliert und nie probiert. Ich hatte head und tail als sentinel definiert, sowie den sentinel. Und weil man den nur 1x braucht, habe ich head und tail renamed. Beim ausprobieren kam heraus, dass insertFirst und insertLast genau verkehrt herum waren. ;-)

	function List () {
	    var sentinel = {};
	    sentinel.next = sentinel;
	    sentinel.prev = sentinel;
	    this.sentinel = sentinel; 
	    this.size = 0;
	}
	List.prototype.insertLast = function (item) {
	    var rec = { value: item };
	    rec.next = this.sentinel;
	    rec.prev = this.sentinel.prev;
	    this.sentinel.prev.next = rec;
	    this.sentinel.prev = rec;
	    this.size += 1;
	    return this;
	};
	List.prototype.insertFirst = function (item) {
	    var rec = { value: item };
	    rec.prev = this.sentinel;
	    rec.next = this.sentinel.next;
	    this.sentinel.next.prev = rec;
	    this.sentinel.next = rec;
	    this.size += 1;
	    return this;
	};
	List.prototype.iterate = function (f) {
	    var rec = this.sentinel.next;
	    while (rec !== this.sentinel) {
		f(rec.value);
		rec = rec.next;
	    }
	    return this;
	};
	List.prototype.reverse = function (f) {
	    var rec = this.sentinel.prev;
	    while (rec !== this.sentinel) {
		f(rec.value);
		rec = rec.prev;
	    }
	};
	List.prototype.nth = function (n) {
	    var rec, i;
	    if (n > this.size-1 || n < 0) return null;
	    if (n < this.size / 2) {
		i = 0;
		rec = this.sentinel;
		do {
		    rec = rec.next;
		    if (i === n) return rec.value;
		    i += 1;
		} while (i <= n);
	    } else {
		i = this.size - 1;
		rec = this.sentinel;
		do {
		    rec = rec.prev;
		    if (i === n) return rec.value;
		    i -= 1;
		} while (i >= n);
	    }
	    return null;
	};
	List.prototype.removeFirst = function () {
	    var rec = this.sentinel.next;
	    if (rec != this.sentinel) {
		this.sentinel.next = rec.next;
		this.sentinel.next.prev = this.sentinel;
		rec.next = null;
		rec.prev = null;
		this.size -= 1;
		return rec.value;
	    }
	    return null;
	};
	List.prototype.removeLast = function () {
	    var rec = this.sentinel.prev;
	    if (rec != this.sentinel) {
		this.sentinel.prev = rec.prev;
		this.sentinel.prev.next = this.sentinel;
		rec.next = null;
		rec.prev = null;
		this.size -= 1;
		return rec.value;
	    }
	    return null;
	};
	exports.List = List;

/* Mit tester.js sieht das besser aus, aber ich war jetzt schneller damit mit Copy + Paste alle Funktionen zu probieren. No errors. */

function print(x) { console.log(x); }

var l = new List();
l.insertFirst("Edward");
l.insertLast("Gerhold");
l.iterate(print);
l.reverse(print);
print(l.nth(1));
print(l.nth(0));
l.removeFirst();
l.removeLast();

l.iterate(print);	// null (keine items)
l.reverse(print);	// null (keine items)
print(l.nth(1));
print(l.nth(0));

l.insertFirst("Edward");
l.insertLast("Gerhold");
l.iterate(print);	
l.reverse(print);
print(l.nth(0));
print(l.nth(1));

var m = new List();
for (var i = 0; i < 10; i++) m.insertFirst(i);
m.iterate(print);

var n = new List();
for (var i = 0; i < 10; i++) n.insertFirst(i);
n.removeFirst(i);
print(n.nth(0));
for (var i = 0; i < 4; i++) n.removeFirst(i);
n.iterate(print);

var o = new List();
for (var i = 0; i < 10; i++) o.insertLast(i);
o.removeLast(i);
print(o.nth(o.size-1));
for (var i = 0; i < 4; i++) o.removeLast(i);
o.iterate(print);


/*
Edward		list l
Gerhold
Gerhold
Edward
Gerhold
Edward
null
null
Edward
Gerhold
Gerhold
Edward
Edward
Gerhold
9	list m insertFirst machts Reverse
8
7
6
5
4
3
2
1
0	
8	list n <- nth(0)
4	
3
2
1
0
8	list o <- nth(size-1)
0
1
2
3
4
*/

Assembler

Weil ich CS61C so geil finde, auch wenn es um MIPS statt AT&T und GNU/as geht, denke ich natuerlich jetzt bei jedem Befehl dran, wie er wohl in Maschinensprache aussieht. Das hilft beim Uebertragen der Engine in eine richtige kompilierte Hochsprache, weil ich wohl von Anfang an Assembly nehmen werde, statt einfach C zu programmieren.

Runtime Semantics

Es gibt zum Beispiel eine VarDeclaredNames Liste, die zu bestimmten Statements ausgewertet wird. Darin sind alle in dem "eingrueckten" Block definierten Variablen vorhanden, damit man sie hoisten kann.

Die Evaluation wird in der Spezifikation ausfuehrlich erklaert, und die bisherigen von mir geschriebenen AST-Evaluationen bekommen dann die Algorithmen der Runtime Semantics.

Wenn ich wichtige Dinge drueber lerne, halte ich die Seite mit ein paar Eindruecken auf dem Laufenden. Auf Seite 188 der Spec ist die Runtime fuer mich erstmal beendet, aber morgen kann ich wohl den Rest drucken.

Ich habe am Computer versucht, vom Bildschirm die eval() Funktion abzulesen und zu integrieren. Das Intrinsics Objekt enthaelt all diese Built-In Funktionen unter ihren %Namen% gespeichert und das globale Objekt bekommt sie alle zugewiesen. Sie sind als Properties des globalen Objekts definiert. Das Ablesen vom Bildschirm ist sehr umstaendlich, weil der Algorithmus kaum auf den Schirm passt. Auf einem 4 auf 1 Druck hat man ihn perfekt zum schrittweise umsetzen vor sich. Darum freue ich mich auf den Rest gedruckt.

Parallelismus

Das Gute an 61C ist, dass es auch eine super Einführung in parallele Architekturen von heute ist. Das ändert meine Sicht auf die Sache, denn das Dualcore Notebook von 2008, was ich 2010 gebraucht kaufte, ist ja kaputt, und ich fiel auf einen alten 32bit PIII zurueck. Heute geht nichts mehr ohne. Das Programmiermodel wird guenstigerweise dort und am MIT unterrichtet. Und ganz blind bin ich ja auch nicht, so dass ich das und aktuelle Serverwerbungen natuerlich mitverfolgt habe. Natuerlich schade, das Notebook startete direkt im Shared Memory Multiprocessing (SMP) Modus und ich hatte wenigstens auf 2 teilen koennen, aber gut ist, den Sinn der Sache (parallele Verarbeitung) und die Bedeutung von heute (heute programmiert man nur noch parallel) zu verstehen. So kann ich die Algorithmen bereits verstehen und es macht mehr Spass, sich dazu zu zwingen.

Mehr Exoten

Mittlerweile habe ich auch das ArrayExoticObject, das StringExoticObject und das ArgumentsExoticObject entdeckt. Und verstanden, dass auch hierfuer die OrdinaryObject Operationen [[..]] neu definiert wurden. Mit gedrucktem Papier ist es einfacher, die sich wiederholenden Funktionsnamen (mit unterschiedlichen Inhalten) zu unterscheiden. Dauert auch nicht mehr lange, bis ich sie einbaue. Zur Zeit lese ich den kommenden Standard zuhause und unterwegs. Ich kann den Druck am Dienstag, allein wegen der Runtime Semantics (fuer den Grammatik-AST Interpreter Teil) und der Intrinsics (ich brauche %Error% und %TypeError% und %ReferenceError% dringender wie %Object% und %Array%, kaum noch erwarten.

cs61c

Eigentlich geil, Machine Structures. Transistoren, Schaltkreise, Digitales Design, Datapath, Processor, Memory, I/O. Von unten nach oben wird alles im Schnelldurchlauf (3 Monate mit Lab) erklaert. Man lernt C nach Assembler (fuer MIPS Prozessoren) zu konvertieren, was ich da ueber MIPS lerne, gilt fuer alle Assembler, und 6.035, 6.172 und die anderen Kurse, wo Assembler drankommt, profitieren davon. Ich habe eine ganz neue Sicht auf die Maschinensprache bekommen. Schaltkreise lernt man neben Signalen und Systemen aber auch am MIT in Open Course Ware. Und ich habe gesehen, es gibt noch mehr dazu. Kein Grund das nicht zu ueben. Ich bin erst in 35 Jahren in Rente. Bis dahin kann ich die Kasse noch komplett auffuellen.

HashTable im ArrayBuffer

Was ich fuer meinen ArrayBuffer-Heap mit TypedArray Views, Garbage Collector und loads und stores uebrigens ueberlegt habe. Hashtables. Ein Hashtable passt wunderbar in den Buffer, ist er doch in echt auch nur ein Array. Ausserdem kann man die Succint (Bitgrossen) Datenstrukturen aus Advanced Data Structures drauf ausprobieren. Sowas kann man brauchen. Aber der Hashtable im Arraybuffer ist mein Tip an mich und euch. Der ist nuetzlich.

globalThis gesehen

    globalThis.DefineOwnProperty("undefined", { value: Undefined, writeable: false, enumerable: false, configurable: false });

Fehlt noch der Rest des Docs gedruckt, denn da steht drin, wie man Object denn implementiert. Aus Spass habe ich Object.create improvisiert, wo ich eh Objekte mit OrdinaryObjectCreate erzeuge. Das darf ich aber fuer defineProperty bis hasOwnProperty machen. Und das steht im Doc. Dienstag kann ich das erst drucken.

n shell
js> let x = function f () {}
undefined
js> x
{ type: 'function',
  thisValue: null,
  HomeObject: null,
  bindings: {},
  Prototype: undefined,
  Scope: 
   { bindings: { x: [Object] },
     outer: 
      { type: 'object',
        HomeObject: null,
        Integrity: '',
        bindings: [Object],
        outer: null,
        Private: false } },
  Code: [],
  FormalParameters: [] }
js> Symbol
{ type: 'function',
  thisValue: null,
  HomeObject: null,
  bindings: {},
  Prototype: undefined,
  Scope: null,
  Code: null,
  FormalParameters: [],
  Construct: [Function: new_Symbol],
  Call: [Function: new_Symbol] }
js> Object
{ type: 'function',
  thisValue: null,
  HomeObject: null,
  bindings: 
   { create: 
      { value: [Object],
        writeable: false,
        enumerable: false,
        configurable: false } },
  Prototype: { bindings: {}, Prototype: null, Scope: null, type: 'object' },
  Scope: null,
  Code: null,
  FormalParameters: [],
  Construct: [Function] }
js> Object.create
{ type: 'function',
  thisValue: null,
  HomeObject: null,
  bindings: {},
  Prototype: undefined,
  Scope: null,
  Code: null,
  FormalParameters: [],
  Call: [Function],
  Construct: null }
js> 

define("ecma-api", function(require,exports,module){}) oder nicht

ecma-api von syntaxjs-interpreter zu trennen hat zur Folge, dass man [[Call]] und [[Construct]] zum Beispiel neu schreiben kann, weil sie fuer den Mozilla-Parser-API-AST gebaut sind, wenn man zum Beispiel Bytecode nutzen will. Das geht einfach so, indem man OrdinaryFunction.prototype.Call und .Construct ersetzt, nachdem man sie importiert hat. Aber das muss man wissen, und ich vorneweg mit dokumentieren.

Ansonsten sieht das aber gut aus. Die Evaluation von dem AST, die sollte (in Zukunft) den Runtime Semantics der Spezifikation gleich kommen. Mein Druck ist ab Seite 188 (200) zuende und wir sind gerade bei for. Am Dienstag kriege ich den Rest. Da wird sich noch was tun. Auch wenn andere die Nacht durchprogrammieren und ich nur ne halbe Stunde am Tag. Irgendwann sind wir beide fertig. ecma-api zu schreiben erfordert einen export pro Funktion und einen import pro Modul, wo ich sie haben will.

Ob das was bringt, oder ob ich den ganzen Code wieder mit dem AST zusammenbringe, ich weiss nicht. Eher sollte ich sie getrennt lassen und die ecma api mit dem ArrayBuffer Memory Management fuer Ordinary Objects und Functions und Reference Types zusammenbringen. Auch wenn es viele gibt, die was koennen. So einfach ist es dann trotzdem nicht, dass es von alleine geht.

LLVM + emscripten + asm.js

Wie portiert man nativen C/C++ Code mit SDL und OpenGL nach JavaScript ? llvm-clang, emscripten und asm.js. asm.js ist dafuer fuer das Typechecking noetig, um AOT-compiled modules zu erzeugen.

Mit asm.js, einem strikten JavaScript Subset, was zur Zeit in Spidermonkey prototyped wird. asm.js definiert einige Regeln, die es ermoeglichen AOT (Ahead of Time) zu kompilieren. Dazu gehoert statische und dynamische Validierung. Wenn das Modul failt, wird es interpetiert oder JIT kompiliert. Wenn es passiert, AOT, und damit Faktor 2 gegenueber C/C++, wie man mit der in nur 4 Tagen portierten Unreal Game Engine nachweisen konnte. Und das ist noch vor der Optimierung von asm.js. Zur statischen Validierung gehoert neben Einhaltung des Subsets das Abschalten von Spekulationen durch Typisierung wie x = +x; als erste Zeile nach dem Funktionskopf function f(x) { oder bitwise coercion mit |0, die auf jeden Fall eine ganze Zahl produziert.

asmjs.org

emscripten ist ein front-end fuer llvm. Wenn man mit clang ein C/C++ Programm kompiliert, kommt eine LLVM Intermediate Representation (IR) raus. Das ist bei Compilern immer so. Diese kann noch optimiert werden, oder ist es gerade worden. Die einzige IR, die bei mir momentan rauskommt, ist der AST. Der AST, der noch Bugs hat. Das hier, emscripten ist was ganz anderes. Man hat mit emscripten, wie die Github Seite, die auf den folgenden Link erscheint, noch mehr, als nur die Unreal Engine portiert.

emscripten.org

llvm ist die Definition fuer eine IR, die bei Compilern rauskommt, sofern ich das bis hierhin verstanden habe. Und weil ich gar nichts drueber weiss, wollte ich mit meinem Kommentar hierauf hinaus: Das ist etwas umfangreicher und kostet einige Stunden...

llvm.org

Wer weiss, vielleicht eignet sich LLVM ja auch fuer syntax.js. Vielleicht kann man ja auch mal von JS nach C/C++ kompilieren. Dann kann man die Maschine, wenn sie fertig ist auch kompilieren. Was emscripten naemlich kann. Python oder Lua wurden mit emscripten nach JavaScript kompiliert. Wer sich fuer sowas interessiert ist hier am Ende der Technologie von heute angekommen.

(HTML5 ist nicht nur ein Buzzword, aber hier ist jetzt kein Platz mehr...(* emscripten kompiliert nach HTML5))

Ordinary, Exotic, Object

Auch das Intrinsic Object, was die Standard-Library von EcmaScript enthaelt, also alle predefinierten %Objekte%, ist selbst ein Ordinary Object. Dem werden die %Standardobjekte% einfach zugewiesen, und bei Bedarf abgefragt.

Und die @@create und @@toPrimitve "well-known-Symbols" definieren, wie man in ES6 mit Symbolen machen kann, unsichtbare Properties, die man nur mit den Symbolen lesen kann. So kann man beim Einbauen der Symbole gleich pruefen, ob man Symbole einbauen kann. Praktischerweise.

Wenn ein Algorithmus sagt return "Infinity", dann meint er, ich solle eine Completion("normal", Infinity, "") returnen. Bei einem ReturnStatement wird anstelle von "normal" "return" angegeben. Diese gilt als abrupte Completion. Wenn man sich dran gewoehnt hat, ist das einfach. Leider kostet das ewige erzeugen von Completions, auch das wiederverwenden, natuerlich auch was.

Ich sehe, da ich die JS Benchmarks "kenne" (ein paar), bereits einen Faktor 20 (die Konstante c >= 20) auf das Ausfuehren eines Skripts zukommen. Mein eigener Interpreter waere so Faktor 5 gewesen, weil ich alles in kurzem, knappen JS geschrieben haette, aber die API nach Ecma, das macht mindestens 20 drauf. Und 20 ist nur beweislos anhand der durchschnittlichen Laenge der zu verwendenden Operationen geschaetzt Es kann auch mehr sein..

ReturnIfAbrupt(argument)

8.2.3.4 ReturnIfAbrupt

Algorithm steps that say

1. ReturnIfAbrupt(argument)

mean the same thing as 

1. If argument is an abrupt completion, then return argument
2. Else if argument is a Completion Record, then let argument be argument.value

Es gibt einen wunderbaren Algorithmus, ReturnIfAbrupt, der staendig drankommt. In C ist er ganz billig mit einem MACRO zu realisieren, weil man den Code damit inlined. In JS ist das etwas trickyer. Denn das return kann man nicht einfach in eine function packen.

function ifAbrupt(argument) {
    if (!(argument instanceof CompletionRecord) || argument.type !== "normal") return argument;
    return argument.value;
}
function isAbrupt(completion) {
    if (completion instanceof CompletionRecord && completion.type !== "normal") return true;
    return false;
}

// Wird zu

if ((value=ifAbrupt(value)) && isAbrupt(value)) return value;
	
    // Klammer nicht vergessen, sonst wird das && der value zugewiesen.
Als ich ihn abschrieb, fiel mir prompt die Alternative ein, die mir vorher nicht einfiel.
    if (isAbrupt(arg)) return arg;
    arg = unwrap(arg);

Was daran gelegen haben mag, dass ich 2. ueber 1. stellte. Ich hatte das zum ersten mal am Bildschirm gelesen. Wie man sieht, sind es zwei Schritte. Und bleiben zwei Schritte. Wenn ich das zum Schluss noch bemerken darf.

MemberExpression

Schon kann man die neuen OrdinaryObjects und OrdinaryFunctions in Aktion sehen. Ich habe gestern angefangen, alles neu zu schreiben. Vorhin habe ich weitergemacht. Mittlerweile sind Environments und Objects schoen sauber getrennt und mir ist die Sache schoen schluessig.

Die benutzen jetzt [[Get]] und [[Set]] sowie [[HasProperty]], [[HasOwnProperty]], [[DefineOwnProperty]] und [[GetOwnProperty]] statt [[CreateMutableBinding]] und [[GetBindingValue]] der Environment.

js> var o = {};
undefined
js> o
{ bindings: {},
  Prototype: null,
  Scope: 
   { bindings: { Symbol: [Object], a: [Object], b: [Object], o: [Object] },
     outer: { bindings: {}, outer: null } } }
js> o.a = 10;
true
js> o.a
10
js> o.b = function (x) { return x*x; };
true
js> o.b
{ thisValue: null,
  HomeObject: null,
  Scope: 
   { bindings: { Symbol: [Object], a: [Object], b: [Object], o: [Object] },
     outer: { bindings: {}, outer: null } },
  Code: [ { type: 'ReturnStatement', expression: [Object] } ],
  FormalParameters: 
   [ { type: 'Identifier',
       details: undefined,
       value: 'x',
       computed: 'x',
       loc: [Object] } ] }
js> o.b(10)
100
js> 

Geht noch weiter. Wichtigste Aenderung ist halt, dass die Environments nicht mehr stellvertretend fuer meine Objekte und Funktionen stehen, sondern diese etwas kompakter sind und die Environment bei Bedarf hinzugezogen wird.

js> c
18
js> b
11
js> [c,b] = [b, c];
18
js> c
11
js> b
18
js> 

Nun ist mir auch klar, warum in CodeRealm einmal globalThis und einmal globalEnv steht. Das globalThis ist ein kleines Ordinary Object, das gleiche, was in der globalEnv eingepluggt wird. Aber die Referenzen zu denen stehen im Realm, damit man die immer wieder findet.

js> var o = {};
undefined
js> o.method = function (x) { return x*x; }
true
js> function x (y) { return o.method(y); }
{ thisValue: null,
  HomeObject: null,
  Scope: 
   { bindings: 
      { Symbol: [Object],
        a: [Object],
        b: [Object],
        c: [Object],
        o: [Object],
        x: [Object] },
     outer: { bindings: {}, outer: null } },
  Code: [ { type: 'ReturnStatement', expression: [Object] } ],
  FormalParameters: 
   [ { type: 'Identifier',
       details: undefined,
       value: 'y',
       computed: 'y',
       loc: [Object] } ] }
js> x(100)
10000
js> 

Im Draft ist das alles so schluessig und einfach erklaert. Man brauchte es mir nur zu drucken. Und naechste Woche kriege ich den Rest ab Seite 200 gedruckt, yeah.

     outer: { bindings: {}, outer: null } } }
js> Symbol
{ thisValue: null,
  HomeObject: null,
  Scope: null,
  Code: null,
  FormalParameters: [],
  Construct: [Function: new_Symbol],
  Call: [Function: new_Symbol] }
js> new Symbol("eddie");
{ bindings: 
   { name: 
      { value: 'eddie',
        writeable: true,
        enumerable: true,
        configurable: true } },
  Prototype: null,
  Scope: null,
  type: 'symbol',
  Private: true }
js> new Symbol();
{ bindings: {},
  Prototype: null,
  Scope: null,
  type: 'symbol',
  Private: true }
js> 

CS61C

In CS61C kann man Assembler lernen. Der Kurs hat zwar nur Slides auf dem Schirm und englisches Gebrabbel. Das ist aber hochwertig und danach kann ich Maschinensprache sprechen. Ich guck mir den jetzt an. (Das ganze heisst Machine Structures und ihr wisst schon, was ich meine...)

ES6

Ja, Ordinary Objects sind keine Object Environments. Objects haben [[Get]], [[Set]] und [[HasPropery]]. Und Environments haben [[*Binding*]]. Ein Objekt oder eine Funktion wird einer Environment zugewiesen, oder umgekehrt. Jedenfalls schreibe ich alles nochmal neu, geht am schnellsten. Habe schon angefangen.

Das Dokument endet bei den ersten Statements. Das reichte, um sich erstmal einen Überblick über das, was syntax.js kann, zu verschaffen. Der Standard liest sich übrigens gut, wenn man einmal drin ist.

Kein Wunder, dass ich nicht an dem Ding weitergeschrieben habe, wie es war. Ich war so blockiert. Ich spüre die Wirklichkeit, wie ich immer sage.

ArrayBufferHeap mit Garbage Collector

Donnerstag (vom Mittwoch)

Vor drei Tagen habe ich den Heap erfunden. Das Listing, was nur wenige Zeilen lang ist, ist weiter unten noch im Original zu sehen. Es hat strings oder numbers angenommen und dann in einen unterschiedlichen TypedArray konvertiert, der von einer Store Funktion auf einem ArrayBuffer erzeugt wurde.

In MIT 6.172 Performance Engineering of Software Systems Lektion 10 Dynamic Storage Allocation kommen Stackallocator, Freelist und andere Themen dran. Unter anderem auch der Garbage Collector mit Mark & Sweep, sowie Reference Counting. Schnell war begriffen, dass intern ein Graph verwaltet wird. Wobei die Vertices die allozierten Objekte und die Edges die Pointer sind. Mit BFS mit dequeue oder DFS mit stack kann man den Graphen dann durchlaufen. Das nutzt man fuer den Garbage Collector. Man besucht alle erreichbaren Knoten und markiert sie als "live". Alle anderen sind "garbage" und werden gesweept. Alle live Objekte werden nun in einen neuen TO-Space kopiert, wodurch sie wieder in einer Reihenfolge liegen. Der alte FROM-Space kann freigegeben werden.

Zuerst hat dieser Heap und Garbage Collector nur eine Root zu verwalten, also praktisch lokale Variablen, alles, was mit einem Schritt erreichbar ist. Hinterher wird es halt ein tiefer, vielleicht zyklischer Graph sein.

Issue: Zur Zeit ist heap.free() die einzige Funktion, die es ermoeglicht, Objekte als Garbage zu markieren. Ansonsten sind sie grundsaetzlich erreichbar.

Issue 2 + 3: Die Property Keys werden im folgenden Listing noch im Backend verwaltet, statt mit offset aufgeschrieben. Ist aber logisch, dass ich das noch umschreiben darf. functions duerfen keine Closure nutzen, um korrekt restauriert werden zu koennen. Ich hatte ueberlegt, ob ich functions wenigstens tostringen will. Zur Zeit geht das.

/*
    Wer Bytecode will muss leiden (ueben). function Heap erzeugt einen ArrayBuffer mit load und store Methoden.
*/

function Heap (space) {

    "use strict";

    space = space || 2048;
    var from = new_space(space);
    var to;

    function new_space (space) {
	return {
	    heap: new ArrayBuffer(space),
	    sp: 0,
	    root: Object.create(null)
	};
    }

    function collect() { // Mark and Sweep frei nach 6.172 Lektion 10
	"use strict";
	var v;
	var Q = [];	
	var r,e,v,o,p,q,b,i,j;
	var root = from.root;
	var bytes = 0;
	
	// erstmal alle live objects suchen  
	for (e in root) {
	
	    v = root[e];
	    if (v.mark === 1) {
		Q.push(v); 
		r = v.view;
		bytes += r.byteLength; // ob ich resizen will
	    } else {
		v.view = null;
		v.ptr.offset = null;
	    }
	}
	
	// resize bei genug belegung
	if (bytes > (space / 2)) { 
	    space += Math.floor(space * 2);
	    if ((b=space % 8) !== 0) space += 8-b;
	} else if (bytes < (space / 4)) {
	    space -= Math.floor(space / 2);
	    if ((b=space % 8) !== 0) space -= b;
	}
	
	// neue to space
	to = new_space(space);
	
	// umkopieren in den to space und pointer updaten
	i=0, j=Q.length;
	while (i < j) {
	    v = Q[i]; // Billiger als O(n) Q.shift()
	    i += 1; 
    	    p = store(to, load(v.ptr)); // neue records fuer to wurden in store erzeugt
	    if (p) {
	        o = v.ptr.offset;
	        root[o].view = v.view; // an alle vorherigen besitzer
		root[o].ptr.offset = p.offset; // die neuen daten per reference type (der record bleibt)
		root[p.offset] = root[o];
		root[o] = null;
		delete root[o];
	    }
	}
        // space wechseln
	delete from.root;// = null; 
	delete from.sp;// = null;
	delete from.heap; //= null;
        from = to;
	to = null;
    }
    
    /*
	resize: Loesche den alten View, nachdem ein neuer alloziert wurde
    */

    function resize(view, new_size) {
	"use strict";
	if (view instanceof Uint16Array) {
	} else if (view instanceof Float64Array) {
	}
    }

    function free(ptr) {
	"use strict";
	var rec = from.root[ptr.offset];
	rec.mark = 0;
	if (rec.view) rec.view = null;
	if (rec.map) for (var k in rec.map) free(rec.map[k]);
    }

    function load(ref) {
	"use strict";
	var data, view, map, k, obj, f;
	var rec = from.root[ref.offset];
	if (rec) {
	    if (rec.type == "object") {
		map = rec.map;
		obj = {};
		for (k in map) obj[k] = load(map[k]);
		return obj;
	    } else if (rec.type == "array") {
		map = rec.map;
		obj = [];
		for (k in map) obj[k] = load(map[k]);
		return obj;
	    } else if (rec.type === "function") {
		view = rec.view;
		data = "";
		for (var i=0, j=view.length; i < j; i++) {
		    data += String.fromCharCode(view[i]);
		}
		f = new Function("return "+data);
		f = f();
		return f; 
	    } else if (rec.view) {
    		view = rec.view;
    		if (view instanceof Uint16Array) {
		    data = "";
		    for (var i=0, j=view.length; i < j; i++) {
			data +=  String.fromCharCode(view[i]);
		    }
		    return data;
		} else if (view instanceof Float64Array) {
		    return view[0];
		}
	    }
	} 
    }

    /*
	Speichern auf der Halde (die Wdh darf ich noch DRYen, schoen fuer extractMethods())
    */

    function store(buf, data, size) {
	"use strict";
	
	var offset, view, bpe;
	var type, map;
	var rec;
	var heap = buf.heap;
	
	if (typeof data === "string") {
	    type = "string";
	    	
	    bpe = Uint16Array.BYTES_PER_ELEMENT;	
	    if (size === undefined) size = data.length;
	    offset = buf.sp + (bpe - (buf.sp % bpe)); // align
	
	    if ((offset + (size*bpe)) > space) {
		collect();
		buf = from;
	        offset = buf.sp + (bpe - (buf.sp % bpe)); // align again
	    }
	    
	    view = new Uint16Array(buf.heap, offset, size);
	    for (var i=0; i < data.length; i++) {
		view[i] = data.charCodeAt(i);
    	    }
    	    
	} else if (typeof data === "number") {
	    type = "number";
	    
	    bpe = Float64Array.BYTES_PER_ELEMENT;
	    size = 1;
	    offset = buf.sp + (bpe - (buf.sp % bpe)); // align
	    
	    if ((offset + (size*bpe)) > space) {
		collect();
		buf = from;
	        offset = buf.sp + (bpe - (buf.sp % bpe)); // align again
	    }
	    
	    view = new Float64Array(buf.heap, offset, size);
	    view[0] = data;
	    
	} else if (typeof data === "function") {
	    type = "function";
	    
	    data = data.toString();
	    
	    bpe = Uint16Array.BYTES_PER_ELEMENT;	
	    
	    if (size === undefined) size = data.length;
	    offset = buf.sp + (bpe - (buf.sp % bpe)); // align
	    
	    if ((offset + (size*bpe)) > space) {
		collect();
		buf = from;
	        offset = buf.sp + (bpe - (buf.sp % bpe)); // align again
	    }
	    
	    view = new Uint16Array(buf.heap, offset, size);
	    for (var i=0; i < data.length; i++) {
		view[i] = data.charCodeAt(i);
    	    }
	
	} else if (typeof data === "object") {
	
	    /* Das Ersetzen durch einen view, der Pointer beinhaltet,
	    anstatt eine Propertymap im Backend des ArrayBuffers wie jetzt. */
	    
	    if (Array.isArray(data)) {
		type = "array";
		map = Object.create(null);
		for (var i = 0, j = data.length; i < j; i++) {
		    map[i] = store(buf, data[i]);
		}
		offset = map[0].offset + "MAP"; // ein trick
		
	    } else {
		type = "object";
		map = Object.create(null);
		for (var k in data) {
		    map[k] = store(buf, data[k]);
		}
		for (var k in map) {
		    offset = map[k].offset + "MAP"; // tmp
		    break;
		}
	    }
	}

	if (view) buf.sp = offset + view.byteLength - 1;
 	
 	if (view || map) {
	    
	    buf.root[offset] = rec = {
		mark: 1,
		view: view ? view : undefined,
		type: type,
		map: map ? map : undefined,
		ptr: {
		    offset: offset
		}
	    };
	    
	    return rec.ptr;	// distributed kann ich mir hier bereits promises vorstellen 
	}
    }

    return {
	get buffer  () {
	    return from.heap;
	},
	get byteLength () {
	    return space;
	},
	get used () {
	    return from.sp;
	},
	store: function (data, size) { return store(from, data, size); },
	load: function (ptr) { return load(ptr); },
	resize: function (ptr, size) { return resize(ptr, size); },
	free: function (ptr) { return free(ptr); },
	gc: function () { return collect(from); }
    };

}

Das war der Heap mit Garbage Collector. Jetzt folgt der Teil, mit dem ich gucke, ob es geht, was noch keine Tests ersetzt, aber schonmal zeigt, dass es ueberhaupt geht.

/* Einen dynamischen Heap erzeugt man einfach so */

var heap = Heap(2048);

/*
    die heap.store Funktion gibt Referenzen auf den from_space zurueck 
*/

var string = "Hallo Leute, ich komme aus einem Typed Array.";
var string2 = "Hallo Leute, ich bin an einer anderen Stelle.";

/* 
    Hier ezeuge ich ein paar Pointer mit heap.store
*/

var sp1 = heap.store(string);
var sp2 = heap.store(string2);
var int1 = heap.store(32768); 
var int2 = heap.store(6664337); 
var d1 = heap.store(3.325253); 
var d2 = heap.store(124.27e10); 
var s1 = heap.store(-2352); 
var s2 = heap.store(-3453.3252e2); 

/* 
    Hier gebe ich mit heap.load aus, mit heap.free entferne ich was, mit .gc raeume ich mal auf
*/
console.log("--- Lauf 1");
console.log("used: "+heap.used);
console.log("space: "+heap.byteLength);
console.log(heap.load(sp1));
console.log(heap.load(sp2));
console.log(heap.load(int1));
console.log(heap.load(int2));
console.log(heap.load(d1));
console.log(heap.load(d2));
console.log(heap.load(s1));
console.log(heap.load(s2));
heap.free(sp1);
heap.free(sp2);
console.log("Nach Delete sind die Strings nicht mehr zu laden");
console.log(heap.load(sp1));
console.log(heap.load(sp2));
heap.gc();

console.log("--- Lauf 2");
console.log("Nach GC und wechseln des Spaces");
console.log("used: "+heap.used);
console.log("space: "+heap.byteLength);
console.log(heap.load(sp1));
console.log(heap.load(sp2));
console.log(heap.load(int1));
console.log(heap.load(int2));
console.log(heap.load(d1));
console.log(heap.load(d2));
console.log(heap.load(s1));
console.log(heap.load(s2));
var str = "Das sind jetzt genug Zeichen um den Heap zu ueberlaufen.";
var p;
for (var i = 0; i < 50; i++) {
    p = heap.store(str);
}
console.log("--- Lauf 3");
console.log("used: "+heap.used);
console.log("space: "+heap.byteLength);
console.log(heap.load(p));
console.log(heap.load(sp1));
console.log(heap.load(sp2));
console.log(heap.load(int1));
console.log(heap.load(int2));
console.log(heap.load(d1));
console.log(heap.load(d2));
console.log(heap.load(s1));
console.log(heap.load(s2));
for (var i = 0; i < 250; i++) {
    p = heap.store(str);
}
console.log("--- Lauf 4");
console.log("used: "+heap.used);
console.log("space: "+heap.byteLength);
console.log(heap.load(p));
console.log(heap.load(sp1));
console.log(heap.load(sp2));
console.log(heap.load(int1));
console.log(heap.load(int2));
console.log(heap.load(d1));
console.log(heap.load(d2));
console.log(heap.load(s1));
console.log(heap.load(s2));


console.log("--- Versuch 5");
var Obj1 = { "ich": "war", "ein": "object", 001: "ist eine zahl", "tostring": function () { return "tostringed"; } };
var Arr1 = [ "ich", "habe", "einige", "freihe", "felder" ];

var obj1 = heap.store(Obj1);
var arr1 = heap.store(Arr1);
var f = heap.store(function (x) { return x*x; });
console.log("used nach object und array: "+heap.used);
console.log("space: "+heap.byteLength);

console.log(JSON.stringify(heap.load(obj1)));
console.log(JSON.stringify(heap.load(arr1)));
console.log(heap.load(f)(12));
console.log(heap.load(obj1)["tostring"]());

"Die zwei undefined fuer die Strings sollten richtig sein, denn die Pointer sollten nicht mehr aktiv sein." Darauf folgend habe ich das Programm noch verbessert. Der Garbage Collector schaltet sich kurz vor dem Ueberlauf ein. Das kann man jetzt noch mehr skalieren. Er macht eine Pause vor dem Ueberlauf. Collect verdoppelt den Speicher dann, sofern alles was uebrig bleibt mehr als die Haelfte belegt.

Update: Verkleinern kann sich der Speicher auch, wenn die groesse nach der Kollektion 1/4 unterschreitet, senke ich den Speicher um die Haelfte. Ob das amortized ist, oder Quatsch, weiss ich nicht, wissen andere aus Erfahrung, vielleicht hoere ich mal von, oder man sagt mir das ;-).

--- Lauf 1
used: 231
space: 2048
Hallo Leute, ich komme aus einem Typed Array.
Hallo Leute, ich bin an einer anderen Stelle.
32768
6664337
3.325253
1242700000000
-2352
-345332.52
Nach Delete sind die Strings nicht mehr zu laden
undefined
undefined
--- Lauf 2
Nach GC und wechseln des Spaces
used: 55
space: 1024
undefined
undefined
32768
6664337
3.325253
1242700000000
-2352
-345332.52
--- Lauf 3
used: 5655
space: 9216
Das sind jetzt genug Zeichen um den Heap zu ueberlaufen.
undefined
undefined
32768
6664337
3.325253
1242700000000
-2352
-345332.52
--- Lauf 4
used: 33655
space: 82944
Das sind jetzt genug Zeichen um den Heap zu ueberlaufen.
undefined
undefined
32768
6664337
3.325253
1242700000000
-2352
-345332.52
--- Versuch 5
used nach object und array: 33877
space: 82944
{"1":"ist eine zahl","ich":"war","ein":"object"}
["ich","habe","einige","freihe","felder"]
144
tostringed

In Versuch fuenf sieht man JSON.stringify des restaurierten Objekts und des Arrays. Darauf die Ergebnisse zweier Funktionen, die auf dem Heap landeten und wieder restauriert wurden. Wenn die nicht auf eine lexikalische Umgebung zugreifen, funktionieren die nach dem load dann wieder.

Die map im Backend, welche die Pointer zu den Objektproperties haelt, die muss selbst noch serialisiert werden. Dazu muss ich den Code noch etwas ergaenzen und die map nochmal konvertieren.

Gottseidank

Mittwoch

Gottseidank habe ich die Seiten gedruckt. Ich kann die Fehler in meiner zwischen Browser- und Editorfenster, oder PDF und Editorfenster, oder aus losen Erinnerungen zusammengestellten Erstimplementierung deutlich erkennen.

Ich ueberlege ob ich das Objekt nicht noch von der ObjektEnvironment getrennt zu behandeln habe und es in selbige nur eingepluggt wird. Das wird sich aus den Operationen ergeben, mit wem ich nun arbeite. Ich bin gespannt.

Ich bin mir sicher, hier am Bildschirm zwar viel gelesen und verstanden zu haben, aber auch was wichtiges (ungefaehr fuenf Zeilen tief) uebersehen zu haben, um die Zusammenhaenge richtig zu sehen.

Richtig vermutet!

Andere Themen am Mittwoch: Garbage Collection Lektion aus CS61B (Berkeley). Entdeckung von CS61C auf YouTube (Machine Structures! Das ganze Semester! Ein MUSS fuer mich!) Kurzes Update meiner hiphop Seite, einfach eine Weile liegenbleiben zu koennen.

(Code weiter oben)

Es ist soweit

Dienstag

Heute habe ich in der PSB die ersten 200 Seiten des ES6 Drafts drucken koennen. 4 auf 1 Seite und sehr gut lesbar. Endlich kann ich die Funktionen umsetzen. Beides am Bildschirm, Draft und Source, ist fast unmoeglich. Jetzt ist alles uebersichtlich und ich schaetze bald schnell voran zu kommen. Ich habe gerade erstmal riesigen Spass beim lesen.

    // letztens schon auf den ersten Seiten entdeckt

    function floor(x) {
	return x - (x % 1);
    }
    
    function abs(x) {
	return x < 0 ? -x : x;
    }
    
    function sign(x) {
	return x < 0 ? -1 : 1;
    }
    
    // noch ohne zuhilfenahme des dokuments, ein +  (temp.)
    
    function ToNumber (V) {
	return +V;
    }
    
    // tabellen setze ich ueberall ein, order one
    
    var ReturnZero = {
	"NaN": true,
	"Infinity": true,
	"-Infinity": true,
	"0": true
    };
    
    // diese spezifikation ist leserlich
    
    function ToUint16(V) {
	var number = ToNumber(V);
	if (number=ifAbrupt(number) && isAbrupt(number)) return number;
	if (ReturnZero[number]) return +0;
	var int = sign(number) * floor(abs(number));
	var int16bit = int % (1<<16);
	return int16bit;
    }

Excellentes Feeling, lesen und schreiben zu können, ohne dass das eine dem anderen in den Weg kommt.

Ausserdem habe ich Funktionen wie IntegerIndexedObjectCreate, IntegerIndexedElementGet und IntegerIndexedElementSet noch nicht beachten koennen. Diese zum Beispiel erzaehlen, wie ich einen Typed Array kreire. Andere Funktionen wie CheckObjectCoercible, die eine abnormale Completion, eine Throw TypeError Completion generieren soll, wenn man null oder undefined coercen will, sind ebenso einfach zu lesen.

Das ist jetzt übersichtlich wie noch nie.

Debugger Statement

Eine gute Idee ist, das Debugger Statement den aktuellen ExecutionContext ausgeben zu lassen. So kann ich zwischendurch kontrollieren.

Was ich dringend brauche, es gibt keinen Array. Ausserdem ist die MemberExpression noch nicht darauf eingerichtet, bei einer AssignmentExpression zu versuchen, sie zu erzeugen. Das habe ich weiter unten schon bemerkt.

Wichtig ist ausserdem, ich hatte in den letzten Tagen bereits verstanden, durchgelesen und vorformuliert, dass ich die globale Environment falsch begonnen habe. Die muss ich berichtigen. Dann sind die Primitive und Reference Type Wrapper in Betrieb zu nehmen.

linux-qc59:/l # n shell
js> debugger;
DebuggerStatement at line (unknown), (unknown)

Execution Context: state
null
Execution Context: realm
{ Intrinsics: 
   { '%Object%': [Function: Object],
     '%ObjectPrototype%': {},
     '%Array%': [Function: Array],
     '%ArrayPrototype%': [],
     '%ObjectPrototype_toString%': [Function],
     '%Symbol%': 
      { type: 'function',
        thisValue: null,
        bindings: {},
        outer: null,
        varEnv: [Object],
        lexEnv: [Object],
        HomeObject: null,
        Code: null,
        FormalParameters: [],
        Construct: [Function: new_Symbol],
        Call: [Function: new_Symbol] } },
  globalThis: 
   { type: 'object',
     HomeObject: null,
     Integrity: '',
     bindings: {},
     outer: null,
     Private: false },
  globalEnv: { bindings: {}, outer: null },
  loader: null }
Execution Context: varEnv
{ bindings: 
   { Symbol: 
      { name: 'Symbol',
        value: [Object],
        writeable: true,
        init: true,
        deleteable: undefined } },
  outer: { bindings: {}, outer: null } }
Execution Context: lexEnv
{ bindings: 
   { Symbol: 
      { name: 'Symbol',
        value: [Object],
        writeable: true,
        init: true,
        deleteable: undefined } },
  outer: { bindings: {}, outer: null } }
DebuggerStatement End
undefined
js> 

Sieht immernoch aus wie vor ein paar Tagen. Uebrigens ist die Completion nicht korrekt eingebaut. Als ich meine "jsval" (von spidermonkey inspirierter Name) verwendete, um das Ergebnis der letzten ToValue zu speichern, war alles in Ordnung. Als ich die Completion einbaute bin ich rasch müde geworden und hab in die Loops cmpl = ToValue(body[i]) geschrieben und die dann einfach zurueckgegeben. Jetzt wird bei einer neuen Eingabe die nicht mehr zurueck auf undefined gesetzt. Und nachdem ich breaks, throws und returns gegen die Completion getauscht hatte, liefen auch die nicht mehr, weil ich den Code zum Auswerten müde geworden nicht mehr geschrieben hatte.

Just in diesem Moment ruft mein Schatz mich und fragt ob ich denn gehe. Ich hatte vorgeschlagen, Pfandflaschen sammeln zu gehen, um was einzukaufen, für uns und unsere Kaninchen. Jetzt im Sommer liegen mehr Flaschen rum, als im Winter. Man kann gut von Leben und ich verkaufe keine Obdachlosenzeitungen über den Zeitraum.

Jobcenter

Apropos Flaschen sammeln. Im Juli hat das Jobcenter mir bejaht, dass ich eine 6-monatige LPI-1 & LPI-2 Weiterbildung machen will. Ich hatte die Buecher dazu vor dreizehn Jahren schonmal gehabt. Das ist heute natuerlich mehr, moderner und besser. Aber auch was, wo ich mit Leib und Seele sozusagen mitmache.

Diesen Monat, ich war am 2. da, haben sie ihre Meinung geaendert, es wuerde ja Geld kosten, wenn ich sowas machen will. Meine Familie hat mich selbst in diese Hartz-IV Falle gebracht. Es war auf Veranlassung meiner .. lassen wir das mal .. Nun darf ich mich seit Jahren damit rumschlagen, dass man nicht unterstuetzt wird, wenn man noch nichts hat. Es koennte ja Geld kosten. Schoen ist, dass gerade eben erst eine Studie das wiederholt bewiesen hat. Alles was ich kann habe ich mir darum selbst beigebracht. Ich hatte noch nie eine Chance, weil meine Familie mir mit 20 das Leben aus der Hand gerissen hat und mich die zehn Jahre die folgen auch noch verarscht hat. Jetzt bin ich die zwar los und dafuer quer durch Deutschland gezogen, aber werde das Problem nicht los. Meine ... hat mich hier in der Stadt noch zum Amt gekriegt, weil sie mich nicht unterstuetzen wollten. Am Geld lag es nicht. Nur an ihrem Verstand. Naja, die Haelfte der Geschichte habe ich schon gerappt und den Rest nehme ich irgendwann in 1A-Qualitaet auf, darum muss ich mich nicht dran stoeren.

Heap

Aller Anfang ist schwer. Wer einen Bytecode will, braucht einen Heap. Im JavaScript sind die ArrayBuffer dafuer geeignet, das ist mir mittlerweile auch so zu Ohren gekommen. Gestern abend habe ich dann den zweiten Versuch unternommen, einen ArrayBuffer als Heap zu verwenden. Ich bin nach einen Alignment von byteLength * 2 fuer den String dann, beim gucken von 6.172 Performance Engineering dann eingeschlafen. Dort gibt es Stack Allocation, Free Lists, und wenn ich wach geblieben waere, Garbage Collection. Damit geht es dann im folgenden weiter.

Ich ueberlegte gestern abend, ob ich shiften ueben muss (*) oder multiplizieren oder einen Loop, indem ich 1 addiere, wo ich jedesmal % n rechne, probiere (**). Heute habe ich die Loesung gefunden. Hier nochmal mein Kommentar zu den * und **.(*) Ich denke, sinnlos, weil 1 << 1 = 1 * 2 ist, 1 << 2 = 2^2 (1*2*2) ist, oder 60 << 1 = 2 * 60, 60 << 2 = 60*2*2 = 240 ist. Und rechts nur :2 bedeutet. (**) Einfach ist es so, ich habe z.B. 431 Bytes (Int8Array) belegt. Suche das naechsten 8er Alignment, nehme dafuer 1* den Modulus, anstatt in einem Loop. 431 % 8 = 7. und 8-7 = 1, das heisst, ich addiere noch 1 auf die 431 Bytes (432) und habe einen zum Float64 passenden Offset.

/*
    Wer Bytecode will muss leiden (ueben).
*/

var heap = new ArrayBuffer(2048); // 2KB
var string = "Hallo Leute, ich komme aus einem Typed Array.";
var string2 = "Hallo Leute, ich bin an einer anderen Stelle.";
var allocator = { sp: 0 }; // tmp

/*
    Lesen vom Heap
*/

function Load(buf, view) {
    "use strict";
    var data;
    if (view instanceof Int32Array) {
	data = "";
	for (var i=0, j=view.length; i < j; i++) {
	    data += String.fromCharCode(view[i]);
	}
	return data;
    } else if (view instanceof Float64Array) {
	return view[0];
    }
}

/*
    Speichern auf der Halde
*/

function Store(buf, data, size) {
    "use strict";
    var sp = allocator.sp;
    var offset, view;
    if (typeof data === "string") {	
	if (size === undefined) size = data.length;
	offset = sp + (4 - (sp % 4)); // align
	view = new Int32Array(buf, offset, size);
	for (var i=0; i < size; i++) {
	    view[i] = data.charCodeAt(i);
	}
	allocator.sp = offset + view.byteLength - 1;
	return view;
    } else if (typeof data === "number") {
	size = 1;
	offset = sp + (8 - (sp % 8)); // align
	view = new Float64Array(buf, offset, size);
	view[0] = data;
	allocator.sp = offset + view.byteLength - 1;
	return view;
    }
}

/*
    die Store Funktion gibt Referenzen auf den Heap zurueck (tmp den view statt einen ptr-record mit offset und size oder anderer info)
*/

var sp1 = Store(heap, string);
var sp2 = Store(heap, string2);
var int1 = Store(heap, 32768); 
var int2 = Store(heap, 6664337); 
var d1 = Store(heap, 3.325253); 
var d2 = Store(heap, 124.27e10); 
var s1 = Store(heap, -2352); 
var s2 = Store(heap, -3453.3252e2); 

/* 
    Die Load Funktion holt das, worauf die Referenz zeigt, zurueck 
*/

console.log(Load(heap, sp1));
console.log(Load(heap, sp2));
console.log(Load(heap, int1));
console.log(Load(heap, int2));
console.log(Load(heap, d1));
console.log(Load(heap, d2));
console.log(Load(heap, s1));
console.log(Load(heap, s2));

Nach ein paar Versuchen funktionierte das dann. Das Alignment habe ich zum Teil verstanden. Ich denke aber, ich kann da mit den Bytes noch schlauer umgehen. Und ueberhaupt. Fuer den Bytecode will ich nicht auf Bytes herab, sondern auf Bits. Jedenfalls soweit ich kann. Da habe ich noch viel vor mir. Bislang habe ich nichtmal die Parameter der Builder Funktionen eingegeben, geschweige denn in allen AST-generierenden Funktionen die Builder Funktionen gerufen.

Hallo Leute, ich komme aus einem Typed Array.
Hallo Leute, ich bin an einer anderen Stelle.
32768
6664337
3.325253
1242700000000
-2352
-345332.52

Am Spannendsten ist der Heap mit Mark & Sweep Garbage Collector mit Bread-First Search, dessen Queue und From und To Space. In 6.172 Lektion 10 erklaert der Professor, wie der funktioniert.

Flags

Bestimmt 10 Minuten mit & und | probiert und erst nach 20 Minuten mit + und - abgesegnet. BitFlags. Die Version mit | hatte zur Folge, dass ich das Flag nicht = !Flag setzen konnte. Darum war die Addition und Subtraktion des Vielfachen von 2 am passendsten, ich hatte es auf der node.js Kommandozeile ausprobiert..

function BitFlags (byteLen) {
    
    var bits = new Int8Array(byteLen);
    
    function set (n, v) {
	v = +!!v;
	var i = Math.floor(n / 8);
	if (i > byteLen || n < 0) throw new RangeError("Out of Bounds");
	var k = 1 << (n % 8)
	
	console.log("setze "+n+": "+ !!v ); // debug + entferne
	
	if (v) {
	    if (!(bits[i] & k)) bits[i] += k;
	} else {
	    if (bits[i] & k) bits[i] -= k;
	}
    }
    
    function is (n) {
	var i = Math.floor(n/8);
	if (i > byteLen || n < 0) throw new RangeError("Out of Bounds");
	return !!(bits[i] & (1 << (n % 8)));
    }

    var bf = {
	bits: bits,
	set: set,
	is: is
    };

    return bf;
}
/*
// Tester.js sieht da besser aus... 
// Muss mich noch umgewoehnen, es direkt 
// in function (test) zu probieren

var set1 = BitFlags(1);
var set2 = BitFlags(2);

set1.set(1, false);
set1.set(2, false);
set1.set(3, true);
console.log(set1.is(0));
console.log(set1.is(1));
console.log(set1.is(2));
console.log(set1.is(3));

set1.set(1, false);
set1.set(2, true);
set1.set(3, false);
console.log(set1.is(0));
console.log(set1.is(1));
console.log(set1.is(2));
console.log(set1.is(3));

set1.set(1, true);
set1.set(2, true);
set1.set(3, true);
console.log(set1.is(0));
console.log(set1.is(1));
console.log(set1.is(2));
console.log(set1.is(3));

set1.set(1, false);
set1.set(2, false);
set1.set(3, false);
console.log(set1.is(0));
console.log(set1.is(1));
console.log(set1.is(2));
console.log(set1.is(3));

set2.set(7, true);
set2.set(8, true);
set2.set(12, true);
console.log(set2.is(6));
console.log(set2.is(7));
console.log(set2.is(8));
console.log(set2.is(12));

// --- heraus kommt

setze 1: false
setze 2: false
setze 3: true
false
false
false
true
setze 1: false
setze 2: true
setze 3: false
false
false
true
false
setze 1: true
setze 2: true
setze 3: true
false
true
true
true
setze 1: false
setze 2: false
setze 3: false
false
false
false
false
setze 7: true
setze 8: true
setze 12: true
false
true
true
true
*/

Das sind neben den Views auf dem Heap fuer Zahlen und Strings die ersten Ideen fuer Bitflaggen.

Pre-Shell

Ich hatte vorhin die Idee, doch einfach mal eine Shell mit toValue zu schreiben, dass ich nicht immer Tests brauche oder HTML-Elemente, sondern einfach mal so tippen kann. Das ganze war in 5 Minuten eingehackt. Der wichtigste Fall der zu klaeren ist, ist der, mehrere Umgebungen, die nacheinander wieder reaktiviert werden koennen, wieder suspendiert und nachher nochmal benutzt, zu verwalten.

var syntaxjs = require("./syntax.js").syntaxjs;
var readline = require("readline");
// readline kann von stdin oder von streams lesen und schreiben
var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});
// die prompt funktion sorgt dafuer die eingabe mit syntaxjs auszufuehren
function prompt() {
    rl.question("js> ", function (code) {
	var val;
	if (code === ".quit") {
	    console.log("Quitting the shell");
	    process.exit();
	} else {
	    try {
		val = syntaxjs.toValue(code, true);
	    } catch (ex) {
		val = ex;
	    } finally {
		console.log(val);
		setTimeout(prompt,0);
	    }
	}
    });
}
// der einstiegspunkt fuer den shellautomaten
prompt();

Das funktioniert natuerlich bereits, bringt aber neue Anforderungen mit sich, wie, dass die Environment beim naechsten toValue Call die gleiche ist. Dazu habe ich erstmal shellmode und initialized hinzugefuegt. Sollte shellmode aktiv sein, wird beim ersten mal initialisiert. Sollte man toValue ohne Shellmode rufen, wird die Umgebung automatisch wieder geloescht. [Hier ist also noch eine dicke Luecke.]

linux-qc59:/l # n shell.js
js> var x = 10;
undefined
js> x
10
js> x + 110
120
js> function f(x) { return x*x; }
{ type: 'function',
  thisValue: null,
  bindings: {},
  outer: 
   { bindings: { Symbol: [Object], x: [Object], f: [Object] },
     outer: { bindings: {}, outer: null } },
  varEnv: { bindings: {}, outer: { bindings: [Object], outer: [Object] } },
  lexEnv: { bindings: {}, outer: { bindings: [Object], outer: [Object] } },
  HomeObject: null,
  Code: [ { type: 'ReturnStatement', expression: [Object] } ],
  FormalParameters: 
   [ { type: 'Identifier',
       details: undefined,
       value: 'x',
       computed: 'x',
       loc: [Object] } ] }
js> f(222)
49284
js> .quit
Quitting the shell

Hier sieht man die Funktion anstelle der ToString Ausgabe des Sources. Interessant ist, wenn ich der Funktion f jetzt eine Property zuweisen will, f.a = 10, dass es noch gar nicht geht. Ohne die Kommandozeile haette ich laenger gebraucht. Selbst Tests sind nicht so aussagekraeftig, wie das Eintippen am Shellprompt. Ich werde in Zukunft viel Zeit dadurch sparen und kann mich voll auf die Vervollstaendigung (das ist noch sau-viel) konzentrieren.

Update: Ich habe bereits einige Aaah´s und Oooh´s und Lachkraempfe hinter mir, was dabei rauskommt, wenn ich Standardeingaben ausprobiere. :-) Es ist noch viel (sehr viel) zu tun.

Symbol

Wiederholung von unten. Ein Versuch %Symbol% zu rufen. %Symbol% ist wie %Object% oder %Array% und Co. im Intrinsics Objekt im Code Realm zu finden.

var sym = new Symbol();
typeof sym;

Hier sollte jetzt tester.js was ausgeben:

Im Chrome gibt es auch einen Symbol Constructor, der ein "object" zurueckgibt und ein Argument nimmt, mit dem eine Name Property erzeugt wird. Zuerst dachte ich, das ist vielleicht das Symbol, mittlerweile meine ich, das gehoert zu irgendeinem anderen Programm, was das Wort Symbol verwendet.

Ich hatte meinem Symbol dann auch argList[0] eingebaut, was aber wohl nicht richtig war. Denn das Kapitel ueber "Symbol Exotic Objects" sagt nichts von "name". Darum habe ich den Code wieder geloescht.

linux-qc59:/l # n shell
js> let sym = new Symbol("eddie");
undefined
js> sym.name
eddie
js> 

Hier rede ich von einem [[dicken]] Fehler, den ich machte, den ich aber erst bemerke: Ich habe dem Symbol Constructor gerade die Moeglichkeit hinzugefuegt, den ersten Parameter zu einer Name Property (ImmutableBinding) zu machen. Gefreut hat es mich, dass bei sym.name die MemberExpression ausgewertet wurde. Ich glaube, mit [[GetBindingValue]] hole ich sie von der ObjectEnvironment ab. Wo ich das Draft lese, sollte ich aber wohl [[Get]] nehmen. Insbesondere Doppel-Dreifach-Definitionen sind mir aufgefallen und Hilfsfunktionen, die man haette zusammenfassen koennen (auch in einer streng typisierten speicherunsicheren Sprache). Ich lese immernoch das Draft, anstatt weiter zu programmieren. (Um so einfacher wird es)

Elisions

ECMAScript 6 bietet auch die praktische Moeglichkeit, Elisions, d.h. Auslassungen zu definieren. Beim Zuweisen sehr nuetzlich, weil man so einige Elemente ueberspringen kann.

let a;
[,,,a] = [1,2,3,4];
a;

Hier sollte jetzt tester.js was ausgeben:

Chrome

Ich habe gerade mal meine libX11, glibc upgedated, damit ich einen neuen Chrome starten kann. Ich wollte das Programm mal mit den DevTools durchleuchten. Ich hatte die letzten Wochen nur Firefox und Opera genutzt.

Es gab ein paar doppelte Identifier. Waehrend in Firefox und Opera die letzte Declaration gewinnt, stuerzt Chrome ab. Problem erkannt, Problem gebannt.

SequenceExpression

Endlich einen Bug gefunden, den ich noch nicht kannte. Ich weiss, dass ich bei kommagetrennten Expressions wie a+b,c+d eigentlich eine SequenceExpression generiere. Nur erscheint da keine.

Ich hab gerade (gestern) mal meinen Editor ausprobiert, -damit-, der gibt ja irgendwann den AST vom Eingetippten zurueck. Nur fehlte der Rest hinter dem Komma. Zuerst bei zwei Identifiern, dann bei zwei Additionen. Ich darf nach dem "geschluckten" Komma Operator gucken. Ich kann mich daran erinnern, mal Sequenzen gehabt zu haben. Das war damals im Januar.

( + Expression + ) ist ein ExpressionStatement in der Mozilla Parser API Und SequenceExpression ist eine Expression, Expression [, Expression].

Was mir gelegen kommt, denn ich wollte Expression an sich neu schreiben. Um mit der Covergrammatik von den ArrowFunctions ohne Fehler zurechtzukommen. Die hatte ich noch nicht eingebaut. An der Stelle ist noch eine ES5 Definition, genau wie bei FormalParametersList, verwendet worden, die das neue Verhalten nicht berücksichtigt.

let a = 10, b = 12; let d = 10; 
a, b+d;

Hier sollte jetzt tester.js was ausgeben:

Das Problem ist also, dass die Sequenz gar nicht mehr gelesen wird. Ich kümmere mich gleich drum. Es ist so heiss, dass ich gar nicht am Rechner sitzen will. Aber das muss erledigt werden.

let a = 10, b = 12;  let c = 10; let d = 10; 
((a+b, b+d), c*d) + d + c, a+c;

Hier sollte jetzt tester.js was ausgeben:

Update: Jeder gefixte Bug bringt einen neuen mit sich. Joke. Natuerlich nicht. Normalerweise killt man mit einem Bugfix gleich mehrere andere Bugs. In diesem Fall haben die Klammern aber ganz normale ExpressionStatements zu bleiben und nach der Operatorprecedence (90 = hoechste) einfach als erstes ausgewertet zu werden, beziehungsweise deren Ergebnis an die linke oder rechte Seite der naechsten Expression gegeben zu werden. Ich glaube jetzt kommen bei Klammern auch Sequenzen raus. Ist mit einem der naechsten Edits wieder verschwunden.

Alte Bekannte

(plot.js und BST2.js)

Letzten Monat habe ich gar nicht gemalt, und auch nicht viel mit Mathematik zu tun gehabt. Dabei ist das ein Hobby von mir, was sich durchaus auszuüben lohnt, weil es unendlich Spass macht.

Vielleicht fallen mir ja diesen Monat zur Arbeit an dem Syntaxprogramm ein paar neue Ideen zum Messen der Tests oder zum Zeichnen der ASTs und Bytes ein. Ein paar Ideen dazu hatte ich längst im Kopf gehabt. Kennt man ja. Ein faellt einem viel. Arbeit ist es aber noch mehr.

Warum ein Binary Tree nicht als AST geeignet ist, sieht man an den Keys. Der AST ist einfach ein gerichteter Graph mit einer Wurzel. Bislang habe ich keine Parentpointer gebraucht.

Das andere ist, der AST hat nicht nur left und right nodes. Die werden bei den BinaryExpressions (der langen Liste an Mathesymbolen) gebraucht, aber die Statements haben ganz andere Felder und auch Listen (hier Arrays) werden oefter gebraucht, um zum Beispiel Function Bodies, oder While Bodies zu agglomerieren..

Warum der Plot rechts peinlich ist? Wie ich O und Omega eingesetzt habe. Die zwei Monate darauf habe ich mich ausgiebig mit Upper- und Lowerbounds beschaeftigt. Ich denke hier ist ueberhaupt eine Generalueberholung faellig.

Der BST (das ist BST 2) faellt bei Wiederverwenden auch auf. Die Art, wie key und value ausgegeben werden ist nicht schoen. Auf den ersten Blick dachte ich mir, zu billig. Wenn man gerade beim Thema BST selbst ist, ist es vielleicht nicht so wichtig. Jetzt zaehlt wieder nur, was man sehen kann.

Rechts mit der Beschriftung bin ich nicht einverstanden. Ich habe mir Muehe gegeben, viele Stunden mehr ueber Bounds zu machen.


SetMutableBinding(Lex,Nam,Val)

Die Variablen identifiers, variables und global aus der Januar Version sind oder waren zu entfernen. In der AssignmentExpression gibt es ein AssignmentPattern fuer zwei Arrays. Ich hab jetzt das API ausgewechselt, weil es gar nicht mehr ging. Ich wundere mich, der BindingPattern Code vom Destructuring Assignment ist futsch. Und sehe da zwei AssignmentVarianten, die nur eine sein sollten. Ich glaube in der VariableDeclaration wird das BindingPattern aufgegriffen. Hier ist ein Defekt zu melden. Denn wenn auch die Variable mit dem Teil der AssignmentExpression berechnet wird, kann ich die Variable mit Undefined hoisten und an Zeile xxx zuweisen, was eine Optimierung des AST erfordert. Wenn ich das nicht tu, wird der Code dumm.

8 tests completed in : 43ms
Number of Tests: 8
Executed assertions: 8
Passed assertions: 8
Failed assertions: 0
Unexpected exceptions: 0
PASS: assert: actual=22, expected=22: message=muss 22 sein (var x = 10, y = 12; x += y; x;)
PASS: assert: actual=120, expected=120: message=muss 120 sein (var x = 10, y = 12; x *= y; x;)
PASS: assert: actual=264, expected=264: message=muss 264 sein (var x = 10, y = 12; x *= y; y+=x; y+y;)
PASS: assert: actual=12, expected=12: message=muss 12 sein (var x = 10, y = 12; [y,x]=[x,y];
PASS: assert: actual=12, expected=12: message=muss 12 sein (var x = 10, y = 12; [y,x]=[x,y]; x;
PASS: assert: actual=10, expected=10: message=muss 10 sein (var x = 10, y = 12; [y,x]=[x,y]; y;
PASS: assert: actual=22, expected=22: message=muss 22 sein (var x = 10, y = 12; [y,x]=[x,y]; x+y;
PASS: assert: actual=1.2, expected=1.2: message=muss 1.2 sein (var x = 10, y = 12; [y,x]=[x,y]; x / y;

TypeOf

Das erste Ergebnis der Type(O) Operation von gestern Abend. Ihr Ergebnis wird bei einer UnaryExpression beim Operator "typeof" zurueck gegeben. Ziel ist es, ein Symbol zu erzeugen und mit typeof sym dann den neuen Typen "symbol" zu erhalten. Dazu muessen gleich mehrere Dinge implementiert werden. Die Type Operation und alles, was in der Spec dazu steht, sowie das Intrinsic Objekt %Symbol%, was die interne Funktion dazu ist. Ein [native Code] sozusagen.

EXCEPTION THROWN AT TEST 9
9 tests completed in : 143ms
Number of Tests: 9
Executed assertions: 8
Passed assertions: 8
Failed assertions: 0
Unexpected exceptions: 1
PASS: assert: actual=undefined, expected=undefined: message=undefined ist undefined
PASS: assert: actual=number, expected=number: message=1 ist eine Number
PASS: assert: actual=object, expected=object: message=/expr/i ist ein object
PASS: assert: actual=string, expected=string: message='string' ist ein string
PASS: assert: actual=object, expected=object: message={} ist ein object
PASS: assert: actual=object, expected=object: message=null ist ein object
PASS: assert: actual=boolean, expected=boolean: message=true ist ein boolean
PASS: assert: actual=boolean, expected=boolean: message=false ist ein boolean
EXCEPTION: Exception thrown at function () {
    var r = syn.toValue("var v = new Symbol(); typeof v;");
    this.assert(r, "symbol...: Symbol ist noch nicht definiert oder von mir noch nicht gefunden worden.
ReferenceError: Symbol ist noch nicht definiert oder von mir noch nicht gefunden worden.
    at Identifier (/l/syntax.js:4906:9)
    at ToValue (/l/syntax.js:5368:25)
    at CallExpression (/l/syntax.js:4809:25)
    at ToValue (/l/syntax.js:5368:25)
    at NewExpression (/l/syntax.js:5154:16)
    at ToValue (/l/syntax.js:5368:25)
    at VariableDeclaration (/l/syntax.js:4946:83)
    at ToValue (/l/syntax.js:5368:25)
    at Program (/l/syntax.js:5311:25)
    at ToValue (/l/syntax.js:5368:25)

Wenn man auf ToValue klickt, kriegt man das Ergebnis zurück.

typeof "Hallo Welt";

Wenn das funktioniert, dann funktioniert gleich mehreres.

var sym = new Symbol(); typeof sym;

Im AST ist mir anhand des Callstacks was aufgefallen. New- und CallExpression sind meiner Meinung nach zu einer zusammenzufassen, um [[Construct]] nach Plan rufen zu können.

Beim Betrachten des Callstacks denke ich mir, er liest sich Klasse. Nur doppelt so gross, um in ToValue den Typen und den Handler zu ermitteln. ToValue ist sehr klein, und macht nicht mehr, als die Funktion zu laden, zu rufen. Sie gibt sogar die Completions zurueck,

10 tests completed in : 47ms
Number of Tests: 10
Executed assertions: 10
Passed assertions: 10
Failed assertions: 0
Unexpected exceptions: 0
PASS: assert: actual=function, expected=function: message=f ist eine function
PASS: assert: actual=undefined, expected=undefined: message=undefined ist undefined
PASS: assert: actual=number, expected=number: message=1 ist eine Number
PASS: assert: actual=object, expected=object: message=/expr/i ist ein object
PASS: assert: actual=string, expected=string: message='string' ist ein string
PASS: assert: actual=object, expected=object: message={} ist ein object
PASS: assert: actual=object, expected=object: message=null ist ein object
PASS: assert: actual=boolean, expected=boolean: message=true ist ein boolean
PASS: assert: actual=boolean, expected=boolean: message=false ist ein boolean
PASS: assert: actual=symbol, expected=symbol: message=v ist ein symbol

Juli

Habe ich als juli-2013.html archiviert. Den Rest des Monats habe ich mit Lesen im ECMA Draft verbracht, was ganz schön anstrengend ist, wenn man dafür vor dem Bildschirm sitzen muss und keinen Desktop mehr frei hat und der Computer selbst total ungeeignet zum Arbeiten ist. Alles in allem habe ich das Programm aber aufgeräumt und gut auf die neuen Aufgaben vorbereitet.

Juni

Habe ich als juni-2013.html archiviert. Die Seite ist den Binary Search Trees, dem Splay Tree, AVL Tree gewidmet. Zeigt aber auch eine wichtige Benchmark, die zeigt, wie günstig Objektliterale, und wie gefäährlich Arrays, aber auch Regular Expressions, für die Performance von zum Beispiel Parsern sind. Ausserdem gibt es einen gerichteten Graphen mit konstantem Zugriff auf die Edges, Fibonacci und Recursion Trees, die für die Rekursionsanalyse unverzichtbar sind.