pluotsorbet/context.js

278 строки
7.1 KiB
JavaScript
Исходник Обычный вид История

2014-07-24 01:00:36 +04:00
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
function Context() {
2014-07-24 11:48:21 +04:00
this.frames = [];
}
Context.prototype.current = function() {
var frames = this.frames;
return frames[frames.length - 1];
}
Context.prototype.pushFrame = function(methodInfo, consumes) {
var caller = this.current();
var callee = new Frame(methodInfo);
callee.locals = caller.stack;
callee.localsBase = caller.stack.length - consumes;
this.frames.push(callee);
return callee;
}
Context.prototype.popFrame = function() {
var callee = this.frames.pop();
var caller = this.current();
if (callee.localsBase)
caller.stack.length = callee.localsBase;
return caller;
}
2014-07-26 00:09:49 +04:00
Context.prototype.pushClassInitFrame = function(classInfo) {
if (classInfo.initialized)
return;
2014-07-26 22:42:02 +04:00
classInfo.thread = this.thread;
var syntheticMethod = {
classInfo: {
constant_pool: [
null,
{ class_index: 2, name_and_type_index: 4 },
{ name_index: 3 },
{ bytes: "java/lang/Class" },
{ name_index: 5, signature_index: 6 },
{ bytes: "invoke_clinit" },
{ bytes: "()V" },
{ class_index: 2, name_and_type_index: 8 },
{ name_index: 9, signature_index: 10 },
{ bytes: "init9" },
{ bytes: "()V" },
],
},
code: [
0x2a, // aload_0
0x59, // dup
0x59, // dup
0x59, // dup
0xc2, // monitorenter
2014-07-26 22:42:02 +04:00
0xb7, 0x00, 0x01, // invokespecial <idx=1>
0xb7, 0x00, 0x07, // invokespecial <idx=7>
0xc3, // monitorexit
2014-07-26 22:42:02 +04:00
0xb1, // return
],
exception_table: [],
};
this.current().stack.push(classInfo.getClassObject());
this.pushFrame(syntheticMethod, 1);
}
Context.prototype.backTrace = function() {
var stack = [];
this.frames.forEach(function(frame) {
var methodInfo = frame.methodInfo;
2014-07-26 04:54:34 +04:00
if (!methodInfo || !methodInfo.name)
return;
var className = methodInfo.classInfo.className;
var methodName = methodInfo.name;
var signature = Signature.parse(methodInfo.signature);
var IN = signature.IN;
var args = [];
var lp = 0;
for (var n = 0; n < IN.length; ++n) {
var arg = frame.locals[frame.localsBase + lp];
++lp;
switch (IN[n].type) {
case "long":
case "double":
++lp;
break;
case "object":
if (arg === null)
arg = "null";
else if (arg.class.className === "java/lang/String")
arg = "'" + util.fromJavaString(arg) + "'";
else
arg = "<" + arg.class.className + ">";
}
args.push(arg);
}
2014-07-26 04:54:34 +04:00
stack.push(methodInfo.classInfo.className + "." + methodInfo.name + ":" + frame.ip +
"(" + args.join(",") + ")");
});
return stack.join("\n");
}
2014-07-26 00:09:49 +04:00
Context.prototype.raiseException = function(className, message) {
if (!message)
message = "";
message = "" + message;
var syntheticMethod = {
classInfo: {
constant_pool: [
2014-07-26 00:23:10 +04:00
null,
{ name_index: 2 },
{ bytes: className },
2014-07-26 00:23:10 +04:00
{ tag: TAGS.CONSTANT_String, string_index: 4 },
{ bytes: message },
2014-07-26 00:23:10 +04:00
{ class_index: 1, name_and_type_index: 6 },
{ name_index: 7, signature_index: 8 },
{ bytes: "<init>" },
{ bytes: "(Ljava/lang/String;)V" },
],
},
code: [
2014-07-26 00:24:30 +04:00
0xbb, 0x00, 0x01, // new <idx=1>
0x59, // dup
0x12, 0x03, // ldc <idx=2>
0xb7, 0x00, 0x05, // invokespecial <idx=5>
0xbf // athrow
],
exception_table: [],
};
this.pushFrame(syntheticMethod, 0);
2014-07-26 00:09:49 +04:00
throw VM.Yield;
}
Context.prototype.newString = function(s) {
var obj = CLASSES.newObject(CLASSES.java_lang_String);
var length = s.length;
var chars = CLASSES.newPrimitiveArray("C", length);
for (var n = 0; n < length; ++n)
chars[n] = s.charCodeAt(n);
obj["java/lang/String$value"] = chars;
obj["java/lang/String$offset"] = 0;
obj["java/lang/String$count"] = length;
return obj;
2014-07-24 01:28:32 +04:00
}
2014-07-26 00:09:49 +04:00
2014-07-26 02:24:14 +04:00
Context.prototype.execute = function(stopFrame) {
2014-07-26 00:09:49 +04:00
while (this.current() !== stopFrame) {
try {
VM.execute(this);
} catch (e) {
2014-07-26 04:54:34 +04:00
switch (e) {
case VM.Yield:
break;
case VM.Pause:
return;
default:
2014-07-26 00:09:49 +04:00
throw e;
2014-07-26 04:54:34 +04:00
}
2014-07-26 00:09:49 +04:00
}
}
}
2014-07-26 02:24:14 +04:00
Context.prototype.start = function(stopFrame) {
if (this.current() === stopFrame)
return;
var ctx = this;
2014-07-26 04:54:34 +04:00
ctx.stopFrame = stopFrame;
2014-07-26 02:24:14 +04:00
window.setZeroTimeout(function() {
try {
VM.execute(ctx);
} catch (e) {
2014-07-26 04:54:34 +04:00
switch (e) {
case VM.Yield:
break;
case VM.Pause:
return;
default:
2014-07-26 02:24:14 +04:00
throw e;
2014-07-26 04:54:34 +04:00
}
2014-07-26 02:24:14 +04:00
}
ctx.start(stopFrame);
});
}
2014-07-26 04:54:34 +04:00
Context.prototype.resume = function() {
this.start(this.stopFrame);
}
2014-07-26 22:42:02 +04:00
2014-07-29 12:07:15 +04:00
Context.prototype.block = function(obj, queue, lockLevel, timeout) {
2014-07-29 01:25:23 +04:00
if (!obj[queue])
obj[queue] = [];
obj[queue].push(this);
this.lockLevel = lockLevel;
2014-07-29 12:07:15 +04:00
if (timeout) {
2014-07-29 12:13:42 +04:00
var self = this;
2014-07-29 12:07:15 +04:00
this.lockTimeout = window.setTimeout(function () {
2014-07-29 12:13:42 +04:00
self.lockTimeout = null;
// If the timeout hits, remove us from the queue and resume.
obj[queue].forEach(function(ctx, n) {
if (ctx === self) {
obj[queue][n] = null;
while (ctx.lockLevel-- > 0)
ctx.monitorEnter(obj);
ctx.resume();
}
});
2014-07-29 12:07:15 +04:00
}, timeout);
} else {
this.lockTimeout = null;
}
2014-07-29 01:25:23 +04:00
throw VM.Pause;
}
2014-07-29 12:07:15 +04:00
Context.prototype.unblock = function(obj, queue, notifyAll, callback) {
while (obj[queue] && obj[queue].length) {
var ctx = obj[queue].pop();
if (!ctx)
continue;
2014-07-29 12:13:42 +04:00
if (ctx.lockTimeout !== null)
window.clearTimeout(ctx.lockTimeout);
2014-07-29 12:07:15 +04:00
callback(ctx);
if (!notifyAll)
break;
}
}
2014-07-26 22:42:02 +04:00
Context.prototype.monitorEnter = function(obj) {
var lock = obj.lock;
if (!lock) {
obj.lock = { thread: this.thread, level: 1 };
2014-07-26 22:42:02 +04:00
return;
}
if (lock.thread === this.thread) {
++lock.level;
2014-07-26 22:42:02 +04:00
return;
}
this.block(obj, "ready", 1);
2014-07-26 22:42:02 +04:00
}
Context.prototype.monitorExit = function(obj) {
var lock = obj.lock;
if (lock.thread !== this.thread)
this.raiseException("java/lang/IllegalMonitorStateException");
if (--lock.level > 0) {
2014-07-26 22:42:02 +04:00
return;
}
obj.lock = null;
2014-07-29 12:07:15 +04:00
this.unblock(obj, "ready", false, function(ctx) {
while (ctx.lockLevel-- > 0)
ctx.monitorEnter(obj);
ctx.resume();
2014-07-29 12:07:15 +04:00
});
}
Context.prototype.wait = function(obj, timeout) {
var lock = obj.lock;
if (!lock || lock.thread !== this.thread)
this.raiseException("java/lang/IllegalMonitorStateException");
2014-07-29 12:07:15 +04:00
if (timeout < 0)
this.raiseException("java/lang/IllegalArgumentException");
var lockLevel = lock.level;
while (lock.level > 0)
this.monitorExit(obj);
2014-07-29 12:07:15 +04:00
this.block(obj, "waiting", lockLevel, timeout);
}
Context.prototype.notify = function(obj, notifyAll) {
if (!obj.lock || obj.lock.thread !== this.thread)
this.raiseException("java/lang/IllegalMonitorStateException");
2014-07-29 12:07:15 +04:00
this.unblock(obj, "waiting", notifyAll, function(ctx) {
2014-07-28 23:54:57 +04:00
if (!obj.ready)
obj.ready = [];
2014-07-29 12:07:15 +04:00
obj.ready.push(ctx);
});
2014-07-26 22:42:02 +04:00
}