Passed
Push — wip-public-release ( a47743...14cdbd )
by Bogdan
02:52
created

RequireCallback   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 11
dl 0
loc 167
ccs 0
cts 70
cp 0
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A callback() 0 21 4
A resolve() 0 12 4
A getMain() 0 4 1
A buildNativeModule() 0 7 1
B buildModule() 0 23 4
A handleNativeModule() 0 21 2
B handleSourceModule() 0 45 2
1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of the pinepain/js-sandbox PHP library.
5
 *
6
 * Copyright (c) 2016-2017 Bogdan Padalko <[email protected]>
7
 *
8
 * Licensed under the MIT license: http://opensource.org/licenses/MIT
9
 *
10
 * For the full copyright and license information, please view the
11
 * LICENSE file that was distributed with this source or visit
12
 * http://opensource.org/licenses/MIT
13
 */
14
15
16
namespace Pinepain\JsSandbox\Modules;
17
18
19
use League\Flysystem\Util;
20
use Pinepain\JsSandbox\Exceptions\NativeException;
21
use Pinepain\JsSandbox\Exceptions\SandboxException;
22
use Pinepain\JsSandbox\Modules\Repositories\NativeModulesRepositoryInterface;
23
use Pinepain\JsSandbox\Modules\Repositories\SourceModulesRepositoryInterface;
24
use Pinepain\JsSandbox\Wrappers\FunctionComponents\Runtime\ExecutionContextInterface;
25
use Throwable;
26
use V8\ObjectValue;
27
use function strlen;
28
29
30
class RequireCallback implements RequireCallbackInterface
31
{
32
    /**
33
     * @var ModulesCacheInterface
34
     */
35
    private $cache;
36
    /**
37
     * @var ModulesStackInterface
38
     */
39
    private $stack;
40
    /**
41
     * @var NativeModulesRepositoryInterface
42
     */
43
    private $native;
44
    /**
45
     * @var SourceModulesRepositoryInterface
46
     */
47
    private $source;
48
49
    public function __construct(ModulesCacheInterface $cache, ModulesStackInterface $stack, NativeModulesRepositoryInterface $native, SourceModulesRepositoryInterface $source)
50
    {
51
        $this->cache  = $cache;
52
        $this->stack  = $stack;
53
        $this->native = $native;
54
        $this->source = $source;
55
    }
56
57
    public function callback(ExecutionContextInterface $execution, string $id)
58
    {
59
        $id = $this->resolve($id);
60
61
        if ($this->cache->has($id)) {
62
            return $this->cache->get($id)->getExports();
63
        }
64
65
        // TODO: should we freeze export if it's an object?
66
        //https://codereview.chromium.org/1889903003/diff/1/include/v8.h
67
68
        if ($this->native->has($id)) {
69
            return $this->handleNativeModule($execution, $id, $this->native->get($id), $this->stack->top());
70
        }
71
72
        if ($this->source->has($id)) {
73
            return $this->handleSourceModule($execution, $id, $this->source->get($id), $this->stack->top());
74
        }
75
76
        throw new NativeException("Cannot find module '{$id}'");
77
    }
78
79
    public function resolve(string $id)
80
    {
81
        $id = trim($id);
82
83
        if (strlen($id) && ('/' != $id[0]) && $top = $this->stack->top()) {
84
            assert(null !== $top);
85
            // relative path, try to append directory from current top module, if available
86
            $id = $top->getDirname() . '/' . $id;
87
        }
88
89
        return Util::normalizePath($id);
90
    }
91
92
    public function getMain(): ?ModuleInterface
93
    {
94
        return $this->stack->bottom();
95
    }
96
97
    protected function buildNativeModule(string $filename, ?ModuleInterface $top): ModuleInterface
98
    {
99
        $id     = $filename;
100
        $module = new Module($id, $filename, Util::dirname($filename), $top);
101
102
        return $module;
103
    }
104
105
    protected function buildModule(string $filename, ?ModuleInterface $top): ModuleInterface
106
    {
107
        $id = $filename;
108
109
        if (null === $top) {
110
            $id = '.';
111
        }
112
113
        $module = new Module($id, $filename, Util::dirname($filename), $top);
114
115
        if ($top) {
116
            if ($top->isLoaded()) {
117
                // UNLIKELY
118
                // this should never happens, but just in case
119
                throw new SandboxException('Modifying loaded module is not allowed');
120
            }
121
122
            // add current module as top one child
123
            $top->addChild($module);
124
        }
125
126
        return $module;
127
    }
128
129
    protected function handleNativeModule(ExecutionContextInterface $execution, string $id, NativeModulePrototypeInterface $prototype, ?ModuleInterface $top)
130
    {
131
        $context = $execution->getContext();
132
133
        $module = $this->buildNativeModule($id, $top);
134
135
        $exports = new ObjectValue($context);
136
        $module->setExports($exports);
137
138
        $this->cache->put($id, $module);
139
140
        try {
141
            $prototype->compile($execution->getIsolate(), $context, $module);
142
            $module->setLoaded();
143
        } catch (Throwable $e) {
144
            $this->cache->remove($id);
145
            throw $e;
146
        }
147
148
        return $module->getExports();
149
    }
150
151
    protected function handleSourceModule(ExecutionContextInterface $execution, string $id, SourceModuleBuilderInterface $builder, ?ModuleInterface $top)
152
    {
153
        $module = $this->buildModule($id, $top);
154
155
        $this->stack->push($module); // put current module at the top
156
157
        $context = $execution->getContext();
158
159
        $exports = new ObjectValue($context);
160
        $module->setExports($exports);
161
162
        $this->cache->put($id, $module);
163
164
        try {
165
            $function = $builder->build($execution->getIsolate(), $context, $module);
166
167
            // Alternatively:
168
            //$native = new NativeFunctionWrapper($context, $exports, $function, $execution->getWrapper());
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
169
            //$native->call($exports, $execution->getFunctionObject(), $module, $module->getFilename(), $module->getDirname())
0 ignored issues
show
Unused Code Comprehensibility introduced by
74% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
170
171
            // Even shorter alternative
172
            //$native = $execution->wrapNativeFunction($exports, $function);
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
173
            //$native->call($exports, $execution->getFunctionObject(), $module, $module->getFilename(), $module->getDirname())
0 ignored issues
show
Unused Code Comprehensibility introduced by
74% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
174
175
            // NOTE: we need to wrap all this values as we will manually call V8 FunctionObject
176
            $require    = $execution->getFunctionObject();
177
            $js_module  = $execution->wrap($module);
178
            $__filename = $execution->wrap($module->getFilename());
179
            $__dirname  = $execution->wrap($module->getDirname());
180
181
            // exports, require, module, __filename, __dirname
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
182
            $args = [$exports, $require, $js_module, $__filename, $__dirname];
183
184
            $function->call($context, $exports, $args);
185
            //$function->Call($context, $execution->getThis(), $args);
0 ignored issues
show
Unused Code Comprehensibility introduced by
77% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
186
            $module->setLoaded();
187
        } catch (Throwable $e) {
188
            $this->cache->remove($id);
189
            throw $e;
190
        } finally {
191
            $this->stack->pop(); // remove current module from the top
192
        }
193
194
        return $module->getExports();
195
    }
196
}
197