So I got a dreaded Illegal Invocation error in Chrome. I'm using Web Audio API (HTML5 is awesome btw), to build a framework of audio effects and signal routing. Basically it automatically routes the nodes based on where in the array they are. But I included my own class/objects for complex effects, or modules. These have multiple nodes within them and can delogate what goes where as if it was a single input/output node.
Anyways, when the object is created it throws the error right at the point of node creation (the private one within the module). I've read about this happening with console a lot, or loss of inherent "this". But I don't know why its happening here since the variable to grab the node constructor is a global variable. Any ideas?
var context, nodes;
var Delay = function(_context, _time, _feedback, _wet) {
this.type = "Delay";
this.delay = new context.createDelayNode(); //Console points error here.
this.feedback = new context.createGainNode();
this.crossfade = new context.createGain();
}
function GotStream(stream) {
context = new AudioContext();
nodes = [ new Delay(1,1,1) ]; //Start point of error
...
}
Obviously its stripped a bit, but that is the exact nesting of functions/variables. I should note I've tried adding .call(window), and .bind(window) to the node constructor with no luck
Related
I have this class Game.
class Game {
constructor(player) {
this.player1 = player;
}
getHost() {
// player has property nickname
return `Host: ${this.player1.nickname}`;
}
}
I believe the issue I am having is that getHost() is becoming unbound from it's context, however I'm not sure how to fix it. I'm learning JS and I'm pretty confused about this language's usage of this. I'm also using socket.io for the first time, so this adds a lot to the confusion for me.
My server script (the server works, just showing relevant parts):
io.on('connection', (socket) => {
// passed defined player
socket.on('host game', (player) => {
const game = new Game(player);
console.log(game.getHost()); // still works here
io.sockets.emit("game created", game);
});
});
Client:
socket.on("game created", (game) => {
const gameElement = document.createElement("li");
gameElement.innerHTML = game.getHost(); // ERROR
games.appendChild(gameElement);
});
On the above marked line, I get the Uncaught TypeError: game.getHost is not a function.
Sorry if the socket.io functions make it more confusing, basically just when the game is passed from server to client, it doesn't work anymore.
Thanks for the help
The issue is that socketio is serializing the object for you. Since a websocket is still just HTTP, you can't send an actual JS object over it. socketio handles serializing your object to JSON using JSON.stringify(game) behind the scenes. It will also convert that JSON string back into an object for you on the client side too.
Unfortunately when you do this you lose methods. For example:
let game = new Game({nickname: 'Bob Vance'});
game = JSON.stringify(game);
console.log(game) // will output {"player1":{"nickname":"Bob Vance"}}
You should see {"player1":{"nickname":"Bob Vance"}} in the console because serializing to JSON drops the methods but preserves properties. This is just normal JavaScript behaviour.
So what you have to do to get the methods back is create a new instance of a game on the client-side, and then use Object.assign() to build the object back up.
Example
let serializedGame= JSON.parse(game);
let newGameInstance = new Game({nickname: game.nickname});
Object.assign(newGameInstance, serializedGame);
Now you should be ok to call newGameInstance.getHost()
Maybe it is stupid question (I am newbie to C++, just wanted to use it as library for android), but I am not able to run evaluation of some JS multiple times.
I have started with "hello world" tutorial. But then I have wanted simple thing, re-run main (just wrap content of tutorial code into function and run it twice in newly empty main.
This is what I got:
#
# Fatal error in ../src/isolate.cc, line 1868
# Check failed: thread_data_table_.
#
==== C stack trace ===============================
1: 0xa890b9
2: 0x6a22fc
3: 0x42694f
4: 0x405f66
5: 0x405ec7
6: __libc_start_main
7: 0x405dc9
Illegal instruction (core dumped)
This cames after creating new isolate
Isolate* isolate = Isolate::New(create_params);
Well, what I should do? Am I using wrong construct or so? Should I close/delete/clear something more?
In bigger view I just want to do evaluate function, that can be triggered multiple times, and beside that also run multiple js snipets in same context (how to split this function?).
Any idea?
UPDATE:
Ok, lets say that the main can be split into three logical parts:
init
int main(int argc, char* argv[]) {
// Initialize V8.
V8::InitializeICU();
V8::InitializeExternalStartupData(argv[0]);
Platform* platform = platform::CreateDefaultPlatform();
V8::InitializePlatform(platform);
V8::Initialize();
// Create a new Isolate and make it the current one.
ArrayBufferAllocator allocator;
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = &allocator;
evaluation
Isolate* isolate = Isolate::New(create_params);
{
Isolate::Scope isolate_scope(isolate);
// Create a stack-allocated handle scope.
HandleScope handle_scope(isolate);
// Create a new context.
Local<Context> context = Context::New(isolate);
// Enter the context for compiling and running the hello world script.
Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
Local<String> source =
String::NewFromUtf8(isolate, "'Hello' + ', World!'",
NewStringType::kNormal).ToLocalChecked();
// Compile the source code.
Local<Script> script = Script::Compile(context, source).ToLocalChecked();
// Run the script to get the result.
Local<Value> result = script->Run(context).ToLocalChecked();
// Convert the result to an UTF8 string and print it.
String::Utf8Value utf8(result);
printf("%s\n", *utf8);
}
isolate->Dispose();
and clean
// Dispose and tear down V8.
V8::Dispose();
V8::ShutdownPlatform();
delete platform;
return 0;
Now as I said before if I run main consists of init->evaluation->clean twice, that mean init->evaluation->clean->init->evaluation->clean, then the error occurs. I have figured out, that if I extract evaluation part into separate function I can run it multiple times e.g. as init->(evaluation){2}->clean
Is that how should it work? Next step is to divide this main into tree separate function that mean I have to have static member with platform? Could it cause leak somehow?
NOTE: that I want to run it from android, that mean e.g. click in UI, propagate js source to C via JNI and then call c++ V8, which is already initialized or not. hm?
Prefered way is to have "blackbox", but if I have to hold platform, so be it. It maybe could be also faster without re-initialization of V8, right?
UPDATE 2:
Well, still have problems with splitting evaluation part to achieve multiple runs in same isolate/context.
I have splitted it after creating context with stored isolate and context, but with no luck. When in second part try to create source string it fails, probably because of using stored isolate (something with isolate scope I guess).
:(
My assumption as I introduced in UPDATE1 was correct. That part works well.
According to UPDATE2 I have splitted evaluation part into two.
First for initialize isolate and context:
mIsolate = Isolate::New(mCreate_params);
Isolate::Scope isolate_scope(mIsolate);
{
// Create a stack-allocated handle scope.
HandleScope handle_scope(mIsolate);
v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(mIsolate);
// Bind the global 'print' function to the C++ Print callback.
global->Set(v8::String::NewFromUtf8(mIsolate, "print"), v8::FunctionTemplate::New(mIsolate, Print));
// Create a new context.
mContext = Context::New(mIsolate, NULL, global);
Persistent<Context, CopyablePersistentTraits<Context>> persistent(mIsolate, mContext);
mContext_persistent = persistent;
}
and second that will run js in same context:
Isolate::Scope isolate_scope(mIsolate);
{
HandleScope handle_scope(mIsolate);
mContext = Local<Context>::New(mIsolate, mContext_persistent);
// Enter the context for compiling and running the hello world script.
Context::Scope context_scope(mContext);
{
// Create a string containing the JavaScript source code.
Local<String> source =
String::NewFromUtf8(mIsolate, js_source, NewStringType::kNormal).ToLocalChecked();
// Compile the source code.
Local<Script> script = Script::Compile(mContext, source).ToLocalChecked();
TryCatch trycatch(mIsolate);
// Run the script to get the result.
v8::Local<v8::Value> result;
if(!script->Run(mContext).ToLocal(&result)){
v8::String::Utf8Value exception_str(trycatch.Exception());
dprint(*exception_str);
}else{
if(!result->IsUndefined()){
String::Utf8Value utf8(result);
dprint(*utf8);
}
}
}
}
Well the code works very well on linux, but I still have some issues when I try to run first part for the second time (create new context) on android:
A/art: art/runtime/thread.cc:986] pthread_getschedparam failed for DumpState: No such process
A/art: art/runtime/base/mutex.cc:485] Unexpected state_ 0 in unlock for logging lock
But that's another question I guess. Peace.
Did you initialize v8 more than once?
v8::V8::Initialize() this method should be called once per process.
deep into project source file "v8/src/v8.cc", you will find the prove
bool V8::Initialize() {
InitializeOncePerProcess();
return true;
}
The doc for yeoman unit testing seems to be oriented around integration testing, namely running the entire generator and then examining the side effects produced i.e. for the existence of certain files. For this you can use helpers.run().
This is all fine and well, but I also want to be able to unit test a single method (or "priority") and test internal states of the generator i.e. internal vars. I have been able to do this before by using createGenerator like so:
subAngularGenerator = helpers.createGenerator('webvr-decorator:sub-angular', [
path.join(__dirname, '../generators/sub-angular')
],
null,
{'artifacts': artifacts, appName: APP_NAME, userNames: userNames,
});
This has no RunContext, but I can usually add enough things to the structure so that it will run. For instance:
// mixin common class
_.extend(subAngularGenerator.prototype, require('../lib/common.js'));
// we need to do this to properly feed in options and args
subAngularGenerator.initializing();
// override the artifacts hash
subAngularGenerator.artifacts = artifacts;
// call method
subAngularGenerator._injectDependencies(fp, 'controller', ['service1', 'service2']);
Which allows me to test internal state:
var fileContents = subAngularGenerator.fs.read(fp);
var regex = /\('MainCtrl', function \(\$scope, service1, service2\)/m;
assert(regex.test(fileContents));
This works fine as long as the method is basic javascript, like for/next loops and such. If the method make use of any 'this' variables, like this.async(), I get 'this.async' is not a function.
initialPrompt: function () {
var prompts = [];
var done = this.async(); //if this weren't needed my ut would work
...
I can manually add a dummy this.async, but then I go down the rabbit's hole with other errors, like 'no store available':
AssertionError: A store parameter is required
at Object.promptSuggestion.prefillQuestions (node_modules/yeoman-generator/lib/util/prompt-suggestion.js:98:3)
at RunContext.Base.prompt (node_modules/yeoman-generator/lib/base.js:218:32)
at RunContext.module.exports.AppBase.extend.prompting.initialPrompt (generators/app/index.js:147:12)
at Context.<anonymous> (test/test-app.js:158:42)
I tried to create a runContext and then add my generator to that:
var helpers = require('yeoman-generator').test;
// p.s. is there a better way to get RunContext?
var RunContext = require('../node_modules/yeoman-generator/lib/test/run-context');
before(function (done) {
appGenerator = helpers.createGenerator('webvr-decorator:app', [
path.join(__dirname, '../generators/app')
],
null,
appName: APP_NAME, userNames: userNames,
{});
app = new RunContext(appGenerator); //add generator to runContext
});
app.Generator.prompting.initialPrompt(); //gets async not defined
But this gets the same problem.
My theory is the problem has to with 'this' contexts. Normally the method runs with the 'this' context of the entire generator (which has a this.async etc), but when I run the method individually, the 'this' context is just that of the method/function itself (which has no async in its context). If this is true, then it's really more of a javascript question, and not a yeoman one.
It seems like there should be an easy way to unit test individual methods that depend on the generator context such as calls to this.async. I referred to generator-node as an example of best practices, but it only appears to be doing integration testing.
Does anyone have any better ideas, or do I need to just keep futzing around with JavaScript techniques?
Many Thanks.
I was able to get it to work, but it's a total hack. I was able to decorate a RunContext with the necessary artifacts, and then using apply, I put my generator in the context of the RunContext:
var appGenerator;
var app;
before(function (done) {
// create a generator
appGenerator = helpers.createGenerator('webvr-decorator:app', [
path.join(__dirname, '../generators/app')
],
null,
appName: APP_NAME, userNames: userNames,
{}
);
// get a RunContext
app = new RunContext(appGenerator);
// the following did *not* work -- prompts were not auto-answered
app.withPrompts({'continue': true, 'artifactsToRename': {'mainCtrl' : 'main'}});
//add the following functions and hashes from the generator to the RunContext
app.prompt = appGenerator.prompt;
app._globalConfig = appGenerator._globalConfig;
app.env = appGenerator.env;
// the following two lines are specific to my app only
app.globals = {};
app.globals.MAIN_CTRL = 'main';
done();
});
it('prompting works', function () {
// Run the generator in the context of RunContext by using js 'call'
appGenerator.prompting.initialPrompt.call(app);
}
I no longer get any 'missing functions' messages, but unfortunately the prompts are not being automatically provided by the unit test, so the method stops waiting for something to feed the prompts.
The big "secret" was to call with apply which you can use to override the default this context. I put the generator in the context of the RunContext, which verifies my theory that the problem is about being in the improper context.
I assume there's a much better way to do this and that I'm totally missing something. But I thought I'd at least document what I had to do to get it to work. In the end, I moved the variable initialization code from the 'prompting'method, into the 'initializing' method, and since my 'intializing' method has no Yeoman runtime dependencies, I was able to use a simple generator without a RunContext. But that was just fortuitous in this case. In the general case, I would still like to find out the proper way to invoke a single method.
Okay, not completely sure how to phrase this, but I'm gonna try my best.
I have been trying to use node's new vm module, and I wanted to enable Buffer support for the code running within the vm.
Here is the initial code I was using:
let vm = require('vm');
let someCode = '... some unsafe code ...';
let result = vm.runInNewContext(
someCode,
{ Buffer: Buffer }
);
However, as I quickly discovered, this will return the node process's Buffer class, so if someone were to modify Buffer.prototype within the vm, this will also change the Buffer outside the vm.
Therefore, after looking at the docs I tried changing the sandbox object to:
{ Buffer: require('buffer').Buffer }
After some checking, I discovered to my dismay that this doesn't work as expected either, since require somehow returns the same object:
let b = require('buffer').Buffer;
console.log(b === Buffer); // This becomes true
Is it possible to create a new Buffer class using the vm, or will I have to further safeguard by using child_process in some fancy way?
I currently have a Web Application that runs off a global Javascript-based API, and it is initialized like this:
var Api = {
someVar: "test",
someFunction: function() {
return "foo";
}
}
This API is shared across many "Widgets" that live in the Web Application, and they should all run off this single Api instance so they can pass data to each other.
AJAX is currently used to load these Widgets, for example in widgets/mywidget.html, and it's placed in, say, <div id='widget_<random number>'>...</div>
Certain other parts of the code may choose to add more functionality to Api, and it's currently done like this:
Api.myExtension = {
myNewFunction: function() {
return "bar";
}
}
However, some issues arise from this kind of usage:
Problem One: What if one Widget (these may be provided by third-parties) decides to hide some code within, and does something similar to Api = {}, destroying the global Api var everything lives on, and breaking the whole Application? Is it possible to protect this Api variable from being overwritten from outside? Only "extending" is allowed (adding new things), but "removing/changing" is not allowed. i.e.:
Api.foo = { test: "bar" } // allowed
Api.someVar = "changing the existing someVar"; // not allowed
The following code is located "inside" Api, for example:
var Api = {
Debug: {
Messages = new Array,
Write: function() {
Api.Debug.Messages.push("test"); // allowed
}
}
}
Api.Debug.Messages.push("test 2"); // not allowed
Probable Solutions I've Thought Of:
Suppose we simply use frames to resolve this issue. The Apis provided are now separate from each other. However, there's additional overhead when loading Api again and again if I have many Widgets running, and they can no longer communicate with the "Host" of the widgets (the page where frames reside in), for example, I may want to tell the host to show a notification: Api.Notify.Show("Test"), but it cannot do so because this Api is completely independent from other instances, and it cannot communicate with the "Host"
Using something like a "getter" and "setter" function for the Api to be read and written. I'm unsure on how to implement this, so any help on directions on how to implement this is welcome!
A mixture of 1/2?
There's no good way to prevent having a "third party" widget overwrite the a global variable. Generally it is the responsibility of whoever is putting together the final application to ensure that whatever JavaScripts they are using aren't littering the global namespace and conflicting. The best thing you can do in that direction is give your "Api" a nice, unique name.
What I think can help you a lot is something like the "revealing pattern", which would be a way of doing the "getters and setters" you mentioned, plus more if you needed it.
A simple, useless example would be like the following:
var Api = (function () {
// private variable
var myArray = [];
return {
addItem: function (newItem) {
myArray.push(newItem);
},
printItems: function () {
console.log("lots if items");
}
};
})();
Api.addItem("Hello, world");
Api.extensionValue = 5;
I think you should make a clear delineation of what is shared, or "singleton" data, and keep those items private, as with myArray in my example.
Make it a constant:
const Api = "hi";
Api = 0;
alert(Api); //"hi"
Take a look at
Object.freeze
More info here
Here is a code example from Mozilla's page:
var obj = {
prop: function (){},
foo: "bar"
};
// New properties may be added, existing properties may be changed or removed
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;
var o = Object.freeze(obj);
assert(Object.isFrozen(obj) === true);
// Now any changes will fail
obj.foo = "quux"; // silently does nothing
obj.quaxxor = "the friendly duck"; // silently doesn't add the property
// ...and in strict mode such attempts will throw TypeErrors
function fail(){
"use strict";
obj.foo = "sparky"; // throws a TypeError
delete obj.quaxxor; // throws a TypeError
obj.sparky = "arf"; // throws a TypeError
}
fail();
// Attempted changes through Object.defineProperty will also throw
Object.defineProperty(obj, "ohai", { value: 17 }); // throws a TypeError
Object.defineProperty(obj, "foo", { value: "eit" }); // throws a TypeError
However browser support is still partial
EDIT: see Kernel James's answer, it's more relevant to your question (freeze will protect the object, but not protect reassigning it. however const will) same issue with limited browser support though.
The only way (at least that I can think of) to protect your global variable is to prevent the Widgets from having a direct access to it. This can be achieved by using frames functions, as you suggested. You should create an object that contains all the functions that the Widgets should be able to use, and pass such to each Widget. For example:
var Api = {
widgetApi = {
someFunction: function(){
// ...
}
},
addWidget:function(){
var temp = this.widgetApi.constructor();
for(var key in this.widgetApi)
temp[key] = clone(this.widgetApi[key]);
return temp;
}
// Include other variables that Widgets can't use
}
This way, the Widgets could execute functions and communicate with the host or global variable Api. To set variables, the Widget would be editing its private object, rather than the global one. For every frame (that represents a Widget), you must initialize or create a copy of the widgetApi object, and probably store it inside an array, in such a way that an instance of a Widget is stored in the main Api object.
For example, given <iframe id="widget"></iframe>
You would do the following:
var widget = document.getElementById("widget");
widget.contentWindow.Api = Api.addWidget();
widget.contentWindow.parent = null;
widget.contentWindow.top = null;
Additionally, in every frame you would need to set the parent and top variables to null so that the Widgets wouldn't be able to access the data of the main frame. I haven't tested this method in a while, so there might be ways to get around setting those variables to null.