Вячеслав Егоров
Tech Talks NSU, March 2014
// Adding integers.
function add(a, b) {
return a + b;
}
// Adding doubles.
function add(a, b) {
return a + b;
}
// Concatenating strings.
function add(a, b) {
return a + b;
}
// Arrays are just objects with
// properties "0", "1", "2", ...
Array.prototype[1] = "ha!";
var arr = [0, /* hole */ , 2];
arr[1] // => "ha!"
// Making a "class".
function Dog(name, breed) {
this.name = name;
this.breed = breed;
}
Dog.prototype.woof = function () {
/* ... */
};
// Inheriting from a class.
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype =
Object.create(Animal.prototype);
// Another way to create a dog.
var dog = {
name: name,
breed: breed,
woof: function () { /* ... */ }
};
// Yet another way.
function makeADog(name, breed) {
// name and breed are now "private"
return {
woof: function () { /* ... */ }
}
}
// Yet another way.
var Dog = {
woof: function () { /* ... */ }
};
function makeADog() {
var dog = Object.create(Dog);
dog.name = name;
dog.breed = name;
return dog;
}
pointer: xx...x1
function tag(ptr) { // ptr & 1 === 0
return ptr | 1;
}
function untag(val) {
return val & ~1;
}
function isPtr(val) {
return (val & 1) === 1;
}
small integer: xx...x0
function tag(val) {
return val << 1;
}
function untag(val) {
return val >> 1;
}
function isSmi(val) {
return (val & 1) === 0
}
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
p2.z = 5;
var arr = [];
for (var i = 0; i < 101; i++)
arr[i] = Math.sqrt(i);
// Want "real" fast method calls
C.prototype.methodFoo = function () {
/* ... */
};
C.prototype.methodBar = function () {
/* ... */
};
obj.methodFoo();
// Don't want (pseudo-code)
m = LoadProperty(obj, "methodFoo")
CheckIfFunction(m)
Invoke(m, obj)
// Want (pseudo-code)
CheckClass(obj, klass0);
Invoke(methodFoo, obj);
// What is "perf-unfriendly" here?
function buyDog() {
return {
woof: function () {
/* ... */
}
}
}
woof
⇒delete obj.prop
function Load(receiver, property) {
var O = ToObject(receiver);
var P = ToString(property);
var desc = O.[[GetProperty]](P);
if (desc === $undefined) return $undefined;
if (IsDataDescriptor(desc)) return desc.Value;
assert(IsAccessorDescriptor(desc));
var getter = desc.Get;
if (getter === $undefined) return $undefined;
return getter.[[Call]](receiver);
}
JSObject.prototype.[[GetProperty]] = function (P) {
var prop = this.[[GetOwnProperty]](P);
if (prop !== $undefined) return prop;
var proto = this.[[Proto]];
if (proto === $null) return $undefined;
return proto.[[GetPropery]](P);
};
JSObject.prototype.[[GetOwnProperty]] = function (P) {
return this.properties.get(P);
};
function Load(receiver, property) {
var O = ToObject(receiver);
var P = ToString(property);
var desc = O.[[GetProperty]](P);
if (desc === $undefined) return $undefined;
if (IsDataDescriptor(desc)) return desc.Value;
assert(IsAccessorDescriptor(desc));
var getter = desc.Get;
if (getter === $undefined) return $undefined;
return getter.[[Call]](receiver);
}
JSObject.prototype.[[GetProperty]] = function (P) {
var prop = this.[[GetOwnProperty]](P);
if (prop !== $undefined) return prop;
var proto = this.[[Proto]];
if (proto === $null) return $undefined;
return proto.[[GetPropery]](P);
};
JSObject.prototype.[[GetOwnProperty]] = function (P) {
return this. properties.get(P);
};
function Load(receiver, property) {
var O = ToObject(receiver);
var P = ToString(property);
var desc = O.[[GetProperty]](P);
if (desc === $undefined) return $undefined;
if (IsDataDescriptor(desc)) return desc.Value;
assert(IsAccessorDescriptor(desc));
var getter = desc.Get;
if (getter === $undefined) return $undefined;
return getter.[[Call]](receiver);
}
JSObject.prototype.[[GetProperty]] = function (P) {
var prop = this.[[GetOwnProperty]](P);
if (prop !== $undefined) return prop;
var proto = this.[[Proto]];
if (proto === $null) return $undefined;
return proto.[[GetPropery]](P);
};
JSObject.prototype.[[GetOwnProperty]] = function (P) {
return this. properties.get(P);
};
;; Compiled code
mov eax, obj
mov ecx, "foo"
call LoadIC_Initialize
// Runtime system.
function LoadIC_Initialize(obj, prop) {
var lookupResult = obj.lookup(prop);
patch(lookupResult.compile());
return lookupResult.value;
}
// Runtime system.
function LoadIC_Initialize(obj, prop) {
var lookupResult = obj.lookup(prop);
patch(lookupResult.compile());
return lookupResult.value;
}
;; Compiled LoadIC Stub
0xabcdef:
cmp [eax - 1], klass0
jnz LoadIC_Miss
mov eax, [eax + 11]
ret
;; Compiled code
mov eax, obj
mov ecx, "foo"
call 0xabcdef ;; patched!
;; Compiled LoadIC Stub
0xabcdef:
cmp [eax - 1], klass0
jnz LoadIC_Miss
mov eax, [eax + 11]
ret
;; Compiled code
mov eax, obj
mov ecx, "foo"
call 0xabcdef ;; patched!
;; Compiled LoadIC Stub
0xabcdef:
cmp [eax - 1], klass0
jnz LoadIC_Miss
mov eax, [eax + 11]
ret
;; Compiled code
mov eax, obj
mov ecx, "foo"
call 0xabcdef ;; patched!
;; Compiled LoadIC Stub
0xabcdef:
cmp [eax - 1], klass0
jnz LoadIC_Miss
mov eax, [eax + 11]
ret
;; Compiled code
mov eax, obj
mov ecx, "foo"
call 0xabcdef ;; patched!
;; Compiled LoadIC Stub
0xabcdef:
cmp [eax - 1], klass0
jnz LoadIC_Miss
mov eax, [eax + 11]
ret
;; Compiled code
mov eax, obj
mov ecx, "foo"
call 0xabcdef ;; patched!
Vector.prototype.length = function () {
return Math.sqrt(this.x * this.x +
this.y * this.y);
};
Vector.prototype.length = function () {
return Math.sqrt(this.x * this.x +
this.y * this.y);
};
Vector.prototype.length = function () {
return Math.sqrt(this.x * this.x +
this.y * this.y);
};
CheckMap v0, klass
v1 = Load v0, @12
CheckMap v0, klass
v2 = Load v0, @12
d3 = TaggedToDouble v1
d4 = TaggedToDouble v2
d5 = Mul d3, d4
CheckMap v0, klass
v6 = Load v0, @16
CheckMap v0, klass
v7 = Load v0, @16
d8 = TaggedToDouble v6
d9 = TaggedToDouble v7
d10 = Mul d8, d9
d11 = Add d5, d10
CheckMap v0, klass
v1 = Load v0, @12
d3 = TaggedToDouble v1
d5 = Mul d3, d3
v6 = Load v0, @16
d8 = TaggedToDouble v6
d10 = Mul d8, d8
d11 = Add d5, d10