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';
|
|
|
|
|
2014-08-07 04:04:44 +04:00
|
|
|
function Context(runtime) {
|
2014-07-24 11:48:21 +04:00
|
|
|
this.frames = [];
|
2014-08-07 21:10:54 +04:00
|
|
|
this.runtime = runtime;
|
|
|
|
this.runtime.addContext(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
Context.prototype.kill = function() {
|
|
|
|
this.runtime.removeContext(this);
|
2014-07-26 00:06:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Context.prototype.current = function() {
|
|
|
|
var frames = this.frames;
|
|
|
|
return frames[frames.length - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
Context.prototype.pushFrame = function(methodInfo, consumes) {
|
|
|
|
var caller = this.current();
|
2014-10-01 01:30:56 +04:00
|
|
|
var callee = new Frame(methodInfo, caller.stack, caller.stack.length - consumes);
|
2014-07-26 00:06:48 +04:00
|
|
|
this.frames.push(callee);
|
2014-09-12 01:27:11 +04:00
|
|
|
Instrument.callEnterHooks(methodInfo, caller, callee);
|
2014-07-26 00:06:48 +04:00
|
|
|
return callee;
|
|
|
|
}
|
|
|
|
|
|
|
|
Context.prototype.popFrame = function() {
|
|
|
|
var callee = this.frames.pop();
|
|
|
|
var caller = this.current();
|
2014-09-12 01:27:11 +04:00
|
|
|
Instrument.callExitHooks(callee.methodInfo, caller, callee);
|
2014-07-26 00:06:48 +04:00
|
|
|
if (callee.localsBase)
|
|
|
|
caller.stack.length = callee.localsBase;
|
|
|
|
return caller;
|
|
|
|
}
|
|
|
|
|
2014-07-26 00:09:49 +04:00
|
|
|
Context.prototype.pushClassInitFrame = function(classInfo) {
|
2014-08-07 04:11:27 +04:00
|
|
|
if (this.runtime.initialized[classInfo.className])
|
2014-07-26 00:09:49 +04:00
|
|
|
return;
|
2014-07-26 22:42:02 +04:00
|
|
|
classInfo.thread = this.thread;
|
|
|
|
var syntheticMethod = {
|
2014-09-22 12:46:49 +04:00
|
|
|
syntheticKey: "ClassInitSynthetic:" + classInfo.className,
|
2014-07-26 22:42:02 +04:00
|
|
|
classInfo: {
|
2014-09-26 06:20:41 +04:00
|
|
|
vmc: {},
|
|
|
|
vfc: {},
|
2014-07-26 22:42:02 +04:00
|
|
|
constant_pool: [
|
|
|
|
null,
|
2014-07-29 15:26:03 +04:00
|
|
|
{ tag: TAGS.CONSTANT_Methodref, class_index: 2, name_and_type_index: 4 },
|
|
|
|
{ tag: TAGS.CONSTANT_Class, name_index: 3 },
|
2014-07-26 22:42:02 +04:00
|
|
|
{ bytes: "java/lang/Class" },
|
|
|
|
{ name_index: 5, signature_index: 6 },
|
|
|
|
{ bytes: "invoke_clinit" },
|
|
|
|
{ bytes: "()V" },
|
2014-07-29 15:26:03 +04:00
|
|
|
{ tag: TAGS.CONSTANT_Methodref, class_index: 2, name_and_type_index: 8 },
|
2014-07-26 22:42:02 +04:00
|
|
|
{ name_index: 9, signature_index: 10 },
|
|
|
|
{ bytes: "init9" },
|
|
|
|
{ bytes: "()V" },
|
|
|
|
],
|
|
|
|
},
|
|
|
|
code: [
|
|
|
|
0x2a, // aload_0
|
|
|
|
0x59, // dup
|
2014-07-28 23:42:42 +04:00
|
|
|
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>
|
2014-07-28 23:42:42 +04:00
|
|
|
0xc3, // monitorexit
|
2014-07-26 22:42:02 +04:00
|
|
|
0xb1, // return
|
|
|
|
],
|
|
|
|
exception_table: [],
|
|
|
|
};
|
2014-08-07 03:48:17 +04:00
|
|
|
this.current().stack.push(classInfo.getClassObject(this));
|
2014-07-26 22:42:02 +04:00
|
|
|
this.pushFrame(syntheticMethod, 1);
|
2014-07-26 00:06:48 +04:00
|
|
|
}
|
|
|
|
|
2014-07-26 00:09:49 +04:00
|
|
|
Context.prototype.raiseException = function(className, message) {
|
2014-07-26 00:06:48 +04:00
|
|
|
if (!message)
|
|
|
|
message = "";
|
|
|
|
message = "" + message;
|
|
|
|
var syntheticMethod = {
|
2014-09-22 12:35:12 +04:00
|
|
|
syntheticKey: "RaiseExceptionSynthetic",
|
2014-07-26 00:06:48 +04:00
|
|
|
classInfo: {
|
2014-09-26 06:20:41 +04:00
|
|
|
vmc: {},
|
|
|
|
vfc: {},
|
2014-07-26 00:06:48 +04:00
|
|
|
constant_pool: [
|
2014-07-26 00:23:10 +04:00
|
|
|
null,
|
2014-07-29 15:26:03 +04:00
|
|
|
{ tag: TAGS.CONSTANT_Class, name_index: 2 },
|
2014-07-26 00:06:48 +04:00
|
|
|
{ bytes: className },
|
2014-07-26 00:23:10 +04:00
|
|
|
{ tag: TAGS.CONSTANT_String, string_index: 4 },
|
2014-07-26 00:06:48 +04:00
|
|
|
{ bytes: message },
|
2014-07-29 15:26:03 +04:00
|
|
|
{ tag: TAGS.CONSTANT_Methodref, class_index: 1, name_and_type_index: 6 },
|
2014-07-26 00:23:10 +04:00
|
|
|
{ name_index: 7, signature_index: 8 },
|
2014-07-26 00:06:48 +04:00
|
|
|
{ bytes: "<init>" },
|
|
|
|
{ bytes: "(Ljava/lang/String;)V" },
|
2014-07-26 00:27:21 +04:00
|
|
|
],
|
2014-07-26 00:06:48 +04:00
|
|
|
},
|
|
|
|
code: [
|
2014-07-26 00:24:30 +04:00
|
|
|
0xbb, 0x00, 0x01, // new <idx=1>
|
2014-07-26 00:06:48 +04:00
|
|
|
0x59, // dup
|
2014-07-26 00:25:49 +04:00
|
|
|
0x12, 0x03, // ldc <idx=2>
|
|
|
|
0xb7, 0x00, 0x05, // invokespecial <idx=5>
|
2014-07-26 00:06:48 +04:00
|
|
|
0xbf // athrow
|
|
|
|
],
|
2014-07-26 00:27:21 +04:00
|
|
|
exception_table: [],
|
2014-07-26 00:06:48 +04:00
|
|
|
};
|
|
|
|
this.pushFrame(syntheticMethod, 0);
|
|
|
|
}
|
|
|
|
|
2014-08-26 20:47:51 +04:00
|
|
|
Context.prototype.raiseExceptionAndYield = function(className, message) {
|
|
|
|
this.raiseException(className, message);
|
|
|
|
throw VM.Yield;
|
|
|
|
}
|
|
|
|
|
2014-07-26 02:24:14 +04:00
|
|
|
Context.prototype.execute = function(stopFrame) {
|
2014-09-22 11:40:01 +04:00
|
|
|
Instrument.callResumeHooks(this.current());
|
2014-10-01 04:23:24 +04:00
|
|
|
do {
|
2014-07-26 00:09:49 +04:00
|
|
|
try {
|
|
|
|
VM.execute(this);
|
|
|
|
} catch (e) {
|
2014-07-26 04:54:34 +04:00
|
|
|
switch (e) {
|
|
|
|
case VM.Yield:
|
2014-09-23 12:51:33 +04:00
|
|
|
// Ignore the yield and continue executing instructions on this thread.
|
2014-07-26 04:54:34 +04:00
|
|
|
break;
|
|
|
|
case VM.Pause:
|
2014-09-22 10:59:15 +04:00
|
|
|
Instrument.callPauseHooks(this.current());
|
2014-07-26 04:54:34 +04:00
|
|
|
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-10-01 04:23:24 +04:00
|
|
|
} while (this.current() !== stopFrame);
|
2014-07-26 00:09:49 +04:00
|
|
|
}
|
2014-07-26 02:24:14 +04:00
|
|
|
|
|
|
|
Context.prototype.start = function(stopFrame) {
|
|
|
|
var ctx = this;
|
2014-07-26 04:54:34 +04:00
|
|
|
ctx.stopFrame = stopFrame;
|
2014-10-01 04:23:24 +04:00
|
|
|
|
2014-07-26 02:24:14 +04:00
|
|
|
window.setZeroTimeout(function() {
|
2014-09-23 12:51:33 +04:00
|
|
|
Instrument.callResumeHooks(ctx.current());
|
2014-07-26 02:24:14 +04:00
|
|
|
try {
|
|
|
|
VM.execute(ctx);
|
|
|
|
} catch (e) {
|
2014-07-26 04:54:34 +04:00
|
|
|
switch (e) {
|
|
|
|
case VM.Yield:
|
|
|
|
break;
|
|
|
|
case VM.Pause:
|
2014-09-22 10:59:15 +04:00
|
|
|
Instrument.callPauseHooks(ctx.current());
|
2014-07-26 04:54:34 +04:00
|
|
|
return;
|
|
|
|
default:
|
2014-08-05 22:48:37 +04:00
|
|
|
console.info(e);
|
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
|
|
|
}
|
2014-09-23 12:51:33 +04:00
|
|
|
Instrument.callPauseHooks(ctx.current());
|
2014-10-01 04:23:24 +04:00
|
|
|
|
|
|
|
if (ctx.current() === stopFrame) {
|
|
|
|
ctx.kill();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
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:52:56 +04:00
|
|
|
Context.prototype.block = function(obj, queue, lockLevel) {
|
2014-07-29 01:25:23 +04:00
|
|
|
if (!obj[queue])
|
|
|
|
obj[queue] = [];
|
|
|
|
obj[queue].push(this);
|
2014-07-29 02:14:09 +04:00
|
|
|
this.lockLevel = lockLevel;
|
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;
|
|
|
|
callback(ctx);
|
|
|
|
if (!notifyAll)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-29 13:15:09 +04:00
|
|
|
Context.prototype.wakeup = function(obj) {
|
2014-07-29 13:24:21 +04:00
|
|
|
if (this.lockTimeout !== null) {
|
|
|
|
window.clearTimeout(this.lockTimeout);
|
|
|
|
this.lockTimeout = null;
|
|
|
|
}
|
2014-07-29 13:15:09 +04:00
|
|
|
if (obj.lock) {
|
2014-07-29 13:24:21 +04:00
|
|
|
if (!obj.ready)
|
|
|
|
obj.ready = [];
|
|
|
|
obj.ready.push(this);
|
2014-07-29 13:15:09 +04:00
|
|
|
} else {
|
|
|
|
while (this.lockLevel-- > 0)
|
|
|
|
this.monitorEnter(obj);
|
|
|
|
this.resume();
|
2014-07-29 12:52:56 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-26 22:42:02 +04:00
|
|
|
Context.prototype.monitorEnter = function(obj) {
|
|
|
|
var lock = obj.lock;
|
|
|
|
if (!lock) {
|
2014-07-29 01:36:12 +04:00
|
|
|
obj.lock = { thread: this.thread, level: 1 };
|
2014-07-26 22:42:02 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (lock.thread === this.thread) {
|
2014-07-29 01:36:12 +04:00
|
|
|
++lock.level;
|
2014-07-26 22:42:02 +04:00
|
|
|
return;
|
|
|
|
}
|
2014-07-29 02:14:09 +04:00
|
|
|
this.block(obj, "ready", 1);
|
2014-07-26 22:42:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Context.prototype.monitorExit = function(obj) {
|
|
|
|
var lock = obj.lock;
|
2014-07-29 00:49:14 +04:00
|
|
|
if (lock.thread !== this.thread)
|
2014-08-26 20:47:51 +04:00
|
|
|
this.raiseExceptionAndYield("java/lang/IllegalMonitorStateException");
|
2014-07-29 01:36:12 +04:00
|
|
|
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) {
|
2014-07-29 13:15:09 +04:00
|
|
|
ctx.wakeup(obj);
|
2014-07-29 12:07:15 +04:00
|
|
|
});
|
2014-07-28 23:42:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Context.prototype.wait = function(obj, timeout) {
|
2014-07-29 01:36:12 +04:00
|
|
|
var lock = obj.lock;
|
|
|
|
if (!lock || lock.thread !== this.thread)
|
2014-08-26 20:47:51 +04:00
|
|
|
this.raiseExceptionAndYield("java/lang/IllegalMonitorStateException");
|
2014-07-29 12:07:15 +04:00
|
|
|
if (timeout < 0)
|
2014-08-26 20:47:51 +04:00
|
|
|
this.raiseExceptionAndYield("java/lang/IllegalArgumentException");
|
2014-07-29 01:36:12 +04:00
|
|
|
var lockLevel = lock.level;
|
|
|
|
while (lock.level > 0)
|
|
|
|
this.monitorExit(obj);
|
2014-07-29 12:52:56 +04:00
|
|
|
if (timeout) {
|
|
|
|
var self = this;
|
|
|
|
this.lockTimeout = window.setTimeout(function() {
|
|
|
|
obj.waiting.forEach(function(ctx, n) {
|
|
|
|
if (ctx === self) {
|
|
|
|
obj.waiting[n] = null;
|
2014-07-29 13:15:09 +04:00
|
|
|
ctx.wakeup(obj);
|
2014-07-29 12:52:56 +04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}, timeout);
|
|
|
|
} else {
|
|
|
|
this.lockTimeout = null;
|
|
|
|
}
|
|
|
|
this.block(obj, "waiting", lockLevel);
|
2014-07-28 23:42:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Context.prototype.notify = function(obj, notifyAll) {
|
|
|
|
if (!obj.lock || obj.lock.thread !== this.thread)
|
2014-08-26 20:47:51 +04:00
|
|
|
this.raiseExceptionAndYield("java/lang/IllegalMonitorStateException");
|
2014-07-29 12:07:15 +04:00
|
|
|
this.unblock(obj, "waiting", notifyAll, function(ctx) {
|
2014-07-29 13:24:21 +04:00
|
|
|
ctx.wakeup(obj);
|
2014-07-29 12:07:15 +04:00
|
|
|
});
|
2014-07-26 22:42:02 +04:00
|
|
|
}
|
2014-08-07 00:49:46 +04:00
|
|
|
|
2014-08-07 03:53:35 +04:00
|
|
|
Context.prototype.newPrimitiveArray = function(type, size) {
|
2014-08-07 04:04:44 +04:00
|
|
|
return this.runtime.newPrimitiveArray(type, size);
|
2014-08-07 03:53:35 +04:00
|
|
|
}
|
|
|
|
|
2014-08-07 00:52:18 +04:00
|
|
|
Context.prototype.newArray = function(typeName, size) {
|
2014-08-07 04:04:44 +04:00
|
|
|
return this.runtime.newArray(typeName, size);
|
2014-08-07 00:52:18 +04:00
|
|
|
}
|
|
|
|
|
2014-08-07 00:49:46 +04:00
|
|
|
Context.prototype.newMultiArray = function(typeName, lengths) {
|
2014-08-07 04:04:44 +04:00
|
|
|
return this.runtime.newMultiArray(typeName, lengths);
|
2014-08-07 00:49:46 +04:00
|
|
|
}
|
2014-08-07 03:48:17 +04:00
|
|
|
|
|
|
|
Context.prototype.newObject = function(classInfo) {
|
2014-08-07 04:04:44 +04:00
|
|
|
return this.runtime.newObject(classInfo);
|
2014-08-07 03:48:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Context.prototype.newString = function(s) {
|
2014-08-07 04:04:44 +04:00
|
|
|
return this.runtime.newString(s);
|
2014-08-07 03:48:17 +04:00
|
|
|
}
|