Skip to content

Commit

Permalink
Use snapshot() opcode rather than call chaining, impl. basic destruct…
Browse files Browse the repository at this point in the history
…or support and strict types mode
  • Loading branch information
asmblah committed Mar 18, 2024
1 parent af0d3cc commit dedaf5f
Show file tree
Hide file tree
Showing 137 changed files with 5,093 additions and 3,465 deletions.
2,717 changes: 126 additions & 2,591 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"test": "npm run jshint && npm run mocha"
},
"dependencies": {
"core-js-pure": "^3.30.1",
"core-js-pure": "^3.36.0",
"es6-weak-map": "^2.0.3",
"is-promise": "^4.0.0",
"lie": "^3.3.0",
Expand All @@ -35,13 +35,13 @@
"chai": "^4.3.7",
"chai-as-promised": "^7.1.1",
"jshint": "^2.13.6",
"mocha": "^10.2.0",
"mocha-bootstrap": "^1.0.4",
"mocha": "^10.3.0",
"mocha-bootstrap": "^1.0.6",
"nowdoc": "^1.0.1",
"phptest": "^0.0.10",
"phptoast": "^9.1.1",
"phptojs": "^10.0.2",
"sinon": "^15.0.4",
"phptoast": "^9.2.0",
"phptojs": "^10.1.0",
"sinon": "^17.0.1",
"sinon-chai": "^3.7.0"
},
"engines": {
Expand Down
16 changes: 16 additions & 0 deletions src/Call.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ function Call(
}

_.extend(Call.prototype, {
/**
* Enables strict-types mode for the module this call was performed in.
*/
enableStrictTypes: function () {
this.originalNamespaceScope.enableStrictTypes();
},

/**
* Enters an isolated call within this outer one, making the given NamespaceScope
* and CallInstrumentation the current ones.
Expand Down Expand Up @@ -315,6 +322,15 @@ _.extend(Call.prototype, {
this.finder = finder;
},

/**
* Determines whether the module this call was performed in is in strict-types mode.
*
* @returns {boolean}
*/
isStrictTypesMode: function () {
return this.originalNamespaceScope.isStrictTypesMode();
},

/**
* Determines whether this call is to a userland function (defined inside PHP-land) or not.
*
Expand Down
32 changes: 32 additions & 0 deletions src/CallStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ _.extend(CallStack.prototype, {
this.calls.length = 0;
},

/**
* Enables strict-types mode for the module of the current call.
*/
enableStrictTypes: function () {
// Note that when enabling, we enable strict-types mode for the callee's module and not the caller's,
// unlike when checking, where we check the caller instead.
var call = this.getUserlandCallee();

if (!call) {
throw new Exception('CallStack.enableStrictTypes() :: No userland callee');
}

call.enableStrictTypes();
},

/**
* Fetches the previous Call near the top of the stack, or null if none
*
Expand Down Expand Up @@ -448,6 +463,23 @@ _.extend(CallStack.prototype, {
this.getCurrent().instrument(finder);
},

/**
* Determines whether the module of the caller is in strict-types mode.
*
* @returns {boolean}
*/
isStrictTypesMode: function () {
// Note use of .getUserlandCallee() and not .getUserlandCaller()
// because at the point this is checked, the callee has not been pushed onto the stack.
var call = this.getUserlandCallee();

if (!call) {
throw new Exception('CallStack.isStrictTypesMode() :: No userland callee');
}

return call.isStrictTypesMode();
},

/**
* Determines whether or not the current stack frame is a userland PHP function.
*
Expand Down
21 changes: 20 additions & 1 deletion src/Class.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ module.exports = require('pauser')([
* @param {Userland} userland
* @param {string} name Fully-qualified class name (FQCN)
* @param {string|null} constructorName
* @param {boolean} hasDestructor
* @param {Function} InternalClass
* @param {Object} rootInternalPrototype
* @param {Object} instancePropertiesData
Expand All @@ -87,6 +88,7 @@ module.exports = require('pauser')([
* @param {FFIFactory} ffiFactory
* @param {Function|null} methodCaller Custom method call handler
* @param {CallInstrumentation} instrumentation
* @param {DestructibleObjectRepository} destructibleObjectRepository
* @constructor
*/
function Class(
Expand All @@ -100,6 +102,7 @@ module.exports = require('pauser')([
userland,
name,
constructorName,
hasDestructor,
InternalClass,
rootInternalPrototype,
instancePropertiesData,
Expand All @@ -112,7 +115,8 @@ module.exports = require('pauser')([
valueCoercer,
ffiFactory,
methodCaller,
instrumentation
instrumentation,
destructibleObjectRepository
) {
var classObject = this,
staticProperties = {};
Expand Down Expand Up @@ -257,6 +261,17 @@ module.exports = require('pauser')([
data[VISIBILITY]
);
});

/**
* @type {Function}
*/
this.internalInstantiator = hasDestructor ?
function (objectValue) {
destructibleObjectRepository.registerValue(objectValue);
} :
function () {
// No destructor defined, nothing special to do.
};
}

_.extend(Class.prototype, {
Expand Down Expand Up @@ -1029,6 +1044,10 @@ module.exports = require('pauser')([
_.forOwn(classObject.instancePropertyDefaults, function (propertyValue, name) {
properties[name].initialise(propertyValue);
});

// Use the optimised instantiator created inside this internal Class' constructor
// to handle destructors, if applicable.
classObject.internalInstantiator(objectValue);
},

/**
Expand Down
14 changes: 12 additions & 2 deletions src/Class/ClassFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var _ = require('microdash'),
* @param {Userland} userland
* @param {ExportRepository} exportRepository
* @param {FFIFactory} ffiFactory
* @param {DestructibleObjectRepository} destructibleObjectRepository
* @constructor
*/
function ClassFactory(
Expand All @@ -35,12 +36,17 @@ function ClassFactory(
futureFactory,
userland,
exportRepository,
ffiFactory
ffiFactory,
destructibleObjectRepository
) {
/**
* @type {CallStack}
*/
this.callStack = callStack;
/**
* @type {DestructibleObjectRepository}
*/
this.destructibleObjectRepository = destructibleObjectRepository;
/**
* @type {ExportRepository}
*/
Expand Down Expand Up @@ -87,6 +93,7 @@ _.extend(ClassFactory.prototype, {
* @param {Namespace} namespace
* @param {NamespaceScope} namespaceScope
* @param {string} constructorName
* @param {boolean} hasDestructor
* @param {Function} InternalClass
* @param {Object} rootInternalPrototype
* @param {Object} instanceProperties
Expand All @@ -104,6 +111,7 @@ _.extend(ClassFactory.prototype, {
namespace,
namespaceScope,
constructorName,
hasDestructor,
InternalClass,
rootInternalPrototype,
instanceProperties,
Expand All @@ -128,6 +136,7 @@ _.extend(ClassFactory.prototype, {
factory.userland,
name,
constructorName,
hasDestructor,
InternalClass,
rootInternalPrototype,
instanceProperties,
Expand All @@ -140,7 +149,8 @@ _.extend(ClassFactory.prototype, {
valueCoercer,
factory.ffiFactory,
methodCaller,
instrumentation
instrumentation,
factory.destructibleObjectRepository
);
}
});
Expand Down
1 change: 1 addition & 0 deletions src/Class/ClassPromoter.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ _.extend(ClassPromoter.prototype, {
classDefinition.getNamespace(),
namespaceScope,
classDefinition.getConstructorName(),
classDefinition.hasDestructor(),
InternalClass,
classDefinition.getRootInternalPrototype(),
classDefinition.getInstanceProperties(),
Expand Down
13 changes: 12 additions & 1 deletion src/Class/Definition/ClassDefinition.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

'use strict';

var _ = require('microdash');
var _ = require('microdash'),
hasOwn = {}.hasOwnProperty,
MAGIC_DESTRUCT = '__destruct';

/**
* @param {string} name
Expand Down Expand Up @@ -262,6 +264,15 @@ _.extend(ClassDefinition.prototype, {
*/
getValueCoercer: function () {
return this.valueCoercer;
},

/**
* Determines whether this class defines a destructor.
*
* @returns {boolean}
*/
hasDestructor: function () {
return hasOwn.call(this.methods, MAGIC_DESTRUCT);
}
});

Expand Down
7 changes: 5 additions & 2 deletions src/Control/ControlScope.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,15 @@ _.extend(ControlScope.prototype, {
/**
* Enters a new coroutine, or continues the current one if we are nesting.
*
* @param {{keepStack: boolean}} options
* @returns {Coroutine}
*/
enterCoroutine: function () {
enterCoroutine: function (options) {
var scope = this,
newCoroutine;

options = options || {};

if (scope.nestNextCoroutine) {
scope.nestNextCoroutine = false;

Expand All @@ -58,7 +61,7 @@ _.extend(ControlScope.prototype, {
newCoroutine = scope.coroutineFactory.createCoroutine();

if (scope.currentCoroutine !== null) {
scope.currentCoroutine.suspend();
scope.currentCoroutine.suspend(Boolean(options.keepStack));
}

scope.currentCoroutine = newCoroutine;
Expand Down
8 changes: 6 additions & 2 deletions src/Control/Coroutine.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ _.extend(Coroutine.prototype, {

/**
* Suspends this coroutine, for later resumption.
*
* @param {boolean} keepStack
*/
suspend: function () {
suspend: function (keepStack) {
var coroutine = this;

if (coroutine.suspended) {
Expand All @@ -69,7 +71,9 @@ _.extend(Coroutine.prototype, {
coroutine.savedCallStack = coroutine.callStack.save();

// Clear the call stack at this point, unlike .save().
coroutine.callStack.clear();
if (!keepStack) {
coroutine.callStack.clear();
}

coroutine.suspended = true;
}
Expand Down
47 changes: 46 additions & 1 deletion src/Control/Flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ function Flow(
* @type {string}
*/
this.mode = mode;
/**
* A pre-allocated Present that can be returned when a ChainableInterface is needed.
*
* @type {Present}
*/
this.noopPresent = futureFactory.createPresent(null);
}

_.extend(Flow.prototype, {
Expand Down Expand Up @@ -329,6 +335,15 @@ _.extend(Flow.prototype, {
});
},

/**
* Returns a pre-allocated Present that can be returned when a ChainableInterface is needed.
*
* @returns {Present}
*/
getNoopPresent: function () {
return this.noopPresent;
},

/**
* Maps the given array of inputs to a Future via the given handler, settling any intermediate Futures.
*
Expand Down Expand Up @@ -443,7 +458,37 @@ _.extend(Flow.prototype, {
}

if (pauser) {
pauser(error);
pauser(
error, // TODO: Look into not passing error(pause) here now we have onResume(...)?
function onResume(reenter) {
error.next(
function (/* result */) {
/*
* Note that the result passed here for the opcode we are about to resume
* by re-calling the userland function has already been provided (see Pause),
* so the result argument passed to this callback may be ignored.
*
* If the pause resulted in an error, then we also want to re-call
* the function in order to resume with a throwInto at the correct opcode
* (see catch handler below).
*/
return reenter();
},
function (/* error */) {
/*
* Note that the error passed here for the opcode we are about to throwInto
* by re-calling the userland function has already been provided (see Pause),
* so the error argument passed to this callback may be ignored.
*
* Similar to the above, we want to re-call the function in order to resume
* with a throwInto at the correct opcode.
*/

return reenter();
}
);
}
);
}

// We have intercepted a pause - it must be marked as complete so that the future
Expand Down
5 changes: 4 additions & 1 deletion src/Control/Future.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,13 @@ var _ = require('microdash'),
},
nestCoroutine = function () {
future.controlScope.nestCoroutine();
},
newCoroutine = function (options) {
future.controlScope.enterCoroutine(options);
};

try {
executor(resolve, reject, nestCoroutine);
executor(resolve, reject, nestCoroutine, newCoroutine);
} catch (error) {
if (error instanceof Pause) {
throw new Exception('Unexpected Pause raised by Future executor');
Expand Down
Loading

0 comments on commit dedaf5f

Please sign in to comment.