diff --git a/build.js b/build.js
index 34ab125..3906db9 100644
--- a/build.js
+++ b/build.js
@@ -10,6 +10,8 @@ const conf = {
version: argv[3]
};
+console.log(conf)
+
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
diff --git a/src/assets/favicon.png b/src/assets/favicon.png
new file mode 100644
index 0000000..76661fd
Binary files /dev/null and b/src/assets/favicon.png differ
diff --git a/src/assets/index.html b/src/assets/index.html
new file mode 100644
index 0000000..8a57f1f
--- /dev/null
+++ b/src/assets/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+ JScript Debugger
+
+
+
+ This is the debugger of JScript. It implement the V8 Debugging protocol ,
+ so you can use the devtools in chrome.
+ The debugger is still in early development, so please report any issues to
+ the github repo .
+
+
+
+ Here are the available entrypoints:
+
+ /json/version - version and other stuff about the JScript engine
+ /json/list - a list of all entrypoints
+ /json/protocol - documentation of the implemented V8 protocol
+ /(any target) - websocket entrypoints for debugging
+
+
+
+
+ Running ${NAME} v${VERSION} by ${AUTHOR}
+
+
+
\ No newline at end of file
diff --git a/src/assets/protocol.json b/src/assets/protocol.json
new file mode 100644
index 0000000..246e736
--- /dev/null
+++ b/src/assets/protocol.json
@@ -0,0 +1,2007 @@
+{
+ "version": {
+ "major": "1",
+ "minor": "1"
+ },
+ "domains": [
+ {
+ "domain": "Debugger",
+ "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing\nbreakpoints, stepping through execution, exploring stack traces, etc.",
+ "dependencies": [ "Runtime" ],
+ "types": [
+ { "id": "BreakpointId",
+ "description": "Breakpoint identifier.",
+ "type": "string"
+ },
+ { "id": "CallFrameId",
+ "description": "Call frame identifier.",
+ "type": "string"
+ },
+ { "id": "CallFrame",
+ "description": "JavaScript call frame. Array of call frames form the call stack.",
+ "type": "object",
+ "properties": [
+ { "name": "callFrameId",
+ "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused.",
+ "$ref": "CallFrameId"
+ },
+ { "name": "functionName",
+ "description": "Name of the JavaScript function called on this call frame.",
+ "type": "string"
+ },
+ { "name": "location",
+ "description": "Location of the defining function.",
+ "$ref": "Location"
+ },
+ { "name": "scopeChain",
+ "description": "Scope chain for this call frame.",
+ "type": "array",
+ "items": { "$ref": "Scope" }
+ },
+ { "name": "this",
+ "description": "Always undefined, since `this` is a variable.",
+ "deprecated": true,
+ "$ref": "Runtime.RemoteObject"
+ },
+ { "name": "returnValue",
+ "description": "The value being returned, if the function is at return point.",
+ "optional": true,
+ "$ref": "Runtime.RemoteObject"
+ }
+ ]
+ },
+ { "id": "Location",
+ "description": "Location in the source code.",
+ "type": "object",
+ "properties": [
+ { "name": "scriptId",
+ "description": "Script identifier as reported in the `Debugger.scriptParsed`.",
+ "$ref": "Runtime.ScriptId"
+ },
+ { "name": "lineNumber",
+ "description": "Line number in the script (0-based).",
+ "type": "integer"
+ },
+ { "name": "columnNumber",
+ "description": "Column number in the script (0-based).",
+ "type": "integer"
+ }
+ ]
+ },
+ { "id": "Scope",
+ "description": "Scope definition.",
+ "type": "object",
+ "properties": [
+ { "name": "type",
+ "description": "Scope type.",
+ "type": "string",
+ "enum": [
+ "global",
+ "local",
+ "closure"
+ ]
+ },
+ { "name": "object",
+ "description": "Object representing the scope. The object is a proxy for the scope, unless the scope is global.",
+ "$ref": "Runtime.RemoteObject"
+ },
+ { "name": "name",
+ "optional": true,
+ "type": "string"
+ }
+ ]
+ }
+ ],
+ "commands": [
+ { "name": "enable",
+ "description": "Enables the debugger. This will have some performance penalty.",
+ "parameters": [ ],
+ "returns": [
+ { "name": "debuggerId",
+ "description": "Unique identifier of the debugger.",
+ "experimental": true,
+ "type": "string"
+ }
+ ]
+ },
+ { "name": "disable",
+ "description": "Disables debugging. This will have some performance benefit."
+ },
+
+ { "name": "continueToLocation",
+ "description": "Sets a one-off breakpoint to the specified location, and continues exectuion.",
+ "parameters": [
+ { "name": "location",
+ "description": "Location to continue to.",
+ "$ref": "Location"
+ }
+ ]
+ },
+ { "name": "evaluateOnCallFrame",
+ "description": "Evaluates expression on a given call frame, without triggering any breakpoints. The expression should be pure.",
+ "parameters": [
+ { "name": "callFrameId",
+ "description": "Call frame to evaluate on.",
+ "$ref": "CallFrameId"
+ },
+ { "name": "expression",
+ "description": "Expression to evaluate.",
+ "type": "string"
+ },
+ { "name": "silent",
+ "description": "Ignores any exceptions that may occur during evaluation. Overrides `setPauseOnException` state.",
+ "optional": true,
+ "type": "boolean"
+ },
+ { "name": "returnByValue",
+ "description": "Whether the result is expected to be a JSON object that should be sent by value.",
+ "deprecated": true,
+ "optional": true,
+ "type": "boolean"
+ },
+ { "name": "timeout",
+ "description": "Terminate execution after timing out (number of milliseconds).",
+ "optional": true,
+ "type": "number"
+ }
+ ],
+ "returns": [
+ {
+ "name": "result",
+ "description": "Object wrapper for the evaluation result.",
+ "$ref": "Runtime.RemoteObject"
+ },
+ {
+ "name": "exceptionDetails",
+ "description": "Exception details.",
+ "optional": true,
+ "$ref": "Runtime.ExceptionDetails"
+ }
+ ]
+ },
+ { "name": "getPossibleBreakpoints",
+ "description": "Returns possible locations for breakpoint. scriptId in start and end range locations should be\nthe same.",
+ "parameters": [
+ {
+ "name": "start",
+ "description": "Start of range to search possible breakpoint locations in.",
+ "$ref": "Location"
+ },
+ {
+ "name": "end",
+ "description": "End of range to search possible breakpoint locations in (excluding). When not specified, end\nof scripts is used as end of range.",
+ "optional": true,
+ "$ref": "Location"
+ },
+ {
+ "name": "restrictToFunction",
+ "description": "Only consider locations which are in the same (non-nested) function as start.",
+ "optional": true,
+ "type": "boolean"
+ }
+ ],
+ "returns": [
+ {
+ "name": "locations",
+ "description": "List of the possible breakpoint locations.",
+ "type": "array",
+ "items": {
+ "$ref": "BreakLocation"
+ }
+ }
+ ]
+ },
+ { "name": "getScriptSource",
+ "description": "Returns source for the script with given id.",
+ "parameters": [
+ {
+ "name": "scriptId",
+ "description": "Id of the script to get source for.",
+ "$ref": "Runtime.ScriptId"
+ }
+ ],
+ "returns": [
+ {
+ "name": "scriptSource",
+ "description": "Script source (empty in case of Wasm bytecode).",
+ "type": "string"
+ },
+ {
+ "name": "bytecode",
+ "description": "Wasm bytecode.",
+ "optional": true,
+ "type": "binary"
+ }
+ ]
+ },
+ { "name": "getWasmBytecode",
+ "description": "This command is deprecated. Use getScriptSource instead.",
+ "deprecated": true,
+ "parameters": [
+ {
+ "name": "scriptId",
+ "description": "Id of the Wasm script to get source for.",
+ "$ref": "Runtime.ScriptId"
+ }
+ ],
+ "returns": [
+ {
+ "name": "bytecode",
+ "description": "Script source.",
+ "type": "binary"
+ }
+ ]
+ },
+ { "name": "getStackTrace",
+ "description": "Returns stack trace with given `stackTraceId`.",
+ "experimental": true,
+ "parameters": [
+ {
+ "name": "stackTraceId",
+ "$ref": "Runtime.StackTraceId"
+ }
+ ],
+ "returns": [
+ {
+ "name": "stackTrace",
+ "$ref": "Runtime.StackTrace"
+ }
+ ]
+ },
+ { "name": "pause",
+ "description": "Stops on the next JavaScript statement."
+ },
+ { "name": "pauseOnAsyncCall",
+ "experimental": true,
+ "deprecated": true,
+ "parameters": [
+ {
+ "name": "parentStackTraceId",
+ "description": "Debugger will pause when async call with given stack trace is started.",
+ "$ref": "Runtime.StackTraceId"
+ }
+ ]
+ },
+ { "name": "removeBreakpoint",
+ "description": "Removes JavaScript breakpoint.",
+ "parameters": [
+ {
+ "name": "breakpointId",
+ "$ref": "BreakpointId"
+ }
+ ]
+ },
+ { "name": "restartFrame",
+ "description": "Restarts particular call frame from the beginning.",
+ "deprecated": true,
+ "parameters": [
+ {
+ "name": "callFrameId",
+ "description": "Call frame identifier to evaluate on.",
+ "$ref": "CallFrameId"
+ }
+ ],
+ "returns": [
+ {
+ "name": "callFrames",
+ "description": "New stack trace.",
+ "type": "array",
+ "items": {
+ "$ref": "CallFrame"
+ }
+ },
+ {
+ "name": "asyncStackTrace",
+ "description": "Async stack trace, if any.",
+ "optional": true,
+ "$ref": "Runtime.StackTrace"
+ },
+ {
+ "name": "asyncStackTraceId",
+ "description": "Async stack trace, if any.",
+ "experimental": true,
+ "optional": true,
+ "$ref": "Runtime.StackTraceId"
+ }
+ ]
+ },
+ { "name": "resume",
+ "description": "Resumes JavaScript execution.",
+ "parameters": [
+ {
+ "name": "terminateOnResume",
+ "description": "Set to true to terminate execution upon resuming execution. In contrast\nto Runtime.terminateExecution, this will allows to execute further\nJavaScript (i.e. via evaluation) until execution of the paused code\nis actually resumed, at which point termination is triggered.\nIf execution is currently not paused, this parameter has no effect.",
+ "optional": true,
+ "type": "boolean"
+ }
+ ]
+ },
+ { "name": "searchInContent",
+ "description": "Searches for given string in script content.",
+ "parameters": [
+ {
+ "name": "scriptId",
+ "description": "Id of the script to search in.",
+ "$ref": "Runtime.ScriptId"
+ },
+ {
+ "name": "query",
+ "description": "String to search for.",
+ "type": "string"
+ },
+ {
+ "name": "caseSensitive",
+ "description": "If true, search is case sensitive.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "isRegex",
+ "description": "If true, treats string parameter as regex.",
+ "optional": true,
+ "type": "boolean"
+ }
+ ],
+ "returns": [
+ {
+ "name": "result",
+ "description": "List of search matches.",
+ "type": "array",
+ "items": {
+ "$ref": "SearchMatch"
+ }
+ }
+ ]
+ },
+ { "name": "setAsyncCallStackDepth",
+ "description": "Enables or disables async call stacks tracking.",
+ "parameters": [
+ {
+ "name": "maxDepth",
+ "description": "Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async\ncall stacks (default).",
+ "type": "integer"
+ }
+ ]
+ },
+ { "name": "setBlackboxPatterns",
+ "description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in\nscripts with url matching one of the patterns. VM will try to leave blackboxed script by\nperforming 'step in' several times, finally resorting to 'step out' if unsuccessful.",
+ "experimental": true,
+ "parameters": [
+ {
+ "name": "patterns",
+ "description": "Array of regexps that will be used to check script url for blackbox state.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ ]
+ },
+ { "name": "setBlackboxedRanges",
+ "description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted\nscripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful.\nPositions array contains positions where blackbox state is changed. First interval isn't\nblackboxed. Array should be sorted.",
+ "experimental": true,
+ "parameters": [
+ {
+ "name": "scriptId",
+ "description": "Id of the script.",
+ "$ref": "Runtime.ScriptId"
+ },
+ {
+ "name": "positions",
+ "type": "array",
+ "items": {
+ "$ref": "ScriptPosition"
+ }
+ }
+ ]
+ },
+ { "name": "setBreakpoint",
+ "description": "Sets JavaScript breakpoint at a given location.",
+ "parameters": [
+ {
+ "name": "location",
+ "description": "Location to set breakpoint in.",
+ "$ref": "Location"
+ },
+ {
+ "name": "condition",
+ "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the\nbreakpoint if this expression evaluates to true.",
+ "optional": true,
+ "type": "string"
+ }
+ ],
+ "returns": [
+ {
+ "name": "breakpointId",
+ "description": "Id of the created breakpoint for further reference.",
+ "$ref": "BreakpointId"
+ },
+ {
+ "name": "actualLocation",
+ "description": "Location this breakpoint resolved into.",
+ "$ref": "Location"
+ }
+ ]
+ },
+ { "name": "setInstrumentationBreakpoint",
+ "description": "Sets instrumentation breakpoint.",
+ "parameters": [
+ {
+ "name": "instrumentation",
+ "description": "Instrumentation name.",
+ "type": "string",
+ "enum": [
+ "beforeScriptExecution",
+ "beforeScriptWithSourceMapExecution"
+ ]
+ }
+ ],
+ "returns": [
+ {
+ "name": "breakpointId",
+ "description": "Id of the created breakpoint for further reference.",
+ "$ref": "BreakpointId"
+ }
+ ]
+ },
+ { "name": "setBreakpointByUrl",
+ "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this\ncommand is issued, all existing parsed scripts will have breakpoints resolved and returned in\n`locations` property. Further matching script parsing will result in subsequent\n`breakpointResolved` events issued. This logical breakpoint will survive page reloads.",
+ "parameters": [
+ {
+ "name": "lineNumber",
+ "description": "Line number to set breakpoint at.",
+ "type": "integer"
+ },
+ {
+ "name": "columnNumber",
+ "description": "Offset in the line to set breakpoint at.",
+ "optional": true,
+ "type": "integer"
+ },
+ {
+ "name": "url",
+ "description": "URL of the resources to set breakpoint on.",
+ "optional": true,
+ "type": "string"
+ },
+ {
+ "name": "urlRegex",
+ "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either `url` or\n`urlRegex` must be specified.",
+ "optional": true,
+ "type": "string"
+ },
+ {
+ "name": "scriptHash",
+ "description": "Script hash of the resources to set breakpoint on.",
+ "optional": true,
+ "type": "string"
+ },
+ {
+ "name": "condition",
+ "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the\nbreakpoint if this expression evaluates to true.",
+ "optional": true,
+ "type": "string"
+ }
+ ],
+ "returns": [
+ {
+ "name": "breakpointId",
+ "description": "Id of the created breakpoint for further reference.",
+ "$ref": "BreakpointId"
+ },
+ {
+ "name": "locations",
+ "description": "List of the locations this breakpoint resolved into upon addition.",
+ "type": "array",
+ "items": {
+ "$ref": "Location"
+ }
+ }
+ ]
+ },
+ { "name": "setBreakpointOnFunctionCall",
+ "description": "Sets JavaScript breakpoint before each call to the given function.\nIf another function was created from the same source as a given one,\ncalling it will also trigger the breakpoint.",
+ "experimental": true,
+ "parameters": [
+ {
+ "name": "objectId",
+ "description": "Function object id.",
+ "$ref": "Runtime.RemoteObjectId"
+ },
+ {
+ "name": "condition",
+ "description": "Expression to use as a breakpoint condition. When specified, debugger will\nstop on the breakpoint if this expression evaluates to true.",
+ "optional": true,
+ "type": "string"
+ }
+ ],
+ "returns": [
+ {
+ "name": "breakpointId",
+ "description": "Id of the created breakpoint for further reference.",
+ "$ref": "BreakpointId"
+ }
+ ]
+ },
+ { "name": "setBreakpointsActive",
+ "description": "Activates / deactivates all breakpoints on the page.",
+ "parameters": [
+ {
+ "name": "active",
+ "description": "New value for breakpoints active state.",
+ "type": "boolean"
+ }
+ ]
+ },
+ { "name": "setPauseOnExceptions",
+ "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or\nno exceptions. Initial pause on exceptions state is `none`.",
+ "parameters": [
+ {
+ "name": "state",
+ "description": "Pause on exceptions mode.",
+ "type": "string",
+ "enum": [
+ "none",
+ "uncaught",
+ "all"
+ ]
+ }
+ ]
+ },
+ { "name": "setReturnValue",
+ "description": "Changes return value in top frame. Available only at return break position.",
+ "experimental": true,
+ "parameters": [
+ {
+ "name": "newValue",
+ "description": "New return value.",
+ "$ref": "Runtime.CallArgument"
+ }
+ ]
+ },
+ { "name": "setScriptSource",
+ "description": "Edits JavaScript source live.",
+ "parameters": [
+ {
+ "name": "scriptId",
+ "description": "Id of the script to edit.",
+ "$ref": "Runtime.ScriptId"
+ },
+ {
+ "name": "scriptSource",
+ "description": "New content of the script.",
+ "type": "string"
+ },
+ {
+ "name": "dryRun",
+ "description": "If true the change will not actually be applied. Dry run may be used to get result\ndescription without actually modifying the code.",
+ "optional": true,
+ "type": "boolean"
+ }
+ ],
+ "returns": [
+ {
+ "name": "callFrames",
+ "description": "New stack trace in case editing has happened while VM was stopped.",
+ "optional": true,
+ "type": "array",
+ "items": {
+ "$ref": "CallFrame"
+ }
+ },
+ {
+ "name": "stackChanged",
+ "description": "Whether current call stack was modified after applying the changes.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "asyncStackTrace",
+ "description": "Async stack trace, if any.",
+ "optional": true,
+ "$ref": "Runtime.StackTrace"
+ },
+ {
+ "name": "asyncStackTraceId",
+ "description": "Async stack trace, if any.",
+ "experimental": true,
+ "optional": true,
+ "$ref": "Runtime.StackTraceId"
+ },
+ {
+ "name": "exceptionDetails",
+ "description": "Exception details if any.",
+ "optional": true,
+ "$ref": "Runtime.ExceptionDetails"
+ }
+ ]
+ },
+ { "name": "setSkipAllPauses",
+ "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc).",
+ "parameters": [
+ {
+ "name": "skip",
+ "description": "New value for skip pauses state.",
+ "type": "boolean"
+ }
+ ]
+ },
+ { "name": "setVariableValue",
+ "description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be\nmutated manually.",
+ "parameters": [
+ {
+ "name": "scopeNumber",
+ "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch'\nscope types are allowed. Other scopes could be manipulated manually.",
+ "type": "integer"
+ },
+ {
+ "name": "variableName",
+ "description": "Variable name.",
+ "type": "string"
+ },
+ {
+ "name": "newValue",
+ "description": "New variable value.",
+ "$ref": "Runtime.CallArgument"
+ },
+ {
+ "name": "callFrameId",
+ "description": "Id of callframe that holds variable.",
+ "$ref": "CallFrameId"
+ }
+ ]
+ },
+ { "name": "stepInto",
+ "description": "Steps into the function call."
+ },
+ { "name": "stepOut",
+ "description": "Steps out of the function call."
+ },
+ { "name": "stepOver",
+ "description": "Steps over the statement."
+ }
+ ],
+ "events": [
+ { "name": "breakpointResolved",
+ "description": "Fired when breakpoint is resolved to an actual script and location.",
+ "parameters": [
+ {
+ "name": "breakpointId",
+ "description": "Breakpoint unique identifier.",
+ "$ref": "BreakpointId"
+ },
+ {
+ "name": "location",
+ "description": "Actual breakpoint location.",
+ "$ref": "Location"
+ }
+ ]
+ },
+ { "name": "paused",
+ "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria.",
+ "parameters": [
+ {
+ "name": "callFrames",
+ "description": "Call stack the virtual machine stopped on.",
+ "type": "array",
+ "items": {
+ "$ref": "CallFrame"
+ }
+ },
+ {
+ "name": "reason",
+ "description": "Pause reason.",
+ "type": "string",
+ "enum": [
+ "ambiguous",
+ "assert",
+ "CSPViolation",
+ "debugCommand",
+ "DOM",
+ "EventListener",
+ "exception",
+ "instrumentation",
+ "OOM",
+ "other",
+ "promiseRejection",
+ "XHR"
+ ]
+ },
+ {
+ "name": "data",
+ "description": "Object containing break-specific auxiliary properties.",
+ "optional": true,
+ "type": "object"
+ },
+ {
+ "name": "hitBreakpoints",
+ "description": "Hit breakpoints IDs",
+ "optional": true,
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "asyncStackTrace",
+ "description": "Async stack trace, if any.",
+ "optional": true,
+ "$ref": "Runtime.StackTrace"
+ },
+ {
+ "name": "asyncStackTraceId",
+ "description": "Async stack trace, if any.",
+ "experimental": true,
+ "optional": true,
+ "$ref": "Runtime.StackTraceId"
+ },
+ {
+ "name": "asyncCallStackTraceId",
+ "description": "Never present, will be removed.",
+ "experimental": true,
+ "deprecated": true,
+ "optional": true,
+ "$ref": "Runtime.StackTraceId"
+ }
+ ]
+ },
+ { "name": "resumed",
+ "description": "Fired when the virtual machine resumed execution."
+ },
+ { "name": "scriptFailedToParse",
+ "description": "Fired when virtual machine fails to parse the script.",
+ "parameters": [
+ {
+ "name": "scriptId",
+ "description": "Identifier of the script parsed.",
+ "$ref": "Runtime.ScriptId"
+ },
+ {
+ "name": "url",
+ "description": "URL or name of the script parsed (if any).",
+ "type": "string"
+ },
+ {
+ "name": "startLine",
+ "description": "Line offset of the script within the resource with given URL (for script tags).",
+ "type": "integer"
+ },
+ {
+ "name": "startColumn",
+ "description": "Column offset of the script within the resource with given URL.",
+ "type": "integer"
+ },
+ {
+ "name": "endLine",
+ "description": "Last line of the script.",
+ "type": "integer"
+ },
+ {
+ "name": "endColumn",
+ "description": "Length of the last line of the script.",
+ "type": "integer"
+ },
+ {
+ "name": "hash",
+ "description": "Content hash of the script.",
+ "type": "string"
+ },
+ {
+ "name": "executionContextAuxData",
+ "description": "Embedder-specific auxiliary data.",
+ "optional": true,
+ "type": "object"
+ },
+ {
+ "name": "sourceMapURL",
+ "description": "URL of source map associated with script (if any).",
+ "optional": true,
+ "type": "string"
+ },
+ {
+ "name": "hasSourceURL",
+ "description": "True, if this script has sourceURL.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "isModule",
+ "description": "True, if this script is ES6 module.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "length",
+ "description": "This script length.",
+ "optional": true,
+ "type": "integer"
+ },
+ {
+ "name": "stackTrace",
+ "description": "JavaScript top stack frame of where the script parsed event was triggered if available.",
+ "experimental": true,
+ "optional": true,
+ "$ref": "Runtime.StackTrace"
+ },
+ {
+ "name": "codeOffset",
+ "description": "If the scriptLanguage is WebAssembly, the code section offset in the module.",
+ "experimental": true,
+ "optional": true,
+ "type": "integer"
+ },
+ {
+ "name": "scriptLanguage",
+ "description": "The language of the script.",
+ "experimental": true,
+ "optional": true,
+ "$ref": "Debugger.ScriptLanguage"
+ },
+ {
+ "name": "embedderName",
+ "description": "The name the embedder supplied for this script.",
+ "experimental": true,
+ "optional": true,
+ "type": "string"
+ }
+ ]
+ },
+ { "name": "scriptParsed",
+ "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected\nscripts upon enabling debugger.",
+ "parameters": [
+ {
+ "name": "scriptId",
+ "description": "Identifier of the script parsed.",
+ "$ref": "Runtime.ScriptId"
+ },
+ {
+ "name": "url",
+ "description": "URL or name of the script parsed (if any).",
+ "type": "string"
+ },
+ {
+ "name": "startLine",
+ "description": "Line offset of the script within the resource with given URL (for script tags).",
+ "type": "integer"
+ },
+ {
+ "name": "startColumn",
+ "description": "Column offset of the script within the resource with given URL.",
+ "type": "integer"
+ },
+ {
+ "name": "endLine",
+ "description": "Last line of the script.",
+ "type": "integer"
+ },
+ {
+ "name": "endColumn",
+ "description": "Length of the last line of the script.",
+ "type": "integer"
+ },
+ {
+ "name": "hash",
+ "description": "Content hash of the script.",
+ "type": "string"
+ },
+ {
+ "name": "executionContextAuxData",
+ "description": "Embedder-specific auxiliary data.",
+ "optional": true,
+ "type": "object"
+ },
+ {
+ "name": "isLiveEdit",
+ "description": "True, if this script is generated as a result of the live edit operation.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "sourceMapURL",
+ "description": "URL of source map associated with script (if any).",
+ "optional": true,
+ "type": "string"
+ },
+ {
+ "name": "hasSourceURL",
+ "description": "True, if this script has sourceURL.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "isModule",
+ "description": "True, if this script is ES6 module.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "length",
+ "description": "This script length.",
+ "optional": true,
+ "type": "integer"
+ },
+ {
+ "name": "stackTrace",
+ "description": "JavaScript top stack frame of where the script parsed event was triggered if available.",
+ "experimental": true,
+ "optional": true,
+ "$ref": "Runtime.StackTrace"
+ },
+ {
+ "name": "codeOffset",
+ "description": "If the scriptLanguage is WebAssembly, the code section offset in the module.",
+ "experimental": true,
+ "optional": true,
+ "type": "integer"
+ },
+ {
+ "name": "scriptLanguage",
+ "description": "The language of the script.",
+ "experimental": true,
+ "optional": true,
+ "$ref": "Debugger.ScriptLanguage"
+ },
+ {
+ "name": "debugSymbols",
+ "description": "If the scriptLanguage is WebASsembly, the source of debug symbols for the module.",
+ "experimental": true,
+ "optional": true,
+ "$ref": "Debugger.DebugSymbols"
+ },
+ {
+ "name": "embedderName",
+ "description": "The name the embedder supplied for this script.",
+ "experimental": true,
+ "optional": true,
+ "type": "string"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "domain": "Runtime",
+ "types": [
+ { "id": "ScriptId",
+ "description": "Unique script identifier.",
+ "type": "string"
+ },
+ { "id": "RemoteObjectId",
+ "description": "Unique object identifier.",
+ "type": "string"
+ },
+
+ { "id": "Timestamp",
+ "description": "Number of milliseconds since epoch.",
+ "type": "number"
+ },
+ { "id": "TimeDelta",
+ "description": "Number of milliseconds.",
+ "type": "number"
+ },
+
+ { "id": "UnserializableValue",
+ "description": "Primitive value which cannot be JSON-stringified. Includes values `-0`, `NaN`, `Infinity`,\n`-Infinity`, and bigint literals.",
+ "type": "string"
+ },
+ { "id": "RemoteObject",
+ "description": "Mirror object referencing original JavaScript object.",
+ "type": "object",
+ "properties": [
+ { "name": "type",
+ "description": "Object type.",
+ "type": "string",
+ "enum": [
+ "object",
+ "function",
+ "undefined",
+ "string",
+ "number",
+ "boolean",
+ "symbol"
+ ]
+ },
+ { "name": "subtype",
+ "description": "Object subtype hint. Specified for `object` type values only.",
+ "optional": true,
+ "type": "string",
+ "enum": [
+ "array",
+ "null",
+ "regexp",
+ "date",
+ "map",
+ "set",
+ "generator",
+ "promise"
+ ]
+ },
+ { "name": "className",
+ "description": "Object class (constructor) name. Specified for `object` type values only.",
+ "optional": true,
+ "type": "string"
+ },
+ { "name": "value",
+ "description": "Remote object value in case of primitive values or JSON values (if it was requested).",
+ "optional": true,
+ "type": "any"
+ },
+ { "name": "unserializableValue",
+ "description": "Primitive value which can not be JSON-stringified does not have `value`, but gets this\nproperty.",
+ "optional": true,
+ "$ref": "UnserializableValue"
+ },
+ { "name": "description",
+ "description": "String representation of the object.",
+ "optional": true,
+ "type": "string"
+ },
+ { "name": "objectId",
+ "description": "Unique object identifier (for non-primitive values).",
+ "optional": true,
+ "$ref": "RemoteObjectId"
+ }
+ ]
+ },
+ { "id": "MemberDescriptor",
+ "description": "Object member descriptor, mirroring the object descriptor in JavaScript (https://developer.mozilla.org/en-US/docs/Glossary/Property/JavaScript).",
+ "type": "object",
+ "properties": [
+ { "name": "name",
+ "description": "String representation of the member name.",
+ "type": "string"
+ },
+ { "name": "value",
+ "description": "The value of the member, if it's a field.",
+ "optional": true,
+ "$ref": "RemoteObject"
+ },
+ { "name": "writable",
+ "description": "Whether or not the member is writable. Present only if the member is a field.",
+ "optional": true,
+ "type": "boolean"
+ },
+ { "name": "configurable",
+ "description": "Whether or not the member is configurable.",
+ "type": "boolean"
+ },
+ { "name": "enumerable",
+ "description": "Whether or not the member is enumerable..",
+ "type": "boolean"
+ },
+ { "name": "get",
+ "description": "The getter of the member. Present only if the member is a property and the property has a getter.",
+ "optional": true,
+ "$ref": "RemoteObject"
+ },
+ { "name": "set",
+ "description": "The setter of the member. Present only if the member is a property and the property has a setter.",
+ "optional": true,
+ "$ref": "RemoteObject"
+ },
+ { "name": "wasThrown",
+ "description": "True if the member's value was thrown during the evaluation of the getter.",
+ "optional": true,
+ "type": "boolean"
+ },
+ { "name": "isOwn",
+ "description": "True if the property is owned for the object.",
+ "optional": true,
+ "type": "boolean"
+ },
+ { "name": "symbol",
+ "description": "If the member name is a symbol, this will be the reference of the symbol.",
+ "optional": true,
+ "$ref": "RemoteObject"
+ }
+ ]
+ },
+ { "id": "CallArgument",
+ "description": "Represents function call argument. Only one of the three optional fields should be specified, or none for undefined.",
+ "type": "object",
+ "properties": [
+ { "name": "value",
+ "description": "Primitive value or serializable javascript object.",
+ "optional": true,
+ "type": "any"
+ },
+ { "name": "unserializableValue",
+ "description": "Primitive value which can not be JSON-stringified.",
+ "optional": true,
+ "$ref": "UnserializableValue"
+ },
+ { "name": "objectId",
+ "description": "Remote object handle.",
+ "optional": true,
+ "$ref": "RemoteObjectId"
+ }
+ ]
+ },
+ { "id": "ExceptionDetails",
+ "description": "Detailed information about an exception.",
+ "type": "object",
+ "properties": [
+ { "name": "exceptionId",
+ "description": "Exception id.",
+ "type": "integer"
+ },
+ { "name": "text",
+ "description": "The string representation of the exception.",
+ "type": "string"
+ },
+ { "name": "exception",
+ "description": "Exception object if available.",
+ "optional": true,
+ "$ref": "RemoteObject"
+ },
+
+ { "name": "stackTrace",
+ "description": "JavaScript stack trace if available.",
+ "$ref": "StackTrace"
+ },
+ { "name": "scriptId",
+ "description": "Script ID of the exception location.",
+ "$ref": "ScriptId"
+ },
+ { "name": "lineNumber",
+ "description": "Line number of the exception location (0-based).",
+ "type": "integer"
+ },
+ { "name": "columnNumber",
+ "description": "Column number of the exception location (0-based).",
+ "type": "integer"
+ }
+ ]
+ },
+ { "id": "CallFrame",
+ "description": "Stack entry for runtime errors and assertions.",
+ "type": "object",
+ "properties": [
+ { "name": "functionName",
+ "description": "JavaScript function name.",
+ "type": "string"
+ },
+ { "name": "scriptId",
+ "description": "JavaScript script id.",
+ "$ref": "ScriptId"
+ },
+ { "name": "lineNumber",
+ "description": "JavaScript script line number (0-based).",
+ "type": "integer"
+ },
+ { "name": "columnNumber",
+ "description": "JavaScript script column number (0-based).",
+ "type": "integer"
+ }
+ ]
+ },
+ { "id": "StackTrace",
+ "description": "Call frames for assertions or error messages.",
+ "type": "object",
+ "properties": [
+ { "name": "description",
+ "description": "String label of this stack trace. For async traces this may be a name of the function that\ninitiated the async call.",
+ "optional": true,
+ "type": "string"
+ },
+ { "name": "callFrames",
+ "description": "A list of all the frames in the stack trace (excluding internal functions).",
+ "type": "array",
+ "items": { "$ref": "CallFrame" }
+ }
+ ]
+ }
+ ],
+ "commands": [
+ { "name": "awaitPromise",
+ "description": "Add handler to promise with given promise object id.",
+ "parameters": [
+ { "name": "promiseObjectId",
+ "description": "Identifier of the promise.",
+ "$ref": "RemoteObjectId"
+ },
+ { "name": "returnByValue",
+ "description": "Whether the result is expected to be a JSON object that should be sent by value.",
+ "optional": true,
+ "type": "boolean"
+ }
+ ],
+ "returns": [
+ { "name": "result",
+ "description": "Promise result. Will contain rejected value if promise was rejected.",
+ "$ref": "RemoteObject"
+ },
+ { "name": "exceptionDetails",
+ "description": "Exception details if stack strace is available.",
+ "optional": true,
+ "$ref": "ExceptionDetails"
+ }
+ ]
+ },
+ { "name": "callFunctionOn",
+ "description": "Calls function with given declaration on the given object.",
+ "parameters": [
+ { "name": "functionDeclaration",
+ "description": "Declaration of the function to call.",
+ "type": "string"
+ },
+ { "name": "objectId",
+ "description": "Identifier of the object to call function on. Either objectId or executionContextId should be specified.",
+ "optional": true,
+ "$ref": "RemoteObjectId"
+ },
+ { "name": "arguments",
+ "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target\nobject.",
+ "optional": true,
+ "type": "array",
+ "items": {
+ "$ref": "CallArgument"
+ }
+ },
+ { "name": "silent",
+ "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause\nexecution. Overrides `setPauseOnException` state.",
+ "optional": true,
+ "type": "boolean"
+ },
+ { "name": "returnByValue",
+ "description": "Whether the result is expected to be a JSON object which should be sent by value.",
+ "optional": true,
+ "type": "boolean"
+ },
+ { "name": "awaitPromise",
+ "description": "Whether execution should `await` for resulting value and return once awaited promise is\nresolved.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "objectGroup",
+ "description": "Symbolic group name that can be used to release multiple objects. If objectGroup is not\nspecified and objectId is, objectGroup will be inherited from object.",
+ "optional": true,
+ "type": "string"
+ },
+ {
+ "name": "throwOnSideEffect",
+ "description": "Whether to throw an exception if side effect cannot be ruled out during evaluation.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "generateWebDriverValue",
+ "description": "Whether the result should be serialized according to https://w3c.github.io/webdriver-bidi.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ }
+ ],
+ "returns": [
+ {
+ "name": "result",
+ "description": "Call result.",
+ "$ref": "RemoteObject"
+ },
+ {
+ "name": "exceptionDetails",
+ "description": "Exception details.",
+ "optional": true,
+ "$ref": "ExceptionDetails"
+ }
+ ]
+ },
+ {
+ "name": "compileScript",
+ "description": "Compiles expression.",
+ "parameters": [
+ {
+ "name": "expression",
+ "description": "Expression to compile.",
+ "type": "string"
+ },
+ {
+ "name": "sourceURL",
+ "description": "Source url to be set for the script.",
+ "type": "string"
+ },
+ {
+ "name": "persistScript",
+ "description": "Specifies whether the compiled script should be persisted.",
+ "type": "boolean"
+ },
+ {
+ "name": "executionContextId",
+ "description": "Specifies in which execution context to perform script run. If the parameter is omitted the\nevaluation will be performed in the context of the inspected page.",
+ "optional": true,
+ "$ref": "ExecutionContextId"
+ }
+ ],
+ "returns": [
+ {
+ "name": "scriptId",
+ "description": "Id of the script.",
+ "optional": true,
+ "$ref": "ScriptId"
+ },
+ {
+ "name": "exceptionDetails",
+ "description": "Exception details.",
+ "optional": true,
+ "$ref": "ExceptionDetails"
+ }
+ ]
+ },
+ {
+ "name": "disable",
+ "description": "Disables reporting of execution contexts creation."
+ },
+ {
+ "name": "discardConsoleEntries",
+ "description": "Discards collected exceptions and console API calls."
+ },
+ {
+ "name": "enable",
+ "description": "Enables reporting of execution contexts creation by means of `executionContextCreated` event.\nWhen the reporting gets enabled the event will be sent immediately for each existing execution\ncontext."
+ },
+ {
+ "name": "evaluate",
+ "description": "Evaluates expression on global object.",
+ "parameters": [
+ {
+ "name": "expression",
+ "description": "Expression to evaluate.",
+ "type": "string"
+ },
+ {
+ "name": "objectGroup",
+ "description": "Symbolic group name that can be used to release multiple objects.",
+ "optional": true,
+ "type": "string"
+ },
+ {
+ "name": "includeCommandLineAPI",
+ "description": "Determines whether Command Line API should be available during the evaluation.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "silent",
+ "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause\nexecution. Overrides `setPauseOnException` state.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "contextId",
+ "description": "Specifies in which execution context to perform evaluation. If the parameter is omitted the\nevaluation will be performed in the context of the inspected page.\nThis is mutually exclusive with `uniqueContextId`, which offers an\nalternative way to identify the execution context that is more reliable\nin a multi-process environment.",
+ "optional": true,
+ "$ref": "ExecutionContextId"
+ },
+ {
+ "name": "returnByValue",
+ "description": "Whether the result is expected to be a JSON object that should be sent by value.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "generatePreview",
+ "description": "Whether preview should be generated for the result.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "userGesture",
+ "description": "Whether execution should be treated as initiated by user in the UI.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "awaitPromise",
+ "description": "Whether execution should `await` for resulting value and return once awaited promise is\nresolved.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "throwOnSideEffect",
+ "description": "Whether to throw an exception if side effect cannot be ruled out during evaluation.\nThis implies `disableBreaks` below.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "timeout",
+ "description": "Terminate execution after timing out (number of milliseconds).",
+ "experimental": true,
+ "optional": true,
+ "$ref": "TimeDelta"
+ },
+ {
+ "name": "disableBreaks",
+ "description": "Disable breakpoints during execution.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "replMode",
+ "description": "Setting this flag to true enables `let` re-declaration and top-level `await`.\nNote that `let` variables can only be re-declared if they originate from\n`replMode` themselves.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "allowUnsafeEvalBlockedByCSP",
+ "description": "The Content Security Policy (CSP) for the target might block 'unsafe-eval'\nwhich includes eval(), Function(), setTimeout() and setInterval()\nwhen called with non-callable arguments. This flag bypasses CSP for this\nevaluation and allows unsafe-eval. Defaults to true.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "uniqueContextId",
+ "description": "An alternative way to specify the execution context to evaluate in.\nCompared to contextId that may be reused across processes, this is guaranteed to be\nsystem-unique, so it can be used to prevent accidental evaluation of the expression\nin context different than intended (e.g. as a result of navigation across process\nboundaries).\nThis is mutually exclusive with `contextId`.",
+ "experimental": true,
+ "optional": true,
+ "type": "string"
+ },
+ {
+ "name": "generateWebDriverValue",
+ "description": "Whether the result should be serialized according to https://w3c.github.io/webdriver-bidi.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ }
+ ],
+ "returns": [
+ {
+ "name": "result",
+ "description": "Evaluation result.",
+ "$ref": "RemoteObject"
+ },
+ {
+ "name": "exceptionDetails",
+ "description": "Exception details.",
+ "optional": true,
+ "$ref": "ExceptionDetails"
+ }
+ ]
+ },
+ {
+ "name": "getIsolateId",
+ "description": "Returns the isolate id.",
+ "experimental": true,
+ "returns": [
+ {
+ "name": "id",
+ "description": "The isolate id.",
+ "type": "string"
+ }
+ ]
+ },
+ {
+ "name": "getHeapUsage",
+ "description": "Returns the JavaScript heap usage.\nIt is the total usage of the corresponding isolate not scoped to a particular Runtime.",
+ "experimental": true,
+ "returns": [
+ {
+ "name": "usedSize",
+ "description": "Used heap size in bytes.",
+ "type": "number"
+ },
+ {
+ "name": "totalSize",
+ "description": "Allocated heap size in bytes.",
+ "type": "number"
+ }
+ ]
+ },
+ {
+ "name": "getProperties",
+ "description": "Returns properties of a given object. Object group of the result is inherited from the target\nobject.",
+ "parameters": [
+ {
+ "name": "objectId",
+ "description": "Identifier of the object to return properties for.",
+ "$ref": "RemoteObjectId"
+ },
+ {
+ "name": "ownProperties",
+ "description": "If true, returns properties belonging only to the element itself, not to its prototype\nchain.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "accessorPropertiesOnly",
+ "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not\nreturned either.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "generatePreview",
+ "description": "Whether preview should be generated for the results.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "nonIndexedPropertiesOnly",
+ "description": "If true, returns non-indexed properties only.",
+ "experimental": true,
+ "optional": true,
+ "type": "boolean"
+ }
+ ],
+ "returns": [
+ {
+ "name": "result",
+ "description": "Object properties.",
+ "type": "array",
+ "items": {
+ "$ref": "PropertyDescriptor"
+ }
+ },
+ {
+ "name": "internalProperties",
+ "description": "Internal object properties (only of the element itself).",
+ "optional": true,
+ "type": "array",
+ "items": {
+ "$ref": "InternalPropertyDescriptor"
+ }
+ },
+ {
+ "name": "privateProperties",
+ "description": "Object private properties.",
+ "experimental": true,
+ "optional": true,
+ "type": "array",
+ "items": {
+ "$ref": "PrivatePropertyDescriptor"
+ }
+ },
+ {
+ "name": "exceptionDetails",
+ "description": "Exception details.",
+ "optional": true,
+ "$ref": "ExceptionDetails"
+ }
+ ]
+ },
+ {
+ "name": "globalLexicalScopeNames",
+ "description": "Returns all let, const and class variables from global scope.",
+ "parameters": [
+ {
+ "name": "executionContextId",
+ "description": "Specifies in which execution context to lookup global scope variables.",
+ "optional": true,
+ "$ref": "ExecutionContextId"
+ }
+ ],
+ "returns": [
+ {
+ "name": "names",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ ]
+ },
+ {
+ "name": "queryObjects",
+ "parameters": [
+ {
+ "name": "prototypeObjectId",
+ "description": "Identifier of the prototype to return objects for.",
+ "$ref": "RemoteObjectId"
+ },
+ {
+ "name": "objectGroup",
+ "description": "Symbolic group name that can be used to release the results.",
+ "optional": true,
+ "type": "string"
+ }
+ ],
+ "returns": [
+ {
+ "name": "objects",
+ "description": "Array with objects.",
+ "$ref": "RemoteObject"
+ }
+ ]
+ },
+ {
+ "name": "releaseObject",
+ "description": "Releases remote object with given id.",
+ "parameters": [
+ {
+ "name": "objectId",
+ "description": "Identifier of the object to release.",
+ "$ref": "RemoteObjectId"
+ }
+ ]
+ },
+ {
+ "name": "releaseObjectGroup",
+ "description": "Releases all remote objects that belong to a given group.",
+ "parameters": [
+ {
+ "name": "objectGroup",
+ "description": "Symbolic object group name.",
+ "type": "string"
+ }
+ ]
+ },
+ {
+ "name": "runIfWaitingForDebugger",
+ "description": "Tells inspected instance to run if it was waiting for debugger to attach."
+ },
+ {
+ "name": "runScript",
+ "description": "Runs script with given id in a given context.",
+ "parameters": [
+ {
+ "name": "scriptId",
+ "description": "Id of the script to run.",
+ "$ref": "ScriptId"
+ },
+ {
+ "name": "executionContextId",
+ "description": "Specifies in which execution context to perform script run. If the parameter is omitted the\nevaluation will be performed in the context of the inspected page.",
+ "optional": true,
+ "$ref": "ExecutionContextId"
+ },
+ {
+ "name": "objectGroup",
+ "description": "Symbolic group name that can be used to release multiple objects.",
+ "optional": true,
+ "type": "string"
+ },
+ {
+ "name": "silent",
+ "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause\nexecution. Overrides `setPauseOnException` state.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "includeCommandLineAPI",
+ "description": "Determines whether Command Line API should be available during the evaluation.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "returnByValue",
+ "description": "Whether the result is expected to be a JSON object which should be sent by value.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "generatePreview",
+ "description": "Whether preview should be generated for the result.",
+ "optional": true,
+ "type": "boolean"
+ },
+ {
+ "name": "awaitPromise",
+ "description": "Whether execution should `await` for resulting value and return once awaited promise is\nresolved.",
+ "optional": true,
+ "type": "boolean"
+ }
+ ],
+ "returns": [
+ {
+ "name": "result",
+ "description": "Run result.",
+ "$ref": "RemoteObject"
+ },
+ {
+ "name": "exceptionDetails",
+ "description": "Exception details.",
+ "optional": true,
+ "$ref": "ExceptionDetails"
+ }
+ ]
+ },
+ {
+ "name": "setAsyncCallStackDepth",
+ "description": "Enables or disables async call stacks tracking.",
+ "redirect": "Debugger",
+ "parameters": [
+ {
+ "name": "maxDepth",
+ "description": "Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async\ncall stacks (default).",
+ "type": "integer"
+ }
+ ]
+ },
+ {
+ "name": "setCustomObjectFormatterEnabled",
+ "experimental": true,
+ "parameters": [
+ {
+ "name": "enabled",
+ "type": "boolean"
+ }
+ ]
+ },
+ {
+ "name": "setMaxCallStackSizeToCapture",
+ "experimental": true,
+ "parameters": [
+ {
+ "name": "size",
+ "type": "integer"
+ }
+ ]
+ },
+ {
+ "name": "terminateExecution",
+ "description": "Terminate current or next JavaScript execution.\nWill cancel the termination when the outer-most script execution ends.",
+ "experimental": true
+ },
+ {
+ "name": "addBinding",
+ "description": "If executionContextId is empty, adds binding with the given name on the\nglobal objects of all inspected contexts, including those created later,\nbindings survive reloads.\nBinding function takes exactly one argument, this argument should be string,\nin case of any other input, function throws an exception.\nEach binding function call produces Runtime.bindingCalled notification.",
+ "experimental": true,
+ "parameters": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "executionContextId",
+ "description": "If specified, the binding would only be exposed to the specified\nexecution context. If omitted and `executionContextName` is not set,\nthe binding is exposed to all execution contexts of the target.\nThis parameter is mutually exclusive with `executionContextName`.\nDeprecated in favor of `executionContextName` due to an unclear use case\nand bugs in implementation (crbug.com/1169639). `executionContextId` will be\nremoved in the future.",
+ "deprecated": true,
+ "optional": true,
+ "$ref": "ExecutionContextId"
+ },
+ {
+ "name": "executionContextName",
+ "description": "If specified, the binding is exposed to the executionContext with\nmatching name, even for contexts created after the binding is added.\nSee also `ExecutionContext.name` and `worldName` parameter to\n`Page.addScriptToEvaluateOnNewDocument`.\nThis parameter is mutually exclusive with `executionContextId`.",
+ "experimental": true,
+ "optional": true,
+ "type": "string"
+ }
+ ]
+ },
+ {
+ "name": "removeBinding",
+ "description": "This method does not remove binding function from global object but\nunsubscribes current runtime agent from Runtime.bindingCalled notifications.",
+ "experimental": true,
+ "parameters": [
+ {
+ "name": "name",
+ "type": "string"
+ }
+ ]
+ },
+ {
+ "name": "getExceptionDetails",
+ "description": "This method tries to lookup and populate exception details for a\nJavaScript Error object.\nNote that the stackTrace portion of the resulting exceptionDetails will\nonly be populated if the Runtime domain was enabled at the time when the\nError was thrown.",
+ "experimental": true,
+ "parameters": [
+ {
+ "name": "errorObjectId",
+ "description": "The error object for which to resolve the exception details.",
+ "$ref": "RemoteObjectId"
+ }
+ ],
+ "returns": [
+ {
+ "name": "exceptionDetails",
+ "optional": true,
+ "$ref": "ExceptionDetails"
+ }
+ ]
+ }
+ ],
+ "events": [
+ {
+ "name": "bindingCalled",
+ "description": "Notification is issued every time when binding is called.",
+ "experimental": true,
+ "parameters": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "payload",
+ "type": "string"
+ },
+ {
+ "name": "executionContextId",
+ "description": "Identifier of the context where the call was made.",
+ "$ref": "ExecutionContextId"
+ }
+ ]
+ },
+ {
+ "name": "consoleAPICalled",
+ "description": "Issued when console API was called.",
+ "parameters": [
+ {
+ "name": "type",
+ "description": "Type of the call.",
+ "type": "string",
+ "enum": [
+ "log",
+ "debug",
+ "info",
+ "error",
+ "warning",
+ "dir",
+ "dirxml",
+ "table",
+ "trace",
+ "clear",
+ "startGroup",
+ "startGroupCollapsed",
+ "endGroup",
+ "assert",
+ "profile",
+ "profileEnd",
+ "count",
+ "timeEnd"
+ ]
+ },
+ {
+ "name": "args",
+ "description": "Call arguments.",
+ "type": "array",
+ "items": {
+ "$ref": "RemoteObject"
+ }
+ },
+ {
+ "name": "executionContextId",
+ "description": "Identifier of the context where the call was made.",
+ "$ref": "ExecutionContextId"
+ },
+ {
+ "name": "timestamp",
+ "description": "Call timestamp.",
+ "$ref": "Timestamp"
+ },
+ {
+ "name": "stackTrace",
+ "description": "Stack trace captured when the call was made. The async stack chain is automatically reported for\nthe following call types: `assert`, `error`, `trace`, `warning`. For other types the async call\nchain can be retrieved using `Debugger.getStackTrace` and `stackTrace.parentId` field.",
+ "optional": true,
+ "$ref": "StackTrace"
+ },
+ {
+ "name": "context",
+ "description": "Console context descriptor for calls on non-default console context (not console.*):\n'anonymous#unique-logger-id' for call on unnamed context, 'name#unique-logger-id' for call\non named context.",
+ "experimental": true,
+ "optional": true,
+ "type": "string"
+ }
+ ]
+ },
+ {
+ "name": "exceptionRevoked",
+ "description": "Issued when unhandled exception was revoked.",
+ "parameters": [
+ {
+ "name": "reason",
+ "description": "Reason describing why exception was revoked.",
+ "type": "string"
+ },
+ {
+ "name": "exceptionId",
+ "description": "The id of revoked exception, as reported in `exceptionThrown`.",
+ "type": "integer"
+ }
+ ]
+ },
+ {
+ "name": "exceptionThrown",
+ "description": "Issued when exception was thrown and unhandled.",
+ "parameters": [
+ {
+ "name": "timestamp",
+ "description": "Timestamp of the exception.",
+ "$ref": "Timestamp"
+ },
+ {
+ "name": "exceptionDetails",
+ "$ref": "ExceptionDetails"
+ }
+ ]
+ },
+ {
+ "name": "executionContextCreated",
+ "description": "Issued when new execution context is created.",
+ "parameters": [
+ {
+ "name": "context",
+ "description": "A newly created execution context.",
+ "$ref": "ExecutionContextDescription"
+ }
+ ]
+ },
+ {
+ "name": "executionContextDestroyed",
+ "description": "Issued when execution context is destroyed.",
+ "parameters": [
+ {
+ "name": "executionContextId",
+ "description": "Id of the destroyed context",
+ "$ref": "ExecutionContextId"
+ }
+ ]
+ },
+ {
+ "name": "executionContextsCleared",
+ "description": "Issued when all executionContexts were cleared in browser"
+ },
+ {
+ "name": "inspectRequested",
+ "description": "Issued when object should be inspected (for example, as a result of inspect() command line API\ncall).",
+ "parameters": [
+ {
+ "name": "object",
+ "$ref": "RemoteObject"
+ },
+ {
+ "name": "hints",
+ "type": "object"
+ },
+ {
+ "name": "executionContextId",
+ "description": "Identifier of the context where the call was made.",
+ "experimental": true,
+ "optional": true,
+ "$ref": "ExecutionContextId"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "domain": "NodeWorker",
+ "description": "Implemented partly just because of pesky vscode.",
+ "deprecated": true,
+ "commands": [
+ {
+ "name": "enable",
+ "description": "Used to get the attachedToWorker event",
+ "parameters": [ ]
+ }
+ ],
+ "events": [
+ {
+ "name": "attachedToWorker",
+ "description": "Issued when attached to a worker.",
+ "parameters": [
+ { "name": "sessionId",
+ "description": "Identifier assigned to the session used to send/receive messages.",
+ "$ref": "string"
+ },
+ {
+ "name": "workerInfo",
+ "type": "object",
+ "properties": [
+ { "name": "workerId",
+ "$ref": "string"
+ },
+ { "name": "type",
+ "type": "string"
+ },
+ { "name": "title",
+ "type": "string"
+ },
+ { "name": "url",
+ "type": "string"
+ }
+ ]
+ },
+ { "name": "waitingForDebugger",
+ "type": "boolean"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/me/topchetoeu/jscript/Filename.java b/src/me/topchetoeu/jscript/Filename.java
new file mode 100644
index 0000000..ff7d17d
--- /dev/null
+++ b/src/me/topchetoeu/jscript/Filename.java
@@ -0,0 +1,52 @@
+package me.topchetoeu.jscript;
+
+import java.io.File;
+
+public class Filename {
+ public final String protocol;
+ public final String path;
+
+ public String toString() {
+ return protocol + "://" + path;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + protocol.hashCode();
+ result = prime * result + path.hashCode();
+ return result;
+ }
+
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ var other = (Filename) obj;
+
+ if (protocol == null) {
+ if (other.protocol != null) return false;
+ }
+ else if (!protocol.equals(other.protocol)) return false;
+
+ if (path == null) {
+ if (other.path != null) return false;
+ }
+ else if (!path.equals(other.path)) return false;
+ return true;
+ }
+
+ public static Filename fromFile(File file) {
+ return new Filename("file", file.getAbsolutePath());
+ }
+
+
+ public Filename(String protocol, String path) {
+ this.protocol = protocol;
+ this.path = path;
+ }
+}
diff --git a/src/me/topchetoeu/jscript/Location.java b/src/me/topchetoeu/jscript/Location.java
index bec95d3..40b3138 100644
--- a/src/me/topchetoeu/jscript/Location.java
+++ b/src/me/topchetoeu/jscript/Location.java
@@ -1,18 +1,18 @@
package me.topchetoeu.jscript;
-public class Location {
- public static final Location INTERNAL = new Location(0, 0, "");
+public class Location implements Comparable {
+ public static final Location INTERNAL = new Location(0, 0, new Filename("jscript", "internal"));
private int line;
private int start;
- private String filename;
+ private Filename filename;
public int line() { return line; }
public int start() { return start; }
- public String filename() { return filename; }
+ public Filename filename() { return filename; }
@Override
public String toString() {
- return filename + ":" + line + ":" + start;
+ return filename.toString() + ":" + line + ":" + start;
}
public Location add(int n, boolean clone) {
@@ -55,7 +55,18 @@ public class Location {
return true;
}
- public Location(int line, int start, String filename) {
+ @Override
+ public int compareTo(Location other) {
+ int a = filename.toString().compareTo(other.filename.toString());
+ int b = Integer.compare(line, other.line);
+ int c = Integer.compare(start, other.start);
+
+ if (a != 0) return a;
+ if (b != 0) return b;
+ return c;
+ }
+
+ public Location(int line, int start, Filename filename) {
this.line = line;
this.start = start;
this.filename = filename;
diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java
index 314f9c6..76407f7 100644
--- a/src/me/topchetoeu/jscript/Main.java
+++ b/src/me/topchetoeu/jscript/Main.java
@@ -1,81 +1,66 @@
package me.topchetoeu.jscript;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
+import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
-import me.topchetoeu.jscript.engine.Message;
+import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.Environment;
+import me.topchetoeu.jscript.engine.debug.DebugServer;
+import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.events.Observer;
import me.topchetoeu.jscript.exceptions.EngineException;
+import me.topchetoeu.jscript.exceptions.InterruptException;
import me.topchetoeu.jscript.exceptions.SyntaxException;
+import me.topchetoeu.jscript.exceptions.UncheckedException;
import me.topchetoeu.jscript.lib.Internals;
public class Main {
- static Thread task;
+ static Thread engineTask, debugTask;
static Engine engine;
static Environment env;
-
- public static String streamToString(InputStream in) {
- try {
- StringBuilder out = new StringBuilder();
- BufferedReader br = new BufferedReader(new InputStreamReader(in));
-
- for(var line = br.readLine(); line != null; line = br.readLine()) {
- out.append(line).append('\n');
- }
-
- br.close();
- return out.toString();
- }
- catch (IOException e) {
- return null;
- }
- }
- public static String resourceToString(String name) {
- var str = Main.class.getResourceAsStream("/me/topchetoeu/jscript/" + name);
- if (str == null) return null;
- return streamToString(str);
- }
+ static int j = 0;
private static Observer valuePrinter = new Observer() {
public void next(Object data) {
- try { Values.printValue(null, data); }
- catch (InterruptedException e) { }
+ Values.printValue(null, data);
System.out.println();
}
public void error(RuntimeException err) {
- try { Values.printError(err, null); }
- catch (InterruptedException ex) { return; }
+ Values.printError(err, null);
+ }
+
+ @Override
+ public void finish() {
+ engineTask.interrupt();
}
};
public static void main(String args[]) {
System.out.println(String.format("Running %s v%s by %s", Metadata.NAME, Metadata.VERSION, Metadata.AUTHOR));
- var in = new BufferedReader(new InputStreamReader(System.in));
engine = new Engine();
env = new Environment(null, null, null);
var exited = new boolean[1];
+ var server = new DebugServer();
+ server.targets.put("target", (ws, req) -> SimpleDebugger.get(ws, engine));
- engine.pushMsg(false, new Message(engine), new NativeFunction((ctx, thisArg, _a) -> {
+ engine.pushMsg(false, null, new NativeFunction((ctx, thisArg, _a) -> {
new Internals().apply(env);
env.global.define("exit", _ctx -> {
exited[0] = true;
- task.interrupt();
- throw new InterruptedException();
+ throw new InterruptException();
});
env.global.define("go", _ctx -> {
try {
- var func = _ctx.compile("do.js", new String(Files.readAllBytes(Path.of("do.js"))));
+ var f = Path.of("do.js");
+ var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
return func.call(_ctx);
}
catch (IOException e) {
@@ -86,41 +71,34 @@ public class Main {
return null;
}), null);
- task = engine.start();
+ engineTask = engine.start();
+ debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true);
+
var reader = new Thread(() -> {
try {
- while (true) {
+ for (var i = 0; ; i++) {
try {
- var raw = in.readLine();
+ var raw = Reading.read();
if (raw == null) break;
- engine.pushMsg(false, env.context(new Message(engine)), "", raw, null).toObservable().once(valuePrinter);
- }
- catch (EngineException e) {
- try {
- System.out.println("Uncaught " + e.toString(null));
- }
- catch (EngineException ex) {
- System.out.println("Uncaught [error while converting to string]");
- }
+ valuePrinter.next(engine.pushMsg(false, new Context(engine).pushEnv(env), new Filename("jscript", "repl/" + i + ".js"), raw, null).await());
}
+ catch (EngineException e) { Values.printError(e, ""); }
}
}
- catch (IOException e) {
- e.printStackTrace();
- return;
- }
+ catch (IOException e) { return; }
catch (SyntaxException ex) {
if (exited[0]) return;
System.out.println("Syntax error:" + ex.msg);
}
catch (RuntimeException ex) {
- if (exited[0]) return;
- System.out.println("Internal error ocurred:");
- ex.printStackTrace();
+ if (!exited[0]) {
+ System.out.println("Internal error ocurred:");
+ ex.printStackTrace();
+ }
}
- catch (InterruptedException e) { return; }
- if (exited[0]) return;
+ catch (Throwable e) { throw new UncheckedException(e); }
+ if (exited[0]) debugTask.interrupt();
});
reader.setDaemon(true);
reader.setName("STD Reader");
diff --git a/src/me/topchetoeu/jscript/Reading.java b/src/me/topchetoeu/jscript/Reading.java
new file mode 100644
index 0000000..6d797c8
--- /dev/null
+++ b/src/me/topchetoeu/jscript/Reading.java
@@ -0,0 +1,36 @@
+package me.topchetoeu.jscript;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import me.topchetoeu.jscript.exceptions.UncheckedException;
+
+public class Reading {
+ private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+
+ public static synchronized String read() throws IOException {
+ return reader.readLine();
+ }
+
+ public static String streamToString(InputStream in) {
+ try {
+ StringBuilder out = new StringBuilder();
+ BufferedReader br = new BufferedReader(new InputStreamReader(in));
+
+ for(var line = br.readLine(); line != null; line = br.readLine()) {
+ out.append(line).append('\n');
+ }
+
+ br.close();
+ return out.toString();
+ }
+ catch (Throwable e) { throw new UncheckedException(e); }
+ }
+ public static String resourceToString(String name) {
+ var str = Main.class.getResourceAsStream("/me/topchetoeu/jscript/" + name);
+ if (str == null) return null;
+ return streamToString(str);
+ }
+}
diff --git a/src/me/topchetoeu/jscript/compilation/CompileTarget.java b/src/me/topchetoeu/jscript/compilation/CompileTarget.java
index 0a207ec..1484d19 100644
--- a/src/me/topchetoeu/jscript/compilation/CompileTarget.java
+++ b/src/me/topchetoeu/jscript/compilation/CompileTarget.java
@@ -1,11 +1,15 @@
package me.topchetoeu.jscript.compilation;
import java.util.Map;
+import java.util.TreeSet;
import java.util.Vector;
+import me.topchetoeu.jscript.Location;
+
public class CompileTarget {
public final Vector target = new Vector<>();
- public final Map functions;
+ public final Map functions;
+ public final TreeSet breakpoints;
public Instruction add(Instruction instr) {
target.add(instr);
@@ -14,6 +18,12 @@ public class CompileTarget {
public Instruction set(int i, Instruction instr) {
return target.set(i, instr);
}
+ public void setDebug(int i) {
+ breakpoints.add(target.get(i).location);
+ }
+ public void setDebug() {
+ setDebug(target.size() - 1);
+ }
public Instruction get(int i) {
return target.get(i);
}
@@ -21,7 +31,8 @@ public class CompileTarget {
public Instruction[] array() { return target.toArray(Instruction[]::new); }
- public CompileTarget(Map functions) {
+ public CompileTarget(Map functions, TreeSet breakpoints) {
this.functions = functions;
+ this.breakpoints = breakpoints;
}
}
diff --git a/src/me/topchetoeu/jscript/compilation/CompoundStatement.java b/src/me/topchetoeu/jscript/compilation/CompoundStatement.java
index c76f6ce..d86a5f9 100644
--- a/src/me/topchetoeu/jscript/compilation/CompoundStatement.java
+++ b/src/me/topchetoeu/jscript/compilation/CompoundStatement.java
@@ -11,6 +11,7 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class CompoundStatement extends Statement {
public final Statement[] statements;
+ public Location end;
@Override
public void declare(ScopeRecord varsScope) {
@@ -25,18 +26,23 @@ public class CompoundStatement extends Statement {
if (stm instanceof FunctionStatement) {
int start = target.size();
((FunctionStatement)stm).compile(target, scope, null, true);
- target.get(start).setDebug(true);
+ target.setDebug(start);
target.add(Instruction.discard());
}
}
for (var i = 0; i < statements.length; i++) {
var stm = statements[i];
-
+
if (stm instanceof FunctionStatement) continue;
if (i != statements.length - 1) stm.compileWithDebug(target, scope, false);
else stm.compileWithDebug(target, scope, pollute);
}
+
+ if (end != null) {
+ target.add(Instruction.nop().locate(end));
+ target.setDebug();
+ }
}
@Override
@@ -59,6 +65,11 @@ public class CompoundStatement extends Statement {
else return new CompoundStatement(loc(), res.toArray(Statement[]::new));
}
+ public CompoundStatement setEnd(Location loc) {
+ this.end = loc;
+ return this;
+ }
+
public CompoundStatement(Location loc, Statement ...statements) {
super(loc);
this.statements = statements;
diff --git a/src/me/topchetoeu/jscript/compilation/FunctionBody.java b/src/me/topchetoeu/jscript/compilation/FunctionBody.java
new file mode 100644
index 0000000..718eeec
--- /dev/null
+++ b/src/me/topchetoeu/jscript/compilation/FunctionBody.java
@@ -0,0 +1,17 @@
+package me.topchetoeu.jscript.compilation;
+
+public class FunctionBody {
+ public final Instruction[] instructions;
+ public final String[] captureNames, localNames;
+
+ public FunctionBody(Instruction[] instructions, String[] captureNames, String[] localNames) {
+ this.instructions = instructions;
+ this.captureNames = captureNames;
+ this.localNames = localNames;
+ }
+ public FunctionBody(Instruction[] instructions) {
+ this.instructions = instructions;
+ this.captureNames = new String[0];
+ this.localNames = new String[0];
+ }
+}
diff --git a/src/me/topchetoeu/jscript/compilation/Instruction.java b/src/me/topchetoeu/jscript/compilation/Instruction.java
index 4e3efe5..0ba8e06 100644
--- a/src/me/topchetoeu/jscript/compilation/Instruction.java
+++ b/src/me/topchetoeu/jscript/compilation/Instruction.java
@@ -90,16 +90,11 @@ public class Instruction {
public final Type type;
public final Object[] params;
public Location location;
- public boolean debugged;
public Instruction locate(Location loc) {
this.location = loc;
return this;
}
- public Instruction setDebug(boolean debug) {
- debugged = debug;
- return this;
- }
@SuppressWarnings("unchecked")
public T get(int i) {
@@ -152,14 +147,6 @@ public class Instruction {
public static Instruction debug() {
return new Instruction(null, Type.NOP, "debug");
}
- public static Instruction debugVarNames(String[] names) {
- var args = new Object[names.length + 1];
- args[0] = "dbg_vars";
-
- System.arraycopy(names, 0, args, 1, names.length);
-
- return new Instruction(null, Type.NOP, args);
- }
public static Instruction nop(Object ...params) {
for (var param : params) {
diff --git a/src/me/topchetoeu/jscript/compilation/Statement.java b/src/me/topchetoeu/jscript/compilation/Statement.java
index 7b51b38..663ed12 100644
--- a/src/me/topchetoeu/jscript/compilation/Statement.java
+++ b/src/me/topchetoeu/jscript/compilation/Statement.java
@@ -14,7 +14,7 @@ public abstract class Statement {
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute) {
int start = target.size();
compile(target, scope, pollute);
- if (target.size() != start) target.get(start).setDebug(true);
+ if (target.size() != start) target.setDebug(start);
}
public Location loc() { return _loc; }
diff --git a/src/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java b/src/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java
index 667a982..c51de30 100644
--- a/src/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java
+++ b/src/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java
@@ -10,10 +10,12 @@ public class VariableDeclareStatement extends Statement {
public static class Pair {
public final String name;
public final Statement value;
+ public final Location location;
- public Pair(String name, Statement value) {
+ public Pair(String name, Statement value, Location location) {
this.name = name;
this.value = value;
+ this.location = location;
}
}
@@ -30,16 +32,20 @@ public class VariableDeclareStatement extends Statement {
for (var entry : values) {
if (entry.name == null) continue;
var key = scope.getKey(entry.name);
- if (key instanceof String) target.add(Instruction.makeVar((String)key).locate(loc()));
+ int start = target.size();
+
+ if (key instanceof String) target.add(Instruction.makeVar((String)key).locate(entry.location));
if (entry.value instanceof FunctionStatement) {
((FunctionStatement)entry.value).compile(target, scope, entry.name, false);
- target.add(Instruction.storeVar(key).locate(loc()));
+ target.add(Instruction.storeVar(key).locate(entry.location));
}
else if (entry.value != null) {
entry.value.compile(target, scope, true);
- target.add(Instruction.storeVar(key).locate(loc()));
+ target.add(Instruction.storeVar(key).locate(entry.location));
}
+
+ if (target.size() != start) target.setDebug(start);
}
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
diff --git a/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java b/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java
index 5ed0a1d..5e5b970 100644
--- a/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java
+++ b/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java
@@ -68,7 +68,8 @@ public class SwitchStatement extends Statement {
var loc = target.get(el.getKey()).location;
var i = stmIndexMap.get(el.getValue());
if (i == null) i = target.size();
- target.set(el.getKey(), Instruction.jmpIf(i - el.getKey()).locate(loc).setDebug(true));
+ target.set(el.getKey(), Instruction.jmpIf(i - el.getKey()).locate(loc));
+ target.setDebug(el.getKey());
}
target.add(Instruction.discard().locate(loc()));
diff --git a/src/me/topchetoeu/jscript/compilation/values/CallStatement.java b/src/me/topchetoeu/jscript/compilation/values/CallStatement.java
index b63a93d..a14be7d 100644
--- a/src/me/topchetoeu/jscript/compilation/values/CallStatement.java
+++ b/src/me/topchetoeu/jscript/compilation/values/CallStatement.java
@@ -16,13 +16,14 @@ public class CallStatement extends Statement {
((IndexStatement)func).compile(target, scope, true, true);
}
else {
- target.add(Instruction.loadValue(null).locate(loc()));
+ target.add(Instruction.loadValue(null).locate(loc()));
func.compile(target, scope, true);
}
for (var arg : args) arg.compile(target, scope, true);
- target.add(Instruction.call(args.length).locate(loc()).setDebug(true));
+ target.add(Instruction.call(args.length).locate(loc()));
+ target.setDebug();
if (!pollute) target.add(Instruction.discard().locate(loc()));
}
diff --git a/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java b/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java
index ca11b27..52c292a 100644
--- a/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java
+++ b/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java
@@ -5,6 +5,7 @@ import java.util.Random;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.CompoundStatement;
+import me.topchetoeu.jscript.compilation.FunctionBody;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.Type;
@@ -51,7 +52,7 @@ public class FunctionStatement extends Statement {
var subscope = scope.child();
int start = target.size();
- var funcTarget = new CompileTarget(target.functions);
+ var funcTarget = new CompileTarget(target.functions, target.breakpoints);
subscope.define("this");
var argsVar = subscope.define("arguments");
@@ -69,7 +70,6 @@ public class FunctionStatement extends Statement {
}
body.declare(subscope);
- funcTarget.add(Instruction.debugVarNames(subscope.locals()));
body.compile(funcTarget, subscope, false);
funcTarget.add(Instruction.ret().locate(loc()));
checkBreakAndCont(funcTarget, start);
@@ -77,7 +77,7 @@ public class FunctionStatement extends Statement {
var id = rand.nextLong();
target.add(Instruction.loadFunc(id, subscope.localsCount(), args.length, subscope.getCaptures()).locate(loc()));
- target.functions.put(id, funcTarget.array());
+ target.functions.put(id, new FunctionBody(funcTarget.array(), subscope.captures(), subscope.locals()));
if (name == null) name = this.name;
diff --git a/src/me/topchetoeu/jscript/compilation/values/IndexAssignStatement.java b/src/me/topchetoeu/jscript/compilation/values/IndexAssignStatement.java
index 55a99cd..2cf8b00 100644
--- a/src/me/topchetoeu/jscript/compilation/values/IndexAssignStatement.java
+++ b/src/me/topchetoeu/jscript/compilation/values/IndexAssignStatement.java
@@ -24,14 +24,16 @@ public class IndexAssignStatement extends Statement {
value.compile(target, scope, true);
target.add(Instruction.operation(operation).locate(loc()));
- target.add(Instruction.storeMember(pollute).locate(loc()).setDebug(true));
+ target.add(Instruction.storeMember(pollute).locate(loc()));
+ target.setDebug();
}
else {
object.compile(target, scope, true);
index.compile(target, scope, true);
value.compile(target, scope, true);
- target.add(Instruction.storeMember(pollute).locate(loc()).setDebug(true));
+ target.add(Instruction.storeMember(pollute).locate(loc()));
+ target.setDebug();
}
}
diff --git a/src/me/topchetoeu/jscript/compilation/values/IndexStatement.java b/src/me/topchetoeu/jscript/compilation/values/IndexStatement.java
index 6159f4f..b821cd0 100644
--- a/src/me/topchetoeu/jscript/compilation/values/IndexStatement.java
+++ b/src/me/topchetoeu/jscript/compilation/values/IndexStatement.java
@@ -20,17 +20,17 @@ public class IndexStatement extends AssignableStatement {
return new IndexAssignStatement(loc(), object, index, val, operation);
}
public void compile(CompileTarget target, ScopeRecord scope, boolean dupObj, boolean pollute) {
- int start = 0;
object.compile(target, scope, true);
if (dupObj) target.add(Instruction.dup().locate(loc()));
if (index instanceof ConstantStatement) {
target.add(Instruction.loadMember(((ConstantStatement)index).value).locate(loc()));
+ target.setDebug();
return;
}
index.compile(target, scope, true);
target.add(Instruction.loadMember().locate(loc()));
- target.get(start).setDebug(true);
+ target.setDebug();
if (!pollute) target.add(Instruction.discard().locate(loc()));
}
@Override
diff --git a/src/me/topchetoeu/jscript/compilation/values/NewStatement.java b/src/me/topchetoeu/jscript/compilation/values/NewStatement.java
index dbb632c..d60ef3a 100644
--- a/src/me/topchetoeu/jscript/compilation/values/NewStatement.java
+++ b/src/me/topchetoeu/jscript/compilation/values/NewStatement.java
@@ -16,7 +16,8 @@ public class NewStatement extends Statement {
for (var arg : args) arg.compile(target, scope, true);
- target.add(Instruction.callNew(args.length).locate(loc()).setDebug(true));
+ target.add(Instruction.callNew(args.length).locate(loc()));
+ target.setDebug();
}
public NewStatement(Location loc, Statement func, Statement ...args) {
diff --git a/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java b/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java
index 3016ee2..97479bd 100644
--- a/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java
+++ b/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java
@@ -49,13 +49,8 @@ public class OperationStatement extends Statement {
vals[i] = ((ConstantStatement)args[i]).value;
}
- try {
- return new ConstantStatement(loc(), Values.operation(null, operation, vals));
- }
- catch (EngineException e) {
- return new ThrowStatement(loc(), new ConstantStatement(loc(), e.value));
- }
- catch (InterruptedException e) { return null; }
+ try { return new ConstantStatement(loc(), Values.operation(null, operation, vals)); }
+ catch (EngineException e) { return new ThrowStatement(loc(), new ConstantStatement(loc(), e.value)); }
}
return new OperationStatement(loc(), operation, args);
diff --git a/src/me/topchetoeu/jscript/engine/Context.java b/src/me/topchetoeu/jscript/engine/Context.java
index 480453f..44d7c32 100644
--- a/src/me/topchetoeu/jscript/engine/Context.java
+++ b/src/me/topchetoeu/jscript/engine/Context.java
@@ -1,27 +1,46 @@
package me.topchetoeu.jscript.engine;
+import java.util.Stack;
+import java.util.TreeSet;
+
+import me.topchetoeu.jscript.Filename;
+import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.parsing.Parsing;
public class Context {
- public final Environment env;
- public final Message message;
+ private final Stack env = new Stack<>();
+ public final Data data;
+ public final Engine engine;
- public FunctionValue compile(String filename, String raw) throws InterruptedException {
- var res = Values.toString(this, env.compile.call(this, null, raw, filename));
- return Parsing.compile(message.engine.functions, env, filename, res);
+ public Environment environment() {
+ return env.empty() ? null : env.peek();
}
- public Context setEnv(Environment env) {
- return new Context(env, message);
+ public Context pushEnv(Environment env) {
+ this.env.push(env);
+ return this;
}
- public Context setMsg(Message msg) {
- return new Context(env, msg);
+ public void popEnv() {
+ if (!env.empty()) this.env.pop();
}
- public Context(Environment env, Message msg) {
- this.env = env;
- this.message = msg;
+ public FunctionValue compile(Filename filename, String raw) {
+ var src = Values.toString(this, environment().compile.call(this, null, raw, filename));
+ var debugger = StackData.getDebugger(this);
+ var breakpoints = new TreeSet();
+ var res = Parsing.compile(engine.functions, breakpoints, environment(), filename, src);
+ if (debugger != null) debugger.onSource(filename, src, breakpoints);
+ return res;
+ }
+
+ public Context(Engine engine, Data data) {
+ this.data = new Data(engine.data);
+ if (data != null) this.data.addAll(data);
+ this.engine = engine;
+ }
+ public Context(Engine engine) {
+ this(engine, null);
}
}
diff --git a/src/me/topchetoeu/jscript/engine/Data.java b/src/me/topchetoeu/jscript/engine/Data.java
index 1db4ba8..6c2d58f 100644
--- a/src/me/topchetoeu/jscript/engine/Data.java
+++ b/src/me/topchetoeu/jscript/engine/Data.java
@@ -1,43 +1,52 @@
package me.topchetoeu.jscript.engine;
import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map.Entry;
+import java.util.Map;
@SuppressWarnings("unchecked")
-public class Data implements Iterable, ?>> {
+public class Data {
+ public final Data parent;
private HashMap, Object> data = new HashMap<>();
public Data copy() {
return new Data().addAll(this);
}
- public Data addAll(Iterable, ?>> data) {
- for (var el : data) {
- add((DataKey)el.getKey(), (Object)el.getValue());
+ public Data addAll(Map, ?> data) {
+ for (var el : data.entrySet()) {
+ get((DataKey)el.getKey(), (Object)el.getValue());
+ }
+ return this;
+ }
+ public Data addAll(Data data) {
+ for (var el : data.data.entrySet()) {
+ get((DataKey)el.getKey(), (Object)el.getValue());
}
return this;
}
+ public T remove(DataKey key) {
+ return (T)data.remove(key);
+ }
public Data set(DataKey key, T val) {
- if (val == null) data.remove(key);
- else data.put((DataKey)key, (Object)val);
+ data.put((DataKey)key, (Object)val);
return this;
}
- public T add(DataKey key, T val) {
- if (data.containsKey(key)) return (T)data.get(key);
- else {
- if (val == null) data.remove(key);
- else data.put((DataKey)key, (Object)val);
- return val;
+ public T get(DataKey key, T val) {
+ for (var it = this; it != null; it = it.parent) {
+ if (it.data.containsKey(key)) {
+ return (T)it.data.get((DataKey)key);
+ }
}
+
+ set(key, val);
+ return val;
}
public T get(DataKey key) {
- return get(key, null);
- }
- public T get(DataKey key, T defaultVal) {
- if (!has(key)) return defaultVal;
- else return (T)data.get(key);
+ for (var it = this; it != null; it = it.parent) {
+ if (it.data.containsKey(key)) return (T)it.data.get((DataKey)key);
+ }
+ return null;
}
public boolean has(DataKey> key) { return data.containsKey(key); }
@@ -53,8 +62,10 @@ public class Data implements Iterable, ?>> {
return increase(key, 1, 0);
}
- @Override
- public Iterator, ?>> iterator() {
- return (Iterator, ?>>)data.entrySet();
+ public Data() {
+ this.parent = null;
+ }
+ public Data(Data parent) {
+ this.parent = parent;
}
}
diff --git a/src/me/topchetoeu/jscript/engine/Engine.java b/src/me/topchetoeu/jscript/engine/Engine.java
index ae4b3a7..6c8ed51 100644
--- a/src/me/topchetoeu/jscript/engine/Engine.java
+++ b/src/me/topchetoeu/jscript/engine/Engine.java
@@ -3,29 +3,29 @@ package me.topchetoeu.jscript.engine;
import java.util.HashMap;
import java.util.concurrent.LinkedBlockingDeque;
-import me.topchetoeu.jscript.compilation.Instruction;
+import me.topchetoeu.jscript.Filename;
+import me.topchetoeu.jscript.compilation.FunctionBody;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.events.Awaitable;
import me.topchetoeu.jscript.events.DataNotifier;
-import me.topchetoeu.jscript.exceptions.EngineException;
+import me.topchetoeu.jscript.exceptions.InterruptException;
public class Engine {
private class UncompiledFunction extends FunctionValue {
- public final String filename;
+ public final Filename filename;
public final String raw;
- public final Environment env;
+ private FunctionValue compiled = null;
@Override
- public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
- ctx = ctx.setEnv(env);
- return ctx.compile(filename, raw).call(ctx, thisArg, args);
+ public Object call(Context ctx, Object thisArg, Object ...args) {
+ if (compiled == null) compiled = ctx.compile(filename, raw);
+ return compiled.call(ctx, thisArg, args);
}
- public UncompiledFunction(Environment env, String filename, String raw) {
- super(filename, 0);
+ public UncompiledFunction(Filename filename, String raw) {
+ super(filename + "", 0);
this.filename = filename;
this.raw = raw;
- this.env = env;
}
}
@@ -34,10 +34,10 @@ public class Engine {
public final Object thisArg;
public final Object[] args;
public final DataNotifier notifier = new DataNotifier<>();
- public final Message msg;
+ public final Context ctx;
- public Task(Message ctx, FunctionValue func, Object thisArg, Object[] args) {
- this.msg = ctx;
+ public Task(Context ctx, FunctionValue func, Object thisArg, Object[] args) {
+ this.ctx = ctx;
this.func = func;
this.thisArg = thisArg;
this.args = args;
@@ -51,26 +51,20 @@ public class Engine {
private LinkedBlockingDeque microTasks = new LinkedBlockingDeque<>();
public final int id = ++nextId;
- public final HashMap functions = new HashMap<>();
+ public final HashMap functions = new HashMap<>();
+ public final Data data = new Data().set(StackData.MAX_FRAMES, 10000);
- private void runTask(Task task) throws InterruptedException {
+ private void runTask(Task task) {
try {
- task.notifier.next(task.func.call(task.msg.context(null), task.thisArg, task.args));
- }
- catch (InterruptedException e) {
- task.notifier.error(new RuntimeException(e));
- throw e;
- }
- catch (EngineException e) {
- task.notifier.error(e);
+ task.notifier.next(task.func.call(task.ctx, task.thisArg, task.args));
}
catch (RuntimeException e) {
+ if (e instanceof InterruptException) throw e;
task.notifier.error(e);
- e.printStackTrace();
}
}
- private void run() {
- while (true) {
+ public void run(boolean untilEmpty) {
+ while (!untilEmpty || !macroTasks.isEmpty()) {
try {
runTask(macroTasks.take());
@@ -78,9 +72,9 @@ public class Engine {
runTask(microTasks.take());
}
}
- catch (InterruptedException e) {
+ catch (InterruptedException | InterruptException e) {
for (var msg : macroTasks) {
- msg.notifier.error(new RuntimeException(e));
+ msg.notifier.error(new InterruptException(e));
}
break;
}
@@ -89,7 +83,7 @@ public class Engine {
public Thread start() {
if (this.thread == null) {
- this.thread = new Thread(this::run, "JavaScript Runner #" + id);
+ this.thread = new Thread(() -> run(false), "JavaScript Runner #" + id);
this.thread.start();
}
return this.thread;
@@ -105,17 +99,13 @@ public class Engine {
return this.thread != null;
}
- public Awaitable pushMsg(boolean micro, Message ctx, FunctionValue func, Object thisArg, Object ...args) {
- var msg = new Task(ctx, func, thisArg, args);
+ public Awaitable pushMsg(boolean micro, Context ctx, FunctionValue func, Object thisArg, Object ...args) {
+ var msg = new Task(ctx == null ? new Context(this) : ctx, func, thisArg, args);
if (micro) microTasks.addLast(msg);
else macroTasks.addLast(msg);
return msg.notifier;
}
- public Awaitable pushMsg(boolean micro, Context ctx, String filename, String raw, Object thisArg, Object ...args) {
- return pushMsg(micro, ctx.message, new UncompiledFunction(ctx.env, filename, raw), thisArg, args);
+ public Awaitable pushMsg(boolean micro, Context ctx, Filename filename, String raw, Object thisArg, Object ...args) {
+ return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
}
-
- // public Engine() {
- // this.typeRegister = new NativeTypeRegister();
- // }
}
diff --git a/src/me/topchetoeu/jscript/engine/Environment.java b/src/me/topchetoeu/jscript/engine/Environment.java
index c8136cf..30c055b 100644
--- a/src/me/topchetoeu/jscript/engine/Environment.java
+++ b/src/me/topchetoeu/jscript/engine/Environment.java
@@ -20,11 +20,11 @@ public class Environment {
public final HashMap symbols = new HashMap<>();
public GlobalScope global;
- public WrappersProvider wrappersProvider;
+ public WrappersProvider wrappers;
@Native public FunctionValue compile;
@Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> {
- throw EngineException.ofError("Regular expressions not supported.").setContext(ctx);
+ throw EngineException.ofError("Regular expressions not supported.").setCtx(ctx.environment(), ctx.engine);
});
public Environment addData(Data data) {
@@ -57,7 +57,7 @@ public class Environment {
}
@Native public Environment fork() {
- var res = new Environment(compile, wrappersProvider, global);
+ var res = new Environment(compile, wrappers, global);
res.regexConstructor = regexConstructor;
res.prototypes = new HashMap<>(prototypes);
return res;
@@ -68,8 +68,11 @@ public class Environment {
return res;
}
- public Context context(Message msg) {
- return new Context(this, msg);
+ public Context context(Engine engine, Data data) {
+ return new Context(engine, data).pushEnv(this);
+ }
+ public Context context(Engine engine) {
+ return new Context(engine).pushEnv(this);
}
public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
@@ -77,7 +80,7 @@ public class Environment {
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
if (global == null) global = new GlobalScope();
- this.wrappersProvider = nativeConverter;
+ this.wrappers = nativeConverter;
this.compile = compile;
this.global = global;
}
diff --git a/src/me/topchetoeu/jscript/engine/Message.java b/src/me/topchetoeu/jscript/engine/Message.java
deleted file mode 100644
index b61f522..0000000
--- a/src/me/topchetoeu/jscript/engine/Message.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package me.topchetoeu.jscript.engine;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import me.topchetoeu.jscript.engine.frame.CodeFrame;
-import me.topchetoeu.jscript.exceptions.EngineException;
-
-public class Message {
- public final Engine engine;
-
- private final ArrayList frames = new ArrayList<>();
- public int maxStackFrames = 1000;
-
- public final Data data = new Data();
-
- public List frames() { return Collections.unmodifiableList(frames); }
-
- public Message addData(Data data) {
- this.data.addAll(data);
- return this;
- }
-
- public Message pushFrame(Context ctx, CodeFrame frame) throws InterruptedException {
- this.frames.add(frame);
- if (this.frames.size() > maxStackFrames) throw EngineException.ofRange("Stack overflow!");
- return this;
- }
- public boolean popFrame(CodeFrame frame) {
- if (this.frames.size() == 0) return false;
- if (this.frames.get(this.frames.size() - 1) != frame) return false;
- this.frames.remove(this.frames.size() - 1);
- return true;
- }
-
- public List stackTrace() {
- var res = new ArrayList();
-
- for (var el : frames) {
- var name = el.function.name;
- var loc = el.function.loc();
- var trace = "";
-
- if (loc != null) trace += "at " + loc.toString() + " ";
- if (name != null && !name.equals("")) trace += "in " + name + " ";
-
- trace = trace.trim();
-
- if (!res.equals("")) res.add(trace);
- }
-
- return res;
- }
-
- public Context context(Environment env) {
- return new Context(env, this);
- }
-
- public Message(Engine engine) {
- this.engine = engine;
- }
-}
diff --git a/src/me/topchetoeu/jscript/engine/StackData.java b/src/me/topchetoeu/jscript/engine/StackData.java
new file mode 100644
index 0000000..820016a
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/StackData.java
@@ -0,0 +1,65 @@
+package me.topchetoeu.jscript.engine;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import me.topchetoeu.jscript.engine.debug.Debugger;
+import me.topchetoeu.jscript.engine.frame.CodeFrame;
+import me.topchetoeu.jscript.exceptions.EngineException;
+
+public class StackData {
+ public static final DataKey> FRAMES = new DataKey<>();
+ public static final DataKey MAX_FRAMES = new DataKey<>();
+ public static final DataKey DEBUGGER = new DataKey<>();
+
+ public static void pushFrame(Context ctx, CodeFrame frame) {
+ var frames = ctx.data.get(FRAMES, new ArrayList<>());
+ frames.add(frame);
+ if (frames.size() > ctx.data.get(MAX_FRAMES, 10000)) throw EngineException.ofRange("Stack overflow!");
+ ctx.pushEnv(frame.function.environment);
+ }
+ public static boolean popFrame(Context ctx, CodeFrame frame) {
+ var frames = ctx.data.get(FRAMES, new ArrayList<>());
+ if (frames.size() == 0) return false;
+ if (frames.get(frames.size() - 1) != frame) return false;
+ frames.remove(frames.size() - 1);
+ ctx.popEnv();
+ var dbg = getDebugger(ctx);
+ if (dbg != null) dbg.onFramePop(ctx, frame);
+ return true;
+ }
+ public static CodeFrame peekFrame(Context ctx) {
+ var frames = ctx.data.get(FRAMES, new ArrayList<>());
+ if (frames.size() == 0) return null;
+ return frames.get(frames.size() - 1);
+ }
+
+ public static List frames(Context ctx) {
+ return Collections.unmodifiableList(ctx.data.get(FRAMES, new ArrayList<>()));
+ }
+ public static List stackTrace(Context ctx) {
+ var res = new ArrayList();
+ var frames = frames(ctx);
+
+ for (var i = frames.size() - 1; i >= 0; i--) {
+ var el = frames.get(i);
+ var name = el.function.name;
+ var loc = el.function.loc();
+ var trace = "";
+
+ if (loc != null) trace += "at " + loc.toString() + " ";
+ if (name != null && !name.equals("")) trace += "in " + name + " ";
+
+ trace = trace.trim();
+
+ if (!trace.equals("")) res.add(trace);
+ }
+
+ return res;
+ }
+
+ public static Debugger getDebugger(Context ctx) {
+ return ctx.data.get(DEBUGGER);
+ }
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/DebugController.java b/src/me/topchetoeu/jscript/engine/debug/DebugController.java
new file mode 100644
index 0000000..beec045
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/DebugController.java
@@ -0,0 +1,43 @@
+package me.topchetoeu.jscript.engine.debug;
+
+import java.util.TreeSet;
+
+import me.topchetoeu.jscript.Filename;
+import me.topchetoeu.jscript.Location;
+import me.topchetoeu.jscript.compilation.Instruction;
+import me.topchetoeu.jscript.engine.Context;
+import me.topchetoeu.jscript.engine.frame.CodeFrame;
+import me.topchetoeu.jscript.exceptions.EngineException;
+
+public interface DebugController {
+ /**
+ * Called when a script has been loaded
+ * @param breakpoints
+ */
+ void onSource(Filename filename, String source, TreeSet breakpoints);
+
+ /**
+ * Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned.
+ * This function might pause in order to await debugging commands.
+ * @param ctx The context of execution
+ * @param frame The frame in which execution is occuring
+ * @param instruction The instruction which was or will be executed
+ * @param loc The most recent location the code frame has been at
+ * @param returnVal The return value of the instruction, Runners.NO_RETURN if none
+ * @param error The error that the instruction threw, null if none
+ * @param caught Whether or not the error has been caught
+ * @return Whether or not the frame should restart
+ */
+ boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
+
+ /**
+ * Called immediatly after a frame has been popped out of the frame stack.
+ * This function might pause in order to await debugging commands.
+ * @param ctx The context of execution
+ * @param frame The code frame which was popped out
+ */
+ void onFramePop(Context ctx, CodeFrame frame);
+
+ void connect();
+ void disconnect();
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/DebugHandler.java b/src/me/topchetoeu/jscript/engine/debug/DebugHandler.java
new file mode 100644
index 0000000..7d4e2f9
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/DebugHandler.java
@@ -0,0 +1,35 @@
+package me.topchetoeu.jscript.engine.debug;
+
+public interface DebugHandler {
+ void enable(V8Message msg);
+ void disable(V8Message msg);
+
+ void setBreakpoint(V8Message msg);
+ void setBreakpointByUrl(V8Message msg);
+ void removeBreakpoint(V8Message msg);
+ void continueToLocation(V8Message msg);
+
+ void getScriptSource(V8Message msg);
+ void getPossibleBreakpoints(V8Message msg);
+
+ void resume(V8Message msg);
+ void pause(V8Message msg);
+
+ void stepInto(V8Message msg);
+ void stepOut(V8Message msg);
+ void stepOver(V8Message msg);
+
+ void setPauseOnExceptions(V8Message msg);
+
+ void evaluateOnCallFrame(V8Message msg);
+
+ void getProperties(V8Message msg);
+ void releaseObjectGroup(V8Message msg);
+ /**
+ * This method might not execute the actual code for well-known requests
+ */
+ void callFunctionOn(V8Message msg);
+
+ // void nodeWorkerEnable(V8Message msg);
+ void runtimeEnable(V8Message msg);
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/DebugServer.java b/src/me/topchetoeu/jscript/engine/debug/DebugServer.java
new file mode 100644
index 0000000..c816f3e
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/DebugServer.java
@@ -0,0 +1,238 @@
+package me.topchetoeu.jscript.engine.debug;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.security.MessageDigest;
+import java.util.Base64;
+import java.util.HashMap;
+
+import me.topchetoeu.jscript.Metadata;
+import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
+import me.topchetoeu.jscript.exceptions.SyntaxException;
+import me.topchetoeu.jscript.exceptions.UncheckedException;
+import me.topchetoeu.jscript.exceptions.UncheckedIOException;
+import me.topchetoeu.jscript.json.JSON;
+import me.topchetoeu.jscript.json.JSONList;
+import me.topchetoeu.jscript.json.JSONMap;
+
+public class DebugServer {
+ public static String browserDisplayName = Metadata.NAME + "/" + Metadata.VERSION;
+
+ public final HashMap targets = new HashMap<>();
+
+ private final byte[] favicon, index, protocol;
+
+ private static void send(HttpRequest req, String val) throws IOException {
+ req.writeResponse(200, "OK", "application/json", val.getBytes());
+ }
+
+ // SILENCE JAVA
+ private MessageDigest getDigestInstance() {
+ try {
+ return MessageDigest.getInstance("sha1");
+ }
+ catch (Throwable e) { throw new UncheckedException(e); }
+
+ }
+
+ private static Thread runAsync(Runnable func, String name) {
+ var res = new Thread(func);
+ res.setName(name);
+ res.start();
+ return res;
+ }
+
+ private void handle(WebSocket ws, Debugger debugger) {
+ WebSocketMessage raw;
+
+ debugger.connect();
+
+ while ((raw = ws.receive()) != null) {
+ if (raw.type != Type.Text) {
+ ws.send(new V8Error("Expected a text message."));
+ continue;
+ }
+
+ V8Message msg;
+
+ try {
+ msg = new V8Message(raw.textData());
+ // System.out.println(msg.name + ": " + JSON.stringify(msg.params));
+ }
+ catch (SyntaxException e) {
+ ws.send(new V8Error(e.getMessage()));
+ return;
+ }
+
+ try {
+ switch (msg.name) {
+ case "Debugger.enable": debugger.enable(msg); continue;
+ case "Debugger.disable": debugger.disable(msg); continue;
+
+ case "Debugger.setBreakpoint": debugger.setBreakpoint(msg); continue;
+ case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
+ case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue;
+ case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue;
+
+ case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue;
+ case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue;
+
+ case "Debugger.resume": debugger.resume(msg); continue;
+ case "Debugger.pause": debugger.pause(msg); continue;
+
+ case "Debugger.stepInto": debugger.stepInto(msg); continue;
+ case "Debugger.stepOut": debugger.stepOut(msg); continue;
+ case "Debugger.stepOver": debugger.stepOver(msg); continue;
+
+ case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue;
+ case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue;
+
+ case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue;
+ case "Runtime.getProperties": debugger.getProperties(msg); continue;
+ case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue;
+ // case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue;
+ case "Runtime.enable": debugger.runtimeEnable(msg); continue;
+ }
+
+ if (
+ msg.name.startsWith("DOM.") ||
+ msg.name.startsWith("DOMDebugger.") ||
+ msg.name.startsWith("Emulation.") ||
+ msg.name.startsWith("Input.") ||
+ msg.name.startsWith("Network.") ||
+ msg.name.startsWith("Page.")
+ ) ws.send(new V8Error("This isn't a browser..."));
+ else ws.send(new V8Error("This API is not supported yet."));
+ }
+ catch (Throwable e) {
+ e.printStackTrace();
+ throw new UncheckedException(e);
+ }
+ }
+
+ debugger.disconnect();
+ }
+ private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) {
+ var key = req.headers.get("sec-websocket-key");
+
+ if (key == null) {
+ req.writeResponse(
+ 426, "Upgrade Required", "text/txt",
+ "Expected a WS upgrade".getBytes()
+ );
+ return;
+ }
+
+ var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest(
+ (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()
+ ));
+
+ req.writeCode(101, "Switching Protocols");
+ req.writeHeader("Connection", "Upgrade");
+ req.writeHeader("Sec-WebSocket-Accept", resKey);
+ req.writeLastHeader("Upgrade", "WebSocket");
+
+ var ws = new WebSocket(socket);
+ var debugger = debuggerProvider.getDebugger(ws, req);
+
+ if (debugger == null) {
+ ws.close();
+ return;
+ }
+
+ runAsync(() -> {
+ try { handle(ws, debugger); }
+ catch (RuntimeException e) {
+ ws.send(new V8Error(e.getMessage()));
+ }
+ finally { ws.close(); debugger.disconnect(); }
+ }, "Debug Handler");
+ }
+
+ public void run(InetSocketAddress address) {
+ try {
+ ServerSocket server = new ServerSocket();
+ server.bind(address);
+
+ try {
+ while (true) {
+ var socket = server.accept();
+ var req = HttpRequest.read(socket);
+
+ if (req == null) continue;
+
+ switch (req.path) {
+ case "/json/version":
+ send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}");
+ break;
+ case "/json/list":
+ case "/json": {
+ var res = new JSONList();
+
+ for (var el : targets.entrySet()) {
+ res.add(new JSONMap()
+ .set("description", "JScript debugger")
+ .set("favicon", "/favicon.ico")
+ .set("id", el.getKey())
+ .set("type", "node")
+ .set("webSocketDebuggerUrl", "ws://" + address.getHostString() + ":" + address.getPort() + "/" + el.getKey())
+ );
+ }
+ send(req, JSON.stringify(res));
+ break;
+ }
+ case "/json/protocol":
+ req.writeResponse(200, "OK", "application/json", protocol);
+ break;
+ case "/json/new":
+ case "/json/activate":
+ case "/json/close":
+ case "/devtools/inspector.html":
+ req.writeResponse(
+ 501, "Not Implemented", "text/txt",
+ "This feature isn't (and probably won't be) implemented.".getBytes()
+ );
+ break;
+ case "/":
+ case "/index.html":
+ req.writeResponse(200, "OK", "text/html", index);
+ break;
+ case "/favicon.ico":
+ req.writeResponse(200, "OK", "image/png", favicon);
+ break;
+ default:
+ if (req.path.length() > 1 && targets.containsKey(req.path.substring(1))) {
+ onWsConnect(req, socket, targets.get(req.path.substring(1)));
+ }
+ break;
+ }
+ }
+ }
+ finally { server.close(); }
+ }
+ catch (IOException e) { throw new UncheckedIOException(e); }
+ }
+
+ public Thread start(InetSocketAddress address, boolean daemon) {
+ var res = new Thread(() -> run(address), "Debug Server");
+ res.setDaemon(daemon);
+ res.start();
+ return res;
+ }
+
+ public DebugServer() {
+ try {
+ this.favicon = getClass().getClassLoader().getResourceAsStream("assets/favicon.png").readAllBytes();
+ this.protocol = getClass().getClassLoader().getResourceAsStream("assets/protocol.json").readAllBytes();
+ var index = new String(getClass().getClassLoader().getResourceAsStream("assets/index.html").readAllBytes());
+ this.index = index
+ .replace("${NAME}", Metadata.NAME)
+ .replace("${VERSION}", Metadata.VERSION)
+ .replace("${AUTHOR}", Metadata.AUTHOR)
+ .getBytes();
+ }
+ catch (IOException e) { throw new UncheckedIOException(e); }
+ }
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/Debugger.java b/src/me/topchetoeu/jscript/engine/debug/Debugger.java
new file mode 100644
index 0000000..3c704af
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/Debugger.java
@@ -0,0 +1,5 @@
+package me.topchetoeu.jscript.engine.debug;
+
+public interface Debugger extends DebugHandler, DebugController {
+
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/DebuggerProvider.java b/src/me/topchetoeu/jscript/engine/debug/DebuggerProvider.java
new file mode 100644
index 0000000..cd15ffc
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/DebuggerProvider.java
@@ -0,0 +1,5 @@
+package me.topchetoeu.jscript.engine.debug;
+
+public interface DebuggerProvider {
+ Debugger getDebugger(WebSocket socket, HttpRequest req);
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/HttpRequest.java b/src/me/topchetoeu/jscript/engine/debug/HttpRequest.java
new file mode 100644
index 0000000..bff2bdb
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/HttpRequest.java
@@ -0,0 +1,102 @@
+package me.topchetoeu.jscript.engine.debug;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.IllegalFormatException;
+import java.util.Map;
+
+public class HttpRequest {
+ public final String method;
+ public final String path;
+ public final Map headers;
+ public final OutputStream out;
+
+
+ public void writeCode(int code, String name) {
+ try { out.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); }
+ catch (IOException e) { }
+ }
+ public void writeHeader(String name, String value) {
+ try { out.write((name + ": " + value + "\r\n").getBytes()); }
+ catch (IOException e) { }
+ }
+ public void writeLastHeader(String name, String value) {
+ try { out.write((name + ": " + value + "\r\n\r\n").getBytes()); }
+ catch (IOException e) { }
+ }
+ public void writeHeadersEnd() {
+ try { out.write("\n".getBytes()); }
+ catch (IOException e) { }
+ }
+
+ public void writeResponse(int code, String name, String type, byte[] data) {
+ writeCode(code, name);
+ writeHeader("Content-Type", type);
+ writeLastHeader("Content-Length", data.length + "");
+ try {
+ out.write(data);
+ out.close();
+ }
+ catch (IOException e) { }
+ }
+ public void writeResponse(int code, String name, String type, InputStream data) {
+ try {
+ writeResponse(code, name, type, data.readAllBytes());
+ }
+ catch (IOException e) { }
+ }
+
+ public HttpRequest(String method, String path, Map headers, OutputStream out) {
+ this.method = method;
+ this.path = path;
+ this.headers = headers;
+ this.out = out;
+ }
+
+ // We dont need no http library
+ public static HttpRequest read(Socket socket) {
+ try {
+ var str = socket.getInputStream();
+ var lines = new BufferedReader(new InputStreamReader(str));
+ var line = lines.readLine();
+ var i1 = line.indexOf(" ");
+ var i2 = line.indexOf(" ", i1 + 1);
+
+ if (i1 < 0 || i2 < 0) {
+ socket.close();
+ return null;
+ }
+
+ var method = line.substring(0, i1).trim().toUpperCase();
+ var path = line.substring(i1 + 1, i2).trim();
+ var headers = new HashMap();
+
+ while (!(line = lines.readLine()).isEmpty()) {
+ var i = line.indexOf(":");
+ if (i < 0) continue;
+ var name = line.substring(0, i).trim().toLowerCase();
+ var value = line.substring(i + 1).trim();
+
+ if (name.length() == 0) continue;
+ headers.put(name, value);
+ }
+
+ if (headers.containsKey("content-length")) {
+ try {
+ var i = Integer.parseInt(headers.get("content-length"));
+ str.skip(i);
+ }
+ catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ }
+ }
+
+ return new HttpRequest(method, path, headers, socket.getOutputStream());
+ }
+ catch (IOException | NullPointerException e) { return null; }
+ }
+}
+
diff --git a/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java b/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java
new file mode 100644
index 0000000..a900973
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java
@@ -0,0 +1,788 @@
+package me.topchetoeu.jscript.engine.debug;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import me.topchetoeu.jscript.Filename;
+import me.topchetoeu.jscript.Location;
+import me.topchetoeu.jscript.compilation.Instruction;
+import me.topchetoeu.jscript.compilation.Instruction.Type;
+import me.topchetoeu.jscript.engine.Context;
+import me.topchetoeu.jscript.engine.Engine;
+import me.topchetoeu.jscript.engine.StackData;
+import me.topchetoeu.jscript.engine.frame.CodeFrame;
+import me.topchetoeu.jscript.engine.frame.Runners;
+import me.topchetoeu.jscript.engine.scope.GlobalScope;
+import me.topchetoeu.jscript.engine.values.ArrayValue;
+import me.topchetoeu.jscript.engine.values.CodeFunction;
+import me.topchetoeu.jscript.engine.values.FunctionValue;
+import me.topchetoeu.jscript.engine.values.ObjectValue;
+import me.topchetoeu.jscript.engine.values.Symbol;
+import me.topchetoeu.jscript.engine.values.Values;
+import me.topchetoeu.jscript.events.Notifier;
+import me.topchetoeu.jscript.exceptions.EngineException;
+import me.topchetoeu.jscript.json.JSON;
+import me.topchetoeu.jscript.json.JSONElement;
+import me.topchetoeu.jscript.json.JSONList;
+import me.topchetoeu.jscript.json.JSONMap;
+import me.topchetoeu.jscript.lib.DateLib;
+import me.topchetoeu.jscript.lib.MapLib;
+import me.topchetoeu.jscript.lib.PromiseLib;
+import me.topchetoeu.jscript.lib.RegExpLib;
+import me.topchetoeu.jscript.lib.SetLib;
+import me.topchetoeu.jscript.lib.GeneratorLib.Generator;
+
+public class SimpleDebugger implements Debugger {
+ public static final String CHROME_GET_PROP_FUNC = "function s(e){let t=this;const n=JSON.parse(e);for(let e=0,i=n.length;e breakpoints;
+
+ public Source(int id, Filename filename, String source, TreeSet breakpoints) {
+ this.id = id;
+ this.filename = filename;
+ this.source = source;
+ this.breakpoints = breakpoints;
+ }
+ }
+ private static class Breakpoint {
+ public final int id;
+ public final Location location;
+ public final String condition;
+
+ public Breakpoint(int id, Location location, String condition) {
+ this.id = id;
+ this.location = location;
+ this.condition = condition;
+ }
+ }
+ private static class BreakpointCandidate {
+ public final int id;
+ public final String condition;
+ public final Pattern pattern;
+ public final int line, start;
+ public final HashSet resolvedBreakpoints = new HashSet<>();
+
+ public BreakpointCandidate(int id, Pattern pattern, int line, int start, String condition) {
+ this.id = id;
+ this.pattern = pattern;
+ this.line = line;
+ this.start = start;
+ if (condition != null && condition.trim().equals("")) condition = null;
+ this.condition = condition;
+ }
+ }
+ private class Frame {
+ public CodeFrame frame;
+ public CodeFunction func;
+ public int id;
+ public ObjectValue local, capture, global;
+ public JSONMap serialized;
+ public Location location;
+ public boolean debugData = false;
+
+ public void updateLoc(Location loc) {
+ serialized.set("location", serializeLocation(loc));
+ this.location = loc;
+ }
+
+ public Frame(Context ctx, CodeFrame frame, int id) {
+ this.frame = frame;
+ this.func = frame.function;
+ this.id = id;
+ this.local = new ObjectValue();
+ this.location = frame.function.loc();
+
+ this.global = frame.function.environment.global.obj;
+ this.local = frame.getLocalScope(ctx, true);
+ this.capture = frame.getCaptureScope(ctx, true);
+ this.local.setPrototype(ctx, capture);
+ this.capture.setPrototype(ctx, global);
+
+ if (location != null) {
+ debugData = true;
+ this.serialized = new JSONMap()
+ .set("callFrameId", id + "")
+ .set("functionName", func.name)
+ .set("location", serializeLocation(location))
+ .set("scopeChain", new JSONList()
+ .add(new JSONMap().set("type", "local").set("name", "Local Scope").set("object", serializeObj(ctx, local)))
+ .add(new JSONMap().set("type", "closure").set("name", "Closure").set("object", serializeObj(ctx, capture)))
+ .add(new JSONMap().set("type", "global").set("name", "Global Scope").set("object", serializeObj(ctx, global)))
+ )
+ .setNull("this");
+ }
+ }
+ }
+
+ private class RunResult {
+ public final Context ctx;
+ public final Object result;
+ public final EngineException error;
+
+ public RunResult(Context ctx, Object result, EngineException error) {
+ this.ctx = ctx;
+ this.result = result;
+ this.error = error;
+ }
+ }
+
+ public boolean enabled = true;
+ public CatchType execptionType = CatchType.ALL;
+ public State state = State.RESUMED;
+
+ public final WebSocket ws;
+ public final Engine target;
+
+ private HashMap idToBptCand = new HashMap<>();
+
+ private HashMap idToBreakpoint = new HashMap<>();
+ private HashMap locToBreakpoint = new HashMap<>();
+ private HashSet tmpBreakpts = new HashSet<>();
+
+ private HashMap filenameToId = new HashMap<>();
+ private HashMap idToSource = new HashMap<>();
+ private ArrayList pendingSources = new ArrayList<>();
+
+ private HashMap idToFrame = new HashMap<>();
+ private HashMap codeFrameToFrame = new HashMap<>();
+
+ private HashMap idToObject = new HashMap<>();
+ private HashMap objectToId = new HashMap<>();
+ private HashMap objectToCtx = new HashMap<>();
+ private HashMap> objectGroups = new HashMap<>();
+
+ private Notifier updateNotifier = new Notifier();
+
+ private int nextId = new Random().nextInt() & 0x7FFFFFFF;
+ private Location prevLocation = null;
+ private Frame stepOutFrame = null, currFrame = null;
+
+ private int nextId() {
+ return nextId++ ^ 1630022591 /* big prime */;
+ }
+
+ private void updateFrames(Context ctx) {
+ var frame = StackData.peekFrame(ctx);
+ if (frame == null) return;
+
+ if (!codeFrameToFrame.containsKey(frame)) {
+ var id = nextId();
+ var fr = new Frame(ctx, frame, id);
+
+ idToFrame.put(id, fr);
+ codeFrameToFrame.put(frame, fr);
+ }
+
+ currFrame = codeFrameToFrame.get(frame);
+ }
+ private JSONList serializeFrames(Context ctx) {
+ var res = new JSONList();
+ var frames = StackData.frames(ctx);
+
+ for (var i = frames.size() - 1; i >= 0; i--) {
+ res.add(codeFrameToFrame.get(frames.get(i)).serialized);
+ }
+
+ return res;
+ }
+
+ private Location correctLocation(Source source, Location loc) {
+ var set = source.breakpoints;
+
+ if (set.contains(loc)) return loc;
+
+ var tail = set.tailSet(loc);
+ if (tail.isEmpty()) return null;
+
+ return tail.first();
+ }
+ private Location deserializeLocation(JSONElement el, boolean correct) {
+ if (!el.isMap()) throw new RuntimeException("Expected location to be a map.");
+ var id = Integer.parseInt(el.map().string("scriptId"));
+ var line = (int)el.map().number("lineNumber") + 1;
+ var column = (int)el.map().number("columnNumber") + 1;
+
+ if (!idToSource.containsKey(id)) throw new RuntimeException(String.format("The specified source %s doesn't exist.", id));
+
+ var res = new Location(line, column, idToSource.get(id).filename);
+ if (correct) res = correctLocation(idToSource.get(id), res);
+ return res;
+ }
+ private JSONMap serializeLocation(Location loc) {
+ var source = filenameToId.get(loc.filename());
+ return new JSONMap()
+ .set("scriptId", source + "")
+ .set("lineNumber", loc.line() - 1)
+ .set("columnNumber", loc.start() - 1);
+ }
+
+ private Integer objectId(Context ctx, ObjectValue obj) {
+ if (objectToId.containsKey(obj)) return objectToId.get(obj);
+ else {
+ int id = nextId();
+ objectToId.put(obj, id);
+ if (ctx != null) objectToCtx.put(obj, ctx);
+ idToObject.put(id, obj);
+ return id;
+ }
+ }
+ private JSONMap serializeObj(Context ctx, Object val, boolean recurse) {
+ val = Values.normalize(null, val);
+
+ if (val == Values.NULL) {
+ return new JSONMap()
+ .set("type", "object")
+ .set("subtype", "null")
+ .setNull("value")
+ .set("description", "null");
+ }
+
+ if (val instanceof ObjectValue) {
+ var obj = (ObjectValue)val;
+ var id = objectId(ctx, obj);
+ var type = "object";
+ String subtype = null;
+ String className = null;
+
+ if (obj instanceof FunctionValue) type = "function";
+ if (obj instanceof ArrayValue) subtype = "array";
+
+ if (Values.isWrapper(val, RegExpLib.class)) subtype = "regexp";
+ if (Values.isWrapper(val, DateLib.class)) subtype = "date";
+ if (Values.isWrapper(val, MapLib.class)) subtype = "map";
+ if (Values.isWrapper(val, SetLib.class)) subtype = "set";
+ if (Values.isWrapper(val, Generator.class)) subtype = "generator";
+ if (Values.isWrapper(val, PromiseLib.class)) subtype = "promise";
+
+ try { className = Values.toString(ctx, Values.getMember(ctx, obj.getMember(ctx, "constructor"), "name")); }
+ catch (Exception e) { }
+
+ var res = new JSONMap()
+ .set("type", type)
+ .set("objectId", id + "");
+
+ if (subtype != null) res.set("subtype", subtype);
+ if (className != null) {
+ res.set("className", className);
+ res.set("description", "{ " + className + " }");
+ }
+
+ return res;
+ }
+
+ if (val == null) return new JSONMap().set("type", "undefined");
+ if (val instanceof String) return new JSONMap().set("type", "string").set("value", (String)val);
+ if (val instanceof Boolean) return new JSONMap().set("type", "boolean").set("value", (Boolean)val);
+ if (val instanceof Symbol) return new JSONMap().set("type", "symbol").set("description", val.toString());
+ if (val instanceof Number) {
+ var num = (double)(Number)val;
+ var res = new JSONMap().set("type", "number");
+
+ if (Double.POSITIVE_INFINITY == num) res.set("unserializableValue", "Infinity");
+ else if (Double.NEGATIVE_INFINITY == num) res.set("unserializableValue", "-Infinity");
+ else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(-0d)) res.set("unserializableValue", "-0");
+ else if (Double.isNaN(num)) res.set("unserializableValue", "NaN");
+ else res.set("value", num);
+
+ return res;
+ }
+
+ throw new IllegalArgumentException("Unexpected JS object.");
+ }
+ private JSONMap serializeObj(Context ctx, Object val) {
+ return serializeObj(ctx, val, true);
+ }
+ private void setObjectGroup(String name, Object val) {
+ if (val instanceof ObjectValue) {
+ var obj = (ObjectValue)val;
+
+ if (objectGroups.containsKey(name)) objectGroups.get(name).add(obj);
+ else objectGroups.put(name, new ArrayList<>(List.of(obj)));
+ }
+ }
+ private void releaseGroup(String name) {
+ var objs = objectGroups.remove(name);
+
+ if (objs != null) {
+ for (var obj : objs) {
+ var id = objectToId.remove(obj);
+ if (id != null) idToObject.remove(id);
+ }
+ }
+ }
+ private Object deserializeArgument(JSONMap val) {
+ if (val.isString("objectId")) return idToObject.get(Integer.parseInt(val.string("objectId")));
+ else if (val.isString("unserializableValue")) switch (val.string("unserializableValue")) {
+ case "NaN": return Double.NaN;
+ case "-Infinity": return Double.NEGATIVE_INFINITY;
+ case "Infinity": return Double.POSITIVE_INFINITY;
+ case "-0": return -0.;
+ }
+ return JSON.toJs(val.get("value"));
+ }
+
+ private JSONMap serializeException(Context ctx, EngineException err) {
+ String text = null;
+
+ try {
+ text = Values.toString(ctx, err.value);
+ }
+ catch (EngineException e) {
+ text = "[error while stringifying]";
+ }
+
+ var res = new JSONMap()
+ .set("exceptionId", nextId())
+ .set("exception", serializeObj(ctx, err.value))
+ .set("exception", serializeObj(ctx, err.value))
+ .set("text", text);
+
+ return res;
+ }
+
+ private void resume(State state) {
+ this.state = state;
+ ws.send(new V8Event("Debugger.resumed", new JSONMap()));
+ updateNotifier.next();
+ }
+ private void pauseDebug(Context ctx, Breakpoint bp) {
+ state = State.PAUSED_NORMAL;
+ var map = new JSONMap()
+ .set("callFrames", serializeFrames(ctx))
+ .set("reason", "debugCommand");
+
+ if (bp != null) map.set("hitBreakpoints", new JSONList().add(bp.id + ""));
+ ws.send(new V8Event("Debugger.paused", map));
+ }
+ private void pauseException(Context ctx) {
+ state = State.PAUSED_EXCEPTION;
+ var map = new JSONMap()
+ .set("callFrames", serializeFrames(ctx))
+ .set("reason", "exception");
+
+ ws.send(new V8Event("Debugger.paused", map));
+ }
+
+ private void sendSource(Source src) {
+ ws.send(new V8Event("Debugger.scriptParsed", new JSONMap()
+ .set("scriptId", src.id + "")
+ .set("hash", src.source.hashCode())
+ .set("url", src.filename + "")
+ ));
+ }
+
+ private void addBreakpoint(Breakpoint bpt) {
+ idToBreakpoint.put(bpt.id, bpt);
+ locToBreakpoint.put(bpt.location, bpt);
+
+ ws.send(new V8Event("Debugger.breakpointResolved", new JSONMap()
+ .set("breakpointId", bpt.id)
+ .set("location", serializeLocation(bpt.location))
+ ));
+ }
+
+ private RunResult run(Frame codeFrame, String code) {
+ var engine = new Engine();
+ var env = codeFrame.func.environment.fork();
+
+ ObjectValue global = env.global.obj,
+ capture = codeFrame.frame.getCaptureScope(null, enabled),
+ local = codeFrame.frame.getLocalScope(null, enabled);
+
+ capture.setPrototype(null, global);
+ local.setPrototype(null, capture);
+ env.global = new GlobalScope(local);
+
+ var ctx = new Context(engine).pushEnv(env);
+ var awaiter = engine.pushMsg(false, ctx, new Filename("temp", "exec"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
+
+ engine.run(true);
+
+ try { return new RunResult(ctx, awaiter.await(), null); }
+ catch (EngineException e) { return new RunResult(ctx, null, e); }
+ }
+
+ @Override public void enable(V8Message msg) {
+ enabled = true;
+ ws.send(msg.respond());
+
+ for (var el : pendingSources) sendSource(el);
+ pendingSources.clear();
+
+ updateNotifier.next();
+ }
+ @Override public void disable(V8Message msg) {
+ enabled = false;
+ ws.send(msg.respond());
+ updateNotifier.next();
+ }
+
+ @Override public void getScriptSource(V8Message msg) {
+ int id = Integer.parseInt(msg.params.string("scriptId"));
+ ws.send(msg.respond(new JSONMap().set("scriptSource", idToSource.get(id).source)));
+ }
+ @Override public void getPossibleBreakpoints(V8Message msg) {
+ var src = idToSource.get(Integer.parseInt(msg.params.map("start").string("scriptId")));
+ var start = deserializeLocation(msg.params.get("start"), false);
+ var end = msg.params.isMap("end") ? deserializeLocation(msg.params.get("end"), false) : null;
+
+ var res = new JSONList();
+
+ for (var loc : src.breakpoints.tailSet(start, true)) {
+ if (end != null && loc.compareTo(end) > 0) break;
+ res.add(serializeLocation(loc));
+ }
+
+ ws.send(msg.respond(new JSONMap().set("locations", res)));
+ }
+
+ @Override public void pause(V8Message msg) {
+ }
+
+ @Override public void resume(V8Message msg) {
+ resume(State.RESUMED);
+ ws.send(msg.respond(new JSONMap()));
+ }
+
+ @Override public void setBreakpoint(V8Message msg) {
+ // int id = nextId();
+ // var loc = deserializeLocation(msg.params.get("location"), true);
+ // var bpt = new Breakpoint(id, loc, null);
+ // breakpoints.put(loc, bpt);
+ // idToBrpt.put(id, bpt);
+ // ws.send(msg.respond(new JSONMap()
+ // .set("breakpointId", id)
+ // .set("actualLocation", serializeLocation(loc))
+ // ));
+ }
+ @Override public void setBreakpointByUrl(V8Message msg) {
+ var line = (int)msg.params.number("lineNumber") + 1;
+ var col = (int)msg.params.number("columnNumber", 0) + 1;
+
+ Pattern regex;
+
+ if (msg.params.isString("url")) regex = Pattern.compile(Pattern.quote(msg.params.string("url")));
+ else regex = Pattern.compile(msg.params.string("urlRegex"));
+
+ var bpcd = new BreakpointCandidate(nextId(), regex, line, col, null);
+ idToBptCand.put(bpcd.id, bpcd);
+
+ var locs = new JSONList();
+
+ for (var src : idToSource.values()) {
+ if (regex.matcher(src.filename.toString()).matches()) {
+ var loc = correctLocation(src, new Location(line, col, src.filename));
+ if (loc == null) continue;
+ var bp = new Breakpoint(nextId(), loc, bpcd.condition);
+
+ bpcd.resolvedBreakpoints.add(bp);
+ locs.add(serializeLocation(loc));
+ addBreakpoint(bp);
+ }
+ }
+
+ ws.send(msg.respond(new JSONMap()
+ .set("breakpointId", bpcd.id + "")
+ .set("locations", locs)
+ ));
+ }
+ @Override public void removeBreakpoint(V8Message msg) {
+ var id = Integer.parseInt(msg.params.string("breakpointId"));
+
+ if (idToBptCand.containsKey(id)) {
+ var bpcd = idToBptCand.get(id);
+ for (var bp : bpcd.resolvedBreakpoints) {
+ idToBreakpoint.remove(bp.id);
+ locToBreakpoint.remove(bp.location);
+ }
+ idToBptCand.remove(id);
+ }
+ else if (idToBreakpoint.containsKey(id)) {
+ var bp = idToBreakpoint.remove(id);
+ locToBreakpoint.remove(bp.location);
+ }
+ ws.send(msg.respond());
+ }
+ @Override public void continueToLocation(V8Message msg) {
+ var loc = deserializeLocation(msg.params.get("location"), true);
+
+ tmpBreakpts.add(loc);
+
+ resume(State.RESUMED);
+ ws.send(msg.respond());
+ }
+
+ @Override public void setPauseOnExceptions(V8Message msg) {
+ ws.send(new V8Error("i dont wanna to"));
+ }
+
+ @Override public void stepInto(V8Message msg) {
+ if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
+ else {
+ prevLocation = currFrame.location;
+ stepOutFrame = currFrame;
+ resume(State.STEPPING_IN);
+ ws.send(msg.respond());
+ }
+ }
+ @Override public void stepOut(V8Message msg) {
+ if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
+ else {
+ prevLocation = currFrame.location;
+ stepOutFrame = currFrame;
+ resume(State.STEPPING_OUT);
+ ws.send(msg.respond());
+ }
+ }
+ @Override public void stepOver(V8Message msg) {
+ if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
+ else {
+ prevLocation = currFrame.location;
+ stepOutFrame = currFrame;
+ resume(State.STEPPING_OVER);
+ ws.send(msg.respond());
+ }
+ }
+
+ @Override public void evaluateOnCallFrame(V8Message msg) {
+ var cfId = Integer.parseInt(msg.params.string("callFrameId"));
+ var expr = msg.params.string("expression");
+ var group = msg.params.string("objectGroup", null);
+
+ var cf = idToFrame.get(cfId);
+ var res = run(cf, expr);
+
+ if (group != null) setObjectGroup(group, res.result);
+
+ if (res.error != null) ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeObj(res.ctx, res.result))));
+
+ ws.send(msg.respond(new JSONMap().set("result", serializeObj(res.ctx, res.result))));
+ }
+
+ @Override public void releaseObjectGroup(V8Message msg) {
+ var group = msg.params.string("objectGroup");
+ releaseGroup(group);
+ ws.send(msg.respond());
+ }
+ @Override public void getProperties(V8Message msg) {
+ var obj = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
+ var own = msg.params.bool("ownProperties") || true;
+ var currOwn = true;
+
+ var res = new JSONList();
+
+ while (obj != null) {
+ var ctx = objectToCtx.get(obj);
+
+ for (var key : obj.keys(true)) {
+ var propDesc = new JSONMap();
+
+ if (obj.properties.containsKey(key)) {
+ var prop = obj.properties.get(key);
+
+ propDesc.set("name", Values.toString(ctx, key));
+ if (prop.getter != null) propDesc.set("get", serializeObj(ctx, prop.getter));
+ if (prop.setter != null) propDesc.set("set", serializeObj(ctx, prop.setter));
+ propDesc.set("enumerable", obj.memberEnumerable(key));
+ propDesc.set("configurable", obj.memberConfigurable(key));
+ propDesc.set("isOwn", currOwn);
+ res.add(propDesc);
+ }
+ else {
+ propDesc.set("name", Values.toString(ctx, key));
+ propDesc.set("value", serializeObj(ctx, obj.getMember(ctx, key)));
+ propDesc.set("writable", obj.memberWritable(key));
+ propDesc.set("enumerable", obj.memberEnumerable(key));
+ propDesc.set("configurable", obj.memberConfigurable(key));
+ propDesc.set("isOwn", currOwn);
+ res.add(propDesc);
+ }
+ }
+
+ obj = obj.getPrototype(ctx);
+
+ var protoDesc = new JSONMap();
+ protoDesc.set("name", "__proto__");
+ protoDesc.set("value", serializeObj(ctx, obj == null ? Values.NULL : obj));
+ protoDesc.set("writable", true);
+ protoDesc.set("enumerable", false);
+ protoDesc.set("configurable", false);
+ protoDesc.set("isOwn", currOwn);
+ res.add(protoDesc);
+
+ currOwn = false;
+ if (own) break;
+ }
+
+ ws.send(msg.respond(new JSONMap().set("result", res)));
+ }
+ @Override public void callFunctionOn(V8Message msg) {
+ var src = msg.params.string("functionDeclaration");
+ var args = msg.params.list("arguments", new JSONList()).stream().map(v -> v.map()).map(this::deserializeArgument).collect(Collectors.toList());
+ var thisArg = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
+ var ctx = objectToCtx.get(thisArg);
+
+ switch (src) {
+ case CHROME_GET_PROP_FUNC: {
+ var path = JSON.parse(new Filename("tmp", "json"), (String)args.get(0)).list();
+ Object res = thisArg;
+ for (var el : path) res = Values.getMember(ctx, res, JSON.toJs(el));
+ ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res))));
+ return;
+ }
+ default:
+ ws.send(new V8Error("A non well-known function was used with callFunctionOn."));
+ break;
+ }
+ }
+
+ @Override
+ public void runtimeEnable(V8Message msg) {
+ ws.send(msg.respond());
+ }
+
+ @Override public void onSource(Filename filename, String source, TreeSet locations) {
+ int id = nextId();
+ var src = new Source(id, filename, source, locations);
+
+ filenameToId.put(filename, id);
+ idToSource.put(id, src);
+
+ for (var bpcd : idToBptCand.values()) {
+ if (!bpcd.pattern.matcher(filename.toString()).matches()) continue;
+ var loc = correctLocation(src, new Location(bpcd.line, bpcd.start, filename));
+ var bp = new Breakpoint(nextId(), loc, bpcd.condition);
+ if (loc == null) continue;
+ bpcd.resolvedBreakpoints.add(bp);
+ addBreakpoint(bp);
+ }
+
+ if (!enabled) pendingSources.add(src);
+ else sendSource(src);
+ }
+ @Override public boolean onInstruction(Context ctx, CodeFrame cf, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
+ if (!enabled) return false;
+
+ updateFrames(ctx);
+ var frame = codeFrameToFrame.get(cf);
+
+ if (!frame.debugData) return false;
+
+ if (instruction.location != null) frame.updateLoc(instruction.location);
+ var loc = frame.location;
+ var isBreakpointable = loc != null && (
+ idToSource.get(filenameToId.get(loc.filename())).breakpoints.contains(loc) ||
+ returnVal != Runners.NO_RETURN
+ );
+
+ if (error != null && !caught && StackData.frames(ctx).size() > 1) error = null;
+
+ if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) {
+ pauseException(ctx);
+ }
+ else if (isBreakpointable && locToBreakpoint.containsKey(loc)) {
+ var bp = locToBreakpoint.get(loc);
+ var ok = bp.condition == null ? true : Values.toBoolean(run(currFrame, bp.condition));
+ if (ok) pauseDebug(ctx, locToBreakpoint.get(loc));
+ }
+ else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null);
+ else if (instruction.type == Type.NOP && instruction.match("debug")) pauseDebug(ctx, null);
+
+ while (enabled) {
+ switch (state) {
+ case PAUSED_EXCEPTION:
+ case PAUSED_NORMAL: break;
+
+ case STEPPING_OUT:
+ case RESUMED: return false;
+ case STEPPING_IN:
+ if (!prevLocation.equals(loc)) {
+ if (isBreakpointable) pauseDebug(ctx, null);
+ else if (returnVal != Runners.NO_RETURN) pauseDebug(ctx, null);
+ else return false;
+ }
+ else return false;
+ break;
+ case STEPPING_OVER:
+ if (stepOutFrame.frame == frame.frame) {
+ if (returnVal != Runners.NO_RETURN) {
+ state = State.STEPPING_OUT;
+ return false;
+ }
+ else if (isBreakpointable && (
+ !loc.filename().equals(prevLocation.filename()) ||
+ loc.line() != prevLocation.line()
+ )) pauseDebug(ctx, null);
+ else return false;
+ }
+ else return false;
+ break;
+ }
+ updateNotifier.await();
+ }
+
+ return false;
+ }
+ @Override public void onFramePop(Context ctx, CodeFrame frame) {
+ updateFrames(ctx);
+
+ try { idToFrame.remove(codeFrameToFrame.remove(frame).id); }
+ catch (NullPointerException e) { }
+
+ if (StackData.frames(ctx).size() == 0) resume(State.RESUMED);
+ else if (stepOutFrame != null && stepOutFrame.frame == frame &&
+ (state == State.STEPPING_OUT || state == State.STEPPING_IN)
+ ) {
+ pauseDebug(ctx, null);
+ updateNotifier.await();
+ }
+ }
+
+ @Override public void connect() {
+ target.data.set(StackData.DEBUGGER, this);
+ }
+ @Override public void disconnect() {
+ target.data.remove(StackData.DEBUGGER);
+ enabled = false;
+ updateNotifier.next();
+ }
+
+ private SimpleDebugger(WebSocket ws, Engine target) {
+ this.ws = ws;
+ this.target = target;
+ }
+
+ public static SimpleDebugger get(WebSocket ws, Engine target) {
+ if (target.data.has(StackData.DEBUGGER)) {
+ ws.send(new V8Error("A debugger is already attached to this engine."));
+ return null;
+ }
+ else {
+ var res = new SimpleDebugger(ws, target);
+ return res;
+ }
+ }
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/V8Error.java b/src/me/topchetoeu/jscript/engine/debug/V8Error.java
new file mode 100644
index 0000000..854e1cd
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/V8Error.java
@@ -0,0 +1,19 @@
+package me.topchetoeu.jscript.engine.debug;
+
+import me.topchetoeu.jscript.json.JSON;
+import me.topchetoeu.jscript.json.JSONMap;
+
+public class V8Error {
+ public final String message;
+
+ public V8Error(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public String toString() {
+ return JSON.stringify(new JSONMap().set("error", new JSONMap()
+ .set("message", message)
+ ));
+ }
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/V8Event.java b/src/me/topchetoeu/jscript/engine/debug/V8Event.java
new file mode 100644
index 0000000..a83e20b
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/V8Event.java
@@ -0,0 +1,22 @@
+package me.topchetoeu.jscript.engine.debug;
+
+import me.topchetoeu.jscript.json.JSON;
+import me.topchetoeu.jscript.json.JSONMap;
+
+public class V8Event {
+ public final String name;
+ public final JSONMap params;
+
+ public V8Event(String name, JSONMap params) {
+ this.name = name;
+ this.params = params;
+ }
+
+ @Override
+ public String toString() {
+ return JSON.stringify(new JSONMap()
+ .set("method", name)
+ .set("params", params)
+ );
+ }
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/V8Message.java b/src/me/topchetoeu/jscript/engine/debug/V8Message.java
new file mode 100644
index 0000000..82aa121
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/V8Message.java
@@ -0,0 +1,51 @@
+package me.topchetoeu.jscript.engine.debug;
+
+import java.util.Map;
+
+import me.topchetoeu.jscript.Filename;
+import me.topchetoeu.jscript.json.JSON;
+import me.topchetoeu.jscript.json.JSONElement;
+import me.topchetoeu.jscript.json.JSONMap;
+
+public class V8Message {
+ public final String name;
+ public final int id;
+ public final JSONMap params;
+
+ public V8Message(String name, int id, Map params) {
+ this.name = name;
+ this.params = new JSONMap(params);
+ this.id = id;
+ }
+ public V8Result respond(JSONMap result) {
+ return new V8Result(id, result);
+ }
+ public V8Result respond() {
+ return new V8Result(id, new JSONMap());
+ }
+
+ public V8Message(JSONMap raw) {
+ if (!raw.isNumber("id")) throw new IllegalArgumentException("Expected number property 'id'.");
+ if (!raw.isString("method")) throw new IllegalArgumentException("Expected string property 'method'.");
+
+ this.name = raw.string("method");
+ this.id = (int)raw.number("id");
+ this.params = raw.contains("params") ? raw.map("params") : new JSONMap();
+ }
+ public V8Message(String raw) {
+ this(JSON.parse(new Filename("jscript", "json-msg"), raw).map());
+ }
+
+ public JSONMap toMap() {
+ var res = new JSONMap();
+ return res;
+ }
+ @Override
+ public String toString() {
+ return JSON.stringify(new JSONMap()
+ .set("method", name)
+ .set("params", params)
+ .set("id", id)
+ );
+ }
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/V8Result.java b/src/me/topchetoeu/jscript/engine/debug/V8Result.java
new file mode 100644
index 0000000..f605cef
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/V8Result.java
@@ -0,0 +1,22 @@
+package me.topchetoeu.jscript.engine.debug;
+
+import me.topchetoeu.jscript.json.JSON;
+import me.topchetoeu.jscript.json.JSONMap;
+
+public class V8Result {
+ public final int id;
+ public final JSONMap result;
+
+ public V8Result(int id, JSONMap result) {
+ this.id = id;
+ this.result = result;
+ }
+
+ @Override
+ public String toString() {
+ return JSON.stringify(new JSONMap()
+ .set("id", id)
+ .set("result", result)
+ );
+ }
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/WebSocket.java b/src/me/topchetoeu/jscript/engine/debug/WebSocket.java
new file mode 100644
index 0000000..6e6efa9
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/WebSocket.java
@@ -0,0 +1,207 @@
+package me.topchetoeu.jscript.engine.debug;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
+import me.topchetoeu.jscript.exceptions.UncheckedIOException;
+
+public class WebSocket implements AutoCloseable {
+ public long maxLength = 2000000;
+
+ private Socket socket;
+ private boolean closed = false;
+
+ private OutputStream out() {
+ try { return socket.getOutputStream(); }
+ catch (IOException e) { throw new UncheckedIOException(e); }
+ }
+ private InputStream in() {
+ try { return socket.getInputStream(); }
+ catch (IOException e) { throw new UncheckedIOException(e); }
+ }
+
+ private long readLen(int byteLen) {
+ long res = 0;
+
+ try {
+ if (byteLen == 126) {
+ res |= in().read() << 8;
+ res |= in().read();
+ return res;
+ }
+ else if (byteLen == 127) {
+ res |= in().read() << 56;
+ res |= in().read() << 48;
+ res |= in().read() << 40;
+ res |= in().read() << 32;
+ res |= in().read() << 24;
+ res |= in().read() << 16;
+ res |= in().read() << 8;
+ res |= in().read();
+ return res;
+ }
+ else return byteLen;
+ }
+ catch (IOException e) { throw new UncheckedIOException(e); }
+ }
+ private byte[] readMask(boolean has) {
+ if (has) {
+ try { return new byte[] {
+ (byte)in().read(),
+ (byte)in().read(),
+ (byte)in().read(),
+ (byte)in().read()
+ }; }
+ catch (IOException e) { throw new UncheckedIOException(e); }
+ }
+ else return new byte[4];
+ }
+
+ private void writeLength(long len) {
+ try {
+ if (len < 126) {
+ out().write((int)len);
+ }
+ else if (len < 0xFFFF) {
+ out().write(126);
+ out().write((int)(len >> 8) & 0xFF);
+ out().write((int)len & 0xFF);
+ }
+ else {
+ out().write(127);
+ out().write((int)(len >> 56) & 0xFF);
+ out().write((int)(len >> 48) & 0xFF);
+ out().write((int)(len >> 40) & 0xFF);
+ out().write((int)(len >> 32) & 0xFF);
+ out().write((int)(len >> 24) & 0xFF);
+ out().write((int)(len >> 16) & 0xFF);
+ out().write((int)(len >> 8) & 0xFF);
+ out().write((int)len & 0xFF);
+ }
+ }
+ catch (IOException e) { throw new UncheckedIOException(e); }
+ }
+ private synchronized void write(int type, byte[] data) {
+ try {
+ out().write(type | 0x80);
+ writeLength(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out().write(data[i]);
+ }
+ }
+ catch (IOException e) { throw new UncheckedIOException(e); }
+ }
+
+ public void send(String data) {
+ if (closed) throw new IllegalStateException("Object is closed.");
+ write(1, data.getBytes());
+ }
+ public void send(byte[] data) {
+ if (closed) throw new IllegalStateException("Object is closed.");
+ write(2, data);
+ }
+ public void send(WebSocketMessage msg) {
+ if (msg.type == Type.Binary) send(msg.binaryData());
+ else send(msg.textData());
+ }
+ public void send(Object data) {
+ if (closed) throw new IllegalStateException("Object is closed.");
+ write(1, data.toString().getBytes());
+ }
+
+ public void close(String reason) {
+ if (socket != null) {
+ try {
+ write(8, reason.getBytes());
+ socket.close();
+ }
+ catch (Throwable e) { }
+ }
+
+ socket = null;
+ closed = true;
+ }
+ public void close() {
+ close("");
+ }
+
+ private WebSocketMessage fail(String reason) {
+ System.out.println("WebSocket Error: " + reason);
+ close(reason);
+ return null;
+ }
+
+ private byte[] readData() {
+ try {
+ var maskLen = in().read();
+ var hasMask = (maskLen & 0x80) != 0;
+ var len = (int)readLen(maskLen & 0x7F);
+ var mask = readMask(hasMask);
+
+ if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size");
+ else {
+ var buff = new byte[len];
+
+ if (in().read(buff) < len) fail("WebSocket Error: payload too short");
+ else {
+ for (int i = 0; i < len; i++) {
+ buff[i] ^= mask[(int)(i % 4)];
+ }
+ return buff;
+ }
+ }
+
+ return null;
+ }
+ catch (IOException e) { throw new UncheckedIOException(e); }
+ }
+
+ public WebSocketMessage receive() {
+ try {
+ var data = new ByteArrayOutputStream();
+ var type = 0;
+
+ while (socket != null && !closed) {
+ var finId = in().read();
+ if (finId < 0) break;
+ var fin = (finId & 0x80) != 0;
+ int id = finId & 0x0F;
+
+ if (id == 0x8) { close(); return null; }
+ if (id >= 0x8) {
+ if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented");
+ if (id == 0x9) write(0xA, data.toByteArray());
+ continue;
+ }
+
+ if (type == 0) type = id;
+ if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment");
+
+ var buff = readData();
+ if (buff == null) break;
+
+ if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size");
+ data.write(buff);
+
+ if (!fin) continue;
+ var raw = data.toByteArray();
+
+ if (type == 1) return new WebSocketMessage(new String(raw));
+ else return new WebSocketMessage(raw);
+ }
+ }
+ catch (IOException e) {
+ close();
+ }
+
+ return null;
+ }
+
+ public WebSocket(Socket socket) {
+ this.socket = socket;
+ }
+}
diff --git a/src/me/topchetoeu/jscript/engine/debug/WebSocketMessage.java b/src/me/topchetoeu/jscript/engine/debug/WebSocketMessage.java
new file mode 100644
index 0000000..e0b82b5
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/debug/WebSocketMessage.java
@@ -0,0 +1,29 @@
+package me.topchetoeu.jscript.engine.debug;
+
+public class WebSocketMessage {
+ public static enum Type {
+ Text,
+ Binary,
+ }
+
+ public final Type type;
+ private final Object data;
+
+ public final String textData() {
+ if (type != Type.Text) throw new IllegalStateException("Message is not text.");
+ return (String)data;
+ }
+ public final byte[] binaryData() {
+ if (type != Type.Binary) throw new IllegalStateException("Message is not binary.");
+ return (byte[])data;
+ }
+
+ public WebSocketMessage(String data) {
+ this.type = Type.Text;
+ this.data = data;
+ }
+ public WebSocketMessage(byte[] data) {
+ this.type = Type.Binary;
+ this.data = data;
+ }
+}
diff --git a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java
index 2ab02a3..1e2b4c7 100644
--- a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java
+++ b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java
@@ -3,14 +3,18 @@ package me.topchetoeu.jscript.engine.frame;
import java.util.Stack;
import me.topchetoeu.jscript.Location;
+import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Context;
+import me.topchetoeu.jscript.engine.StackData;
import me.topchetoeu.jscript.engine.scope.LocalScope;
import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.CodeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
+import me.topchetoeu.jscript.engine.values.ScopeValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException;
+import me.topchetoeu.jscript.exceptions.InterruptException;
public class CodeFrame {
private class TryCtx {
@@ -54,6 +58,33 @@ public class CodeFrame {
public boolean jumpFlag = false;
private Location prevLoc = null;
+ public ObjectValue getLocalScope(Context ctx, boolean props) {
+ var names = new String[scope.locals.length];
+
+ for (int i = 0; i < scope.locals.length; i++) {
+ var name = "local_" + (i - 2);
+
+ if (i == 0) name = "this";
+ else if (i == 1) name = "arguments";
+ else if (i < function.localNames.length) name = function.localNames[i];
+
+ names[i] = name;
+ }
+
+ return new ScopeValue(scope.locals, names);
+ }
+ public ObjectValue getCaptureScope(Context ctx, boolean props) {
+ var names = new String[scope.captures.length];
+
+ for (int i = 0; i < scope.captures.length; i++) {
+ var name = "capture_" + (i - 2);
+ if (i < function.captureNames.length) name = function.captureNames[i];
+ names[i] = name;
+ }
+
+ return new ScopeValue(scope.captures, names);
+ }
+
public void addTry(int n, int catchN, int finallyN) {
var res = new TryCtx(codePtr + 1, n, catchN, finallyN);
@@ -93,35 +124,33 @@ public class CodeFrame {
stack[stackPtr++] = Values.normalize(ctx, val);
}
- private void setCause(Context ctx, EngineException err, EngineException cause) throws InterruptedException {
- if (err.value instanceof ObjectValue) {
- Values.setMember(ctx, err, ctx.env.symbol("Symbol.cause"), cause);
- }
+ private void setCause(Context ctx, EngineException err, EngineException cause) {
err.cause = cause;
}
- private Object nextNoTry(Context ctx) throws InterruptedException {
- if (Thread.currentThread().isInterrupted()) throw new InterruptedException();
+ private Object nextNoTry(Context ctx, Instruction instr) {
+ if (Thread.currentThread().isInterrupted()) throw new InterruptException();
if (codePtr < 0 || codePtr >= function.body.length) return null;
- var instr = function.body[codePtr];
-
- var loc = instr.location;
- if (loc != null) prevLoc = loc;
-
try {
this.jumpFlag = false;
return Runners.exec(ctx, instr, this);
}
catch (EngineException e) {
- throw e.add(function.name, prevLoc).setContext(ctx);
+ throw e.add(function.name, prevLoc).setCtx(function.environment, ctx.engine);
}
}
- public Object next(Context ctx, Object value, Object returnValue, EngineException error) throws InterruptedException {
+ public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
if (value != Runners.NO_RETURN) push(ctx, value);
+ var debugger = StackData.getDebugger(ctx);
if (returnValue == Runners.NO_RETURN && error == null) {
- try { returnValue = nextNoTry(ctx); }
+ try {
+ var instr = function.body[codePtr];
+
+ if (debugger != null) debugger.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
+ returnValue = nextNoTry(ctx, instr);
+ }
catch (EngineException e) { error = e; }
}
@@ -144,7 +173,7 @@ public class CodeFrame {
}
else if (returnValue != Runners.NO_RETURN) {
if (tryCtx.hasFinally) {
- tryCtx.retVal = error;
+ tryCtx.retVal = returnValue;
newState = TryCtx.STATE_FINALLY_RETURNED;
}
break;
@@ -164,6 +193,7 @@ public class CodeFrame {
tryCtx.err = error;
newState = TryCtx.STATE_FINALLY_THREW;
}
+ setCause(ctx, error, tryCtx.err);
break;
}
else if (returnValue != Runners.NO_RETURN) {
@@ -214,6 +244,7 @@ public class CodeFrame {
case TryCtx.STATE_CATCH:
scope.catchVars.add(new ValueVariable(false, tryCtx.err.value));
codePtr = tryCtx.catchStart;
+ if (debugger != null) debugger.onInstruction(ctx, this, function.body[codePtr], null, error, true);
break;
default:
codePtr = tryCtx.finallyStart;
@@ -222,22 +253,16 @@ public class CodeFrame {
return Runners.NO_RETURN;
}
- if (error != null) throw error.setContext(ctx);
- if (returnValue != Runners.NO_RETURN) return returnValue;
- return Runners.NO_RETURN;
- }
+ if (error != null) {
+ if (debugger != null) debugger.onInstruction(ctx, this, function.body[codePtr], null, error, false);
+ throw error;
+ }
+ if (returnValue != Runners.NO_RETURN) {
+ if (debugger != null) debugger.onInstruction(ctx, this, function.body[codePtr], returnValue, null, false);
+ return returnValue;
+ }
- public Object run(Context ctx) throws InterruptedException {
- try {
- ctx.message.pushFrame(ctx, this);
- while (true) {
- var res = next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, null);
- if (res != Runners.NO_RETURN) return res;
- }
- }
- finally {
- ctx.message.popFrame(this);
- }
+ return Runners.NO_RETURN;
}
public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
diff --git a/src/me/topchetoeu/jscript/engine/frame/Runners.java b/src/me/topchetoeu/jscript/engine/frame/Runners.java
index ba56ad2..0c5366d 100644
--- a/src/me/topchetoeu/jscript/engine/frame/Runners.java
+++ b/src/me/topchetoeu/jscript/engine/frame/Runners.java
@@ -22,15 +22,15 @@ public class Runners {
public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) {
throw new EngineException(frame.pop());
}
- public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) {
throw EngineException.ofSyntax((String)instr.get(0));
}
- private static Object call(Context ctx, Object func, Object thisArg, Object ...args) throws InterruptedException {
+ private static Object call(Context ctx, Object func, Object thisArg, Object ...args) {
return Values.call(ctx, func, thisArg, args);
}
- public static Object execCall(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execCall(Context ctx, Instruction instr, CodeFrame frame) {
var callArgs = frame.take(instr.get(0));
var func = frame.pop();
var thisArg = frame.pop();
@@ -40,7 +40,7 @@ public class Runners {
frame.codePtr++;
return NO_RETURN;
}
- public static Object execCallNew(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execCallNew(Context ctx, Instruction instr, CodeFrame frame) {
var callArgs = frame.take(instr.get(0));
var funcObj = frame.pop();
@@ -61,13 +61,13 @@ public class Runners {
return NO_RETURN;
}
- public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) {
var name = (String)instr.get(0);
- ctx.env.global.define(name);
+ ctx.environment().global.define(name);
frame.codePtr++;
return NO_RETURN;
}
- public static Object execDefProp(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execDefProp(Context ctx, Instruction instr, CodeFrame frame) {
var setter = frame.pop();
var getter = frame.pop();
var name = frame.pop();
@@ -82,7 +82,7 @@ public class Runners {
frame.codePtr++;
return NO_RETURN;
}
- public static Object execInstanceof(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execInstanceof(Context ctx, Instruction instr, CodeFrame frame) {
var type = frame.pop();
var obj = frame.pop();
@@ -97,7 +97,7 @@ public class Runners {
frame.codePtr++;
return NO_RETURN;
}
- public static Object execKeys(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execKeys(Context ctx, Instruction instr, CodeFrame frame) {
var val = frame.pop();
var arr = new ObjectValue();
@@ -117,7 +117,7 @@ public class Runners {
return NO_RETURN;
}
- public static Object execTry(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execTry(Context ctx, Instruction instr, CodeFrame frame) {
frame.addTry(instr.get(0), instr.get(1), instr.get(2));
frame.codePtr++;
return NO_RETURN;
@@ -155,10 +155,10 @@ public class Runners {
frame.codePtr++;
return NO_RETURN;
}
- public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) {
var i = instr.get(0);
- if (i instanceof String) frame.push(ctx, ctx.env.global.get(ctx, (String)i));
+ if (i instanceof String) frame.push(ctx, ctx.environment().global.get(ctx, (String)i));
else frame.push(ctx, frame.scope.get((int)i).get(ctx));
frame.codePtr++;
@@ -170,7 +170,7 @@ public class Runners {
return NO_RETURN;
}
public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) {
- frame.push(ctx, ctx.env.global.obj);
+ frame.push(ctx, ctx.environment().global.obj);
frame.codePtr++;
return NO_RETURN;
}
@@ -191,15 +191,15 @@ public class Runners {
captures[i - 3] = frame.scope.get(instr.get(i));
}
- var body = ctx.message.engine.functions.get(id);
- var func = new CodeFunction(ctx.env, "", localsN, len, captures, body);
+ var body = ctx.engine.functions.get(id);
+ var func = new CodeFunction(ctx.environment(), "", localsN, len, captures, body);
frame.push(ctx, func);
frame.codePtr++;
return NO_RETURN;
}
- public static Object execLoadMember(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execLoadMember(Context ctx, Instruction instr, CodeFrame frame) {
var key = frame.pop();
var obj = frame.pop();
@@ -212,12 +212,12 @@ public class Runners {
frame.codePtr++;
return NO_RETURN;
}
- public static Object execLoadKeyMember(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execLoadKeyMember(Context ctx, Instruction instr, CodeFrame frame) {
frame.push(ctx, instr.get(0));
return execLoadMember(ctx, instr, frame);
}
- public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
- frame.push(ctx, ctx.env.regexConstructor.call(ctx, null, instr.get(0), instr.get(1)));
+ public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) {
+ frame.push(ctx, ctx.environment().regexConstructor.call(ctx, null, instr.get(0), instr.get(1)));
frame.codePtr++;
return NO_RETURN;
}
@@ -227,7 +227,7 @@ public class Runners {
frame.codePtr++;
return NO_RETURN;
}
- public static Object execStoreMember(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execStoreMember(Context ctx, Instruction instr, CodeFrame frame) {
var val = frame.pop();
var key = frame.pop();
var obj = frame.pop();
@@ -237,11 +237,11 @@ public class Runners {
frame.codePtr++;
return NO_RETURN;
}
- public static Object execStoreVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execStoreVar(Context ctx, Instruction instr, CodeFrame frame) {
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
var i = instr.get(0);
- if (i instanceof String) ctx.env.global.set(ctx, (String)i, val);
+ if (i instanceof String) ctx.environment().global.set(ctx, (String)i, val);
else frame.scope.get((int)i).set(ctx, val);
frame.codePtr++;
@@ -258,7 +258,7 @@ public class Runners {
frame.jumpFlag = true;
return NO_RETURN;
}
- public static Object execJmpIf(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execJmpIf(Context ctx, Instruction instr, CodeFrame frame) {
if (Values.toBoolean(frame.pop())) {
frame.codePtr += (int)instr.get(0);
frame.jumpFlag = true;
@@ -266,7 +266,7 @@ public class Runners {
else frame.codePtr ++;
return NO_RETURN;
}
- public static Object execJmpIfNot(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execJmpIfNot(Context ctx, Instruction instr, CodeFrame frame) {
if (Values.not(frame.pop())) {
frame.codePtr += (int)instr.get(0);
frame.jumpFlag = true;
@@ -275,7 +275,7 @@ public class Runners {
return NO_RETURN;
}
- public static Object execIn(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execIn(Context ctx, Instruction instr, CodeFrame frame) {
var obj = frame.pop();
var index = frame.pop();
@@ -283,13 +283,13 @@ public class Runners {
frame.codePtr++;
return NO_RETURN;
}
- public static Object execTypeof(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execTypeof(Context ctx, Instruction instr, CodeFrame frame) {
String name = instr.get(0);
Object obj;
if (name != null) {
- if (ctx.env.global.has(ctx, name)) {
- obj = ctx.env.global.get(ctx, name);
+ if (ctx.environment().global.has(ctx, name)) {
+ obj = ctx.environment().global.get(ctx, name);
}
else obj = null;
}
@@ -300,21 +300,12 @@ public class Runners {
frame.codePtr++;
return NO_RETURN;
}
- public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
- if (instr.is(0, "dbg_names")) {
- var names = new String[instr.params.length - 1];
- for (var i = 0; i < instr.params.length - 1; i++) {
- if (!(instr.params[i + 1] instanceof String)) throw EngineException.ofSyntax("NOP dbg_names instruction must specify only string parameters.");
- names[i] = (String)instr.params[i + 1];
- }
- frame.scope.setNames(names);
- }
-
+ public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) {
frame.codePtr++;
return NO_RETURN;
}
- public static Object execDelete(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execDelete(Context ctx, Instruction instr, CodeFrame frame) {
var key = frame.pop();
var val = frame.pop();
@@ -324,7 +315,7 @@ public class Runners {
return NO_RETURN;
}
- public static Object execOperation(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object execOperation(Context ctx, Instruction instr, CodeFrame frame) {
Operation op = instr.get(0);
var args = new Object[op.operands];
@@ -335,7 +326,7 @@ public class Runners {
return NO_RETURN;
}
- public static Object exec(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
+ public static Object exec(Context ctx, Instruction instr, CodeFrame frame) {
switch (instr.type) {
case NOP: return execNop(ctx, instr, frame);
case RETURN: return execReturn(ctx, instr, frame);
diff --git a/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java b/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java
index e447f2a..62027d8 100644
--- a/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java
+++ b/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java
@@ -15,7 +15,7 @@ public class GlobalScope implements ScopeRecord {
@Override
public GlobalScope parent() { return null; }
- public boolean has(Context ctx, String name) throws InterruptedException {
+ public boolean has(Context ctx, String name) {
return obj.hasMember(ctx, name, false);
}
public Object getKey(String name) {
@@ -32,13 +32,7 @@ public class GlobalScope implements ScopeRecord {
}
public Object define(String name) {
- try {
- if (obj.hasMember(null, name, true)) return name;
- }
- catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return name;
- }
+ if (obj.hasMember(null, name, true)) return name;
obj.defineProperty(null, name, null);
return name;
}
@@ -59,11 +53,11 @@ public class GlobalScope implements ScopeRecord {
define(null, val.name, readonly, val);
}
- public Object get(Context ctx, String name) throws InterruptedException {
+ public Object get(Context ctx, String name) {
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
else return obj.getMember(ctx, name);
}
- public void set(Context ctx, String name, Object val) throws InterruptedException {
+ public void set(Context ctx, String name, Object val) {
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
if (!obj.setMember(ctx, name, val, false)) throw EngineException.ofSyntax("The global '" + name + "' is readonly.");
}
diff --git a/src/me/topchetoeu/jscript/engine/scope/LocalScope.java b/src/me/topchetoeu/jscript/engine/scope/LocalScope.java
index 4ec8991..7645aaf 100644
--- a/src/me/topchetoeu/jscript/engine/scope/LocalScope.java
+++ b/src/me/topchetoeu/jscript/engine/scope/LocalScope.java
@@ -3,7 +3,6 @@ package me.topchetoeu.jscript.engine.scope;
import java.util.ArrayList;
public class LocalScope {
- private String[] names;
public final ValueVariable[] captures;
public final ValueVariable[] locals;
public final ArrayList catchVars = new ArrayList<>();
@@ -15,31 +14,11 @@ public class LocalScope {
else return captures[~i];
}
- public String[] getNames() {
- var res = new String[locals.length];
-
- for (var i = 0; i < locals.length; i++) {
- if (names == null || i >= names.length) res[i] = "local_" + i;
- else res[i] = names[i];
- }
-
- return res;
- }
- public void setNames(String[] names) {
- this.names = names;
- }
public int size() {
return captures.length + locals.length;
}
- public void toGlobal(GlobalScope global) {
- var names = getNames();
- for (var i = 0; i < names.length; i++) {
- global.define(names[i], locals[i]);
- }
- }
-
public LocalScope(int n, ValueVariable[] captures) {
locals = new ValueVariable[n];
this.captures = captures;
diff --git a/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java b/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java
index 7d5cf12..d196716 100644
--- a/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java
+++ b/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java
@@ -11,6 +11,9 @@ public class LocalScopeRecord implements ScopeRecord {
private final ArrayList captures = new ArrayList<>();
private final ArrayList locals = new ArrayList<>();
+ public String[] captures() {
+ return captures.toArray(String[]::new);
+ }
public String[] locals() {
return locals.toArray(String[]::new);
}
@@ -59,7 +62,7 @@ public class LocalScopeRecord implements ScopeRecord {
return name;
}
- public boolean has(Context ctx, String name) throws InterruptedException {
+ public boolean has(Context ctx, String name) {
return
global.has(ctx, name) ||
locals.contains(name) ||
diff --git a/src/me/topchetoeu/jscript/engine/scope/Variable.java b/src/me/topchetoeu/jscript/engine/scope/Variable.java
index 6bd3394..8715bd0 100644
--- a/src/me/topchetoeu/jscript/engine/scope/Variable.java
+++ b/src/me/topchetoeu/jscript/engine/scope/Variable.java
@@ -3,7 +3,7 @@ package me.topchetoeu.jscript.engine.scope;
import me.topchetoeu.jscript.engine.Context;
public interface Variable {
- Object get(Context ctx) throws InterruptedException;
+ Object get(Context ctx);
default boolean readonly() { return true; }
- default void set(Context ctx, Object val) throws InterruptedException { }
+ default void set(Context ctx, Object val) { }
}
diff --git a/src/me/topchetoeu/jscript/engine/values/ArrayValue.java b/src/me/topchetoeu/jscript/engine/values/ArrayValue.java
index 006c8d4..44a9543 100644
--- a/src/me/topchetoeu/jscript/engine/values/ArrayValue.java
+++ b/src/me/topchetoeu/jscript/engine/values/ArrayValue.java
@@ -122,7 +122,7 @@ public class ArrayValue extends ObjectValue implements Iterable {
}
@Override
- protected Object getField(Context ctx, Object key) throws InterruptedException {
+ protected Object getField(Context ctx, Object key) {
if (key instanceof Number) {
var i = ((Number)key).doubleValue();
if (i >= 0 && i - Math.floor(i) == 0) {
@@ -133,7 +133,7 @@ public class ArrayValue extends ObjectValue implements Iterable {
return super.getField(ctx, key);
}
@Override
- protected boolean setField(Context ctx, Object key, Object val) throws InterruptedException {
+ protected boolean setField(Context ctx, Object key, Object val) {
if (key instanceof Number) {
var i = Values.number(key);
if (i >= 0 && i - Math.floor(i) == 0) {
@@ -145,7 +145,7 @@ public class ArrayValue extends ObjectValue implements Iterable {
return super.setField(ctx, key, val);
}
@Override
- protected boolean hasField(Context ctx, Object key) throws InterruptedException {
+ protected boolean hasField(Context ctx, Object key) {
if (key instanceof Number) {
var i = Values.number(key);
if (i >= 0 && i - Math.floor(i) == 0) {
@@ -156,7 +156,7 @@ public class ArrayValue extends ObjectValue implements Iterable {
return super.hasField(ctx, key);
}
@Override
- protected void deleteField(Context ctx, Object key) throws InterruptedException {
+ protected void deleteField(Context ctx, Object key) {
if (key instanceof Number) {
var i = Values.number(key);
if (i >= 0 && i - Math.floor(i) == 0) {
@@ -212,7 +212,7 @@ public class ArrayValue extends ObjectValue implements Iterable {
for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ctx, values[i]);
}
- public static ArrayValue of(Context ctx, Collection values) {
+ public static ArrayValue of(Context ctx, Collection> values) {
return new ArrayValue(ctx, values.toArray(Object[]::new));
}
}
diff --git a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java
index c0025b3..e2dc2bf 100644
--- a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java
+++ b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java
@@ -1,16 +1,20 @@
package me.topchetoeu.jscript.engine.values;
import me.topchetoeu.jscript.Location;
+import me.topchetoeu.jscript.compilation.FunctionBody;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment;
+import me.topchetoeu.jscript.engine.StackData;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
+import me.topchetoeu.jscript.engine.frame.Runners;
import me.topchetoeu.jscript.engine.scope.ValueVariable;
public class CodeFunction extends FunctionValue {
public final int localsN;
public final int length;
public final Instruction[] body;
+ public final String[] captureNames, localNames;
public final ValueVariable[] captures;
public Environment environment;
@@ -28,16 +32,29 @@ public class CodeFunction extends FunctionValue {
}
@Override
- public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
- return new CodeFrame(ctx, thisArg, args, this).run(ctx.setEnv(environment));
+ public Object call(Context ctx, Object thisArg, Object ...args) {
+ var frame = new CodeFrame(ctx, thisArg, args, this);
+ try {
+ StackData.pushFrame(ctx, frame);
+
+ while (true) {
+ var res = frame.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, null);
+ if (res != Runners.NO_RETURN) return res;
+ }
+ }
+ finally {
+ StackData.popFrame(ctx, frame);
+ }
}
- public CodeFunction(Environment environment, String name, int localsN, int length, ValueVariable[] captures, Instruction[] body) {
+ public CodeFunction(Environment environment, String name, int localsN, int length, ValueVariable[] captures, FunctionBody body) {
super(name, length);
this.captures = captures;
+ this.captureNames = body.captureNames;
+ this.localNames = body.localNames;
this.environment = environment;
this.localsN = localsN;
this.length = length;
- this.body = body;
+ this.body = body.instructions;
}
}
diff --git a/src/me/topchetoeu/jscript/engine/values/FunctionValue.java b/src/me/topchetoeu/jscript/engine/values/FunctionValue.java
index 612f9b8..865a381 100644
--- a/src/me/topchetoeu/jscript/engine/values/FunctionValue.java
+++ b/src/me/topchetoeu/jscript/engine/values/FunctionValue.java
@@ -14,26 +14,26 @@ public abstract class FunctionValue extends ObjectValue {
return "function(...) { ...}";
}
- public abstract Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException;
- public Object call(Context ctx) throws InterruptedException {
+ public abstract Object call(Context ctx, Object thisArg, Object ...args);
+ public Object call(Context ctx) {
return call(ctx, null);
}
@Override
- protected Object getField(Context ctx, Object key) throws InterruptedException {
+ protected Object getField(Context ctx, Object key) {
if (key.equals("name")) return name;
if (key.equals("length")) return length;
return super.getField(ctx, key);
}
@Override
- protected boolean setField(Context ctx, Object key, Object val) throws InterruptedException {
+ protected boolean setField(Context ctx, Object key, Object val) {
if (key.equals("name")) name = Values.toString(ctx, val);
else if (key.equals("length")) length = (int)Values.toNumber(ctx, val);
else return super.setField(ctx, key, val);
return true;
}
@Override
- protected boolean hasField(Context ctx, Object key) throws InterruptedException {
+ protected boolean hasField(Context ctx, Object key) {
if (key.equals("name")) return true;
if (key.equals("length")) return true;
return super.hasField(ctx, key);
diff --git a/src/me/topchetoeu/jscript/engine/values/NativeFunction.java b/src/me/topchetoeu/jscript/engine/values/NativeFunction.java
index 2056717..c9b3def 100644
--- a/src/me/topchetoeu/jscript/engine/values/NativeFunction.java
+++ b/src/me/topchetoeu/jscript/engine/values/NativeFunction.java
@@ -4,13 +4,13 @@ import me.topchetoeu.jscript.engine.Context;
public class NativeFunction extends FunctionValue {
public static interface NativeFunctionRunner {
- Object run(Context ctx, Object thisArg, Object[] args) throws InterruptedException;
+ Object run(Context ctx, Object thisArg, Object[] args);
}
public final NativeFunctionRunner action;
@Override
- public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
+ public Object call(Context ctx, Object thisArg, Object ...args) {
return action.run(ctx, thisArg, args);
}
diff --git a/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java b/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java
index 9005e5f..49d785e 100644
--- a/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java
+++ b/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java
@@ -7,9 +7,8 @@ public class NativeWrapper extends ObjectValue {
public final Object wrapped;
@Override
- public ObjectValue getPrototype(Context ctx) throws InterruptedException {
- if (prototype == NATIVE_PROTO)
- return ctx.env.wrappersProvider.getProto(wrapped.getClass());
+ public ObjectValue getPrototype(Context ctx) {
+ if (prototype == NATIVE_PROTO) return ctx.environment().wrappers.getProto(wrapped.getClass());
else return super.getPrototype(ctx);
}
diff --git a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java
index fbd474f..7bdf0a3 100644
--- a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java
+++ b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java
@@ -145,19 +145,17 @@ public class ObjectValue {
return true;
}
- public ObjectValue getPrototype(Context ctx) throws InterruptedException {
+ public ObjectValue getPrototype(Context ctx) {
try {
- if (prototype == OBJ_PROTO) return ctx.env.proto("object");
- if (prototype == ARR_PROTO) return ctx.env.proto("array");
- if (prototype == FUNC_PROTO) return ctx.env.proto("function");
- if (prototype == ERR_PROTO) return ctx.env.proto("error");
- if (prototype == RANGE_ERR_PROTO) return ctx.env.proto("rangeErr");
- if (prototype == SYNTAX_ERR_PROTO) return ctx.env.proto("syntaxErr");
- if (prototype == TYPE_ERR_PROTO) return ctx.env.proto("typeErr");
- }
- catch (NullPointerException e) {
- return null;
+ if (prototype == OBJ_PROTO) return ctx.environment().proto("object");
+ if (prototype == ARR_PROTO) return ctx.environment().proto("array");
+ if (prototype == FUNC_PROTO) return ctx.environment().proto("function");
+ if (prototype == ERR_PROTO) return ctx.environment().proto("error");
+ if (prototype == RANGE_ERR_PROTO) return ctx.environment().proto("rangeErr");
+ if (prototype == SYNTAX_ERR_PROTO) return ctx.environment().proto("syntaxErr");
+ if (prototype == TYPE_ERR_PROTO) return ctx.environment().proto("typeErr");
}
+ catch (NullPointerException e) { return null; }
return (ObjectValue)prototype;
}
@@ -172,14 +170,14 @@ public class ObjectValue {
else if (Values.isObject(val)) {
var obj = Values.object(val);
- if (ctx != null && ctx.env != null) {
- if (obj == ctx.env.proto("object")) prototype = OBJ_PROTO;
- else if (obj == ctx.env.proto("array")) prototype = ARR_PROTO;
- else if (obj == ctx.env.proto("function")) prototype = FUNC_PROTO;
- else if (obj == ctx.env.proto("error")) prototype = ERR_PROTO;
- else if (obj == ctx.env.proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO;
- else if (obj == ctx.env.proto("typeErr")) prototype = TYPE_ERR_PROTO;
- else if (obj == ctx.env.proto("rangeErr")) prototype = RANGE_ERR_PROTO;
+ if (ctx != null && ctx.environment() != null) {
+ if (obj == ctx.environment().proto("object")) prototype = OBJ_PROTO;
+ else if (obj == ctx.environment().proto("array")) prototype = ARR_PROTO;
+ else if (obj == ctx.environment().proto("function")) prototype = FUNC_PROTO;
+ else if (obj == ctx.environment().proto("error")) prototype = ERR_PROTO;
+ else if (obj == ctx.environment().proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO;
+ else if (obj == ctx.environment().proto("typeErr")) prototype = TYPE_ERR_PROTO;
+ else if (obj == ctx.environment().proto("rangeErr")) prototype = RANGE_ERR_PROTO;
else prototype = obj;
}
else prototype = obj;
@@ -203,19 +201,19 @@ public class ObjectValue {
return true;
}
- protected Property getProperty(Context ctx, Object key) throws InterruptedException {
+ protected Property getProperty(Context ctx, Object key) {
if (properties.containsKey(key)) return properties.get(key);
var proto = getPrototype(ctx);
if (proto != null) return proto.getProperty(ctx, key);
else return null;
}
- protected Object getField(Context ctx, Object key) throws InterruptedException {
+ protected Object getField(Context ctx, Object key) {
if (values.containsKey(key)) return values.get(key);
var proto = getPrototype(ctx);
if (proto != null) return proto.getField(ctx, key);
else return null;
}
- protected boolean setField(Context ctx, Object key, Object val) throws InterruptedException {
+ protected boolean setField(Context ctx, Object key, Object val) {
if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) {
((FunctionValue)val).name = Values.toString(ctx, key);
}
@@ -223,14 +221,14 @@ public class ObjectValue {
values.put(key, val);
return true;
}
- protected void deleteField(Context ctx, Object key) throws InterruptedException {
+ protected void deleteField(Context ctx, Object key) {
values.remove(key);
}
- protected boolean hasField(Context ctx, Object key) throws InterruptedException {
+ protected boolean hasField(Context ctx, Object key) {
return values.containsKey(key);
}
- public final Object getMember(Context ctx, Object key, Object thisArg) throws InterruptedException {
+ public final Object getMember(Context ctx, Object key, Object thisArg) {
key = Values.normalize(ctx, key);
if ("__proto__".equals(key)) {
@@ -246,11 +244,11 @@ public class ObjectValue {
}
else return getField(ctx, key);
}
- public final Object getMember(Context ctx, Object key) throws InterruptedException {
+ public final Object getMember(Context ctx, Object key) {
return getMember(ctx, key, this);
}
- public final boolean setMember(Context ctx, Object key, Object val, Object thisArg, boolean onlyProps) throws InterruptedException {
+ public final boolean setMember(Context ctx, Object key, Object val, Object thisArg, boolean onlyProps) {
key = Values.normalize(ctx, key); val = Values.normalize(ctx, val);
var prop = getProperty(ctx, key);
@@ -269,11 +267,11 @@ public class ObjectValue {
else if (nonWritableSet.contains(key)) return false;
else return setField(ctx, key, val);
}
- public final boolean setMember(Context ctx, Object key, Object val, boolean onlyProps) throws InterruptedException {
+ public final boolean setMember(Context ctx, Object key, Object val, boolean onlyProps) {
return setMember(ctx, Values.normalize(ctx, key), Values.normalize(ctx, val), this, onlyProps);
}
- public final boolean hasMember(Context ctx, Object key, boolean own) throws InterruptedException {
+ public final boolean hasMember(Context ctx, Object key, boolean own) {
key = Values.normalize(ctx, key);
if (key != null && key.equals("__proto__")) return true;
@@ -283,7 +281,7 @@ public class ObjectValue {
var proto = getPrototype(ctx);
return proto != null && proto.hasMember(ctx, key, own);
}
- public final boolean deleteMember(Context ctx, Object key) throws InterruptedException {
+ public final boolean deleteMember(Context ctx, Object key) {
key = Values.normalize(ctx, key);
if (!memberConfigurable(key)) return false;
@@ -294,7 +292,7 @@ public class ObjectValue {
return true;
}
- public final ObjectValue getMemberDescriptor(Context ctx, Object key) throws InterruptedException {
+ public final ObjectValue getMemberDescriptor(Context ctx, Object key) {
key = Values.normalize(ctx, key);
var prop = properties.get(key);
diff --git a/src/me/topchetoeu/jscript/engine/values/ScopeValue.java b/src/me/topchetoeu/jscript/engine/values/ScopeValue.java
new file mode 100644
index 0000000..768e3c2
--- /dev/null
+++ b/src/me/topchetoeu/jscript/engine/values/ScopeValue.java
@@ -0,0 +1,51 @@
+package me.topchetoeu.jscript.engine.values;
+
+import java.util.HashMap;
+import java.util.List;
+
+import me.topchetoeu.jscript.engine.Context;
+import me.topchetoeu.jscript.engine.scope.ValueVariable;
+
+public class ScopeValue extends ObjectValue {
+ public final ValueVariable[] variables;
+ public final HashMap names = new HashMap<>();
+
+ @Override
+ protected Object getField(Context ctx, Object key) {
+ if (names.containsKey(key)) return variables[names.get(key)].get(ctx);
+ return super.getField(ctx, key);
+ }
+ @Override
+ protected boolean setField(Context ctx, Object key, Object val) {
+ if (names.containsKey(key)) {
+ variables[names.get(key)].set(ctx, val);
+ return true;
+ }
+
+ return super.setField(ctx, key, val);
+ }
+ @Override
+ protected void deleteField(Context ctx, Object key) {
+ if (names.containsKey(key)) return;
+ super.deleteField(ctx, key);
+ }
+ @Override
+ protected boolean hasField(Context ctx, Object key) {
+ if (names.containsKey(key)) return true;
+ return super.hasField(ctx, key);
+ }
+ @Override
+ public List keys(boolean includeNonEnumerable) {
+ var res = super.keys(includeNonEnumerable);
+ res.addAll(names.keySet());
+ return res;
+ }
+
+ public ScopeValue(ValueVariable[] variables, String[] names) {
+ this.variables = variables;
+ for (var i = 0; i < names.length && i < variables.length; i++) {
+ this.names.put(names[i], i);
+ this.nonConfigurableSet.add(names[i]);
+ }
+ }
+}
diff --git a/src/me/topchetoeu/jscript/engine/values/Values.java b/src/me/topchetoeu/jscript/engine/values/Values.java
index cc27f10..ccc94bf 100644
--- a/src/me/topchetoeu/jscript/engine/values/Values.java
+++ b/src/me/topchetoeu/jscript/engine/values/Values.java
@@ -16,6 +16,7 @@ import me.topchetoeu.jscript.engine.frame.ConvertHint;
import me.topchetoeu.jscript.exceptions.ConvertException;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.SyntaxException;
+import me.topchetoeu.jscript.exceptions.UncheckedException;
public class Values {
public static final Object NULL = new Object();
@@ -67,7 +68,7 @@ public class Values {
return "object";
}
- private static Object tryCallConvertFunc(Context ctx, Object obj, String name) throws InterruptedException {
+ private static Object tryCallConvertFunc(Context ctx, Object obj, String name) {
var func = getMember(ctx, obj, name);
if (func != null) {
@@ -88,7 +89,7 @@ public class Values {
obj == NULL;
}
- public static Object toPrimitive(Context ctx, Object obj, ConvertHint hint) throws InterruptedException {
+ public static Object toPrimitive(Context ctx, Object obj, ConvertHint hint) {
obj = normalize(ctx, obj);
if (isPrimitive(obj)) return obj;
@@ -96,12 +97,8 @@ public class Values {
var second = hint == ConvertHint.VALUEOF ? "toString" : "valueOf";
if (ctx != null) {
- try {
- return tryCallConvertFunc(ctx, obj, first);
- }
- catch (EngineException unused) {
- return tryCallConvertFunc(ctx, obj, second);
- }
+ try { return tryCallConvertFunc(ctx, obj, first); }
+ catch (EngineException unused) { return tryCallConvertFunc(ctx, obj, second); }
}
throw EngineException.ofType("Value couldn't be converted to a primitive.");
@@ -113,20 +110,18 @@ public class Values {
if (obj instanceof Boolean) return (Boolean)obj;
return true;
}
- public static double toNumber(Context ctx, Object obj) throws InterruptedException {
+ public static double toNumber(Context ctx, Object obj) {
var val = toPrimitive(ctx, obj, ConvertHint.VALUEOF);
if (val instanceof Number) return number(val);
if (val instanceof Boolean) return ((Boolean)val) ? 1 : 0;
if (val instanceof String) {
- try {
- return Double.parseDouble((String)val);
- }
- catch (NumberFormatException e) { }
+ try { return Double.parseDouble((String)val); }
+ catch (Throwable e) { throw new UncheckedException(e); }
}
return Double.NaN;
}
- public static String toString(Context ctx, Object obj) throws InterruptedException {
+ public static String toString(Context ctx, Object obj) {
var val = toPrimitive(ctx, obj, ConvertHint.VALUEOF);
if (val == null) return "undefined";
@@ -146,47 +141,47 @@ public class Values {
return "Unknown value";
}
- public static Object add(Context ctx, Object a, Object b) throws InterruptedException {
+ public static Object add(Context ctx, Object a, Object b) {
if (a instanceof String || b instanceof String) return toString(ctx, a) + toString(ctx, b);
else return toNumber(ctx, a) + toNumber(ctx, b);
}
- public static double subtract(Context ctx, Object a, Object b) throws InterruptedException {
+ public static double subtract(Context ctx, Object a, Object b) {
return toNumber(ctx, a) - toNumber(ctx, b);
}
- public static double multiply(Context ctx, Object a, Object b) throws InterruptedException {
+ public static double multiply(Context ctx, Object a, Object b) {
return toNumber(ctx, a) * toNumber(ctx, b);
}
- public static double divide(Context ctx, Object a, Object b) throws InterruptedException {
+ public static double divide(Context ctx, Object a, Object b) {
return toNumber(ctx, a) / toNumber(ctx, b);
}
- public static double modulo(Context ctx, Object a, Object b) throws InterruptedException {
+ public static double modulo(Context ctx, Object a, Object b) {
return toNumber(ctx, a) % toNumber(ctx, b);
}
- public static double negative(Context ctx, Object obj) throws InterruptedException {
+ public static double negative(Context ctx, Object obj) {
return -toNumber(ctx, obj);
}
- public static int and(Context ctx, Object a, Object b) throws InterruptedException {
+ public static int and(Context ctx, Object a, Object b) {
return (int)toNumber(ctx, a) & (int)toNumber(ctx, b);
}
- public static int or(Context ctx, Object a, Object b) throws InterruptedException {
+ public static int or(Context ctx, Object a, Object b) {
return (int)toNumber(ctx, a) | (int)toNumber(ctx, b);
}
- public static int xor(Context ctx, Object a, Object b) throws InterruptedException {
+ public static int xor(Context ctx, Object a, Object b) {
return (int)toNumber(ctx, a) ^ (int)toNumber(ctx, b);
}
- public static int bitwiseNot(Context ctx, Object obj) throws InterruptedException {
+ public static int bitwiseNot(Context ctx, Object obj) {
return ~(int)toNumber(ctx, obj);
}
- public static int shiftLeft(Context ctx, Object a, Object b) throws InterruptedException {
+ public static int shiftLeft(Context ctx, Object a, Object b) {
return (int)toNumber(ctx, a) << (int)toNumber(ctx, b);
}
- public static int shiftRight(Context ctx, Object a, Object b) throws InterruptedException {
+ public static int shiftRight(Context ctx, Object a, Object b) {
return (int)toNumber(ctx, a) >> (int)toNumber(ctx, b);
}
- public static long unsignedShiftRight(Context ctx, Object a, Object b) throws InterruptedException {
+ public static long unsignedShiftRight(Context ctx, Object a, Object b) {
long _a = (long)toNumber(ctx, a);
long _b = (long)toNumber(ctx, b);
@@ -195,7 +190,7 @@ public class Values {
return _a >>> _b;
}
- public static int compare(Context ctx, Object a, Object b) throws InterruptedException {
+ public static int compare(Context ctx, Object a, Object b) {
a = toPrimitive(ctx, a, ConvertHint.VALUEOF);
b = toPrimitive(ctx, b, ConvertHint.VALUEOF);
@@ -207,7 +202,7 @@ public class Values {
return !toBoolean(obj);
}
- public static boolean isInstanceOf(Context ctx, Object obj, Object proto) throws InterruptedException {
+ public static boolean isInstanceOf(Context ctx, Object obj, Object proto) {
if (obj == null || obj == NULL || proto == null || proto == NULL) return false;
var val = getPrototype(ctx, obj);
@@ -219,7 +214,7 @@ public class Values {
return false;
}
- public static Object operation(Context ctx, Operation op, Object ...args) throws InterruptedException {
+ public static Object operation(Context ctx, Operation op, Object ...args) {
switch (op) {
case ADD: return add(ctx, args[0], args[1]);
case SUBTRACT: return subtract(ctx, args[0], args[1]);
@@ -260,7 +255,7 @@ public class Values {
}
}
- public static Object getMember(Context ctx, Object obj, Object key) throws InterruptedException {
+ public static Object getMember(Context ctx, Object obj, Object key) {
obj = normalize(ctx, obj); key = normalize(ctx, key);
if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined.");
if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null.");
@@ -280,7 +275,7 @@ public class Values {
else if (key != null && key.equals("__proto__")) return proto;
else return proto.getMember(ctx, key, obj);
}
- public static boolean setMember(Context ctx, Object obj, Object key, Object val) throws InterruptedException {
+ public static boolean setMember(Context ctx, Object obj, Object key, Object val) {
obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val);
if (obj == null) throw EngineException.ofType("Tried to access member of undefined.");
if (obj == NULL) throw EngineException.ofType("Tried to access member of null.");
@@ -290,7 +285,7 @@ public class Values {
var proto = getPrototype(ctx, obj);
return proto.setMember(ctx, key, val, obj, true);
}
- public static boolean hasMember(Context ctx, Object obj, Object key, boolean own) throws InterruptedException {
+ public static boolean hasMember(Context ctx, Object obj, Object key, boolean own) {
if (obj == null || obj == NULL) return false;
obj = normalize(ctx, obj); key = normalize(ctx, key);
@@ -308,31 +303,31 @@ public class Values {
var proto = getPrototype(ctx, obj);
return proto != null && proto.hasMember(ctx, key, own);
}
- public static boolean deleteMember(Context ctx, Object obj, Object key) throws InterruptedException {
+ public static boolean deleteMember(Context ctx, Object obj, Object key) {
if (obj == null || obj == NULL) return false;
obj = normalize(ctx, obj); key = normalize(ctx, key);
if (isObject(obj)) return object(obj).deleteMember(ctx, key);
else return false;
}
- public static ObjectValue getPrototype(Context ctx, Object obj) throws InterruptedException {
+ public static ObjectValue getPrototype(Context ctx, Object obj) {
if (obj == null || obj == NULL) return null;
obj = normalize(ctx, obj);
if (isObject(obj)) return object(obj).getPrototype(ctx);
if (ctx == null) return null;
- if (obj instanceof String) return ctx.env.proto("string");
- else if (obj instanceof Number) return ctx.env.proto("number");
- else if (obj instanceof Boolean) return ctx.env.proto("bool");
- else if (obj instanceof Symbol) return ctx.env.proto("symbol");
+ if (obj instanceof String) return ctx.environment().proto("string");
+ else if (obj instanceof Number) return ctx.environment().proto("number");
+ else if (obj instanceof Boolean) return ctx.environment().proto("bool");
+ else if (obj instanceof Symbol) return ctx.environment().proto("symbol");
return null;
}
- public static boolean setPrototype(Context ctx, Object obj, Object proto) throws InterruptedException {
+ public static boolean setPrototype(Context ctx, Object obj, Object proto) {
obj = normalize(ctx, obj);
return isObject(obj) && object(obj).setPrototype(ctx, proto);
}
- public static List getMembers(Context ctx, Object obj, boolean own, boolean includeNonEnumerable) throws InterruptedException {
+ public static List getMembers(Context ctx, Object obj, boolean own, boolean includeNonEnumerable) {
List res = new ArrayList<>();
if (isObject(obj)) res = object(obj).keys(includeNonEnumerable);
@@ -352,7 +347,7 @@ public class Values {
return res;
}
- public static ObjectValue getMemberDescriptor(Context ctx, Object obj, Object key) throws InterruptedException {
+ public static ObjectValue getMemberDescriptor(Context ctx, Object obj, Object key) {
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ctx, key);
else if (obj instanceof String && key instanceof Number) {
var i = ((Number)key).intValue();
@@ -370,11 +365,11 @@ public class Values {
else return null;
}
- public static Object call(Context ctx, Object func, Object thisArg, Object ...args) throws InterruptedException {
+ public static Object call(Context ctx, Object func, Object thisArg, Object ...args) {
if (!isFunction(func)) throw EngineException.ofType("Tried to call a non-function value.");
return function(func).call(ctx, thisArg, args);
}
- public static Object callNew(Context ctx, Object func, Object ...args) throws InterruptedException {
+ public static Object callNew(Context ctx, Object func, Object ...args) {
var res = new ObjectValue();
var proto = Values.getMember(ctx, func, "prototype");
res.setPrototype(ctx, proto);
@@ -395,7 +390,7 @@ public class Values {
return a == b || a.equals(b);
}
- public static boolean looseEqual(Context ctx, Object a, Object b) throws InterruptedException {
+ public static boolean looseEqual(Context ctx, Object a, Object b) {
a = normalize(ctx, a); b = normalize(ctx, b);
// In loose equality, null is equivalent to undefined
@@ -446,14 +441,14 @@ public class Values {
if (val instanceof Class) {
if (ctx == null) return null;
- else return ctx.env.wrappersProvider.getConstr((Class>)val);
+ else return ctx.environment().wrappers.getConstr((Class>)val);
}
return new NativeWrapper(val);
}
@SuppressWarnings("unchecked")
- public static T convert(Context ctx, Object obj, Class clazz) throws InterruptedException {
+ public static T convert(Context ctx, Object obj, Class clazz) {
if (clazz == Void.class) return null;
if (obj instanceof NativeWrapper) {
@@ -520,7 +515,7 @@ public class Values {
public static Iterable toJavaIterable(Context ctx, Object obj) {
return () -> {
try {
- var symbol = ctx.env.symbol("Symbol.iterator");
+ var symbol = ctx.environment().symbol("Symbol.iterator");
var iteratorFunc = getMember(ctx, obj, symbol);
if (!isFunction(iteratorFunc)) return Collections.emptyIterator();
@@ -536,7 +531,7 @@ public class Values {
public boolean consumed = true;
private FunctionValue next = (FunctionValue)nextFunc;
- private void loadNext() throws InterruptedException {
+ private void loadNext() {
if (next == null) value = null;
else if (consumed) {
var curr = object(next.call(ctx, iterator));
@@ -551,46 +546,30 @@ public class Values {
@Override
public boolean hasNext() {
- try {
- loadNext();
- return next != null;
- }
- catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return false;
- }
+ loadNext();
+ return next != null;
}
@Override
public Object next() {
- try {
- loadNext();
- var res = value;
- value = null;
- consumed = true;
- return res;
- }
- catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return null;
- }
+ loadNext();
+ var res = value;
+ value = null;
+ consumed = true;
+ return res;
}
};
}
- catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return null;
- }
catch (IllegalArgumentException | NullPointerException e) {
return Collections.emptyIterator();
}
};
}
- public static ObjectValue fromJavaIterator(Context ctx, Iterator> it) throws InterruptedException {
+ public static ObjectValue fromJavaIterator(Context ctx, Iterator> it) {
var res = new ObjectValue();
try {
- var key = getMember(ctx, getMember(ctx, ctx.env.proto("symbol"), "constructor"), "iterator");
+ var key = getMember(ctx, getMember(ctx, ctx.environment().proto("symbol"), "constructor"), "iterator");
res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg));
}
catch (IllegalArgumentException | NullPointerException e) { }
@@ -603,11 +582,11 @@ public class Values {
return res;
}
- public static ObjectValue fromJavaIterable(Context ctx, Iterable> it) throws InterruptedException {
+ public static ObjectValue fromJavaIterable(Context ctx, Iterable> it) {
return fromJavaIterator(ctx, it.iterator());
}
- private static void printValue(Context ctx, Object val, HashSet passed, int tab) throws InterruptedException {
+ private static void printValue(Context ctx, Object val, HashSet passed, int tab) {
if (passed.contains(val)) {
System.out.print("[circular]");
return;
@@ -679,14 +658,15 @@ public class Values {
else if (val instanceof String) System.out.print("'" + val + "'");
else System.out.print(Values.toString(ctx, val));
}
- public static void printValue(Context ctx, Object val) throws InterruptedException {
+ public static void printValue(Context ctx, Object val) {
printValue(ctx, val, new HashSet<>(), 0);
}
- public static void printError(RuntimeException err, String prefix) throws InterruptedException {
+ public static void printError(RuntimeException err, String prefix) {
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
try {
if (err instanceof EngineException) {
- System.out.println(prefix + " " + ((EngineException)err).toString(((EngineException)err).ctx));
+ var ee = ((EngineException)err);
+ System.out.println(prefix + " " + ee.toString(new Context(ee.engine).pushEnv(ee.env)));
}
else if (err instanceof SyntaxException) {
System.out.println("Syntax error:" + ((SyntaxException)err).msg);
diff --git a/src/me/topchetoeu/jscript/events/Awaitable.java b/src/me/topchetoeu/jscript/events/Awaitable.java
index 7583892..8f2d9ec 100644
--- a/src/me/topchetoeu/jscript/events/Awaitable.java
+++ b/src/me/topchetoeu/jscript/events/Awaitable.java
@@ -1,7 +1,9 @@
package me.topchetoeu.jscript.events;
+import me.topchetoeu.jscript.exceptions.InterruptException;
+
public interface Awaitable {
- T await() throws FinishedException, InterruptedException;
+ T await() throws FinishedException;
default Observable toObservable() {
return sub -> {
@@ -10,9 +12,7 @@ public interface Awaitable {
sub.next(await());
sub.finish();
}
- catch (InterruptedException | FinishedException e) {
- sub.finish();
- }
+ catch (InterruptException | FinishedException e) { sub.finish(); }
catch (RuntimeException e) {
sub.error(e);
}
diff --git a/src/me/topchetoeu/jscript/events/DataNotifier.java b/src/me/topchetoeu/jscript/events/DataNotifier.java
index e52d16e..cfcff15 100644
--- a/src/me/topchetoeu/jscript/events/DataNotifier.java
+++ b/src/me/topchetoeu/jscript/events/DataNotifier.java
@@ -19,7 +19,7 @@ public class DataNotifier implements Awaitable {
isErr = false;
notifier.next();
}
- public T await() throws InterruptedException {
+ public T await() {
notifier.await();
try {
diff --git a/src/me/topchetoeu/jscript/events/Notifier.java b/src/me/topchetoeu/jscript/events/Notifier.java
index 36e45b9..96dbee8 100644
--- a/src/me/topchetoeu/jscript/events/Notifier.java
+++ b/src/me/topchetoeu/jscript/events/Notifier.java
@@ -1,5 +1,7 @@
package me.topchetoeu.jscript.events;
+import me.topchetoeu.jscript.exceptions.InterruptException;
+
public class Notifier {
private boolean ok = false;
@@ -7,8 +9,11 @@ public class Notifier {
ok = true;
notifyAll();
}
- public synchronized void await() throws InterruptedException {
- while (!ok) wait();
- ok = false;
+ public synchronized void await() {
+ try {
+ while (!ok) wait();
+ ok = false;
+ }
+ catch (InterruptedException e) { throw new InterruptException(e); }
}
}
diff --git a/src/me/topchetoeu/jscript/exceptions/EngineException.java b/src/me/topchetoeu/jscript/exceptions/EngineException.java
index fab5276..3f740b1 100644
--- a/src/me/topchetoeu/jscript/exceptions/EngineException.java
+++ b/src/me/topchetoeu/jscript/exceptions/EngineException.java
@@ -5,6 +5,8 @@ import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.Context;
+import me.topchetoeu.jscript.engine.Engine;
+import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
@@ -12,7 +14,8 @@ import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
public class EngineException extends RuntimeException {
public final Object value;
public EngineException cause;
- public Context ctx = null;
+ public Environment env = null;
+ public Engine engine = null;
public final List stackTrace = new ArrayList<>();
public EngineException add(String name, Location location) {
@@ -28,12 +31,13 @@ public class EngineException extends RuntimeException {
this.cause = cause;
return this;
}
- public EngineException setContext(Context ctx) {
- this.ctx = ctx;
+ public EngineException setCtx(Environment env, Engine engine) {
+ if (this.env == null) this.env = env;
+ if (this.engine == null) this.engine = engine;
return this;
}
- public String toString(Context ctx) throws InterruptedException {
+ public String toString(Context ctx) {
var ss = new StringBuilder();
try {
ss.append(Values.toString(ctx, value)).append('\n');
diff --git a/src/me/topchetoeu/jscript/exceptions/InterruptException.java b/src/me/topchetoeu/jscript/exceptions/InterruptException.java
new file mode 100644
index 0000000..f198f49
--- /dev/null
+++ b/src/me/topchetoeu/jscript/exceptions/InterruptException.java
@@ -0,0 +1,8 @@
+package me.topchetoeu.jscript.exceptions;
+
+public class InterruptException extends RuntimeException {
+ public InterruptException() { }
+ public InterruptException(Throwable e) {
+ super(e);
+ }
+}
diff --git a/src/me/topchetoeu/jscript/exceptions/UncheckedException.java b/src/me/topchetoeu/jscript/exceptions/UncheckedException.java
new file mode 100644
index 0000000..4e730e7
--- /dev/null
+++ b/src/me/topchetoeu/jscript/exceptions/UncheckedException.java
@@ -0,0 +1,7 @@
+package me.topchetoeu.jscript.exceptions;
+
+public class UncheckedException extends RuntimeException {
+ public UncheckedException(Throwable err) {
+ super(err);
+ }
+}
diff --git a/src/me/topchetoeu/jscript/exceptions/UncheckedIOException.java b/src/me/topchetoeu/jscript/exceptions/UncheckedIOException.java
new file mode 100644
index 0000000..fae7e82
--- /dev/null
+++ b/src/me/topchetoeu/jscript/exceptions/UncheckedIOException.java
@@ -0,0 +1,9 @@
+package me.topchetoeu.jscript.exceptions;
+
+import java.io.IOException;
+
+public class UncheckedIOException extends RuntimeException {
+ public UncheckedIOException(IOException e) {
+ super(e);
+ }
+}
diff --git a/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java b/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java
index 3a12212..c70043c 100644
--- a/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java
+++ b/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java
@@ -9,6 +9,7 @@ import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.exceptions.EngineException;
+import me.topchetoeu.jscript.exceptions.UncheckedException;
public class NativeWrapperProvider implements WrappersProvider {
private final HashMap, FunctionValue> constructors = new HashMap<>();
@@ -120,7 +121,7 @@ public class NativeWrapperProvider implements WrappersProvider {
var init = overload.getAnnotation(NativeInit.class);
if (init == null || init.value() != InitType.PROTOTYPE) continue;
try { overload.invoke(null, ctx, res); }
- catch (ReflectiveOperationException e) { e.printStackTrace(); }
+ catch (Throwable e) { throw new UncheckedException(e); }
}
applyMethods(ctx, true, res, clazz);
@@ -152,7 +153,7 @@ public class NativeWrapperProvider implements WrappersProvider {
var init = overload.getAnnotation(NativeInit.class);
if (init == null || init.value() != InitType.CONSTRUCTOR) continue;
try { overload.invoke(null, ctx, func); }
- catch (ReflectiveOperationException e) { e.printStackTrace(); }
+ catch (Throwable e) { throw new UncheckedException(e); }
}
if (((OverloadFunction)func).overloads.size() == 0) {
@@ -180,7 +181,7 @@ public class NativeWrapperProvider implements WrappersProvider {
var init = overload.getAnnotation(NativeInit.class);
if (init == null || init.value() != InitType.NAMESPACE) continue;
try { overload.invoke(null, ctx, res); }
- catch (ReflectiveOperationException e) { e.printStackTrace(); }
+ catch (Throwable e) { throw new UncheckedException(e); }
}
applyMethods(ctx, false, res, clazz);
diff --git a/src/me/topchetoeu/jscript/interop/Overload.java b/src/me/topchetoeu/jscript/interop/Overload.java
index a4548e1..0da14c1 100644
--- a/src/me/topchetoeu/jscript/interop/Overload.java
+++ b/src/me/topchetoeu/jscript/interop/Overload.java
@@ -9,10 +9,7 @@ import me.topchetoeu.jscript.engine.Context;
public class Overload {
public static interface OverloadRunner {
- Object run(Context ctx, Object thisArg, Object[] args) throws
- InterruptedException,
- ReflectiveOperationException,
- IllegalArgumentException;
+ Object run(Context ctx, Object thisArg, Object[] args) throws ReflectiveOperationException, IllegalArgumentException;
}
public final OverloadRunner runner;
diff --git a/src/me/topchetoeu/jscript/interop/OverloadFunction.java b/src/me/topchetoeu/jscript/interop/OverloadFunction.java
index 5f54ceb..593378a 100644
--- a/src/me/topchetoeu/jscript/interop/OverloadFunction.java
+++ b/src/me/topchetoeu/jscript/interop/OverloadFunction.java
@@ -15,7 +15,7 @@ import me.topchetoeu.jscript.exceptions.EngineException;
public class OverloadFunction extends FunctionValue {
public final List overloads = new ArrayList<>();
- public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
+ public Object call(Context ctx, Object thisArg, Object ...args) {
loop: for (var overload : overloads) {
Object[] newArgs = new Object[overload.params.length];
@@ -76,14 +76,10 @@ public class OverloadFunction extends FunctionValue {
try {
return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs));
}
- catch (InstantiationException e) {
- throw EngineException.ofError("The class may not be instantiated.");
- }
- catch (IllegalAccessException | IllegalArgumentException e) {
- continue;
- }
+ catch (InstantiationException e) { throw EngineException.ofError("The class may not be instantiated."); }
+ catch (IllegalAccessException | IllegalArgumentException e) { continue; }
catch (InvocationTargetException e) {
- var loc = new Location(0, 0, "");
+ var loc = Location.INTERNAL;
if (e.getTargetException() instanceof EngineException) {
throw ((EngineException)e.getTargetException()).add(name, loc);
}
@@ -95,10 +91,7 @@ public class OverloadFunction extends FunctionValue {
}
}
catch (ReflectiveOperationException e) {
- throw EngineException.ofError(e.getMessage()).add(name, new Location(0, 0, ""));
- }
- catch (Exception e) {
- throw e;
+ throw EngineException.ofError(e.getMessage()).add(name, Location.INTERNAL);
}
}
diff --git a/src/me/topchetoeu/jscript/json/JSON.java b/src/me/topchetoeu/jscript/json/JSON.java
index 0869990..7d58bf4 100644
--- a/src/me/topchetoeu/jscript/json/JSON.java
+++ b/src/me/topchetoeu/jscript/json/JSON.java
@@ -1,8 +1,15 @@
package me.topchetoeu.jscript.json;
+import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
+import me.topchetoeu.jscript.Filename;
+import me.topchetoeu.jscript.engine.Context;
+import me.topchetoeu.jscript.engine.values.ArrayValue;
+import me.topchetoeu.jscript.engine.values.ObjectValue;
+import me.topchetoeu.jscript.engine.values.Values;
+import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.parsing.Operator;
import me.topchetoeu.jscript.parsing.ParseRes;
@@ -10,20 +17,77 @@ import me.topchetoeu.jscript.parsing.Parsing;
import me.topchetoeu.jscript.parsing.Token;
public class JSON {
+ public static Object toJs(JSONElement val) {
+ if (val.isBoolean()) return val.bool();
+ if (val.isString()) return val.string();
+ if (val.isNumber()) return val.number();
+ if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJs).collect(Collectors.toList()));
+ if (val.isMap()) {
+ var res = new ObjectValue();
+ for (var el : val.map().entrySet()) {
+ res.defineProperty(null, el.getKey(), toJs(el.getValue()));
+ }
+ return res;
+ }
+ if (val.isNull()) return Values.NULL;
+ return null;
+ }
+ private static JSONElement fromJs(Context ctx, Object val, HashSet prev) {
+ if (val instanceof Boolean) return JSONElement.bool((boolean)val);
+ if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
+ if (val instanceof String) return JSONElement.string((String)val);
+ if (val == Values.NULL) return JSONElement.NULL;
+ if (val instanceof ObjectValue) {
+ if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
+ prev.add(val);
+
+ var res = new JSONMap();
+
+ for (var el : ((ObjectValue)val).keys(false)) {
+ var jsonEl = fromJs(ctx, ((ObjectValue)val).getMember(ctx, el), prev);
+ if (jsonEl == null) continue;
+ if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
+ }
+
+ prev.remove(val);
+ return JSONElement.of(res);
+ }
+ if (val instanceof ArrayValue) {
+ if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
+ prev.add(val);
+
+ var res = new JSONList();
+
+ for (var el : ((ArrayValue)val).toArray()) {
+ var jsonEl = fromJs(ctx, el, prev);
+ if (jsonEl == null) jsonEl = JSONElement.NULL;
+ res.add(jsonEl);
+ }
+
+ prev.remove(val);
+ return JSONElement.of(res);
+ }
+ if (val == null) return null;
+ return null;
+ }
+ public static JSONElement fromJs(Context ctx, Object val) {
+ return fromJs(ctx, val, new HashSet<>());
+ }
+
public static ParseRes parseIdentifier(List tokens, int i) {
return Parsing.parseIdentifier(tokens, i);
}
- public static ParseRes parseString(String filename, List tokens, int i) {
+ public static ParseRes parseString(Filename filename, List tokens, int i) {
var res = Parsing.parseString(filename, tokens, i);
if (res.isSuccess()) return ParseRes.res((String)res.result.value, res.n);
else return res.transform();
}
- public static ParseRes parseNumber(String filename, List tokens, int i) {
+ public static ParseRes parseNumber(Filename filename, List tokens, int i) {
var res = Parsing.parseNumber(filename, tokens, i);
if (res.isSuccess()) return ParseRes.res((Double)res.result.value, res.n);
else return res.transform();
}
- public static ParseRes parseBool(String filename, List tokens, int i) {
+ public static ParseRes parseBool(Filename filename, List tokens, int i) {
var id = parseIdentifier(tokens, i);
if (!id.isSuccess()) return ParseRes.failed();
@@ -32,7 +96,7 @@ public class JSON {
else return ParseRes.failed();
}
- public static ParseRes> parseValue(String filename, List tokens, int i) {
+ public static ParseRes> parseValue(Filename filename, List tokens, int i) {
return ParseRes.any(
parseString(filename, tokens, i),
parseNumber(filename, tokens, i),
@@ -42,7 +106,7 @@ public class JSON {
);
}
- public static ParseRes parseMap(String filename, List tokens, int i) {
+ public static ParseRes parseMap(Filename filename, List tokens, int i) {
int n = 0;
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
@@ -82,7 +146,7 @@ public class JSON {
return ParseRes.res(values, n);
}
- public static ParseRes parseList(String filename, List tokens, int i) {
+ public static ParseRes parseList(Filename filename, List tokens, int i) {
int n = 0;
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
@@ -109,7 +173,7 @@ public class JSON {
return ParseRes.res(values, n);
}
- public static JSONElement parse(String filename, String raw) {
+ public static JSONElement parse(Filename filename, String raw) {
var res = parseValue(filename, Parsing.tokenize(filename, raw), 0);
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
else if (res.isError()) throw new SyntaxException(null, res.error);
@@ -120,7 +184,12 @@ public class JSON {
if (el.isNumber()) return Double.toString(el.number());
if (el.isBoolean()) return el.bool() ? "true" : "false";
if (el.isNull()) return "null";
- if (el.isString()) return "\"" + el.string().replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
+ if (el.isString()) return "\"" + el.string()
+ .replace("\\", "\\\\")
+ .replace("\n", "\\n")
+ .replace("\r", "\\r")
+ .replace("\"", "\\\"")
+ + "\"";
if (el.isList()) {
var res = new StringBuilder().append("[");
for (int i = 0; i < el.list().size(); i++) {
diff --git a/src/me/topchetoeu/jscript/json/JSONElement.java b/src/me/topchetoeu/jscript/json/JSONElement.java
index e26aa3a..8e03c7f 100644
--- a/src/me/topchetoeu/jscript/json/JSONElement.java
+++ b/src/me/topchetoeu/jscript/json/JSONElement.java
@@ -65,10 +65,22 @@ public class JSONElement {
return (double)value;
}
public boolean bool() {
- if (!isNumber()) throw new IllegalStateException("Element is not a boolean.");
+ if (!isBoolean()) throw new IllegalStateException("Element is not a boolean.");
return (boolean)value;
}
+ @Override
+ public String toString() {
+ if (isMap()) return "{...}";
+ if (isList()) return "[...]";
+ if (isString()) return (String)value;
+ if (isString()) return (String)value;
+ if (isNumber()) return (double)value + "";
+ if (isBoolean()) return (boolean)value + "";
+ if (isNull()) return "null";
+ return "";
+ }
+
private JSONElement(Type type, Object val) {
this.type = type;
this.value = val;
diff --git a/src/me/topchetoeu/jscript/json/JSONList.java b/src/me/topchetoeu/jscript/json/JSONList.java
index eb343ac..214ca40 100644
--- a/src/me/topchetoeu/jscript/json/JSONList.java
+++ b/src/me/topchetoeu/jscript/json/JSONList.java
@@ -10,6 +10,9 @@ public class JSONList extends ArrayList {
public JSONList(JSONElement ...els) {
super(List.of(els));
}
+ public JSONList(Collection els) {
+ super(els);
+ }
public JSONList addNull() { this.add(JSONElement.NULL); return this; }
public JSONList add(String val) { this.add(JSONElement.of(val)); return this; }
@@ -17,5 +20,7 @@ public class JSONList extends ArrayList {
public JSONList add(boolean val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(Map val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(Collection val) { this.add(JSONElement.of(val)); return this; }
+ public JSONList add(JSONMap val) { this.add(JSONElement.of(val)); return this; }
+ public JSONList add(JSONList val) { this.add(JSONElement.of(val)); return this; }
}
diff --git a/src/me/topchetoeu/jscript/json/JSONMap.java b/src/me/topchetoeu/jscript/json/JSONMap.java
index 203adaf..dc6d845 100644
--- a/src/me/topchetoeu/jscript/json/JSONMap.java
+++ b/src/me/topchetoeu/jscript/json/JSONMap.java
@@ -51,7 +51,7 @@ public class JSONMap implements Map {
public JSONMap map(String path) {
var el = get(path);
- if (el == null) throw new IllegalStateException(String.format("'%s' doesn't exist.", path));
+ if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
return el.map();
}
public JSONMap map(String path, JSONMap defaultVal) {
@@ -63,7 +63,7 @@ public class JSONMap implements Map {
public JSONList list(String path) {
var el = get(path);
- if (el == null) throw new IllegalStateException(String.format("'%s' doesn't exist.", path));
+ if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
return el.list();
}
public JSONList list(String path, JSONList defaultVal) {
@@ -75,7 +75,7 @@ public class JSONMap implements Map {
public String string(String path) {
var el = get(path);
- if (el == null) throw new IllegalStateException(String.format("'%s' doesn't exist.", path));
+ if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
return el.string();
}
public String string(String path, String defaultVal) {
@@ -87,7 +87,7 @@ public class JSONMap implements Map {
public double number(String path) {
var el = get(path);
- if (el == null) throw new IllegalStateException(String.format("'%s' doesn't exist.", path));
+ if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
return el.number();
}
public double number(String path, double defaultVal) {
@@ -99,7 +99,7 @@ public class JSONMap implements Map {
public boolean bool(String path) {
var el = get(path);
- if (el == null) throw new IllegalStateException(String.format("'%s' doesn't exist.", path));
+ if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
return el.bool();
}
public boolean bool(String path, boolean defaultVal) {
diff --git a/src/me/topchetoeu/jscript/lib/ArrayLib.java b/src/me/topchetoeu/jscript/lib/ArrayLib.java
index fc5fcf1..1d2bd65 100644
--- a/src/me/topchetoeu/jscript/lib/ArrayLib.java
+++ b/src/me/topchetoeu/jscript/lib/ArrayLib.java
@@ -17,17 +17,17 @@ import me.topchetoeu.jscript.interop.NativeInit;
import me.topchetoeu.jscript.interop.NativeSetter;
public class ArrayLib {
- @NativeGetter(thisArg = true) public static int length(Context ctx, ArrayValue thisArg) throws InterruptedException {
+ @NativeGetter(thisArg = true) public static int length(Context ctx, ArrayValue thisArg) {
return thisArg.size();
}
- @NativeSetter(thisArg = true) public static void length(Context ctx, ArrayValue thisArg, int len) throws InterruptedException {
+ @NativeSetter(thisArg = true) public static void length(Context ctx, ArrayValue thisArg, int len) {
thisArg.setSize(len);
}
- @Native(thisArg = true) public static ObjectValue values(Context ctx, ArrayValue thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static ObjectValue values(Context ctx, ArrayValue thisArg) {
return Values.fromJavaIterable(ctx, thisArg);
}
- @Native(thisArg = true) public static ObjectValue keys(Context ctx, ArrayValue thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static ObjectValue keys(Context ctx, ArrayValue thisArg) {
return Values.fromJavaIterable(ctx, () -> new Iterator() {
private int i = 0;
@@ -42,7 +42,7 @@ public class ArrayLib {
}
});
}
- @Native(thisArg = true) public static ObjectValue entries(Context ctx, ArrayValue thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static ObjectValue entries(Context ctx, ArrayValue thisArg) {
return Values.fromJavaIterable(ctx, () -> new Iterator() {
private int i = 0;
@@ -59,15 +59,15 @@ public class ArrayLib {
}
@Native(value = "@@Symbol.iterator", thisArg = true)
- public static ObjectValue iterator(Context ctx, ArrayValue thisArg) throws InterruptedException {
+ public static ObjectValue iterator(Context ctx, ArrayValue thisArg) {
return values(ctx, thisArg);
}
@Native(value = "@@Symbol.asyncIterator", thisArg = true)
- public static ObjectValue asyncIterator(Context ctx, ArrayValue thisArg) throws InterruptedException {
+ public static ObjectValue asyncIterator(Context ctx, ArrayValue thisArg) {
return values(ctx, thisArg);
}
- @Native(thisArg = true) public static ArrayValue concat(Context ctx, ArrayValue thisArg, Object ...others) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue concat(Context ctx, ArrayValue thisArg, Object ...others) {
// TODO: Fully implement with non-array spreadable objects
var size = 0;
@@ -92,24 +92,13 @@ public class ArrayLib {
return res;
}
- @Native(thisArg = true) public static void sort(Context ctx, ArrayValue arr, FunctionValue cmp) throws InterruptedException {
- try {
- arr.sort((a, b) -> {
- try {
- var res = Values.toNumber(ctx, cmp.call(ctx, null, a, b));
- if (res < 0) return -1;
- if (res > 0) return 1;
- return 0;
- }
- catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- });
- }
- catch (RuntimeException e) {
- if (e.getCause() instanceof InterruptedException) throw (InterruptedException)e.getCause();
- else throw e;
- }
+ @Native(thisArg = true) public static void sort(Context ctx, ArrayValue arr, FunctionValue cmp) {
+ arr.sort((a, b) -> {
+ var res = Values.toNumber(ctx, cmp.call(ctx, null, a, b));
+ if (res < 0) return -1;
+ if (res > 0) return 1;
+ return 0;
+ });
}
private static int normalizeI(int len, int i, boolean clamp) {
@@ -121,7 +110,7 @@ public class ArrayLib {
return i;
}
- @Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val, int start, int end) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val, int start, int end) {
start = normalizeI(arr.size(), start, true);
end = normalizeI(arr.size(), end, true);
@@ -131,21 +120,21 @@ public class ArrayLib {
return arr;
}
- @Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val, int start) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val, int start) {
return fill(ctx, arr, val, start, arr.size());
}
- @Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val) {
return fill(ctx, arr, val, 0, arr.size());
}
- @Native(thisArg = true) public static boolean every(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static boolean every(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) {
for (var i = 0; i < arr.size(); i++) {
if (!Values.toBoolean(func.call(ctx, thisArg, arr.get(i), i, arr))) return false;
}
return true;
}
- @Native(thisArg = true) public static boolean some(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static boolean some(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) {
for (var i = 0; i < arr.size(); i++) {
if (Values.toBoolean(func.call(ctx, thisArg, arr.get(i), i, arr))) return true;
}
@@ -153,7 +142,7 @@ public class ArrayLib {
return false;
}
- @Native(thisArg = true) public static ArrayValue filter(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue filter(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) {
var res = new ArrayValue(arr.size());
for (int i = 0, j = 0; i < arr.size(); i++) {
@@ -161,20 +150,20 @@ public class ArrayLib {
}
return res;
}
- @Native(thisArg = true) public static ArrayValue map(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue map(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) {
var res = new ArrayValue(arr.size());
for (int i = 0, j = 0; i < arr.size(); i++) {
if (arr.has(i)) res.set(ctx, j++, func.call(ctx, thisArg, arr.get(i), i, arr));
}
return res;
}
- @Native(thisArg = true) public static void forEach(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static void forEach(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) {
for (int i = 0; i < arr.size(); i++) {
if (arr.has(i)) func.call(ctx, thisArg, arr.get(i), i, arr);
}
}
- @Native(thisArg = true) public static ArrayValue flat(Context ctx, ArrayValue arr, int depth) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue flat(Context ctx, ArrayValue arr, int depth) {
var res = new ArrayValue(arr.size());
var stack = new Stack();
var depths = new Stack();
@@ -197,18 +186,18 @@ public class ArrayLib {
return res;
}
- @Native(thisArg = true) public static ArrayValue flatMap(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue flatMap(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) {
return flat(ctx, map(ctx, arr, cmp, thisArg), 1);
}
- @Native(thisArg = true) public static Object find(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static Object find(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) {
for (int i = 0; i < arr.size(); i++) {
if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return arr.get(i);
}
return null;
}
- @Native(thisArg = true) public static Object findLast(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static Object findLast(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) {
for (var i = arr.size() - 1; i >= 0; i--) {
if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return arr.get(i);
}
@@ -216,14 +205,14 @@ public class ArrayLib {
return null;
}
- @Native(thisArg = true) public static int findIndex(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static int findIndex(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) {
for (int i = 0; i < arr.size(); i++) {
if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return i;
}
return -1;
}
- @Native(thisArg = true) public static int findLastIndex(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static int findLastIndex(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) {
for (var i = arr.size() - 1; i >= 0; i--) {
if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return i;
}
@@ -231,7 +220,7 @@ public class ArrayLib {
return -1;
}
- @Native(thisArg = true) public static int indexOf(Context ctx, ArrayValue arr, Object val, int start) throws InterruptedException {
+ @Native(thisArg = true) public static int indexOf(Context ctx, ArrayValue arr, Object val, int start) {
start = normalizeI(arr.size(), start, true);
for (int i = 0; i < arr.size() && i < start; i++) {
@@ -240,7 +229,7 @@ public class ArrayLib {
return -1;
}
- @Native(thisArg = true) public static int lastIndexOf(Context ctx, ArrayValue arr, Object val, int start) throws InterruptedException {
+ @Native(thisArg = true) public static int lastIndexOf(Context ctx, ArrayValue arr, Object val, int start) {
start = normalizeI(arr.size(), start, true);
for (int i = arr.size(); i >= start; i--) {
@@ -250,29 +239,29 @@ public class ArrayLib {
return -1;
}
- @Native(thisArg = true) public static boolean includes(Context ctx, ArrayValue arr, Object el, int start) throws InterruptedException {
+ @Native(thisArg = true) public static boolean includes(Context ctx, ArrayValue arr, Object el, int start) {
return indexOf(ctx, arr, el, start) >= 0;
}
- @Native(thisArg = true) public static Object pop(Context ctx, ArrayValue arr) throws InterruptedException {
+ @Native(thisArg = true) public static Object pop(Context ctx, ArrayValue arr) {
if (arr.size() == 0) return null;
var val = arr.get(arr.size() - 1);
arr.shrink(1);
return val;
}
- @Native(thisArg = true) public static int push(Context ctx, ArrayValue arr, Object ...values) throws InterruptedException {
+ @Native(thisArg = true) public static int push(Context ctx, ArrayValue arr, Object ...values) {
arr.copyFrom(ctx, values, 0, arr.size(), values.length);
return arr.size();
}
- @Native(thisArg = true) public static Object shift(Context ctx, ArrayValue arr) throws InterruptedException {
+ @Native(thisArg = true) public static Object shift(Context ctx, ArrayValue arr) {
if (arr.size() == 0) return null;
var val = arr.get(0);
arr.move(1, 0, arr.size());
arr.shrink(1);
return val;
}
- @Native(thisArg = true) public static int unshift(Context ctx, ArrayValue arr, Object ...values) throws InterruptedException {
+ @Native(thisArg = true) public static int unshift(Context ctx, ArrayValue arr, Object ...values) {
arr.move(0, values.length, arr.size());
arr.copyFrom(ctx, values, 0, 0, values.length);
return arr.size();
@@ -290,7 +279,7 @@ public class ArrayLib {
return slice(ctx, arr, start, arr.size());
}
- @Native(thisArg = true) public static ArrayValue splice(Context ctx, ArrayValue arr, int start, int deleteCount, Object ...items) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue splice(Context ctx, ArrayValue arr, int start, int deleteCount, Object ...items) {
start = normalizeI(arr.size(), start, true);
deleteCount = normalizeI(arr.size(), deleteCount, true);
if (start + deleteCount >= arr.size()) deleteCount = arr.size() - start;
@@ -304,14 +293,14 @@ public class ArrayLib {
return res;
}
- @Native(thisArg = true) public static ArrayValue splice(Context ctx, ArrayValue arr, int start) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue splice(Context ctx, ArrayValue arr, int start) {
return splice(ctx, arr, start, arr.size() - start);
}
- @Native(thisArg = true) public static String toString(Context ctx, ArrayValue arr) throws InterruptedException {
+ @Native(thisArg = true) public static String toString(Context ctx, ArrayValue arr) {
return join(ctx, arr, ",");
}
- @Native(thisArg = true) public static String join(Context ctx, ArrayValue arr, String sep) throws InterruptedException {
+ @Native(thisArg = true) public static String join(Context ctx, ArrayValue arr, String sep) {
var res = new StringBuilder();
var comma = true;
diff --git a/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java b/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java
index f14078c..e372d83 100644
--- a/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java
+++ b/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java
@@ -1,6 +1,7 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.engine.Context;
+import me.topchetoeu.jscript.engine.StackData;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.engine.frame.Runners;
import me.topchetoeu.jscript.engine.values.CodeFunction;
@@ -17,9 +18,10 @@ public class AsyncFunctionLib extends FunctionValue {
private boolean awaiting = false;
- private void next(Context ctx, Object inducedValue, Object inducedError) throws InterruptedException {
+ private void next(Context ctx, Object inducedValue, Object inducedError) {
Object res = null;
- ctx.message.pushFrame(ctx, frame);
+ StackData.pushFrame(ctx, frame);
+ ctx.pushEnv(frame.function.environment);
awaiting = false;
while (!awaiting) {
@@ -37,18 +39,18 @@ public class AsyncFunctionLib extends FunctionValue {
}
}
- ctx.message.popFrame(frame);
+ StackData.popFrame(ctx, frame);
if (awaiting) {
PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject));
}
}
- public Object fulfill(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
+ public Object fulfill(Context ctx, Object thisArg, Object ...args) {
next(ctx, args.length > 0 ? args[0] : null, Runners.NO_RETURN);
return null;
}
- public Object reject(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
+ public Object reject(Context ctx, Object thisArg, Object ...args) {
next(ctx, Runners.NO_RETURN, args.length > 0 ? args[0] : null);
return null;
}
@@ -60,7 +62,7 @@ public class AsyncFunctionLib extends FunctionValue {
}
@Override
- public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
+ public Object call(Context ctx, Object thisArg, Object ...args) {
var handler = new AsyncHelper();
var func = factory.call(ctx, thisArg, new NativeFunction("await", handler::await));
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
diff --git a/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java b/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java
index 81fcd23..ef11c5a 100644
--- a/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java
+++ b/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java
@@ -3,6 +3,7 @@ package me.topchetoeu.jscript.lib;
import java.util.Map;
import me.topchetoeu.jscript.engine.Context;
+import me.topchetoeu.jscript.engine.StackData;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.engine.frame.Runners;
import me.topchetoeu.jscript.engine.values.CodeFunction;
@@ -22,7 +23,7 @@ public class AsyncGeneratorLib extends FunctionValue {
private PromiseLib currPromise;
public CodeFrame frame;
- private void next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) throws InterruptedException {
+ private void next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) {
if (done) {
if (inducedError != Runners.NO_RETURN)
throw new EngineException(inducedError);
@@ -34,7 +35,7 @@ public class AsyncGeneratorLib extends FunctionValue {
}
Object res = null;
- ctx.message.pushFrame(ctx, frame);
+ StackData.pushFrame(ctx, frame);
state = 0;
while (state == 0) {
@@ -55,7 +56,7 @@ public class AsyncGeneratorLib extends FunctionValue {
}
}
- ctx.message.popFrame(frame);
+ StackData.popFrame(ctx, frame);
if (state == 1) {
PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject));
@@ -75,30 +76,30 @@ public class AsyncGeneratorLib extends FunctionValue {
return "Generator [running]";
}
- public Object fulfill(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
+ public Object fulfill(Context ctx, Object thisArg, Object ...args) {
next(ctx, args.length > 0 ? args[0] : null, Runners.NO_RETURN, Runners.NO_RETURN);
return null;
}
- public Object reject(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
+ public Object reject(Context ctx, Object thisArg, Object ...args) {
next(ctx, Runners.NO_RETURN, args.length > 0 ? args[0] : null, Runners.NO_RETURN);
return null;
}
@Native
- public PromiseLib next(Context ctx, Object ...args) throws InterruptedException {
+ public PromiseLib next(Context ctx, Object ...args) {
this.currPromise = new PromiseLib();
if (args.length == 0) next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN);
else next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN);
return this.currPromise;
}
@Native("throw")
- public PromiseLib _throw(Context ctx, Object error) throws InterruptedException {
+ public PromiseLib _throw(Context ctx, Object error) {
this.currPromise = new PromiseLib();
next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error);
return this.currPromise;
}
@Native("return")
- public PromiseLib _return(Context ctx, Object value) throws InterruptedException {
+ public PromiseLib _return(Context ctx, Object value) {
this.currPromise = new PromiseLib();
next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN);
return this.currPromise;
@@ -116,7 +117,7 @@ public class AsyncGeneratorLib extends FunctionValue {
}
@Override
- public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
+ public Object call(Context ctx, Object thisArg, Object ...args) {
var handler = new AsyncGenerator();
var func = factory.call(ctx, thisArg,
new NativeFunction("await", handler::await),
diff --git a/src/me/topchetoeu/jscript/lib/DateLib.java b/src/me/topchetoeu/jscript/lib/DateLib.java
index 5a3d892..575d72b 100644
--- a/src/me/topchetoeu/jscript/lib/DateLib.java
+++ b/src/me/topchetoeu/jscript/lib/DateLib.java
@@ -33,7 +33,7 @@ public class DateLib {
return normal.get(Calendar.YEAR) - 1900;
}
@Native
- public double setYear(Context ctx, double real) throws InterruptedException {
+ public double setYear(Context ctx, double real) {
if (real >= 0 && real <= 99) real = real + 1900;
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.YEAR, (int)real);
@@ -124,56 +124,56 @@ public class DateLib {
}
@Native
- public double setFullYear(Context ctx, double real) throws InterruptedException {
+ public double setFullYear(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.YEAR, (int)real);
updateUTC();
return getTime();
}
@Native
- public double setMonth(Context ctx, double real) throws InterruptedException {
+ public double setMonth(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.MONTH, (int)real);
updateUTC();
return getTime();
}
@Native
- public double setDate(Context ctx, double real) throws InterruptedException {
+ public double setDate(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.DAY_OF_MONTH, (int)real);
updateUTC();
return getTime();
}
@Native
- public double setDay(Context ctx, double real) throws InterruptedException {
+ public double setDay(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.DAY_OF_WEEK, (int)real);
updateUTC();
return getTime();
}
@Native
- public double setHours(Context ctx, double real) throws InterruptedException {
+ public double setHours(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.HOUR_OF_DAY, (int)real);
updateUTC();
return getTime();
}
@Native
- public double setMinutes(Context ctx, double real) throws InterruptedException {
+ public double setMinutes(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.MINUTE, (int)real);
updateUTC();
return getTime();
}
@Native
- public double setSeconds(Context ctx, double real) throws InterruptedException {
+ public double setSeconds(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.SECOND, (int)real);
updateUTC();
return getTime();
}
@Native
- public double setMilliseconds(Context ctx, double real) throws InterruptedException {
+ public double setMilliseconds(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.MILLISECOND, (int)real);
updateUTC();
@@ -181,56 +181,56 @@ public class DateLib {
}
@Native
- public double setUTCFullYear(Context ctx, double real) throws InterruptedException {
+ public double setUTCFullYear(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.YEAR, (int)real);
updateNormal();
return getTime();
}
@Native
- public double setUTCMonth(Context ctx, double real) throws InterruptedException {
+ public double setUTCMonth(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.MONTH, (int)real);
updateNormal();
return getTime();
}
@Native
- public double setUTCDate(Context ctx, double real) throws InterruptedException {
+ public double setUTCDate(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.DAY_OF_MONTH, (int)real);
updateNormal();
return getTime();
}
@Native
- public double setUTCDay(Context ctx, double real) throws InterruptedException {
+ public double setUTCDay(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.DAY_OF_WEEK, (int)real);
updateNormal();
return getTime();
}
@Native
- public double setUTCHours(Context ctx, double real) throws InterruptedException {
+ public double setUTCHours(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.HOUR_OF_DAY, (int)real);
updateNormal();
return getTime();
}
@Native
- public double setUTCMinutes(Context ctx, double real) throws InterruptedException {
+ public double setUTCMinutes(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.MINUTE, (int)real);
updateNormal();
return getTime();
}
@Native
- public double setUTCSeconds(Context ctx, double real) throws InterruptedException {
+ public double setUTCSeconds(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.SECOND, (int)real);
updateNormal();
return getTime();
}
@Native
- public double setUTCMilliseconds(Context ctx, double real) throws InterruptedException {
+ public double setUTCMilliseconds(Context ctx, double real) {
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.MILLISECOND, (int)real);
updateNormal();
diff --git a/src/me/topchetoeu/jscript/lib/ErrorLib.java b/src/me/topchetoeu/jscript/lib/ErrorLib.java
index ffa5f73..0991fce 100644
--- a/src/me/topchetoeu/jscript/lib/ErrorLib.java
+++ b/src/me/topchetoeu/jscript/lib/ErrorLib.java
@@ -2,6 +2,7 @@ package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment;
+import me.topchetoeu.jscript.engine.StackData;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
@@ -11,7 +12,7 @@ import me.topchetoeu.jscript.interop.NativeConstructor;
import me.topchetoeu.jscript.interop.NativeInit;
public class ErrorLib {
- private static String toString(Context ctx, Object cause, Object name, Object message, ArrayValue stack) throws InterruptedException {
+ private static String toString(Context ctx, boolean rethrown, Object cause, Object name, Object message, ArrayValue stack) {
if (name == null) name = "";
else name = Values.toString(ctx, name).trim();
if (message == null) message = "";
@@ -29,17 +30,22 @@ public class ErrorLib {
}
}
- if (cause instanceof ObjectValue) res.append(toString(ctx, cause));
+ if (cause instanceof ObjectValue) {
+ if (rethrown) res.append("\n (rethrown)");
+ else res.append("\nCaused by ").append(toString(ctx, cause));
+ }
return res.toString();
}
- @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) {
if (thisArg instanceof ObjectValue) {
var stack = Values.getMember(ctx, thisArg, "stack");
if (!(stack instanceof ArrayValue)) stack = null;
+ var cause = Values.getMember(ctx, thisArg, ctx.environment().symbol("Symbol.cause"));
return toString(ctx,
- Values.getMember(ctx, thisArg, "cause"),
+ thisArg == cause,
+ cause,
Values.getMember(ctx, thisArg, "name"),
Values.getMember(ctx, thisArg, "message"),
(ArrayValue)stack
@@ -48,11 +54,11 @@ public class ErrorLib {
else return "[Invalid error]";
}
- @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) throws InterruptedException {
+ @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) {
var target = new ObjectValue();
if (thisArg instanceof ObjectValue) target = (ObjectValue)thisArg;
- target.defineProperty(ctx, "stack", new ArrayValue(ctx, ctx.message.stackTrace().toArray()));
+ target.defineProperty(ctx, "stack", ArrayValue.of(ctx, StackData.stackTrace(ctx)));
target.defineProperty(ctx, "name", "Error");
if (message == null) target.defineProperty(ctx, "message", "");
else target.defineProperty(ctx, "message", Values.toString(ctx, message));
diff --git a/src/me/topchetoeu/jscript/lib/FunctionLib.java b/src/me/topchetoeu/jscript/lib/FunctionLib.java
index 2c05aa7..9cdc3ac 100644
--- a/src/me/topchetoeu/jscript/lib/FunctionLib.java
+++ b/src/me/topchetoeu/jscript/lib/FunctionLib.java
@@ -12,10 +12,10 @@ import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeInit;
public class FunctionLib {
- @Native(thisArg = true) public static Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException {
+ @Native(thisArg = true) public static Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) {
return func.call(ctx, thisArg, args.toArray());
}
- @Native(thisArg = true) public static Object call(Context ctx, FunctionValue func, Object thisArg, Object... args) throws InterruptedException {
+ @Native(thisArg = true) public static Object call(Context ctx, FunctionValue func, Object thisArg, Object... args) {
if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function.");
return func.call(ctx, thisArg, args);
diff --git a/src/me/topchetoeu/jscript/lib/GeneratorLib.java b/src/me/topchetoeu/jscript/lib/GeneratorLib.java
index 109491e..e39062b 100644
--- a/src/me/topchetoeu/jscript/lib/GeneratorLib.java
+++ b/src/me/topchetoeu/jscript/lib/GeneratorLib.java
@@ -1,6 +1,7 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.engine.Context;
+import me.topchetoeu.jscript.engine.StackData;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.engine.frame.Runners;
import me.topchetoeu.jscript.engine.values.CodeFunction;
@@ -20,7 +21,7 @@ public class GeneratorLib extends FunctionValue {
@Native("@@Symbol.typeName") public final String name = "Generator";
- private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) throws InterruptedException {
+ private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) {
if (done) {
if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError);
var res = new ObjectValue();
@@ -30,7 +31,7 @@ public class GeneratorLib extends FunctionValue {
}
Object res = null;
- ctx.message.pushFrame(ctx, frame);
+ StackData.pushFrame(ctx, frame);
yielding = false;
while (!yielding) {
@@ -48,7 +49,7 @@ public class GeneratorLib extends FunctionValue {
}
}
- ctx.message.popFrame(frame);
+ StackData.popFrame(ctx, frame);
if (done) frame = null;
else res = frame.pop();
@@ -59,16 +60,16 @@ public class GeneratorLib extends FunctionValue {
}
@Native
- public ObjectValue next(Context ctx, Object ...args) throws InterruptedException {
+ public ObjectValue next(Context ctx, Object ...args) {
if (args.length == 0) return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN);
else return next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN);
}
@Native("throw")
- public ObjectValue _throw(Context ctx, Object error) throws InterruptedException {
+ public ObjectValue _throw(Context ctx, Object error) {
return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error);
}
@Native("return")
- public ObjectValue _return(Context ctx, Object value) throws InterruptedException {
+ public ObjectValue _return(Context ctx, Object value) {
return next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN);
}
@@ -86,7 +87,7 @@ public class GeneratorLib extends FunctionValue {
}
@Override
- public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
+ public Object call(Context ctx, Object thisArg, Object ...args) {
var handler = new Generator();
var func = factory.call(ctx, thisArg, new NativeFunction("yield", handler::yield));
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
diff --git a/src/me/topchetoeu/jscript/lib/Internals.java b/src/me/topchetoeu/jscript/lib/Internals.java
index e09d58e..5fa83e2 100644
--- a/src/me/topchetoeu/jscript/lib/Internals.java
+++ b/src/me/topchetoeu/jscript/lib/Internals.java
@@ -1,7 +1,9 @@
package me.topchetoeu.jscript.lib;
+import java.io.IOException;
import java.util.HashMap;
+import me.topchetoeu.jscript.Reading;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.DataKey;
import me.topchetoeu.jscript.engine.Environment;
@@ -14,12 +16,22 @@ public class Internals {
private static final DataKey> THREADS = new DataKey<>();
private static final DataKey I = new DataKey<>();
- @Native public static void log(Context ctx, Object ...args) throws InterruptedException {
+
+ @Native public static void log(Context ctx, Object ...args) {
for (var arg : args) {
Values.printValue(ctx, arg);
}
System.out.println();
}
+ @Native public static String readline(Context ctx) {
+ try {
+ return Reading.read();
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
@Native public static int setTimeout(Context ctx, FunctionValue func, int delay, Object ...args) {
var thread = new Thread(() -> {
@@ -31,12 +43,12 @@ public class Internals {
}
catch (InterruptedException e) { return; }
- ctx.message.engine.pushMsg(false, ctx.message, func, null, args);
+ ctx.engine.pushMsg(false, ctx, func, null, args);
});
thread.start();
- int i = ctx.env.data.increase(I, 1, 0);
- var threads = ctx.env.data.add(THREADS, new HashMap<>());
+ int i = ctx.environment().data.increase(I, 1, 0);
+ var threads = ctx.environment().data.get(THREADS, new HashMap<>());
threads.put(++i, thread);
return i;
}
@@ -51,19 +63,19 @@ public class Internals {
}
catch (InterruptedException e) { return; }
- ctx.message.engine.pushMsg(false, ctx.message, func, null, args);
+ ctx.engine.pushMsg(false, ctx, func, null, args);
}
});
thread.start();
- int i = ctx.env.data.increase(I, 1, 0);
- var threads = ctx.env.data.add(THREADS, new HashMap<>());
+ int i = ctx.environment().data.increase(I, 1, 0);
+ var threads = ctx.environment().data.get(THREADS, new HashMap<>());
threads.put(++i, thread);
return i;
}
@Native public static void clearTimeout(Context ctx, int i) {
- var threads = ctx.env.data.add(THREADS, new HashMap<>());
+ var threads = ctx.environment().data.get(THREADS, new HashMap<>());
var thread = threads.remove(i);
if (thread != null) thread.interrupt();
@@ -72,15 +84,15 @@ public class Internals {
clearTimeout(ctx, i);
}
- @Native public static double parseInt(Context ctx, String val) throws InterruptedException {
+ @Native public static double parseInt(Context ctx, String val) {
return NumberLib.parseInt(ctx, val);
}
- @Native public static double parseFloat(Context ctx, String val) throws InterruptedException {
+ @Native public static double parseFloat(Context ctx, String val) {
return NumberLib.parseFloat(ctx, val);
}
public void apply(Environment env) {
- var wp = env.wrappersProvider;
+ var wp = env.wrappers;
var glob = env.global = new GlobalScope(wp.getNamespace(Internals.class));
glob.define(null, "Math", false, wp.getNamespace(MathLib.class));
diff --git a/src/me/topchetoeu/jscript/lib/JSONLib.java b/src/me/topchetoeu/jscript/lib/JSONLib.java
index d36a4ea..8ea57d5 100644
--- a/src/me/topchetoeu/jscript/lib/JSONLib.java
+++ b/src/me/topchetoeu/jscript/lib/JSONLib.java
@@ -1,85 +1,22 @@
package me.topchetoeu.jscript.lib;
-import java.util.HashSet;
-import java.util.stream.Collectors;
-
+import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.engine.Context;
-import me.topchetoeu.jscript.engine.values.ArrayValue;
-import me.topchetoeu.jscript.engine.values.ObjectValue;
-import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.interop.Native;
-import me.topchetoeu.jscript.json.JSONElement;
-import me.topchetoeu.jscript.json.JSONList;
-import me.topchetoeu.jscript.json.JSONMap;
+import me.topchetoeu.jscript.json.JSON;
public class JSONLib {
- private static Object toJS(JSONElement val) {
- if (val.isBoolean()) return val.bool();
- if (val.isString()) return val.string();
- if (val.isNumber()) return val.number();
- if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSONLib::toJS).collect(Collectors.toList()));
- if (val.isMap()) {
- var res = new ObjectValue();
- for (var el : val.map().entrySet()) {
- res.defineProperty(null, el.getKey(), toJS(el.getValue()));
- }
- return res;
- }
- if (val.isNull()) return Values.NULL;
- return null;
- }
- private static JSONElement toJSON(Context ctx, Object val, HashSet prev) throws InterruptedException {
- if (val instanceof Boolean) return JSONElement.bool((boolean)val);
- if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
- if (val instanceof String) return JSONElement.string((String)val);
- if (val == Values.NULL) return JSONElement.NULL;
- if (val instanceof ObjectValue) {
- if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
- prev.add(val);
-
- var res = new JSONMap();
-
- for (var el : ((ObjectValue)val).keys(false)) {
- var jsonEl = toJSON(ctx, ((ObjectValue)val).getMember(ctx, el), prev);
- if (jsonEl == null) continue;
- if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
- }
-
- prev.remove(val);
- return JSONElement.of(res);
- }
- if (val instanceof ArrayValue) {
- if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
- prev.add(val);
-
- var res = new JSONList();
-
- for (var el : ((ArrayValue)val).toArray()) {
- var jsonEl = toJSON(ctx, el, prev);
- if (jsonEl == null) jsonEl = JSONElement.NULL;
- res.add(jsonEl);
- }
-
- prev.remove(val);
- return JSONElement.of(res);
- }
- if (val == null) return null;
- return null;
- }
-
@Native
- public static Object parse(Context ctx, String val) throws InterruptedException {
+ public static Object parse(Context ctx, String val) {
try {
- return toJS(me.topchetoeu.jscript.json.JSON.parse("", val));
- }
- catch (SyntaxException e) {
- throw EngineException.ofSyntax(e.msg);
+ return JSON.toJs(JSON.parse(new Filename("jscript", "json"), val));
}
+ catch (SyntaxException e) { throw EngineException.ofSyntax(e.msg); }
}
@Native
- public static String stringify(Context ctx, Object val) throws InterruptedException {
- return me.topchetoeu.jscript.json.JSON.stringify(toJSON(ctx, val, new HashSet<>()));
+ public static String stringify(Context ctx, Object val) {
+ return me.topchetoeu.jscript.json.JSON.stringify(JSON.fromJs(ctx, val));
}
}
diff --git a/src/me/topchetoeu/jscript/lib/MapLib.java b/src/me/topchetoeu/jscript/lib/MapLib.java
index 6fc6dab..bd1217e 100644
--- a/src/me/topchetoeu/jscript/lib/MapLib.java
+++ b/src/me/topchetoeu/jscript/lib/MapLib.java
@@ -16,7 +16,7 @@ public class MapLib {
private LinkedHashMap map = new LinkedHashMap<>();
@Native("@@Symbol.typeName") public final String name = "Map";
- @Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) throws InterruptedException {
+ @Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) {
return this.entries(ctx);
}
@@ -31,17 +31,17 @@ public class MapLib {
return false;
}
- @Native public ObjectValue entries(Context ctx) throws InterruptedException {
+ @Native public ObjectValue entries(Context ctx) {
var res = map.entrySet().stream().map(v -> {
return new ArrayValue(ctx, v.getKey(), v.getValue());
}).collect(Collectors.toList());
return Values.fromJavaIterator(ctx, res.iterator());
}
- @Native public ObjectValue keys(Context ctx) throws InterruptedException {
+ @Native public ObjectValue keys(Context ctx) {
var res = new ArrayList<>(map.keySet());
return Values.fromJavaIterator(ctx, res.iterator());
}
- @Native public ObjectValue values(Context ctx) throws InterruptedException {
+ @Native public ObjectValue values(Context ctx) {
var res = new ArrayList<>(map.values());
return Values.fromJavaIterator(ctx, res.iterator());
}
@@ -61,13 +61,13 @@ public class MapLib {
return map.size();
}
- @NativeGetter public void forEach(Context ctx, FunctionValue func, Object thisArg) throws InterruptedException {
+ @NativeGetter public void forEach(Context ctx, FunctionValue func, Object thisArg) {
var keys = new ArrayList<>(map.keySet());
for (var el : keys) func.call(ctx, thisArg, el, map.get(el), this);
}
- @Native public MapLib(Context ctx, Object iterable) throws InterruptedException {
+ @Native public MapLib(Context ctx, Object iterable) {
for (var el : Values.toJavaIterable(ctx, iterable)) {
try {
set(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1));
diff --git a/src/me/topchetoeu/jscript/lib/NumberLib.java b/src/me/topchetoeu/jscript/lib/NumberLib.java
index 28dac9c..11556e3 100644
--- a/src/me/topchetoeu/jscript/lib/NumberLib.java
+++ b/src/me/topchetoeu/jscript/lib/NumberLib.java
@@ -29,22 +29,22 @@ public class NumberLib {
return val > MIN_SAFE_INTEGER && val < MAX_SAFE_INTEGER;
}
- @Native public static double parseFloat(Context ctx, String val) throws InterruptedException {
+ @Native public static double parseFloat(Context ctx, String val) {
return Values.toNumber(ctx, val);
}
- @Native public static double parseInt(Context ctx, String val) throws InterruptedException {
+ @Native public static double parseInt(Context ctx, String val) {
return (long)Values.toNumber(ctx, val);
}
- @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) throws InterruptedException {
+ @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) {
val = Values.toNumber(ctx, val);
if (thisArg instanceof ObjectValue) return new NumberLib((double)val);
else return val;
}
- @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) {
return Values.toString(ctx, Values.toNumber(ctx, thisArg));
}
- @Native(thisArg = true) public static double valueOf(Context ctx, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static double valueOf(Context ctx, Object thisArg) {
if (thisArg instanceof NumberLib) return ((NumberLib)thisArg).value;
else return Values.toNumber(ctx, thisArg);
}
diff --git a/src/me/topchetoeu/jscript/lib/ObjectLib.java b/src/me/topchetoeu/jscript/lib/ObjectLib.java
index 35f9c6b..f7fce62 100644
--- a/src/me/topchetoeu/jscript/lib/ObjectLib.java
+++ b/src/me/topchetoeu/jscript/lib/ObjectLib.java
@@ -14,7 +14,7 @@ import me.topchetoeu.jscript.interop.NativeConstructor;
import me.topchetoeu.jscript.interop.NativeInit;
public class ObjectLib {
- @Native public static ObjectValue assign(Context ctx, ObjectValue dst, Object... src) throws InterruptedException {
+ @Native public static ObjectValue assign(Context ctx, ObjectValue dst, Object... src) {
for (var obj : src) {
for (var key : Values.getMembers(ctx, obj, true, true)) {
Values.setMember(ctx, dst, key, Values.getMember(ctx, obj, key));
@@ -22,13 +22,13 @@ public class ObjectLib {
}
return dst;
}
- @Native public static ObjectValue create(Context ctx, ObjectValue proto, ObjectValue props) throws InterruptedException {
+ @Native public static ObjectValue create(Context ctx, ObjectValue proto, ObjectValue props) {
var obj = new ObjectValue();
obj.setPrototype(ctx, proto);
return defineProperties(ctx, obj, props);
}
- @Native public static ObjectValue defineProperty(Context ctx, ObjectValue obj, Object key, ObjectValue attrib) throws InterruptedException {
+ @Native public static ObjectValue defineProperty(Context ctx, ObjectValue obj, Object key, ObjectValue attrib) {
var hasVal = attrib.hasMember(ctx, "value", false);
var hasGet = attrib.hasMember(ctx, "get", false);
var hasSet = attrib.hasMember(ctx, "set", false);
@@ -59,7 +59,7 @@ public class ObjectLib {
return obj;
}
- @Native public static ObjectValue defineProperties(Context ctx, ObjectValue obj, ObjectValue attrib) throws InterruptedException {
+ @Native public static ObjectValue defineProperties(Context ctx, ObjectValue obj, ObjectValue attrib) {
for (var key : Values.getMembers(null, obj, false, false)) {
obj.defineProperty(ctx, key, attrib.getMember(ctx, key));
}
@@ -67,7 +67,7 @@ public class ObjectLib {
return obj;
}
- @Native public static ArrayValue keys(Context ctx, Object obj, Object all) throws InterruptedException {
+ @Native public static ArrayValue keys(Context ctx, Object obj, Object all) {
var res = new ArrayValue();
var _all = Values.toBoolean(all);
@@ -77,7 +77,7 @@ public class ObjectLib {
return res;
}
- @Native public static ArrayValue entries(Context ctx, Object obj, Object all) throws InterruptedException {
+ @Native public static ArrayValue entries(Context ctx, Object obj, Object all) {
var res = new ArrayValue();
var _all = Values.toBoolean(all);
@@ -87,7 +87,7 @@ public class ObjectLib {
return res;
}
- @Native public static ArrayValue values(Context ctx, Object obj, Object all) throws InterruptedException {
+ @Native public static ArrayValue values(Context ctx, Object obj, Object all) {
var res = new ArrayValue();
var _all = Values.toBoolean(all);
@@ -98,10 +98,10 @@ public class ObjectLib {
return res;
}
- @Native public static ObjectValue getOwnPropertyDescriptor(Context ctx, Object obj, Object key) throws InterruptedException {
+ @Native public static ObjectValue getOwnPropertyDescriptor(Context ctx, Object obj, Object key) {
return Values.getMemberDescriptor(ctx, obj, key);
}
- @Native public static ObjectValue getOwnPropertyDescriptors(Context ctx, Object obj) throws InterruptedException {
+ @Native public static ObjectValue getOwnPropertyDescriptors(Context ctx, Object obj) {
var res = new ObjectValue();
for (var key : Values.getMembers(ctx, obj, true, true)) {
res.defineProperty(ctx, key, getOwnPropertyDescriptor(ctx, obj, key));
@@ -109,7 +109,7 @@ public class ObjectLib {
return res;
}
- @Native public static ArrayValue getOwnPropertyNames(Context ctx, Object obj, Object all) throws InterruptedException {
+ @Native public static ArrayValue getOwnPropertyNames(Context ctx, Object obj, Object all) {
var res = new ArrayValue();
var _all = Values.toBoolean(all);
@@ -119,7 +119,7 @@ public class ObjectLib {
return res;
}
- @Native public static ArrayValue getOwnPropertySymbols(Context ctx, Object obj) throws InterruptedException {
+ @Native public static ArrayValue getOwnPropertySymbols(Context ctx, Object obj) {
var res = new ArrayValue();
for (var key : Values.getMembers(ctx, obj, true, true)) {
@@ -128,19 +128,19 @@ public class ObjectLib {
return res;
}
- @Native public static boolean hasOwn(Context ctx, Object obj, Object key) throws InterruptedException {
+ @Native public static boolean hasOwn(Context ctx, Object obj, Object key) {
return Values.hasMember(ctx, obj, key, true);
}
- @Native public static ObjectValue getPrototypeOf(Context ctx, Object obj) throws InterruptedException {
+ @Native public static ObjectValue getPrototypeOf(Context ctx, Object obj) {
return Values.getPrototype(ctx, obj);
}
- @Native public static Object setPrototypeOf(Context ctx, Object obj, Object proto) throws InterruptedException {
+ @Native public static Object setPrototypeOf(Context ctx, Object obj, Object proto) {
Values.setPrototype(ctx, obj, proto);
return obj;
}
- @Native public static ObjectValue fromEntries(Context ctx, Object iterable) throws InterruptedException {
+ @Native public static ObjectValue fromEntries(Context ctx, Object iterable) {
var res = new ObjectValue();
for (var el : Values.toJavaIterable(ctx, iterable)) {
@@ -152,23 +152,23 @@ public class ObjectLib {
return res;
}
- @Native public static Object preventExtensions(Context ctx, Object obj) throws InterruptedException {
+ @Native public static Object preventExtensions(Context ctx, Object obj) {
if (obj instanceof ObjectValue) ((ObjectValue)obj).preventExtensions();
return obj;
}
- @Native public static Object seal(Context ctx, Object obj) throws InterruptedException {
+ @Native public static Object seal(Context ctx, Object obj) {
if (obj instanceof ObjectValue) ((ObjectValue)obj).seal();
return obj;
}
- @Native public static Object freeze(Context ctx, Object obj) throws InterruptedException {
+ @Native public static Object freeze(Context ctx, Object obj) {
if (obj instanceof ObjectValue) ((ObjectValue)obj).freeze();
return obj;
}
- @Native public static boolean isExtensible(Context ctx, Object obj) throws InterruptedException {
+ @Native public static boolean isExtensible(Context ctx, Object obj) {
return obj instanceof ObjectValue && ((ObjectValue)obj).extensible();
}
- @Native public static boolean isSealed(Context ctx, Object obj) throws InterruptedException {
+ @Native public static boolean isSealed(Context ctx, Object obj) {
if (obj instanceof ObjectValue && ((ObjectValue)obj).extensible()) {
var _obj = (ObjectValue)obj;
for (var key : _obj.keys(true)) {
@@ -178,7 +178,7 @@ public class ObjectLib {
return true;
}
- @Native public static boolean isFrozen(Context ctx, Object obj) throws InterruptedException {
+ @Native public static boolean isFrozen(Context ctx, Object obj) {
if (obj instanceof ObjectValue && ((ObjectValue)obj).extensible()) {
var _obj = (ObjectValue)obj;
for (var key : _obj.keys(true)) {
@@ -193,18 +193,18 @@ public class ObjectLib {
@Native(thisArg = true) public static Object valueOf(Context ctx, Object thisArg) {
return thisArg;
}
- @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
- var name = Values.getMember(ctx, thisArg, ctx.env.symbol("Symbol.typeName"));
+ @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) {
+ var name = Values.getMember(ctx, thisArg, ctx.environment().symbol("Symbol.typeName"));
if (name == null) name = "Unknown";
else name = Values.toString(ctx, name);
return "[object " + name + "]";
}
- @Native(thisArg = true) public static boolean hasOwnProperty(Context ctx, Object thisArg, Object key) throws InterruptedException {
+ @Native(thisArg = true) public static boolean hasOwnProperty(Context ctx, Object thisArg, Object key) {
return ObjectLib.hasOwn(ctx, thisArg, Values.convert(ctx, key, String.class));
}
- @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object arg) throws InterruptedException {
+ @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object arg) {
if (arg == null || arg == Values.NULL) return new ObjectValue();
else if (arg instanceof Boolean) return BooleanLib.constructor(ctx, thisArg, arg);
else if (arg instanceof Number) return NumberLib.constructor(ctx, thisArg, arg);
diff --git a/src/me/topchetoeu/jscript/lib/PromiseLib.java b/src/me/topchetoeu/jscript/lib/PromiseLib.java
index bd393e8..7c4f7db 100644
--- a/src/me/topchetoeu/jscript/lib/PromiseLib.java
+++ b/src/me/topchetoeu/jscript/lib/PromiseLib.java
@@ -6,7 +6,6 @@ import java.util.Map;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment;
-import me.topchetoeu.jscript.engine.Message;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.NativeFunction;
@@ -14,6 +13,7 @@ import me.topchetoeu.jscript.engine.values.NativeWrapper;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException;
+import me.topchetoeu.jscript.exceptions.InterruptException;
import me.topchetoeu.jscript.interop.InitType;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeInit;
@@ -32,19 +32,19 @@ public class PromiseLib {
}
@Native("resolve")
- public static PromiseLib ofResolved(Context ctx, Object val) throws InterruptedException {
+ public static PromiseLib ofResolved(Context ctx, Object val) {
var res = new PromiseLib();
res.fulfill(ctx, val);
return res;
}
@Native("reject")
- public static PromiseLib ofRejected(Context ctx, Object val) throws InterruptedException {
+ public static PromiseLib ofRejected(Context ctx, Object val) {
var res = new PromiseLib();
res.reject(ctx, val);
return res;
}
- @Native public static PromiseLib any(Context ctx, Object _promises) throws InterruptedException {
+ @Native public static PromiseLib any(Context ctx, Object _promises) {
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
var promises = Values.array(_promises);
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
@@ -69,7 +69,7 @@ public class PromiseLib {
return res;
}
- @Native public static PromiseLib race(Context ctx, Object _promises) throws InterruptedException {
+ @Native public static PromiseLib race(Context ctx, Object _promises) {
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
var promises = Values.array(_promises);
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
@@ -85,7 +85,7 @@ public class PromiseLib {
return res;
}
- @Native public static PromiseLib all(Context ctx, Object _promises) throws InterruptedException {
+ @Native public static PromiseLib all(Context ctx, Object _promises) {
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
var promises = Values.array(_promises);
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
@@ -112,7 +112,7 @@ public class PromiseLib {
return res;
}
- @Native public static PromiseLib allSettled(Context ctx, Object _promises) throws InterruptedException {
+ @Native public static PromiseLib allSettled(Context ctx, Object _promises) {
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
var promises = Values.array(_promises);
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
@@ -155,7 +155,7 @@ public class PromiseLib {
* Thread safe - you can call this from anywhere
* HOWEVER, it's strongly recommended to use this only in javascript
*/
- @Native(thisArg=true) public static Object then(Context ctx, Object thisArg, Object _onFulfill, Object _onReject) throws InterruptedException {
+ @Native(thisArg=true) public static Object then(Context ctx, Object thisArg, Object _onFulfill, Object _onReject) {
var onFulfill = _onFulfill instanceof FunctionValue ? ((FunctionValue)_onFulfill) : null;
var onReject = _onReject instanceof FunctionValue ? ((FunctionValue)_onReject) : null;
@@ -186,9 +186,7 @@ public class PromiseLib {
if (thisArg instanceof PromiseLib) ((PromiseLib)thisArg).handle(ctx, fulfillHandle, rejectHandle);
else {
Object next;
- try {
- next = Values.getMember(ctx, thisArg, "then");
- }
+ try { next = Values.getMember(ctx, thisArg, "then"); }
catch (IllegalArgumentException e) { next = null; }
try {
@@ -206,14 +204,14 @@ public class PromiseLib {
* Thread safe - you can call this from anywhere
* HOWEVER, it's strongly recommended to use this only in javascript
*/
- @Native(value="catch", thisArg=true) public static Object _catch(Context ctx, Object thisArg, Object _onReject) throws InterruptedException {
+ @Native(value="catch", thisArg=true) public static Object _catch(Context ctx, Object thisArg, Object _onReject) {
return then(ctx, thisArg, null, _onReject);
}
/**
* Thread safe - you can call this from anywhere
* HOWEVER, it's strongly recommended to use this only in javascript
*/
- @Native(value="finally", thisArg=true) public static Object _finally(Context ctx, Object thisArg, Object _handle) throws InterruptedException {
+ @Native(value="finally", thisArg=true) public static Object _finally(Context ctx, Object thisArg, Object _handle) {
return then(ctx, thisArg,
new NativeFunction(null, (e, th, _args) -> {
if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx);
@@ -236,12 +234,12 @@ public class PromiseLib {
private boolean handled = false;
private Object val;
- private void resolve(Context ctx, Object val, int state) throws InterruptedException {
+ public void fulfill(Context ctx, Object val) {
if (this.state != STATE_PENDING) return;
if (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx,
- new NativeFunction(null, (e, th, a) -> { this.resolve(ctx, a[0], state); return null; }),
- new NativeFunction(null, (e, th, a) -> { this.resolve(ctx, a[0], STATE_REJECTED); return null; })
+ new NativeFunction(null, (e, th, a) -> { this.fulfill(ctx, a[0]); return null; }),
+ new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; })
);
else {
Object next;
@@ -250,28 +248,54 @@ public class PromiseLib {
try {
if (next instanceof FunctionValue) ((FunctionValue)next).call(ctx, val,
- new NativeFunction((e, _thisArg, a) -> { this.resolve(ctx, a.length > 0 ? a[0] : null, state); return null; }),
- new NativeFunction((e, _thisArg, a) -> { this.resolve(ctx, a.length > 0 ? a[0] : null, STATE_REJECTED); return null; })
+ new NativeFunction((e, _thisArg, a) -> { this.fulfill(ctx, a.length > 0 ? a[0] : null); return null; }),
+ new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; })
);
else {
this.val = val;
- this.state = state;
+ this.state = STATE_FULFILLED;
- if (state == STATE_FULFILLED) {
- for (var handle : handles) handle.fulfilled.call(handle.ctx, null, val);
- }
- else if (state == STATE_REJECTED) {
- for (var handle : handles) handle.rejected.call(handle.ctx, null, val);
- if (handles.size() == 0) {
- ctx.message.engine.pushMsg(true, ctx.message, new NativeFunction((_ctx, _thisArg, _args) -> {
- if (!handled) {
- try { Values.printError(new EngineException(val).setContext(ctx), "(in promise)"); }
- catch (InterruptedException ex) { }
- }
+ for (var handle : handles) handle.fulfilled.call(handle.ctx, null, val);
- return null;
- }), null);
- }
+ handles = null;
+ }
+ }
+ catch (EngineException err) {
+ this.reject(ctx, err.value);
+ }
+ }
+ }
+ public void reject(Context ctx, Object val) {
+ if (this.state != STATE_PENDING) return;
+
+ if (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx,
+ new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; }),
+ new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; })
+ );
+ else {
+ Object next;
+ try { next = Values.getMember(ctx, val, "next"); }
+ catch (IllegalArgumentException e) { next = null; }
+
+ try {
+ if (next instanceof FunctionValue) ((FunctionValue)next).call(ctx, val,
+ new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; }),
+ new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; })
+ );
+ else {
+ this.val = val;
+ this.state = STATE_REJECTED;
+
+ for (var handle : handles) handle.rejected.call(handle.ctx, null, val);
+ if (handles.size() == 0) {
+ ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
+ if (!handled) {
+ Values.printError(new EngineException(val).setCtx(ctx.environment(), ctx.engine), "(in promise)");
+ throw new InterruptException();
+ }
+
+ return null;
+ }), null);
}
handles = null;
@@ -283,23 +307,10 @@ public class PromiseLib {
}
}
- /**
- * Thread safe - call from any thread
- */
- public void fulfill(Context ctx, Object val) throws InterruptedException {
- resolve(ctx, val, STATE_FULFILLED);
- }
- /**
- * Thread safe - call from any thread
- */
- public void reject(Context ctx, Object val) throws InterruptedException {
- resolve(ctx, val, STATE_REJECTED);
- }
-
private void handle(Context ctx, FunctionValue fulfill, FunctionValue reject) {
- if (state == STATE_FULFILLED) ctx.message.engine.pushMsg(true, new Message(ctx.message.engine), fulfill, null, val);
+ if (state == STATE_FULFILLED) ctx.engine.pushMsg(true, ctx, fulfill, null, val);
else if (state == STATE_REJECTED) {
- ctx.message.engine.pushMsg(true, new Message(ctx.message.engine), reject, null, val);
+ ctx.engine.pushMsg(true, ctx, reject, null, val);
handled = true;
}
else handles.add(new Handle(ctx, fulfill, reject));
@@ -314,7 +325,7 @@ public class PromiseLib {
/**
* NOT THREAD SAFE - must be called from the engine executor thread
*/
- @Native public PromiseLib(Context ctx, FunctionValue func) throws InterruptedException {
+ @Native public PromiseLib(Context ctx, FunctionValue func) {
if (!(func instanceof FunctionValue)) throw EngineException.ofType("A function must be passed to the promise constructor.");
try {
func.call(
diff --git a/src/me/topchetoeu/jscript/lib/RangeErrorLib.java b/src/me/topchetoeu/jscript/lib/RangeErrorLib.java
index b0ce327..fc21e47 100644
--- a/src/me/topchetoeu/jscript/lib/RangeErrorLib.java
+++ b/src/me/topchetoeu/jscript/lib/RangeErrorLib.java
@@ -8,7 +8,7 @@ import me.topchetoeu.jscript.interop.NativeConstructor;
import me.topchetoeu.jscript.interop.NativeInit;
public class RangeErrorLib extends ErrorLib {
- @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) throws InterruptedException {
+ @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) {
var target = ErrorLib.constructor(ctx, thisArg, message);
target.defineProperty(ctx, "name", "RangeError");
return target;
diff --git a/src/me/topchetoeu/jscript/lib/RegExpLib.java b/src/me/topchetoeu/jscript/lib/RegExpLib.java
index 3e54df0..9828577 100644
--- a/src/me/topchetoeu/jscript/lib/RegExpLib.java
+++ b/src/me/topchetoeu/jscript/lib/RegExpLib.java
@@ -17,7 +17,7 @@ public class RegExpLib {
private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL);
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]");
- private static String cleanupPattern(Context ctx, Object val) throws InterruptedException {
+ private static String cleanupPattern(Context ctx, Object val) {
if (val == null) return "(?:)";
if (val instanceof RegExpLib) return ((RegExpLib)val).source;
if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExpLib) {
@@ -27,7 +27,7 @@ public class RegExpLib {
if (res.equals("")) return "(?:)";
return res;
}
- private static String cleanupFlags(Context ctx, Object val) throws InterruptedException {
+ private static String cleanupFlags(Context ctx, Object val) {
if (val == null) return "";
return Values.toString(ctx, val);
}
@@ -46,7 +46,7 @@ public class RegExpLib {
}
@Native
- public static RegExpLib escape(Context ctx, Object raw, Object flags) throws InterruptedException {
+ public static RegExpLib escape(Context ctx, Object raw, Object flags) {
return escape(Values.toString(ctx, raw), cleanupFlags(ctx, flags));
}
public static RegExpLib escape(String raw, String flags) {
@@ -97,9 +97,7 @@ public class RegExpLib {
var groups = new ObjectValue();
for (var el : namedGroups) {
- try {
- groups.defineProperty(null, el, matcher.group(el));
- }
+ try { groups.defineProperty(null, el, matcher.group(el)); }
catch (IllegalArgumentException e) { }
}
if (groups.values.size() == 0) groups = null;
@@ -135,7 +133,7 @@ public class RegExpLib {
return "/" + source + "/" + flags();
}
- @Native("@@Symvol.match") public Object match(Context ctx, String target) throws InterruptedException {
+ @Native("@@Symvol.match") public Object match(Context ctx, String target) {
if (this.global) {
var res = new ArrayValue();
Object val;
@@ -152,7 +150,7 @@ public class RegExpLib {
}
}
- @Native("@@Symvol.matchAll") public Object matchAll(Context ctx, String target) throws InterruptedException {
+ @Native("@@Symvol.matchAll") public Object matchAll(Context ctx, String target) {
var pattern = new RegExpLib(this.source, this.flags() + "g");
return Values.fromJavaIterator(ctx, new Iterator() {
@@ -174,7 +172,7 @@ public class RegExpLib {
});
}
- @Native("@@Symvol.split") public ArrayValue split(Context ctx, String target, Object limit, boolean sensible) throws InterruptedException {
+ @Native("@@Symvol.split") public ArrayValue split(Context ctx, String target, Object limit, boolean sensible) {
var pattern = new RegExpLib(this.source, this.flags() + "g");
Object match;
int lastEnd = 0;
@@ -260,7 +258,7 @@ public class RegExpLib {
// else return -1;
// }
// },
- @Native public RegExpLib(Context ctx, Object pattern, Object flags) throws InterruptedException {
+ @Native public RegExpLib(Context ctx, Object pattern, Object flags) {
this(cleanupPattern(ctx, pattern), cleanupFlags(ctx, flags));
}
public RegExpLib(String pattern, String flags) {
diff --git a/src/me/topchetoeu/jscript/lib/SetLib.java b/src/me/topchetoeu/jscript/lib/SetLib.java
index 069c79f..331f023 100644
--- a/src/me/topchetoeu/jscript/lib/SetLib.java
+++ b/src/me/topchetoeu/jscript/lib/SetLib.java
@@ -16,19 +16,19 @@ public class SetLib {
private LinkedHashSet set = new LinkedHashSet<>();
@Native("@@Symbol.typeName") public final String name = "Set";
- @Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) throws InterruptedException {
+ @Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) {
return this.values(ctx);
}
- @Native public ObjectValue entries(Context ctx) throws InterruptedException {
+ @Native public ObjectValue entries(Context ctx) {
var res = set.stream().map(v -> new ArrayValue(ctx, v, v)).collect(Collectors.toList());
return Values.fromJavaIterator(ctx, res.iterator());
}
- @Native public ObjectValue keys(Context ctx) throws InterruptedException {
+ @Native public ObjectValue keys(Context ctx) {
var res = new ArrayList<>(set);
return Values.fromJavaIterator(ctx, res.iterator());
}
- @Native public ObjectValue values(Context ctx) throws InterruptedException {
+ @Native public ObjectValue values(Context ctx) {
var res = new ArrayList<>(set);
return Values.fromJavaIterator(ctx, res.iterator());
}
@@ -51,13 +51,13 @@ public class SetLib {
return set.size();
}
- @NativeGetter public void forEach(Context ctx, FunctionValue func, Object thisArg) throws InterruptedException {
+ @NativeGetter public void forEach(Context ctx, FunctionValue func, Object thisArg) {
var keys = new ArrayList<>(set);
for (var el : keys) func.call(ctx, thisArg, el, el, this);
}
- @Native public SetLib(Context ctx, Object iterable) throws InterruptedException {
+ @Native public SetLib(Context ctx, Object iterable) {
for (var el : Values.toJavaIterable(ctx, iterable)) add(el);
}
}
diff --git a/src/me/topchetoeu/jscript/lib/StringLib.java b/src/me/topchetoeu/jscript/lib/StringLib.java
index 9621487..ae5b8f6 100644
--- a/src/me/topchetoeu/jscript/lib/StringLib.java
+++ b/src/me/topchetoeu/jscript/lib/StringLib.java
@@ -19,7 +19,7 @@ import me.topchetoeu.jscript.interop.NativeInit;
public class StringLib {
public final String value;
- private static String passThis(Context ctx, String funcName, Object val) throws InterruptedException {
+ private static String passThis(Context ctx, String funcName, Object val) {
if (val instanceof StringLib) return ((StringLib)val).value;
else if (val instanceof String) return (String)val;
else throw EngineException.ofType(String.format("'%s' may only be called upon object and primitve strings.", funcName));
@@ -33,50 +33,50 @@ public class StringLib {
return i;
}
- @NativeGetter(thisArg = true) public static int length(Context ctx, Object thisArg) throws InterruptedException {
+ @NativeGetter(thisArg = true) public static int length(Context ctx, Object thisArg) {
return passThis(ctx, "substring", thisArg).length();
}
- @Native(thisArg = true) public static String substring(Context ctx, Object thisArg, int start, Object _end) throws InterruptedException {
+ @Native(thisArg = true) public static String substring(Context ctx, Object thisArg, int start, Object _end) {
var val = passThis(ctx, "substring", thisArg);
start = normalizeI(start, val.length(), true);
int end = normalizeI(_end == null ? val.length() : (int)Values.toNumber(ctx, _end), val.length(), true);
return val.substring(start, end);
}
- @Native(thisArg = true) public static String substr(Context ctx, Object thisArg, int start, Object _len) throws InterruptedException {
+ @Native(thisArg = true) public static String substr(Context ctx, Object thisArg, int start, Object _len) {
var val = passThis(ctx, "substr", thisArg);
int len = _len == null ? val.length() - start : (int)Values.toNumber(ctx, _len);
return substring(ctx, val, start, start + len);
}
- @Native(thisArg = true) public static String toLowerCase(Context ctx, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static String toLowerCase(Context ctx, Object thisArg) {
return passThis(ctx, "toLowerCase", thisArg).toLowerCase();
}
- @Native(thisArg = true) public static String toUpperCase(Context ctx, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static String toUpperCase(Context ctx, Object thisArg) {
return passThis(ctx, "toUpperCase", thisArg).toUpperCase();
}
- @Native(thisArg = true) public static String charAt(Context ctx, Object thisArg, int i) throws InterruptedException {
+ @Native(thisArg = true) public static String charAt(Context ctx, Object thisArg, int i) {
return passThis(ctx, "charAt", thisArg).charAt(i) + "";
}
- @Native(thisArg = true) public static int charCodeAt(Context ctx, Object thisArg, int i) throws InterruptedException {
+ @Native(thisArg = true) public static int charCodeAt(Context ctx, Object thisArg, int i) {
return passThis(ctx, "charCodeAt", thisArg).charAt(i);
}
- @Native(thisArg = true) public static boolean startsWith(Context ctx, Object thisArg, String term, int pos) throws InterruptedException {
+ @Native(thisArg = true) public static boolean startsWith(Context ctx, Object thisArg, String term, int pos) {
return passThis(ctx, "startsWith", thisArg).startsWith(term, pos);
}
- @Native(thisArg = true) public static boolean endsWith(Context ctx, Object thisArg, String term, int pos) throws InterruptedException {
+ @Native(thisArg = true) public static boolean endsWith(Context ctx, Object thisArg, String term, int pos) {
var val = passThis(ctx, "endsWith", thisArg);
return val.lastIndexOf(term, pos) >= 0;
}
- @Native(thisArg = true) public static int indexOf(Context ctx, Object thisArg, Object term, int start) throws InterruptedException {
+ @Native(thisArg = true) public static int indexOf(Context ctx, Object thisArg, Object term, int start) {
var val = passThis(ctx, "indexOf", thisArg);
if (term != null && term != Values.NULL && !(term instanceof String)) {
- var search = Values.getMember(ctx, term, ctx.env.symbol("Symbol.search"));
+ var search = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.search"));
if (search instanceof FunctionValue) {
return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, false, start));
}
@@ -84,11 +84,11 @@ public class StringLib {
return val.indexOf(Values.toString(ctx, term), start);
}
- @Native(thisArg = true) public static int lastIndexOf(Context ctx, Object thisArg, Object term, int pos) throws InterruptedException {
+ @Native(thisArg = true) public static int lastIndexOf(Context ctx, Object thisArg, Object term, int pos) {
var val = passThis(ctx, "lastIndexOf", thisArg);
if (term != null && term != Values.NULL && !(term instanceof String)) {
- var search = Values.getMember(ctx, term, ctx.env.symbol("Symbol.search"));
+ var search = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.search"));
if (search instanceof FunctionValue) {
return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, true, pos));
}
@@ -97,15 +97,15 @@ public class StringLib {
return val.lastIndexOf(Values.toString(ctx, term), pos);
}
- @Native(thisArg = true) public static boolean includes(Context ctx, Object thisArg, Object term, int pos) throws InterruptedException {
+ @Native(thisArg = true) public static boolean includes(Context ctx, Object thisArg, Object term, int pos) {
return lastIndexOf(ctx, passThis(ctx, "includes", thisArg), term, pos) >= 0;
}
- @Native(thisArg = true) public static String replace(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
+ @Native(thisArg = true) public static String replace(Context ctx, Object thisArg, Object term, String replacement) {
var val = passThis(ctx, "replace", thisArg);
if (term != null && term != Values.NULL && !(term instanceof String)) {
- var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace"));
+ var replace = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.replace"));
if (replace instanceof FunctionValue) {
return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement));
}
@@ -113,11 +113,11 @@ public class StringLib {
return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), replacement);
}
- @Native(thisArg = true) public static String replaceAll(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
+ @Native(thisArg = true) public static String replaceAll(Context ctx, Object thisArg, Object term, String replacement) {
var val = passThis(ctx, "replaceAll", thisArg);
if (term != null && term != Values.NULL && !(term instanceof String)) {
- var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace"));
+ var replace = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.replace"));
if (replace instanceof FunctionValue) {
return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement));
}
@@ -126,17 +126,17 @@ public class StringLib {
return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), replacement);
}
- @Native(thisArg = true) public static ArrayValue match(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue match(Context ctx, Object thisArg, Object term, String replacement) {
var val = passThis(ctx, "match", thisArg);
FunctionValue match;
try {
- var _match = Values.getMember(ctx, term, ctx.env.symbol("Symbol.match"));
+ var _match = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.match"));
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
- else if (ctx.env.regexConstructor != null) {
- var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), "");
- _match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.match"));
+ else if (ctx.environment().regexConstructor != null) {
+ var regex = Values.callNew(ctx, ctx.environment().regexConstructor, Values.toString(ctx, term), "");
+ _match = Values.getMember(ctx, regex, ctx.environment().symbol("Symbol.match"));
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
else throw EngineException.ofError("Regular expressions don't support matching.");
}
@@ -148,20 +148,20 @@ public class StringLib {
if (res instanceof ArrayValue) return (ArrayValue)res;
else return new ArrayValue(ctx, "");
}
- @Native(thisArg = true) public static Object matchAll(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
+ @Native(thisArg = true) public static Object matchAll(Context ctx, Object thisArg, Object term, String replacement) {
var val = passThis(ctx, "matchAll", thisArg);
FunctionValue match = null;
try {
- var _match = Values.getMember(ctx, term, ctx.env.symbol("Symbol.matchAll"));
+ var _match = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.matchAll"));
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
}
catch (IllegalArgumentException e) { }
- if (match == null && ctx.env.regexConstructor != null) {
- var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), "g");
- var _match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.matchAll"));
+ if (match == null && ctx.environment().regexConstructor != null) {
+ var regex = Values.callNew(ctx, ctx.environment().regexConstructor, Values.toString(ctx, term), "g");
+ var _match = Values.getMember(ctx, regex, ctx.environment().symbol("Symbol.matchAll"));
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
else throw EngineException.ofError("Regular expressions don't support matching.");
}
@@ -170,13 +170,13 @@ public class StringLib {
return match.call(ctx, term, val);
}
- @Native(thisArg = true) public static ArrayValue split(Context ctx, Object thisArg, Object term, Object lim, boolean sensible) throws InterruptedException {
+ @Native(thisArg = true) public static ArrayValue split(Context ctx, Object thisArg, Object term, Object lim, boolean sensible) {
var val = passThis(ctx, "split", thisArg);
if (lim != null) lim = Values.toNumber(ctx, lim);
if (term != null && term != Values.NULL && !(term instanceof String)) {
- var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace"));
+ var replace = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.replace"));
if (replace instanceof FunctionValue) {
var tmp = ((FunctionValue)replace).call(ctx, term, val, lim, sensible);
@@ -217,30 +217,30 @@ public class StringLib {
return res;
}
- @Native(thisArg = true) public static String slice(Context ctx, Object thisArg, int start, Object _end) throws InterruptedException {
+ @Native(thisArg = true) public static String slice(Context ctx, Object thisArg, int start, Object _end) {
return substring(ctx, passThis(ctx, "slice", thisArg), start, _end);
}
- @Native(thisArg = true) public static String concat(Context ctx, Object thisArg, Object... args) throws InterruptedException {
+ @Native(thisArg = true) public static String concat(Context ctx, Object thisArg, Object... args) {
var res = new StringBuilder(passThis(ctx, "concat", thisArg));
for (var el : args) res.append(Values.toString(ctx, el));
return res.toString();
}
- @Native(thisArg = true) public static String trim(Context ctx, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static String trim(Context ctx, Object thisArg) {
return passThis(ctx, "trim", thisArg).trim();
}
- @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) throws InterruptedException {
+ @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) {
val = Values.toString(ctx, val);
if (thisArg instanceof ObjectValue) return new StringLib((String)val);
else return val;
}
- @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) {
return passThis(ctx, "toString", thisArg);
}
- @Native(thisArg = true) public static String valueOf(Context ctx, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static String valueOf(Context ctx, Object thisArg) {
return passThis(ctx, "valueOf", thisArg);
}
diff --git a/src/me/topchetoeu/jscript/lib/SymbolLib.java b/src/me/topchetoeu/jscript/lib/SymbolLib.java
index 8edb69f..e7be32a 100644
--- a/src/me/topchetoeu/jscript/lib/SymbolLib.java
+++ b/src/me/topchetoeu/jscript/lib/SymbolLib.java
@@ -18,32 +18,33 @@ import me.topchetoeu.jscript.interop.NativeInit;
public class SymbolLib {
private static final Map symbols = new HashMap<>();
- @NativeGetter public static Symbol typeName(Context ctx) { return ctx.env.symbol("Symbol.typeName"); }
- @NativeGetter public static Symbol replace(Context ctx) { return ctx.env.symbol("Symbol.replace"); }
- @NativeGetter public static Symbol match(Context ctx) { return ctx.env.symbol("Symbol.match"); }
- @NativeGetter public static Symbol matchAll(Context ctx) { return ctx.env.symbol("Symbol.matchAll"); }
- @NativeGetter public static Symbol split(Context ctx) { return ctx.env.symbol("Symbol.split"); }
- @NativeGetter public static Symbol search(Context ctx) { return ctx.env.symbol("Symbol.search"); }
- @NativeGetter public static Symbol iterator(Context ctx) { return ctx.env.symbol("Symbol.iterator"); }
- @NativeGetter public static Symbol asyncIterator(Context ctx) { return ctx.env.symbol("Symbol.asyncIterator"); }
+ @NativeGetter public static Symbol typeName(Context ctx) { return ctx.environment().symbol("Symbol.typeName"); }
+ @NativeGetter public static Symbol replace(Context ctx) { return ctx.environment().symbol("Symbol.replace"); }
+ @NativeGetter public static Symbol match(Context ctx) { return ctx.environment().symbol("Symbol.match"); }
+ @NativeGetter public static Symbol matchAll(Context ctx) { return ctx.environment().symbol("Symbol.matchAll"); }
+ @NativeGetter public static Symbol split(Context ctx) { return ctx.environment().symbol("Symbol.split"); }
+ @NativeGetter public static Symbol search(Context ctx) { return ctx.environment().symbol("Symbol.search"); }
+ @NativeGetter public static Symbol iterator(Context ctx) { return ctx.environment().symbol("Symbol.iterator"); }
+ @NativeGetter public static Symbol asyncIterator(Context ctx) { return ctx.environment().symbol("Symbol.asyncIterator"); }
+ @NativeGetter public static Symbol cause(Context ctx) { return ctx.environment().symbol("Symbol.cause"); }
public final Symbol value;
- private static Symbol passThis(Context ctx, String funcName, Object val) throws InterruptedException {
+ private static Symbol passThis(Context ctx, String funcName, Object val) {
if (val instanceof SymbolLib) return ((SymbolLib)val).value;
else if (val instanceof Symbol) return (Symbol)val;
else throw EngineException.ofType(String.format("'%s' may only be called upon object and primitve symbols.", funcName));
}
- @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) throws InterruptedException {
+ @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) {
if (thisArg instanceof ObjectValue) throw EngineException.ofType("Symbol constructor may not be called with new.");
if (val == null) return new Symbol("");
else return new Symbol(Values.toString(ctx, val));
}
- @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) {
return passThis(ctx, "toString", thisArg).value;
}
- @Native(thisArg = true) public static Symbol valueOf(Context ctx, Object thisArg) throws InterruptedException {
+ @Native(thisArg = true) public static Symbol valueOf(Context ctx, Object thisArg) {
return passThis(ctx, "valueOf", thisArg);
}
diff --git a/src/me/topchetoeu/jscript/lib/SyntaxErrorLib.java b/src/me/topchetoeu/jscript/lib/SyntaxErrorLib.java
index 70d61fa..c3b2de5 100644
--- a/src/me/topchetoeu/jscript/lib/SyntaxErrorLib.java
+++ b/src/me/topchetoeu/jscript/lib/SyntaxErrorLib.java
@@ -8,7 +8,7 @@ import me.topchetoeu.jscript.interop.NativeConstructor;
import me.topchetoeu.jscript.interop.NativeInit;
public class SyntaxErrorLib extends ErrorLib {
- @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) throws InterruptedException {
+ @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) {
var target = ErrorLib.constructor(ctx, thisArg, message);
target.defineProperty(ctx, "name", "SyntaxError");
return target;
diff --git a/src/me/topchetoeu/jscript/lib/TypeErrorLib.java b/src/me/topchetoeu/jscript/lib/TypeErrorLib.java
index 9df269c..b15a5b3 100644
--- a/src/me/topchetoeu/jscript/lib/TypeErrorLib.java
+++ b/src/me/topchetoeu/jscript/lib/TypeErrorLib.java
@@ -8,7 +8,7 @@ import me.topchetoeu.jscript.interop.NativeConstructor;
import me.topchetoeu.jscript.interop.NativeInit;
public class TypeErrorLib extends ErrorLib {
- @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) throws InterruptedException {
+ @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) {
var target = ErrorLib.constructor(ctx, thisArg, message);
target.defineProperty(ctx, "name", "TypeError");
return target;
diff --git a/src/me/topchetoeu/jscript/parsing/Parsing.java b/src/me/topchetoeu/jscript/parsing/Parsing.java
index aa74e01..5411fe4 100644
--- a/src/me/topchetoeu/jscript/parsing/Parsing.java
+++ b/src/me/topchetoeu/jscript/parsing/Parsing.java
@@ -6,7 +6,9 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.TreeSet;
+import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.*;
import me.topchetoeu.jscript.compilation.Instruction.Type;
@@ -25,7 +27,7 @@ import me.topchetoeu.jscript.parsing.ParseRes.State;
// TODO: this has to be rewritten
public class Parsing {
public static interface Parser {
- ParseRes parse(String filename, List tokens, int i);
+ ParseRes parse(Filename filename, List tokens, int i);
}
private static class ObjProp {
@@ -69,7 +71,7 @@ public class Parsing {
reserved.add("delete");
reserved.add("break");
reserved.add("continue");
- reserved.add("debug");
+ reserved.add("debugger");
reserved.add("implements");
reserved.add("interface");
reserved.add("package");
@@ -122,7 +124,7 @@ public class Parsing {
private static final int CURR_MULTI_COMMENT = 8;
private static final int CURR_SINGLE_COMMENT = 9;
- private static void addToken(StringBuilder currToken, int currStage, int line, int lastStart, String filename, List tokens) {
+ private static void addToken(StringBuilder currToken, int currStage, int line, int lastStart, Filename filename, List tokens) {
var res = currToken.toString();
switch (currStage) {
@@ -143,7 +145,7 @@ public class Parsing {
// This method is so long because we're tokenizing the string using an iterative approach
// instead of a recursive descent parser. This is mainly done for performance reasons.
- private static ArrayList splitTokens(String filename, String raw) {
+ private static ArrayList splitTokens(Filename filename, String raw) {
var tokens = new ArrayList();
var currToken = new StringBuilder(64);
@@ -572,7 +574,7 @@ public class Parsing {
else return res;
}
- private static List parseTokens(String filename, Collection tokens) {
+ private static List parseTokens(Filename filename, Collection tokens) {
var res = new ArrayList();
for (var el : tokens) {
@@ -593,11 +595,11 @@ public class Parsing {
return res;
}
- public static List tokenize(String filename, String raw) {
+ public static List tokenize(Filename filename, String raw) {
return parseTokens(filename, splitTokens(filename, raw));
}
- public static Location getLoc(String filename, List tokens, int i) {
+ public static Location getLoc(Filename filename, List tokens, int i) {
if (tokens.size() == 0 || tokens.size() == 0) return new Location(1, 1, filename);
if (i >= tokens.size()) i = tokens.size() - 1;
return new Location(tokens.get(i).line, tokens.get(i).start, filename);
@@ -663,7 +665,7 @@ public class Parsing {
return !reserved.contains(name);
}
- public static ParseRes parseString(String filename, List tokens, int i) {
+ public static ParseRes parseString(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
try {
if (tokens.get(i).isString()) {
@@ -675,7 +677,7 @@ public class Parsing {
return ParseRes.failed();
}
}
- public static ParseRes parseNumber(String filename, List tokens, int i) {
+ public static ParseRes parseNumber(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
try {
if (tokens.get(i).isNumber()) {
@@ -688,7 +690,7 @@ public class Parsing {
}
}
- public static ParseRes parseRegex(String filename, List tokens, int i) {
+ public static ParseRes parseRegex(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
try {
if (tokens.get(i).isRegex()) {
@@ -705,7 +707,7 @@ public class Parsing {
}
}
- public static ParseRes parseArray(String filename, List tokens, int i) {
+ public static ParseRes parseArray(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
int n = 0;
if (!isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
@@ -744,7 +746,7 @@ public class Parsing {
return ParseRes.res(new ArrayStatement(loc, values.toArray(Statement[]::new)), n);
}
- public static ParseRes> parseParamList(String filename, List tokens, int i) {
+ public static ParseRes> parseParamList(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
int n = 0;
@@ -776,7 +778,7 @@ public class Parsing {
return ParseRes.res(args, n);
}
- public static ParseRes extends Object> parsePropName(String filename, List tokens, int i) {
+ public static ParseRes extends Object> parsePropName(Filename filename, List tokens, int i) {
var idRes = parseIdentifier(tokens, i);
if (idRes.isSuccess()) return ParseRes.res(idRes.result, 1);
var strRes = parseString(null, tokens, i);
@@ -786,7 +788,7 @@ public class Parsing {
return ParseRes.failed();
}
- public static ParseRes parseObjectProp(String filename, List tokens, int i) {
+ public static ParseRes parseObjectProp(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
int n = 0;
@@ -813,7 +815,7 @@ public class Parsing {
new FunctionStatement(loc, access + " " + name.toString(), argsRes.result.toArray(String[]::new), res.result)
), n);
}
- public static ParseRes parseObject(String filename, List tokens, int i) {
+ public static ParseRes parseObject(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
int n = 0;
if (!isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
@@ -870,7 +872,7 @@ public class Parsing {
return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n);
}
- public static ParseRes parseNew(String filename, List tokens, int i) {
+ public static ParseRes parseNew(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
var n = 0;
if (!isIdentifier(tokens, i + n++, "new")) return ParseRes.failed();
@@ -886,7 +888,7 @@ public class Parsing {
return ParseRes.res(new NewStatement(loc, call.func, call.args), n);
}
- public static ParseRes parseTypeof(String filename, List tokens, int i) {
+ public static ParseRes parseTypeof(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
var n = 0;
if (!isIdentifier(tokens, i + n++, "typeof")) return ParseRes.failed();
@@ -897,7 +899,7 @@ public class Parsing {
return ParseRes.res(new TypeofStatement(loc, valRes.result), n);
}
- public static ParseRes parseVoid(String filename, List tokens, int i) {
+ public static ParseRes parseVoid(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
var n = 0;
if (!isIdentifier(tokens, i + n++, "void")) return ParseRes.failed();
@@ -908,7 +910,7 @@ public class Parsing {
return ParseRes.res(new VoidStatement(loc, valRes.result), n);
}
- public static ParseRes extends Statement> parseDelete(String filename, List tokens, int i) {
+ public static ParseRes extends Statement> parseDelete(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
int n = 0;
if (!isIdentifier(tokens, i + n++, "delete")) return ParseRes.failed();
@@ -929,7 +931,7 @@ public class Parsing {
}
}
- public static ParseRes parseFunction(String filename, List tokens, int i, boolean statement) {
+ public static ParseRes parseFunction(Filename filename, List tokens, int i, boolean statement) {
var loc = getLoc(filename, tokens, i);
int n = 0;
@@ -972,7 +974,7 @@ public class Parsing {
else return ParseRes.error(loc, "Expected a compound statement for function.", res);
}
- public static ParseRes parseUnary(String filename, List tokens, int i) {
+ public static ParseRes parseUnary(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
int n = 0;
@@ -993,7 +995,7 @@ public class Parsing {
if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n);
else return ParseRes.error(loc, String.format("Expected a value after the unary operator '%s'.", op.value), res);
}
- public static ParseRes parsePrefixChange(String filename, List tokens, int i) {
+ public static ParseRes parsePrefixChange(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
int n = 0;
@@ -1010,7 +1012,7 @@ public class Parsing {
if (!(res.result instanceof AssignableStatement)) return ParseRes.error(loc, "Expected assignable value after prefix operator.");
return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, change, false), n + res.n);
}
- public static ParseRes extends Statement> parseParens(String filename, List tokens, int i) {
+ public static ParseRes extends Statement> parseParens(Filename filename, List tokens, int i) {
int n = 0;
if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.failed();
@@ -1023,7 +1025,7 @@ public class Parsing {
return ParseRes.res(res.result, n);
}
@SuppressWarnings("all")
- public static ParseRes extends Statement> parseSimple(String filename, List tokens, int i, boolean statement) {
+ public static ParseRes extends Statement> parseSimple(Filename filename, List tokens, int i, boolean statement) {
var res = new ArrayList<>();
if (!statement) {
@@ -1050,7 +1052,7 @@ public class Parsing {
return ParseRes.any(res.toArray(ParseRes[]::new));
}
- public static ParseRes parseVariable(String filename, List tokens, int i) {
+ public static ParseRes parseVariable(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
var literal = parseIdentifier(tokens, i);
@@ -1066,7 +1068,7 @@ public class Parsing {
return ParseRes.res(new VariableStatement(loc, literal.result), 1);
}
- public static ParseRes extends Statement> parseLiteral(String filename, List tokens, int i) {
+ public static ParseRes extends Statement> parseLiteral(Filename filename, List tokens, int i) {
var loc = getLoc(filename, tokens, i);
var id = parseIdentifier(tokens, i);
if (!id.isSuccess()) return id.transform();
@@ -1094,7 +1096,7 @@ public class Parsing {
}
return ParseRes.failed();
}
- public static ParseRes parseMember(String filename, List tokens, int i, Statement prev, int precedence) {
+ public static ParseRes parseMember(Filename filename, List tokens, int i, Statement prev, int precedence) {
var loc = getLoc(filename, tokens, i);
var n = 0;
@@ -1107,7 +1109,7 @@ public class Parsing {
return ParseRes.res(new IndexStatement(loc, prev, new ConstantStatement(loc, literal.result)), n);
}
- public static ParseRes parseIndex(String filename, List tokens, int i, Statement prev, int precedence) {
+ public static ParseRes